Changeset 3405807
- Timestamp:
- 11/29/2025 01:24:08 PM (3 months ago)
- Location:
- product-tabs-for-woo
- Files:
-
- 482 added
- 6 deleted
- 12 edited
- 1 copied
-
assets/banner-1544x500.jpg (modified) (1 prop) (previous)
-
assets/banner-772x250.jpg (modified) (1 prop) (previous)
-
assets/icon-128x128.png (modified) (1 prop) (previous)
-
assets/icon-256x256.png (modified) (1 prop) (previous)
-
tags/1.0.2 (copied) (copied from product-tabs-for-woo/trunk)
-
tags/1.0.2/assets/css/admin.css (modified) (1 diff)
-
tags/1.0.2/assets/css/sidebar.css (added)
-
tags/1.0.2/assets/js/admin.js (modified) (3 diffs)
-
tags/1.0.2/assets/js/sidebar.js (added)
-
tags/1.0.2/includes/class-iptfw-admin.php (deleted)
-
tags/1.0.2/includes/class-iptfw-settings.php (deleted)
-
tags/1.0.2/includes/class-stackwc-admin.php (added)
-
tags/1.0.2/includes/class-stackwc-ajax.php (added)
-
tags/1.0.2/includes/class-stackwc-columns.php (added)
-
tags/1.0.2/includes/class-stackwc-compatibility.php (added)
-
tags/1.0.2/includes/class-stackwc-conditions.php (added)
-
tags/1.0.2/includes/class-stackwc-meta-boxes.php (added)
-
tags/1.0.2/includes/class-stackwc-post-type.php (added)
-
tags/1.0.2/includes/class-stackwc-settings.php (added)
-
tags/1.0.2/includes/class-stackwc-tabs.php (added)
-
tags/1.0.2/includes/templates (added)
-
tags/1.0.2/includes/templates/meta-box-settings.php (added)
-
tags/1.0.2/languages (deleted)
-
tags/1.0.2/product-tabs-for-woo.php (modified) (5 diffs)
-
tags/1.0.2/readme.txt (modified) (1 diff)
-
tags/1.0.2/stackwc-core (added)
-
tags/1.0.2/stackwc-core/class-stackwc-menu.php (added)
-
tags/1.0.2/stackwc-core/stackwc-core.php (added)
-
tags/1.0.2/stackwc-core/templates (added)
-
tags/1.0.2/stackwc-core/templates/main-page.php (added)
-
tags/1.0.2/vendor (added)
-
tags/1.0.2/vendor/freemius (added)
-
tags/1.0.2/vendor/freemius/LICENSE.txt (added)
-
tags/1.0.2/vendor/freemius/assets (added)
-
tags/1.0.2/vendor/freemius/assets/css (added)
-
tags/1.0.2/vendor/freemius/assets/css/admin (added)
-
tags/1.0.2/vendor/freemius/assets/css/admin/account.css (added)
-
tags/1.0.2/vendor/freemius/assets/css/admin/add-ons.css (added)
-
tags/1.0.2/vendor/freemius/assets/css/admin/affiliation.css (added)
-
tags/1.0.2/vendor/freemius/assets/css/admin/checkout.css (added)
-
tags/1.0.2/vendor/freemius/assets/css/admin/clone-resolution.css (added)
-
tags/1.0.2/vendor/freemius/assets/css/admin/common.css (added)
-
tags/1.0.2/vendor/freemius/assets/css/admin/connect.css (added)
-
tags/1.0.2/vendor/freemius/assets/css/admin/debug.css (added)
-
tags/1.0.2/vendor/freemius/assets/css/admin/dialog-boxes.css (added)
-
tags/1.0.2/vendor/freemius/assets/css/admin/gdpr-optin-notice.css (added)
-
tags/1.0.2/vendor/freemius/assets/css/admin/index.php (added)
-
tags/1.0.2/vendor/freemius/assets/css/admin/optout.css (added)
-
tags/1.0.2/vendor/freemius/assets/css/admin/plugins.css (added)
-
tags/1.0.2/vendor/freemius/assets/css/customizer.css (added)
-
tags/1.0.2/vendor/freemius/assets/css/index.php (added)
-
tags/1.0.2/vendor/freemius/assets/img (added)
-
tags/1.0.2/vendor/freemius/assets/img/index.php (added)
-
tags/1.0.2/vendor/freemius/assets/img/plugin-icon.png (added)
-
tags/1.0.2/vendor/freemius/assets/img/theme-icon.png (added)
-
tags/1.0.2/vendor/freemius/assets/index.php (added)
-
tags/1.0.2/vendor/freemius/assets/js (added)
-
tags/1.0.2/vendor/freemius/assets/js/index.php (added)
-
tags/1.0.2/vendor/freemius/assets/js/jquery.form.js (added)
-
tags/1.0.2/vendor/freemius/assets/js/nojquery.ba-postmessage.js (added)
-
tags/1.0.2/vendor/freemius/assets/js/postmessage.js (added)
-
tags/1.0.2/vendor/freemius/assets/js/pricing (added)
-
tags/1.0.2/vendor/freemius/assets/js/pricing/14fb1bd5b7c41648488b06147f50a0dc.svg (added)
-
tags/1.0.2/vendor/freemius/assets/js/pricing/178afa6030e76635dbe835e111d2c507.png (added)
-
tags/1.0.2/vendor/freemius/assets/js/pricing/27b5a722a5553d9de0170325267fccec.png (added)
-
tags/1.0.2/vendor/freemius/assets/js/pricing/4375c4a3ddc6f637c2ab9a2d7220f91e.png (added)
-
tags/1.0.2/vendor/freemius/assets/js/pricing/45da596e2b512ffc3bb638baaf0fdc4e.png (added)
-
tags/1.0.2/vendor/freemius/assets/js/pricing/a34e046aee1702a5690679750a7f4d0f.svg (added)
-
tags/1.0.2/vendor/freemius/assets/js/pricing/b09d0b38b627c2fa564d050f79f2f064.svg (added)
-
tags/1.0.2/vendor/freemius/assets/js/pricing/c03f665db27af43971565560adfba594.png (added)
-
tags/1.0.2/vendor/freemius/assets/js/pricing/cb5fc4f6ec7ada72e986f6e7dde365bf.png (added)
-
tags/1.0.2/vendor/freemius/assets/js/pricing/d65812c447b4523b42d59018e1c0bb53.png (added)
-
tags/1.0.2/vendor/freemius/assets/js/pricing/f3aac72a8e63997d6bb888f816457e9b.png (added)
-
tags/1.0.2/vendor/freemius/assets/js/pricing/fde48e4609a6ddc11d639fc2421f2afd.png (added)
-
tags/1.0.2/vendor/freemius/assets/js/pricing/freemius-pricing.js (added)
-
tags/1.0.2/vendor/freemius/assets/js/pricing/freemius-pricing.js.LICENSE.txt (added)
-
tags/1.0.2/vendor/freemius/config.php (added)
-
tags/1.0.2/vendor/freemius/includes (added)
-
tags/1.0.2/vendor/freemius/includes/class-freemius-abstract.php (added)
-
tags/1.0.2/vendor/freemius/includes/class-freemius.php (added)
-
tags/1.0.2/vendor/freemius/includes/class-fs-admin-notices.php (added)
-
tags/1.0.2/vendor/freemius/includes/class-fs-api.php (added)
-
tags/1.0.2/vendor/freemius/includes/class-fs-garbage-collector.php (added)
-
tags/1.0.2/vendor/freemius/includes/class-fs-hook-snapshot.php (added)
-
tags/1.0.2/vendor/freemius/includes/class-fs-lock.php (added)
-
tags/1.0.2/vendor/freemius/includes/class-fs-logger.php (added)
-
tags/1.0.2/vendor/freemius/includes/class-fs-options.php (added)
-
tags/1.0.2/vendor/freemius/includes/class-fs-plugin-updater.php (added)
-
tags/1.0.2/vendor/freemius/includes/class-fs-security.php (added)
-
tags/1.0.2/vendor/freemius/includes/class-fs-storage.php (added)
-
tags/1.0.2/vendor/freemius/includes/class-fs-user-lock.php (added)
-
tags/1.0.2/vendor/freemius/includes/customizer (added)
-
tags/1.0.2/vendor/freemius/includes/customizer/class-fs-customizer-support-section.php (added)
-
tags/1.0.2/vendor/freemius/includes/customizer/class-fs-customizer-upsell-control.php (added)
-
tags/1.0.2/vendor/freemius/includes/customizer/index.php (added)
-
tags/1.0.2/vendor/freemius/includes/debug (added)
-
tags/1.0.2/vendor/freemius/includes/debug/class-fs-debug-bar-panel.php (added)
-
tags/1.0.2/vendor/freemius/includes/debug/debug-bar-start.php (added)
-
tags/1.0.2/vendor/freemius/includes/debug/index.php (added)
-
tags/1.0.2/vendor/freemius/includes/entities (added)
-
tags/1.0.2/vendor/freemius/includes/entities/class-fs-affiliate-terms.php (added)
-
tags/1.0.2/vendor/freemius/includes/entities/class-fs-affiliate.php (added)
-
tags/1.0.2/vendor/freemius/includes/entities/class-fs-billing.php (added)
-
tags/1.0.2/vendor/freemius/includes/entities/class-fs-entity.php (added)
-
tags/1.0.2/vendor/freemius/includes/entities/class-fs-payment.php (added)
-
tags/1.0.2/vendor/freemius/includes/entities/class-fs-plugin-info.php (added)
-
tags/1.0.2/vendor/freemius/includes/entities/class-fs-plugin-license.php (added)
-
tags/1.0.2/vendor/freemius/includes/entities/class-fs-plugin-plan.php (added)
-
tags/1.0.2/vendor/freemius/includes/entities/class-fs-plugin-tag.php (added)
-
tags/1.0.2/vendor/freemius/includes/entities/class-fs-plugin.php (added)
-
tags/1.0.2/vendor/freemius/includes/entities/class-fs-pricing.php (added)
-
tags/1.0.2/vendor/freemius/includes/entities/class-fs-scope-entity.php (added)
-
tags/1.0.2/vendor/freemius/includes/entities/class-fs-site.php (added)
-
tags/1.0.2/vendor/freemius/includes/entities/class-fs-subscription.php (added)
-
tags/1.0.2/vendor/freemius/includes/entities/class-fs-user.php (added)
-
tags/1.0.2/vendor/freemius/includes/entities/index.php (added)
-
tags/1.0.2/vendor/freemius/includes/fs-core-functions.php (added)
-
tags/1.0.2/vendor/freemius/includes/fs-essential-functions.php (added)
-
tags/1.0.2/vendor/freemius/includes/fs-html-escaping-functions.php (added)
-
tags/1.0.2/vendor/freemius/includes/fs-plugin-info-dialog.php (added)
-
tags/1.0.2/vendor/freemius/includes/index.php (added)
-
tags/1.0.2/vendor/freemius/includes/l10n.php (added)
-
tags/1.0.2/vendor/freemius/includes/managers (added)
-
tags/1.0.2/vendor/freemius/includes/managers/class-fs-admin-menu-manager.php (added)
-
tags/1.0.2/vendor/freemius/includes/managers/class-fs-admin-notice-manager.php (added)
-
tags/1.0.2/vendor/freemius/includes/managers/class-fs-cache-manager.php (added)
-
tags/1.0.2/vendor/freemius/includes/managers/class-fs-checkout-manager.php (added)
-
tags/1.0.2/vendor/freemius/includes/managers/class-fs-clone-manager.php (added)
-
tags/1.0.2/vendor/freemius/includes/managers/class-fs-contact-form-manager.php (added)
-
tags/1.0.2/vendor/freemius/includes/managers/class-fs-debug-manager.php (added)
-
tags/1.0.2/vendor/freemius/includes/managers/class-fs-gdpr-manager.php (added)
-
tags/1.0.2/vendor/freemius/includes/managers/class-fs-key-value-storage.php (added)
-
tags/1.0.2/vendor/freemius/includes/managers/class-fs-license-manager.php (added)
-
tags/1.0.2/vendor/freemius/includes/managers/class-fs-option-manager.php (added)
-
tags/1.0.2/vendor/freemius/includes/managers/class-fs-permission-manager.php (added)
-
tags/1.0.2/vendor/freemius/includes/managers/class-fs-plan-manager.php (added)
-
tags/1.0.2/vendor/freemius/includes/managers/class-fs-plugin-manager.php (added)
-
tags/1.0.2/vendor/freemius/includes/managers/index.php (added)
-
tags/1.0.2/vendor/freemius/includes/sdk (added)
-
tags/1.0.2/vendor/freemius/includes/sdk/Exceptions (added)
-
tags/1.0.2/vendor/freemius/includes/sdk/Exceptions/ArgumentNotExistException.php (added)
-
tags/1.0.2/vendor/freemius/includes/sdk/Exceptions/EmptyArgumentException.php (added)
-
tags/1.0.2/vendor/freemius/includes/sdk/Exceptions/Exception.php (added)
-
tags/1.0.2/vendor/freemius/includes/sdk/Exceptions/InvalidArgumentException.php (added)
-
tags/1.0.2/vendor/freemius/includes/sdk/Exceptions/OAuthException.php (added)
-
tags/1.0.2/vendor/freemius/includes/sdk/Exceptions/index.php (added)
-
tags/1.0.2/vendor/freemius/includes/sdk/FreemiusBase.php (added)
-
tags/1.0.2/vendor/freemius/includes/sdk/FreemiusWordPress.php (added)
-
tags/1.0.2/vendor/freemius/includes/sdk/LICENSE.txt (added)
-
tags/1.0.2/vendor/freemius/includes/sdk/index.php (added)
-
tags/1.0.2/vendor/freemius/includes/supplements (added)
-
tags/1.0.2/vendor/freemius/includes/supplements/fs-essential-functions-1.1.7.1.php (added)
-
tags/1.0.2/vendor/freemius/includes/supplements/fs-essential-functions-2.2.1.php (added)
-
tags/1.0.2/vendor/freemius/includes/supplements/fs-migration-2.5.1.php (added)
-
tags/1.0.2/vendor/freemius/includes/supplements/index.php (added)
-
tags/1.0.2/vendor/freemius/index.php (added)
-
tags/1.0.2/vendor/freemius/languages (added)
-
tags/1.0.2/vendor/freemius/languages/freemius-cs_CZ.mo (added)
-
tags/1.0.2/vendor/freemius/languages/freemius-da_DK.mo (added)
-
tags/1.0.2/vendor/freemius/languages/freemius-de_DE.mo (added)
-
tags/1.0.2/vendor/freemius/languages/freemius-es_ES.mo (added)
-
tags/1.0.2/vendor/freemius/languages/freemius-fr_FR.mo (added)
-
tags/1.0.2/vendor/freemius/languages/freemius-he_IL.mo (added)
-
tags/1.0.2/vendor/freemius/languages/freemius-hu_HU.mo (added)
-
tags/1.0.2/vendor/freemius/languages/freemius-it_IT.mo (added)
-
tags/1.0.2/vendor/freemius/languages/freemius-ja.mo (added)
-
tags/1.0.2/vendor/freemius/languages/freemius-nl_NL.mo (added)
-
tags/1.0.2/vendor/freemius/languages/freemius-ru_RU.mo (added)
-
tags/1.0.2/vendor/freemius/languages/freemius-ta.mo (added)
-
tags/1.0.2/vendor/freemius/languages/freemius-zh_CN.mo (added)
-
tags/1.0.2/vendor/freemius/languages/freemius.pot (added)
-
tags/1.0.2/vendor/freemius/languages/index.php (added)
-
tags/1.0.2/vendor/freemius/require.php (added)
-
tags/1.0.2/vendor/freemius/start.php (added)
-
tags/1.0.2/vendor/freemius/templates (added)
-
tags/1.0.2/vendor/freemius/templates/account (added)
-
tags/1.0.2/vendor/freemius/templates/account.php (added)
-
tags/1.0.2/vendor/freemius/templates/account/billing.php (added)
-
tags/1.0.2/vendor/freemius/templates/account/index.php (added)
-
tags/1.0.2/vendor/freemius/templates/account/partials (added)
-
tags/1.0.2/vendor/freemius/templates/account/partials/activate-license-button.php (added)
-
tags/1.0.2/vendor/freemius/templates/account/partials/addon.php (added)
-
tags/1.0.2/vendor/freemius/templates/account/partials/deactivate-license-button.php (added)
-
tags/1.0.2/vendor/freemius/templates/account/partials/disconnect-button.php (added)
-
tags/1.0.2/vendor/freemius/templates/account/partials/index.php (added)
-
tags/1.0.2/vendor/freemius/templates/account/partials/site.php (added)
-
tags/1.0.2/vendor/freemius/templates/account/payments.php (added)
-
tags/1.0.2/vendor/freemius/templates/add-ons.php (added)
-
tags/1.0.2/vendor/freemius/templates/add-trial-to-pricing.php (added)
-
tags/1.0.2/vendor/freemius/templates/admin-notice.php (added)
-
tags/1.0.2/vendor/freemius/templates/ajax-loader.php (added)
-
tags/1.0.2/vendor/freemius/templates/api-connectivity-message-js.php (added)
-
tags/1.0.2/vendor/freemius/templates/auto-installation.php (added)
-
tags/1.0.2/vendor/freemius/templates/checkout (added)
-
tags/1.0.2/vendor/freemius/templates/checkout.php (added)
-
tags/1.0.2/vendor/freemius/templates/checkout/frame.php (added)
-
tags/1.0.2/vendor/freemius/templates/checkout/process-redirect.php (added)
-
tags/1.0.2/vendor/freemius/templates/checkout/redirect.php (added)
-
tags/1.0.2/vendor/freemius/templates/clone-resolution-js.php (added)
-
tags/1.0.2/vendor/freemius/templates/connect (added)
-
tags/1.0.2/vendor/freemius/templates/connect.php (added)
-
tags/1.0.2/vendor/freemius/templates/connect/index.php (added)
-
tags/1.0.2/vendor/freemius/templates/connect/permission.php (added)
-
tags/1.0.2/vendor/freemius/templates/connect/permissions-group.php (added)
-
tags/1.0.2/vendor/freemius/templates/contact.php (added)
-
tags/1.0.2/vendor/freemius/templates/debug (added)
-
tags/1.0.2/vendor/freemius/templates/debug.php (added)
-
tags/1.0.2/vendor/freemius/templates/debug/api-calls.php (added)
-
tags/1.0.2/vendor/freemius/templates/debug/index.php (added)
-
tags/1.0.2/vendor/freemius/templates/debug/logger.php (added)
-
tags/1.0.2/vendor/freemius/templates/debug/plugins-themes-sync.php (added)
-
tags/1.0.2/vendor/freemius/templates/debug/scheduled-crons.php (added)
-
tags/1.0.2/vendor/freemius/templates/email.php (added)
-
tags/1.0.2/vendor/freemius/templates/forms (added)
-
tags/1.0.2/vendor/freemius/templates/forms/affiliation.php (added)
-
tags/1.0.2/vendor/freemius/templates/forms/data-debug-mode.php (added)
-
tags/1.0.2/vendor/freemius/templates/forms/deactivation (added)
-
tags/1.0.2/vendor/freemius/templates/forms/deactivation/contact.php (added)
-
tags/1.0.2/vendor/freemius/templates/forms/deactivation/form.php (added)
-
tags/1.0.2/vendor/freemius/templates/forms/deactivation/index.php (added)
-
tags/1.0.2/vendor/freemius/templates/forms/deactivation/retry-skip.php (added)
-
tags/1.0.2/vendor/freemius/templates/forms/email-address-update.php (added)
-
tags/1.0.2/vendor/freemius/templates/forms/index.php (added)
-
tags/1.0.2/vendor/freemius/templates/forms/license-activation.php (added)
-
tags/1.0.2/vendor/freemius/templates/forms/optout.php (added)
-
tags/1.0.2/vendor/freemius/templates/forms/premium-versions-upgrade-handler.php (added)
-
tags/1.0.2/vendor/freemius/templates/forms/premium-versions-upgrade-metadata.php (added)
-
tags/1.0.2/vendor/freemius/templates/forms/resend-key.php (added)
-
tags/1.0.2/vendor/freemius/templates/forms/subscription-cancellation.php (added)
-
tags/1.0.2/vendor/freemius/templates/forms/trial-start.php (added)
-
tags/1.0.2/vendor/freemius/templates/forms/user-change.php (added)
-
tags/1.0.2/vendor/freemius/templates/gdpr-optin-js.php (added)
-
tags/1.0.2/vendor/freemius/templates/index.php (added)
-
tags/1.0.2/vendor/freemius/templates/js (added)
-
tags/1.0.2/vendor/freemius/templates/js/index.php (added)
-
tags/1.0.2/vendor/freemius/templates/js/jquery.content-change.php (added)
-
tags/1.0.2/vendor/freemius/templates/js/open-license-activation.php (added)
-
tags/1.0.2/vendor/freemius/templates/js/permissions.php (added)
-
tags/1.0.2/vendor/freemius/templates/js/style-premium-theme.php (added)
-
tags/1.0.2/vendor/freemius/templates/partials (added)
-
tags/1.0.2/vendor/freemius/templates/partials/index.php (added)
-
tags/1.0.2/vendor/freemius/templates/partials/network-activation.php (added)
-
tags/1.0.2/vendor/freemius/templates/plugin-icon.php (added)
-
tags/1.0.2/vendor/freemius/templates/plugin-info (added)
-
tags/1.0.2/vendor/freemius/templates/plugin-info/description.php (added)
-
tags/1.0.2/vendor/freemius/templates/plugin-info/features.php (added)
-
tags/1.0.2/vendor/freemius/templates/plugin-info/index.php (added)
-
tags/1.0.2/vendor/freemius/templates/plugin-info/screenshots.php (added)
-
tags/1.0.2/vendor/freemius/templates/pricing.php (added)
-
tags/1.0.2/vendor/freemius/templates/secure-https-header.php (added)
-
tags/1.0.2/vendor/freemius/templates/sticky-admin-notice-js.php (added)
-
tags/1.0.2/vendor/freemius/templates/tabs-capture-js.php (added)
-
tags/1.0.2/vendor/freemius/templates/tabs.php (added)
-
trunk/assets/css/admin.css (modified) (1 diff)
-
trunk/assets/css/sidebar.css (added)
-
trunk/assets/js/admin.js (modified) (3 diffs)
-
trunk/assets/js/sidebar.js (added)
-
trunk/includes/class-iptfw-admin.php (deleted)
-
trunk/includes/class-iptfw-settings.php (deleted)
-
trunk/includes/class-stackwc-admin.php (added)
-
trunk/includes/class-stackwc-ajax.php (added)
-
trunk/includes/class-stackwc-columns.php (added)
-
trunk/includes/class-stackwc-compatibility.php (added)
-
trunk/includes/class-stackwc-conditions.php (added)
-
trunk/includes/class-stackwc-meta-boxes.php (added)
-
trunk/includes/class-stackwc-post-type.php (added)
-
trunk/includes/class-stackwc-settings.php (added)
-
trunk/includes/class-stackwc-tabs.php (added)
-
trunk/includes/templates (added)
-
trunk/includes/templates/meta-box-settings.php (added)
-
trunk/languages (deleted)
-
trunk/product-tabs-for-woo.php (modified) (5 diffs)
-
trunk/readme.txt (modified) (1 diff)
-
trunk/stackwc-core (added)
-
trunk/stackwc-core/class-stackwc-menu.php (added)
-
trunk/stackwc-core/stackwc-core.php (added)
-
trunk/stackwc-core/templates (added)
-
trunk/stackwc-core/templates/main-page.php (added)
-
trunk/vendor (added)
-
trunk/vendor/freemius (added)
-
trunk/vendor/freemius/LICENSE.txt (added)
-
trunk/vendor/freemius/assets (added)
-
trunk/vendor/freemius/assets/css (added)
-
trunk/vendor/freemius/assets/css/admin (added)
-
trunk/vendor/freemius/assets/css/admin/account.css (added)
-
trunk/vendor/freemius/assets/css/admin/add-ons.css (added)
-
trunk/vendor/freemius/assets/css/admin/affiliation.css (added)
-
trunk/vendor/freemius/assets/css/admin/checkout.css (added)
-
trunk/vendor/freemius/assets/css/admin/clone-resolution.css (added)
-
trunk/vendor/freemius/assets/css/admin/common.css (added)
-
trunk/vendor/freemius/assets/css/admin/connect.css (added)
-
trunk/vendor/freemius/assets/css/admin/debug.css (added)
-
trunk/vendor/freemius/assets/css/admin/dialog-boxes.css (added)
-
trunk/vendor/freemius/assets/css/admin/gdpr-optin-notice.css (added)
-
trunk/vendor/freemius/assets/css/admin/index.php (added)
-
trunk/vendor/freemius/assets/css/admin/optout.css (added)
-
trunk/vendor/freemius/assets/css/admin/plugins.css (added)
-
trunk/vendor/freemius/assets/css/customizer.css (added)
-
trunk/vendor/freemius/assets/css/index.php (added)
-
trunk/vendor/freemius/assets/img (added)
-
trunk/vendor/freemius/assets/img/index.php (added)
-
trunk/vendor/freemius/assets/img/plugin-icon.png (added)
-
trunk/vendor/freemius/assets/img/theme-icon.png (added)
-
trunk/vendor/freemius/assets/index.php (added)
-
trunk/vendor/freemius/assets/js (added)
-
trunk/vendor/freemius/assets/js/index.php (added)
-
trunk/vendor/freemius/assets/js/jquery.form.js (added)
-
trunk/vendor/freemius/assets/js/nojquery.ba-postmessage.js (added)
-
trunk/vendor/freemius/assets/js/postmessage.js (added)
-
trunk/vendor/freemius/assets/js/pricing (added)
-
trunk/vendor/freemius/assets/js/pricing/14fb1bd5b7c41648488b06147f50a0dc.svg (added)
-
trunk/vendor/freemius/assets/js/pricing/178afa6030e76635dbe835e111d2c507.png (added)
-
trunk/vendor/freemius/assets/js/pricing/27b5a722a5553d9de0170325267fccec.png (added)
-
trunk/vendor/freemius/assets/js/pricing/4375c4a3ddc6f637c2ab9a2d7220f91e.png (added)
-
trunk/vendor/freemius/assets/js/pricing/45da596e2b512ffc3bb638baaf0fdc4e.png (added)
-
trunk/vendor/freemius/assets/js/pricing/a34e046aee1702a5690679750a7f4d0f.svg (added)
-
trunk/vendor/freemius/assets/js/pricing/b09d0b38b627c2fa564d050f79f2f064.svg (added)
-
trunk/vendor/freemius/assets/js/pricing/c03f665db27af43971565560adfba594.png (added)
-
trunk/vendor/freemius/assets/js/pricing/cb5fc4f6ec7ada72e986f6e7dde365bf.png (added)
-
trunk/vendor/freemius/assets/js/pricing/d65812c447b4523b42d59018e1c0bb53.png (added)
-
trunk/vendor/freemius/assets/js/pricing/f3aac72a8e63997d6bb888f816457e9b.png (added)
-
trunk/vendor/freemius/assets/js/pricing/fde48e4609a6ddc11d639fc2421f2afd.png (added)
-
trunk/vendor/freemius/assets/js/pricing/freemius-pricing.js (added)
-
trunk/vendor/freemius/assets/js/pricing/freemius-pricing.js.LICENSE.txt (added)
-
trunk/vendor/freemius/config.php (added)
-
trunk/vendor/freemius/includes (added)
-
trunk/vendor/freemius/includes/class-freemius-abstract.php (added)
-
trunk/vendor/freemius/includes/class-freemius.php (added)
-
trunk/vendor/freemius/includes/class-fs-admin-notices.php (added)
-
trunk/vendor/freemius/includes/class-fs-api.php (added)
-
trunk/vendor/freemius/includes/class-fs-garbage-collector.php (added)
-
trunk/vendor/freemius/includes/class-fs-hook-snapshot.php (added)
-
trunk/vendor/freemius/includes/class-fs-lock.php (added)
-
trunk/vendor/freemius/includes/class-fs-logger.php (added)
-
trunk/vendor/freemius/includes/class-fs-options.php (added)
-
trunk/vendor/freemius/includes/class-fs-plugin-updater.php (added)
-
trunk/vendor/freemius/includes/class-fs-security.php (added)
-
trunk/vendor/freemius/includes/class-fs-storage.php (added)
-
trunk/vendor/freemius/includes/class-fs-user-lock.php (added)
-
trunk/vendor/freemius/includes/customizer (added)
-
trunk/vendor/freemius/includes/customizer/class-fs-customizer-support-section.php (added)
-
trunk/vendor/freemius/includes/customizer/class-fs-customizer-upsell-control.php (added)
-
trunk/vendor/freemius/includes/customizer/index.php (added)
-
trunk/vendor/freemius/includes/debug (added)
-
trunk/vendor/freemius/includes/debug/class-fs-debug-bar-panel.php (added)
-
trunk/vendor/freemius/includes/debug/debug-bar-start.php (added)
-
trunk/vendor/freemius/includes/debug/index.php (added)
-
trunk/vendor/freemius/includes/entities (added)
-
trunk/vendor/freemius/includes/entities/class-fs-affiliate-terms.php (added)
-
trunk/vendor/freemius/includes/entities/class-fs-affiliate.php (added)
-
trunk/vendor/freemius/includes/entities/class-fs-billing.php (added)
-
trunk/vendor/freemius/includes/entities/class-fs-entity.php (added)
-
trunk/vendor/freemius/includes/entities/class-fs-payment.php (added)
-
trunk/vendor/freemius/includes/entities/class-fs-plugin-info.php (added)
-
trunk/vendor/freemius/includes/entities/class-fs-plugin-license.php (added)
-
trunk/vendor/freemius/includes/entities/class-fs-plugin-plan.php (added)
-
trunk/vendor/freemius/includes/entities/class-fs-plugin-tag.php (added)
-
trunk/vendor/freemius/includes/entities/class-fs-plugin.php (added)
-
trunk/vendor/freemius/includes/entities/class-fs-pricing.php (added)
-
trunk/vendor/freemius/includes/entities/class-fs-scope-entity.php (added)
-
trunk/vendor/freemius/includes/entities/class-fs-site.php (added)
-
trunk/vendor/freemius/includes/entities/class-fs-subscription.php (added)
-
trunk/vendor/freemius/includes/entities/class-fs-user.php (added)
-
trunk/vendor/freemius/includes/entities/index.php (added)
-
trunk/vendor/freemius/includes/fs-core-functions.php (added)
-
trunk/vendor/freemius/includes/fs-essential-functions.php (added)
-
trunk/vendor/freemius/includes/fs-html-escaping-functions.php (added)
-
trunk/vendor/freemius/includes/fs-plugin-info-dialog.php (added)
-
trunk/vendor/freemius/includes/index.php (added)
-
trunk/vendor/freemius/includes/l10n.php (added)
-
trunk/vendor/freemius/includes/managers (added)
-
trunk/vendor/freemius/includes/managers/class-fs-admin-menu-manager.php (added)
-
trunk/vendor/freemius/includes/managers/class-fs-admin-notice-manager.php (added)
-
trunk/vendor/freemius/includes/managers/class-fs-cache-manager.php (added)
-
trunk/vendor/freemius/includes/managers/class-fs-checkout-manager.php (added)
-
trunk/vendor/freemius/includes/managers/class-fs-clone-manager.php (added)
-
trunk/vendor/freemius/includes/managers/class-fs-contact-form-manager.php (added)
-
trunk/vendor/freemius/includes/managers/class-fs-debug-manager.php (added)
-
trunk/vendor/freemius/includes/managers/class-fs-gdpr-manager.php (added)
-
trunk/vendor/freemius/includes/managers/class-fs-key-value-storage.php (added)
-
trunk/vendor/freemius/includes/managers/class-fs-license-manager.php (added)
-
trunk/vendor/freemius/includes/managers/class-fs-option-manager.php (added)
-
trunk/vendor/freemius/includes/managers/class-fs-permission-manager.php (added)
-
trunk/vendor/freemius/includes/managers/class-fs-plan-manager.php (added)
-
trunk/vendor/freemius/includes/managers/class-fs-plugin-manager.php (added)
-
trunk/vendor/freemius/includes/managers/index.php (added)
-
trunk/vendor/freemius/includes/sdk (added)
-
trunk/vendor/freemius/includes/sdk/Exceptions (added)
-
trunk/vendor/freemius/includes/sdk/Exceptions/ArgumentNotExistException.php (added)
-
trunk/vendor/freemius/includes/sdk/Exceptions/EmptyArgumentException.php (added)
-
trunk/vendor/freemius/includes/sdk/Exceptions/Exception.php (added)
-
trunk/vendor/freemius/includes/sdk/Exceptions/InvalidArgumentException.php (added)
-
trunk/vendor/freemius/includes/sdk/Exceptions/OAuthException.php (added)
-
trunk/vendor/freemius/includes/sdk/Exceptions/index.php (added)
-
trunk/vendor/freemius/includes/sdk/FreemiusBase.php (added)
-
trunk/vendor/freemius/includes/sdk/FreemiusWordPress.php (added)
-
trunk/vendor/freemius/includes/sdk/LICENSE.txt (added)
-
trunk/vendor/freemius/includes/sdk/index.php (added)
-
trunk/vendor/freemius/includes/supplements (added)
-
trunk/vendor/freemius/includes/supplements/fs-essential-functions-1.1.7.1.php (added)
-
trunk/vendor/freemius/includes/supplements/fs-essential-functions-2.2.1.php (added)
-
trunk/vendor/freemius/includes/supplements/fs-migration-2.5.1.php (added)
-
trunk/vendor/freemius/includes/supplements/index.php (added)
-
trunk/vendor/freemius/index.php (added)
-
trunk/vendor/freemius/languages (added)
-
trunk/vendor/freemius/languages/freemius-cs_CZ.mo (added)
-
trunk/vendor/freemius/languages/freemius-da_DK.mo (added)
-
trunk/vendor/freemius/languages/freemius-de_DE.mo (added)
-
trunk/vendor/freemius/languages/freemius-es_ES.mo (added)
-
trunk/vendor/freemius/languages/freemius-fr_FR.mo (added)
-
trunk/vendor/freemius/languages/freemius-he_IL.mo (added)
-
trunk/vendor/freemius/languages/freemius-hu_HU.mo (added)
-
trunk/vendor/freemius/languages/freemius-it_IT.mo (added)
-
trunk/vendor/freemius/languages/freemius-ja.mo (added)
-
trunk/vendor/freemius/languages/freemius-nl_NL.mo (added)
-
trunk/vendor/freemius/languages/freemius-ru_RU.mo (added)
-
trunk/vendor/freemius/languages/freemius-ta.mo (added)
-
trunk/vendor/freemius/languages/freemius-zh_CN.mo (added)
-
trunk/vendor/freemius/languages/freemius.pot (added)
-
trunk/vendor/freemius/languages/index.php (added)
-
trunk/vendor/freemius/require.php (added)
-
trunk/vendor/freemius/start.php (added)
-
trunk/vendor/freemius/templates (added)
-
trunk/vendor/freemius/templates/account (added)
-
trunk/vendor/freemius/templates/account.php (added)
-
trunk/vendor/freemius/templates/account/billing.php (added)
-
trunk/vendor/freemius/templates/account/index.php (added)
-
trunk/vendor/freemius/templates/account/partials (added)
-
trunk/vendor/freemius/templates/account/partials/activate-license-button.php (added)
-
trunk/vendor/freemius/templates/account/partials/addon.php (added)
-
trunk/vendor/freemius/templates/account/partials/deactivate-license-button.php (added)
-
trunk/vendor/freemius/templates/account/partials/disconnect-button.php (added)
-
trunk/vendor/freemius/templates/account/partials/index.php (added)
-
trunk/vendor/freemius/templates/account/partials/site.php (added)
-
trunk/vendor/freemius/templates/account/payments.php (added)
-
trunk/vendor/freemius/templates/add-ons.php (added)
-
trunk/vendor/freemius/templates/add-trial-to-pricing.php (added)
-
trunk/vendor/freemius/templates/admin-notice.php (added)
-
trunk/vendor/freemius/templates/ajax-loader.php (added)
-
trunk/vendor/freemius/templates/api-connectivity-message-js.php (added)
-
trunk/vendor/freemius/templates/auto-installation.php (added)
-
trunk/vendor/freemius/templates/checkout (added)
-
trunk/vendor/freemius/templates/checkout.php (added)
-
trunk/vendor/freemius/templates/checkout/frame.php (added)
-
trunk/vendor/freemius/templates/checkout/process-redirect.php (added)
-
trunk/vendor/freemius/templates/checkout/redirect.php (added)
-
trunk/vendor/freemius/templates/clone-resolution-js.php (added)
-
trunk/vendor/freemius/templates/connect (added)
-
trunk/vendor/freemius/templates/connect.php (added)
-
trunk/vendor/freemius/templates/connect/index.php (added)
-
trunk/vendor/freemius/templates/connect/permission.php (added)
-
trunk/vendor/freemius/templates/connect/permissions-group.php (added)
-
trunk/vendor/freemius/templates/contact.php (added)
-
trunk/vendor/freemius/templates/debug (added)
-
trunk/vendor/freemius/templates/debug.php (added)
-
trunk/vendor/freemius/templates/debug/api-calls.php (added)
-
trunk/vendor/freemius/templates/debug/index.php (added)
-
trunk/vendor/freemius/templates/debug/logger.php (added)
-
trunk/vendor/freemius/templates/debug/plugins-themes-sync.php (added)
-
trunk/vendor/freemius/templates/debug/scheduled-crons.php (added)
-
trunk/vendor/freemius/templates/email.php (added)
-
trunk/vendor/freemius/templates/forms (added)
-
trunk/vendor/freemius/templates/forms/affiliation.php (added)
-
trunk/vendor/freemius/templates/forms/data-debug-mode.php (added)
-
trunk/vendor/freemius/templates/forms/deactivation (added)
-
trunk/vendor/freemius/templates/forms/deactivation/contact.php (added)
-
trunk/vendor/freemius/templates/forms/deactivation/form.php (added)
-
trunk/vendor/freemius/templates/forms/deactivation/index.php (added)
-
trunk/vendor/freemius/templates/forms/deactivation/retry-skip.php (added)
-
trunk/vendor/freemius/templates/forms/email-address-update.php (added)
-
trunk/vendor/freemius/templates/forms/index.php (added)
-
trunk/vendor/freemius/templates/forms/license-activation.php (added)
-
trunk/vendor/freemius/templates/forms/optout.php (added)
-
trunk/vendor/freemius/templates/forms/premium-versions-upgrade-handler.php (added)
-
trunk/vendor/freemius/templates/forms/premium-versions-upgrade-metadata.php (added)
-
trunk/vendor/freemius/templates/forms/resend-key.php (added)
-
trunk/vendor/freemius/templates/forms/subscription-cancellation.php (added)
-
trunk/vendor/freemius/templates/forms/trial-start.php (added)
-
trunk/vendor/freemius/templates/forms/user-change.php (added)
-
trunk/vendor/freemius/templates/gdpr-optin-js.php (added)
-
trunk/vendor/freemius/templates/index.php (added)
-
trunk/vendor/freemius/templates/js (added)
-
trunk/vendor/freemius/templates/js/index.php (added)
-
trunk/vendor/freemius/templates/js/jquery.content-change.php (added)
-
trunk/vendor/freemius/templates/js/open-license-activation.php (added)
-
trunk/vendor/freemius/templates/js/permissions.php (added)
-
trunk/vendor/freemius/templates/js/style-premium-theme.php (added)
-
trunk/vendor/freemius/templates/partials (added)
-
trunk/vendor/freemius/templates/partials/index.php (added)
-
trunk/vendor/freemius/templates/partials/network-activation.php (added)
-
trunk/vendor/freemius/templates/plugin-icon.php (added)
-
trunk/vendor/freemius/templates/plugin-info (added)
-
trunk/vendor/freemius/templates/plugin-info/description.php (added)
-
trunk/vendor/freemius/templates/plugin-info/features.php (added)
-
trunk/vendor/freemius/templates/plugin-info/index.php (added)
-
trunk/vendor/freemius/templates/plugin-info/screenshots.php (added)
-
trunk/vendor/freemius/templates/pricing.php (added)
-
trunk/vendor/freemius/templates/secure-https-header.php (added)
-
trunk/vendor/freemius/templates/sticky-admin-notice-js.php (added)
-
trunk/vendor/freemius/templates/tabs-capture-js.php (added)
-
trunk/vendor/freemius/templates/tabs.php (added)
Legend:
- Unmodified
- Added
- Removed
-
product-tabs-for-woo/assets/banner-1544x500.jpg
-
Property
svn:mime-type
changed from
application/octet-streamtoimage/jpeg
-
Property
svn:mime-type
changed from
-
product-tabs-for-woo/assets/banner-772x250.jpg
-
Property
svn:mime-type
changed from
application/octet-streamtoimage/jpeg
-
Property
svn:mime-type
changed from
-
product-tabs-for-woo/assets/icon-128x128.png
-
Property
svn:mime-type
changed from
application/octet-streamtoimage/png
-
Property
svn:mime-type
changed from
-
product-tabs-for-woo/assets/icon-256x256.png
-
Property
svn:mime-type
changed from
application/octet-streamtoimage/png
-
Property
svn:mime-type
changed from
-
product-tabs-for-woo/tags/1.0.2/assets/css/admin.css
r3270803 r3405807 23 23 color: #757575; 24 24 font-style: italic; 25 } 26 27 /* Select2 WordPress Admin Styling */ 28 .select2-container { 29 max-width: 400px; 30 } 31 .select2-container--default .select2-selection--multiple { 32 border: 1px solid #8c8f94; 33 border-radius: 4px; 34 min-height: 30px; 35 padding: 2px; 36 } 37 .select2-container--default.select2-container--focus .select2-selection--multiple { 38 border-color: #2271b1; 39 box-shadow: 0 0 0 1px #2271b1; 40 } 41 .select2-container--default .select2-selection--multiple .select2-selection__choice { 42 background-color: #2271b1; 43 border: 1px solid #2271b1; 44 color: #fff; 45 padding: 2px 8px; 46 margin: 2px; 47 } 48 .select2-container--default .select2-selection--multiple .select2-selection__choice__remove { 49 color: #fff; 50 margin-right: 5px; 51 } 52 .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover { 53 color: #f0f0f1; 25 54 } -
product-tabs-for-woo/tags/1.0.2/assets/js/admin.js
r3270803 r3405807 1 1 jQuery(document).ready(function($) { 2 // --- Shared icon picker modal helpers (used on Settings and Post Editor) --- 3 var $iptfwIconModal = null, iptfwAllIcons = null, iptfwIconTarget = null; 4 function iptfwGetAllDashicons(){ 5 if (iptfwAllIcons) return iptfwAllIcons; 6 try { 7 var classes = new Set(); 8 for (var i=0;i<document.styleSheets.length;i++){ 9 var ss = document.styleSheets[i]; 10 if (!ss || !ss.href || ss.href.indexOf('dashicons') === -1) continue; 11 var rules = ss.cssRules || ss.rules; if (!rules) continue; 12 for (var j=0;j<rules.length;j++){ 13 var sel = rules[j].selectorText || ''; 14 if (sel && sel.indexOf('.dashicons-') !== -1){ 15 sel.split(',').forEach(function(part){ 16 var m = (part.trim().match(/\.dashicons-([a-z0-9\-]+)/)); 17 if (m) classes.add('dashicons-' + m[1]); 18 }); 19 } 20 } 21 } 22 iptfwAllIcons = Array.from(classes).sort(); 23 } catch(e){ 24 iptfwAllIcons = ['dashicons-admin-generic','dashicons-star-filled','dashicons-yes','dashicons-heart','dashicons-cart','dashicons-products','dashicons-format-video','dashicons-clipboard','dashicons-analytics','dashicons-download']; 25 } 26 return iptfwAllIcons; 27 } 28 function iptfwEnsureIconModal(){ 29 if ($iptfwIconModal) return $iptfwIconModal; 30 $iptfwIconModal = $('<div id="iptfw-icon-modal" class="iptfw-modal" style="display:none;position:fixed;inset:0;background:rgba(0,0,0,.4);z-index:100000;">\ 31 <div class="iptfw-modal-inner" style="background:#fff;max-width:700px;margin:60px auto;padding:16px;border-radius:6px;">\ 32 <div style="display:flex;justify-content:space-between;align-items:center;">\ 33 <h2 style="margin:0 0 12px;">Select an Icon</h2>\ 34 <button type="button" class="button" id="iptfw-icon-close">Close</button>\ 35 </div>\ 36 <input type="text" id="iptfw-icon-search" placeholder="Search..." class="regular-text" style="width:100%;margin-bottom:12px;"/>\ 37 <div class="iptfw-icon-grid" style="display:grid;grid-template-columns:repeat(6,1fr);gap:10px;max-height:360px;overflow:auto;"></div>\ 38 </div>\ 39 </div>'); 40 $('body').append($iptfwIconModal); 41 function renderIcons(filter){ 42 var icons = iptfwGetAllDashicons(); 43 var $grid = $iptfwIconModal.find('.iptfw-icon-grid').empty(); 44 icons.forEach(function(cls){ 45 if (filter && cls.toLowerCase().indexOf(filter.toLowerCase()) === -1) return; 46 var $item = $('<button type="button" class="button" style="height:48px;display:flex;align-items:center;justify-content:center;">\ 47 <span class="dashicons '+cls+'"></span></button>').data('icon', cls); 48 $grid.append($item); 49 }); 50 } 51 $iptfwIconModal.on('click', '#iptfw-icon-close', function(){ $iptfwIconModal.hide(); }); 52 $iptfwIconModal.on('click', function(e){ if (e.target === $iptfwIconModal[0]) { $iptfwIconModal.hide(); } }); 53 $iptfwIconModal.on('keyup', '#iptfw-icon-search', function(){ renderIcons($(this).val()); }); 54 $iptfwIconModal.on('click', '.iptfw-icon-grid .button', function(){ if (!iptfwIconTarget) return; iptfwIconTarget.val($(this).data('icon')).trigger('change'); $iptfwIconModal.hide(); }); 55 // Initial fill 56 renderIcons(''); 57 return $iptfwIconModal; 58 } 2 59 // Display type toggle functionality 3 60 $('#display_type').on('change', function() { 4 $('#specific_products, #product_ categories, #product_tags').hide();61 $('#specific_products, #product_type, #product_categories, #product_tags, #advanced_conditions').hide(); 5 62 switch($(this).val()) { 6 63 case 'specific': 7 64 $('#specific_products').show(); 8 65 break; 66 case 'product_type': 67 $('#product_type').show(); 68 break; 9 69 case 'categories': 10 70 $('#product_categories').show(); … … 13 73 $('#product_tags').show(); 14 74 break; 75 case 'advanced': 76 $('#advanced_conditions').show(); 77 break; 15 78 } 16 79 }); … … 19 82 $('#display_type').trigger('change'); 20 83 21 // Settings button functionality 22 function initSettingsButton() { 23 var $addNewButton = $('.wrap h1.wp-heading-inline').next('.page-title-action'); 24 if ($addNewButton.length) { 25 $addNewButton.after( 26 '<a href="' + iptfw_admin.settings_url + '" class="page-title-action" style="margin-left: 10px;">' + 27 iptfw_admin.settings_text + '</a>' 28 ); 29 } 30 } 31 32 if (iptfw_admin.is_product_tab_page) { 33 initSettingsButton(); 84 // Initialize Select2 for product search 85 if ($('.iptfw-product-search').length) { 86 $('.iptfw-product-search').select2({ 87 ajax: { 88 url: iptfw_admin.ajax_url, 89 dataType: 'json', 90 delay: 250, 91 data: function (params) { 92 return { 93 q: params.term, 94 action: 'iptfw_search_products', 95 nonce: iptfw_admin.admin_nonce 96 }; 97 }, 98 processResults: function (data) { 99 return { 100 results: data.results 101 }; 102 }, 103 cache: true 104 }, 105 minimumInputLength: 2, 106 placeholder: 'Search for products...', 107 allowClear: true 108 }); 109 } 110 111 // Settings and Reorder are now handled by tabs navigation 112 113 // Global icon picker handler (works on settings page and block editor) 114 $(document).on('click', '.iptfw-icon-picker', function(e){ 115 e.preventDefault(); 116 if (!iptfw_admin.has_premium) { window.alert('This is a Pro feature.'); return; } 117 iptfwEnsureIconModal(); 118 iptfwIconTarget = $('#'+$(this).data('target')); 119 $('#iptfw-icon-search').val(''); 120 $iptfwIconModal.show(); 121 $('#iptfw-icon-search').trigger('focus'); 122 }); 123 124 // Settings: icon picker modal 125 if (iptfw_admin.is_settings_page) { 126 127 // Add picker buttons and preview next to icon fields; hide raw input 128 ['icon_description','icon_additional_information','icon_reviews'].forEach(function(id){ 129 var $input = $('#'+id); 130 if ($input.length) { 131 var $btn = $('<button type="button" class="button iptfw-icon-picker" style="margin-left:8px;">Select Icon</button>').attr('data-target', id); 132 var $preview = $('<span class="iptfw-icon-preview" style="display:inline-flex;align-items:center;justify-content:center;margin-left:8px;min-width:28px;position:relative;height:28px;width:28px;"></span>'); 133 function syncPreview(){ 134 var cls = $input.val(); 135 $preview.empty(); 136 if (cls){ 137 $preview.append($('<span/>').addClass('dashicons').addClass(cls)); 138 // Simple clear button (×) at top-right 139 var $clear = $('<button type="button" class="iptfw-remove-icon" aria-label="Remove icon">×</button>').attr('title','Remove icon'); 140 $clear.css({ 141 position:'absolute', 142 top:'-6px', 143 right:'-6px', 144 width:'18px', 145 height:'18px', 146 lineHeight:'14px', 147 textAlign:'center', 148 background:'#f0f0f1', 149 border:'1px solid #ccd0d4', 150 borderRadius:'50%', 151 cursor:'pointer', 152 padding:0, 153 fontWeight:'bold' 154 }); 155 $preview.append($clear); 156 } 157 } 158 $input.css({width:'0',padding:'0',border:'0',opacity:0,position:'absolute'}); 159 $input.after($btn).after($preview); 160 syncPreview(); 161 $input.on('change', syncPreview); 162 } 163 }); 164 165 // Remove icon handler 166 $(document).on('click', '.iptfw-remove-icon', function(e){ 167 e.preventDefault(); 168 var $wrap = $(this).closest('.iptfw-icon-preview'); 169 var $input = $wrap.prevAll('input[type="text"]').first(); 170 if ($input.length){ 171 $input.val('').trigger('change'); 172 } 173 }); 174 } 175 176 // Post editor: apply picker/preview to custom tab sidebar field if present 177 (function(){ 178 var $input = $('#iptfw_icon'); 179 if ($input.length) { 180 var $btn = $('.iptfw-icon-picker[data-target="iptfw_icon"]'); 181 var $preview = $input.siblings('.iptfw-icon-preview'); 182 // Hide the raw input; keep value for submission 183 $input.css({width:'0',padding:'0',border:'0',opacity:0,position:'absolute'}); 184 // Click handler to open modal on editor 185 $(document).on('click', '.iptfw-icon-picker[data-target="iptfw_icon"]', function(e){ 186 e.preventDefault(); 187 if (!iptfw_admin.has_premium) { window.alert('This is a Pro feature.'); return; } 188 iptfwEnsureIconModal(); 189 iptfwIconTarget = $input; 190 $('#iptfw-icon-search').val(''); 191 $iptfwIconModal.show(); 192 $('#iptfw-icon-search').trigger('focus'); 193 }); 194 function syncPreview(){ 195 var cls = $input.val(); 196 $preview.empty(); 197 if (cls){ 198 $preview.append($('<span/>').addClass('dashicons').addClass(cls)); 199 var $clear = $('<button type="button" class="iptfw-remove-icon" aria-label="Remove icon">×</button>').attr('title','Remove icon'); 200 $clear.css({position:'absolute',top:'-6px',right:'-6px',width:'18px',height:'18px',lineHeight:'14px',textAlign:'center',background:'#f0f0f1',border:'1px solid #ccd0d4',borderRadius:'50%',cursor:'pointer',padding:0,fontWeight:'bold'}); 201 $preview.append($clear); 202 } 203 } 204 $input.on('change', syncPreview); 205 syncPreview(); 206 207 $(document).on('click', '.iptfw-remove-icon', function(e){ 208 if ($preview.has(this).length || $preview.is($(this).parent())) { 209 e.preventDefault(); 210 $input.val('').trigger('change'); 211 } 212 }); 213 } 214 })(); 215 216 // Reorder page sortable + save 217 if (iptfw_admin.is_reorder_page) { 218 var $tbody = $('#iptfw-reorder-list'); 219 if (iptfw_admin.can_reorder) { 220 $tbody.sortable({ 221 items: 'tr', 222 cursor: 'move', 223 axis: 'y', 224 containment: 'parent' 225 }); 226 } else { 227 // Disable interaction 228 $tbody.find('tr').addClass('iptfw-disabled'); 229 } 230 231 $('#iptfw-save-order').on('click', function(e) { 232 e.preventDefault(); 233 var $btn = $(this); 234 var order = []; 235 $tbody.find('tr').each(function() { 236 order.push($(this).data('id').toString()); 237 }); 238 $btn.prop('disabled', true).text('Saving...'); 239 if (!iptfw_admin.can_reorder) { 240 return false; 241 } 242 $.post(iptfw_admin.ajax_url, { 243 action: 'iptfw_save_order', 244 nonce: iptfw_admin.nonce, 245 order: order 246 }).done(function(resp) { 247 var ok = resp && (resp.success === true || resp === '1'); 248 var $notice = $('<div class="notice notice-success is-dismissible"><p>Order saved.</p></div>'); 249 if (!ok) { 250 $notice = $('<div class="notice notice-error is-dismissible"><p>Failed to save order.</p></div>'); 251 } 252 $('.wrap h1').after($notice); 253 }).fail(function(xhr) { 254 var msg = 'Failed to save order'; 255 if (xhr && xhr.responseJSON && xhr.responseJSON.data && xhr.responseJSON.data.message) { 256 msg = xhr.responseJSON.data.message; 257 } 258 var $notice = $('<div class="notice notice-error is-dismissible"><p>'+ msg +'</p></div>'); 259 $('.wrap h1').after($notice); 260 }).always(function(){ 261 $btn.prop('disabled', false).text('Save Order'); 262 }); 263 }); 34 264 } 35 265 }); -
product-tabs-for-woo/tags/1.0.2/product-tabs-for-woo.php
r3294577 r3405807 1 1 <?php 2 2 3 /** 3 * Plugin Name: Product Tabs for Woo 4 * Plugin Name: Product Tabs for WooCommerce 4 5 * Plugin URI: https://stackwc.com/plugins/product-tabs-for-woo/ 5 * Description: Add s custom product tabs to WooCommerce products.6 * Version: 1.0. 16 * Description: Add custom product tabs to your WooCommerce products with advanced display conditions, priority settings, and powerful customization options. 7 * Version: 1.0.2 7 8 * Author: StackWC 8 9 * Author URI: https://stackwc.com/ … … 10 11 * Domain Path: /languages 11 12 * Requires at least: 5.0 12 * Requires PHP: 7. 213 * Requires PHP: 7.4 13 14 * License: GPLv3 14 15 * License URI: https://www.gnu.org/licenses/gpl-3.0.html 15 16 * WC requires at least: 7.2 16 * WC tested up to: 9.8.517 * WC tested up to: 10.3.5 17 18 * Requires Plugins: woocommerce 18 19 * 19 20 * Disclaimer: This plugin is not affiliated with or endorsed by WooCommerce or Automattic. 20 21 * "WooCommerce" is a trademark of Automattic Inc. This is an independent plugin. 21 * Copyright: ilmosys infotech LLP22 * Copyright: StackWC brand by ilmosys infotech LLP 22 23 */ 23 24 if (!defined('ABSPATH')) { 24 if ( !defined( 'ABSPATH' ) ) { 25 25 exit; 26 26 } 27 28 // Define plugin constants first 29 define('IPTFW_VERSION', '1.0.1'); 30 define('IPTFW_FILE', __FILE__); 31 define('IPTFW_PATH', plugin_dir_path(__FILE__)); 32 define('IPTFW_URL', plugin_dir_url(__FILE__)); 33 34 // Check if WooCommerce is active 35 function iptfw_is_woocommerce_active() { 36 return in_array('woocommerce/woocommerce.php', apply_filters('active_plugins', get_option('active_plugins'))); 37 } 38 39 // Declare WooCommerce compatibility 40 function iptfw_declare_woocommerce_compatibility() { 41 if (class_exists('\Automattic\WooCommerce\Utilities\FeaturesUtil')) { 42 \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility('custom_order_tables', __FILE__, true); 43 \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility('cart_checkout_blocks', __FILE__, true); 44 \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility('analytics', __FILE__, true); 45 \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility('product_block_editor', __FILE__, true); 46 \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility('product_block_templates', __FILE__, true); 47 \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility('product_editor', __FILE__, true); 48 \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility('product_custom_fields', __FILE__, true); 49 } 50 } 51 add_action('before_woocommerce_init', 'iptfw_declare_woocommerce_compatibility'); 52 53 // Check WooCommerce compatibility 54 function iptfw_check_woocommerce_compatibility() { 55 if (!iptfw_is_woocommerce_active()) { 56 return; 57 } 58 59 // Get WooCommerce version 60 $wc_version = WC()->version; 61 62 // Get required WooCommerce version from plugin header 63 $required_wc_version = '7.2'; 64 65 // Check if WooCommerce version meets requirements 66 if (version_compare($wc_version, $required_wc_version, '<')) { 67 add_action('admin_notices', function() use ($required_wc_version, $wc_version) { 68 ?> 69 <div class="notice notice-error"> 70 <p><?php 71 /* translators: 1: Required WooCommerce version, 2: Current WooCommerce version */ 72 printf( 73 /* translators: 1: Required WooCommerce version, 2: Current WooCommerce version */ 74 esc_html__('Custom Product Tabs for WooCommerce requires WooCommerce version %1$s or higher. You are running version %2$s. Please update WooCommerce to use this plugin.', 'product-tabs-for-woo'), 75 esc_html($required_wc_version), 76 esc_html($wc_version) 77 ); 78 ?></p> 79 </div> 80 <?php 81 }); 82 return false; 83 } 84 85 return true; 86 } 87 add_action('admin_init', 'iptfw_check_woocommerce_compatibility'); 88 89 // Disable activation if WooCommerce is not active 90 function iptfw_disable_activation() { 91 if (!iptfw_is_woocommerce_active()) { 92 // Get the plugin file 93 $plugin_file = plugin_basename(IPTFW_FILE); 94 95 // Add a notice about WooCommerce requirement 96 add_action('admin_notices', function() { 97 ?> 98 <div class="notice notice-error"> 99 <p><?php esc_html_e('Product Tabs for WooCommerce requires WooCommerce to be installed and activated.', 'product-tabs-for-woo'); ?></p> 100 </div> 101 <?php 102 }); 103 104 // Add a notice in the plugins list 105 add_action('after_plugin_row_' . $plugin_file, function() { 106 ?> 107 <tr class="plugin-update-tr"> 108 <td colspan="4" class="plugin-update colspanchange"> 109 <div class="notice notice-error inline"> 110 <p><?php esc_html_e('This plugin cannot be activated because required plugins are missing or inactive.', 'product-tabs-for-woo'); ?></p> 111 <p><?php esc_html_e('Requires: WooCommerce', 'product-tabs-for-woo'); ?></p> 112 </div> 113 </td> 114 </tr> 115 <?php 116 }); 117 118 // Disable the activate link 119 add_filter('plugin_action_links_' . $plugin_file, function($actions) { 120 if (isset($actions['activate'])) { 121 $actions['activate'] = '<span class="button button-disabled">' . esc_html__('Activate', 'product-tabs-for-woo') . '</span>'; 122 } 123 return $actions; 124 }); 125 126 // Prevent activation 127 add_filter('plugin_action_links_' . $plugin_file, function($actions) { 128 if (isset($actions['activate'])) { 129 unset($actions['activate']); 130 } 131 return $actions; 132 }); 133 } 134 } 135 add_action('admin_init', 'iptfw_disable_activation'); 136 137 // Prevent activation if WooCommerce is not active 138 function iptfw_prevent_activation($plugin) { 139 if ($plugin === plugin_basename(IPTFW_FILE) && !iptfw_is_woocommerce_active()) { 140 wp_die( 141 esc_html__('This plugin requires WooCommerce to be installed and activated.', 'product-tabs-for-woo'), 142 esc_html__('Plugin Activation Error', 'product-tabs-for-woo'), 143 array('back_link' => true) 144 ); 145 } 146 } 147 add_action('activate_plugin', 'iptfw_prevent_activation'); 148 149 // Add compatibility information to WooCommerce status 150 function iptfw_add_compatibility_info($sections) { 151 // Verify nonce for admin actions 152 if (isset($_GET['_wpnonce']) && !wp_verify_nonce(sanitize_text_field(wp_unslash($_GET['_wpnonce'])), 'iptfw_compatibility_nonce')) { 153 return $sections; 154 } 155 156 $sections['iptfw_compatibility'] = array( 157 'title' => esc_html__('Custom Product Tabs Compatibility', 'product-tabs-for-woo'), 158 'callback' => 'iptfw_compatibility_section' 159 ); 160 return $sections; 161 } 162 add_filter('woocommerce_admin_status_tabs', 'iptfw_add_compatibility_info'); 163 164 function iptfw_compatibility_section() { 165 // Verify nonce for admin actions 166 if (isset($_GET['_wpnonce']) && !wp_verify_nonce(sanitize_text_field(wp_unslash($_GET['_wpnonce'])), 'iptfw_compatibility_nonce')) { 167 return; 168 } 169 170 ?> 171 <h2><?php esc_html_e('Custom Product Tabs Compatibility', 'product-tabs-for-woo'); ?></h2> 172 <table class="widefat" cellspacing="0"> 173 <thead> 174 <tr> 175 <th><?php esc_html_e('Feature', 'product-tabs-for-woo'); ?></th> 176 <th><?php esc_html_e('Status', 'product-tabs-for-woo'); ?></th> 177 </tr> 178 </thead> 179 <tbody> 180 <tr> 181 <td><?php esc_html_e('WooCommerce Version', 'product-tabs-for-woo'); ?></td> 182 <td> 183 <?php 184 $wc_version = WC()->version; 185 $required_wc_version = '7.2'; 186 if (version_compare($wc_version, $required_wc_version, '>=')) { 187 echo '<mark class="yes">' . esc_html($wc_version) . '</mark>'; 188 } else { 189 echo '<mark class="error">' . esc_html($wc_version) . ' (' . esc_html__('Update Required', 'product-tabs-for-woo') . ')</mark>'; 190 } 191 ?> 192 </td> 193 </tr> 194 <?php if (class_exists('WC_Admin_Features')): ?> 195 <tr> 196 <td><?php esc_html_e('Analytics', 'product-tabs-for-woo'); ?></td> 197 <td> 198 <?php 199 $features = WC_Admin_Features::get_compatible_features(); 200 if (isset($features['analytics']) && $features['analytics']) { 201 echo '<mark class="warning">' . esc_html__('Enabled - May need testing', 'product-tabs-for-woo') . '</mark>'; 202 } else { 203 echo '<mark class="yes">' . esc_html__('Disabled', 'product-tabs-for-woo') . '</mark>'; 204 } 205 ?> 206 </td> 207 </tr> 208 <?php endif; ?> 209 </tbody> 210 </table> 211 <?php 212 } 213 214 // Then include required files 215 require_once IPTFW_PATH . 'includes/class-iptfw-settings.php'; 216 require_once IPTFW_PATH . 'includes/class-iptfw-admin.php'; 217 218 if (!class_exists('IPTFW_Product_Tabs')) { 27 // Freemius Integration 28 if ( !function_exists( 'iptfw_fs' ) ) { 29 // Create a helper function for easy SDK access. 30 function iptfw_fs() { 31 global $iptfw_fs; 32 if ( !isset( $iptfw_fs ) ) { 33 // Include Freemius SDK. 34 require_once dirname( __FILE__ ) . '/vendor/freemius/start.php'; 35 $iptfw_fs = fs_dynamic_init( array( 36 'id' => '19118', 37 'slug' => 'product-tabs-for-woo', 38 'premium_slug' => 'product-tabs-for-woo-pro', 39 'type' => 'plugin', 40 'public_key' => 'pk_16d450f47372bb8f7243e970e61b8', 41 'is_premium' => false, 42 'premium_suffix' => 'Pro', 43 'has_addons' => true, 44 'has_paid_plans' => true, 45 'menu' => array( 46 'first-path' => 'plugins.php', 47 'support' => false, 48 ), 49 'is_live' => true, 50 ) ); 51 } 52 return $iptfw_fs; 53 } 54 55 // Init Freemius. 56 iptfw_fs(); 57 // Signal that SDK was initiated. 58 do_action( 'iptfw_fs_loaded' ); 59 } 60 // Define plugin constants 61 define( 'IPTFW_VERSION', '1.0.2' ); 62 define( 'IPTFW_FILE', __FILE__ ); 63 define( 'IPTFW_PATH', plugin_dir_path( __FILE__ ) ); 64 define( 'IPTFW_URL', plugin_dir_url( __FILE__ ) ); 65 // Include core files 66 require_once IPTFW_PATH . 'stackwc-core/stackwc-core.php'; 67 require_once IPTFW_PATH . 'includes/class-stackwc-compatibility.php'; 68 require_once IPTFW_PATH . 'includes/class-stackwc-post-type.php'; 69 require_once IPTFW_PATH . 'includes/class-stackwc-settings.php'; 70 require_once IPTFW_PATH . 'includes/class-stackwc-admin.php'; 71 require_once IPTFW_PATH . 'includes/class-stackwc-meta-boxes.php'; 72 require_once IPTFW_PATH . 'includes/class-stackwc-tabs.php'; 73 require_once IPTFW_PATH . 'includes/class-stackwc-conditions.php'; 74 require_once IPTFW_PATH . 'includes/class-stackwc-ajax.php'; 75 require_once IPTFW_PATH . 'includes/class-stackwc-columns.php'; 76 // WooCommerce Compatibility 77 add_action( 'before_woocommerce_init', array('IPTFW_Compatibility', 'declare_woocommerce_compatibility') ); 78 add_action( 'admin_init', array('IPTFW_Compatibility', 'check_woocommerce_compatibility') ); 79 add_action( 'admin_init', array('IPTFW_Compatibility', 'disable_activation') ); 80 add_action( 'activate_plugin', array('IPTFW_Compatibility', 'prevent_activation') ); 81 add_filter( 'woocommerce_admin_status_tabs', array('IPTFW_Compatibility', 'add_compatibility_info') ); 82 // Register plugin core menu 83 add_action( 'init', function () { 84 stackwc_register_plugin_menu( array( 85 'menu_title' => __( 'Product Tabs for Woo', 'product-tabs-for-woo' ), 86 'page_title' => __( 'Product Tabs for WooCommerce Settings', 'product-tabs-for-woo' ), 87 'capability' => 'manage_woocommerce', 88 'menu_slug' => 'iptfw_product_tab', 89 'description' => __( 'Adds custom product tabs to WooCommerce products.', 'product-tabs-for-woo' ), 90 'menu_url' => admin_url( 'edit.php?post_type=iptfw_product_tab&tab=settings' ), 91 'callback' => function () { 92 // Check if user has permission to manage WooCommerce 93 if ( !current_user_can( 'manage_woocommerce' ) ) { 94 wp_die( __( 'Sorry, you are not allowed to access this page.', 'product-tabs-for-woo' ) ); 95 } 96 // Redirect immediately 97 wp_safe_redirect( admin_url( 'edit.php?post_type=iptfw_product_tab&tab=settings' ) ); 98 exit; 99 }, 100 ) ); 101 } ); 102 // Modify submenu URL to point directly to the post type edit page 103 // Run on both admin_menu (late) and admin_init (late) to catch menu registration 104 add_action( 'admin_menu', function () { 105 global $submenu; 106 if ( isset( $submenu['stackwc'] ) ) { 107 foreach ( $submenu['stackwc'] as $key => $item ) { 108 if ( isset( $item[2] ) && $item[2] === 'iptfw_product_tab' ) { 109 // Replace the menu slug with the direct URL 110 $submenu['stackwc'][$key][2] = 'edit.php?post_type=iptfw_product_tab&tab=settings'; 111 break; 112 } 113 } 114 } 115 }, 999 ); 116 add_action( 'admin_init', function () { 117 global $submenu; 118 if ( isset( $submenu['stackwc'] ) ) { 119 foreach ( $submenu['stackwc'] as $key => $item ) { 120 if ( isset( $item[2] ) && $item[2] === 'iptfw_product_tab' ) { 121 // Replace the menu slug with the direct URL 122 $submenu['stackwc'][$key][2] = 'edit.php?post_type=iptfw_product_tab'; 123 break; 124 } 125 } 126 } 127 }, 999 ); 128 // Early redirect hook to catch the page load before WordPress permission checks 129 add_action( 'admin_init', function () { 130 if ( isset( $_GET['page'] ) && $_GET['page'] === 'iptfw_product_tab' && !isset( $_GET['post_type'] ) ) { 131 // Check if user has permission 132 if ( current_user_can( 'manage_woocommerce' ) ) { 133 wp_safe_redirect( admin_url( 'edit.php?post_type=iptfw_product_tab&tab=settings' ) ); 134 exit; 135 } 136 } 137 }, 1 ); 138 // Provide plugin-specific template path 139 add_filter( 140 'stackwc_plugin_template_path', 141 function ( $template_path, $current_page ) { 142 if ( $current_page === 'wc-settings&tab=stackwc_qbnbfw' ) { 143 return STACKWC_QBNBFW_PATH . 'includes/templates/wc-settings&tab=stackwc_qbnbfw.php'; 144 } 145 return $template_path; 146 }, 147 10, 148 2 149 ); 150 /** 151 * Main Plugin Class 152 */ 153 if ( !class_exists( 'IPTFW_Product_Tabs' ) ) { 219 154 class IPTFW_Product_Tabs { 220 155 private static $instance = null; 156 221 157 public $settings; 158 222 159 public $admin; 223 private $current_page = '';224 private $current_tab = '';225 160 226 161 public static function instance() { 227 if ( null === self::$instance) {162 if ( null === self::$instance ) { 228 163 self::$instance = new self(); 229 164 } … … 232 167 233 168 public function __construct() { 234 if ( !iptfw_is_woocommerce_active()) {169 if ( !IPTFW_Compatibility::is_woocommerce_active() ) { 235 170 return; 236 171 } 237 238 172 $this->init_hooks(); 239 $this->set_current_page();240 $this->set_current_tab();241 }242 243 private function set_current_page() {244 // Verify nonce for admin actions245 if (isset($_GET['_wpnonce']) && !wp_verify_nonce(sanitize_text_field(wp_unslash($_GET['_wpnonce'])), 'iptfw_page_nonce')) {246 return;247 }248 $this->current_page = isset($_GET['page']) ? sanitize_text_field(wp_unslash($_GET['page'])) : '';249 }250 251 private function set_current_tab() {252 // Verify nonce for admin actions253 if (isset($_GET['_wpnonce']) && !wp_verify_nonce(sanitize_text_field(wp_unslash($_GET['_wpnonce'])), 'iptfw_tab_nonce')) {254 return;255 }256 $this->current_tab = isset($_GET['tab']) ? sanitize_text_field(wp_unslash($_GET['tab'])) : 'list';257 }258 259 public function is_woocommerce_active() {260 return in_array('woocommerce/woocommerce.php', apply_filters('active_plugins', get_option('active_plugins')));261 }262 263 public function woocommerce_missing_notice() {264 ?>265 <div class="notice notice-error">266 <p><?php esc_html_e('Custom Product Tabs requires WooCommerce to be installed and active.', 'product-tabs-for-woo'); ?></p>267 </div>268 <?php269 173 } 270 174 … … 273 177 $this->admin = new IPTFW_Admin($this); 274 178 $this->settings = new IPTFW_Settings(); 275 276 179 // Add text domain loading 277 add_action('init', array($this, 'load_textdomain')); 278 279 add_action('init', array($this, 'register_post_type')); 280 add_action('admin_init', array($this, 'admin_init')); 281 add_action('add_meta_boxes', array($this, 'add_meta_boxes')); 282 add_action('save_post', array($this, 'save_meta_boxes')); 283 add_filter('woocommerce_product_tabs', array($this, 'add_product_tabs')); 284 285 // Add plugin action links 286 add_filter('plugin_action_links_' . plugin_basename(IPTFW_FILE), array($this, 'add_plugin_action_links')); 287 288 // Add these column filters back 289 add_filter('manage_iptfw_product_tab_posts_columns', array($this, 'set_custom_columns')); 290 add_action('manage_iptfw_product_tab_posts_custom_column', array($this, 'custom_column_content'), 10, 2); 291 add_filter('manage_edit-iptfw_product_tab_sortable_columns', array($this, 'sortable_columns')); 180 add_action( 'init', array($this, 'load_textdomain') ); 181 // Post type and meta fields 182 add_action( 'init', array('IPTFW_Post_Type', 'register_post_type') ); 183 add_action( 'init', array('IPTFW_Post_Type', 'register_meta_fields') ); 184 // Meta boxes 185 add_action( 'add_meta_boxes', array('IPTFW_Meta_Boxes', 'add_meta_boxes') ); 186 add_action( 'save_post', array('IPTFW_Meta_Boxes', 'save_meta_boxes') ); 187 // Product tabs 188 add_filter( 'woocommerce_product_tabs', array('IPTFW_Tabs', 'add_product_tabs') ); 189 // Plugin action links 190 add_filter( 'plugin_action_links_' . plugin_basename( IPTFW_FILE ), array($this, 'add_plugin_action_links') ); 191 // Admin columns 192 add_filter( 'manage_iptfw_product_tab_posts_columns', array('IPTFW_Columns', 'set_custom_columns') ); 193 add_action( 194 'manage_iptfw_product_tab_posts_custom_column', 195 array('IPTFW_Columns', 'custom_column_content'), 196 10, 197 2 198 ); 199 add_filter( 'manage_edit-iptfw_product_tab_sortable_columns', array('IPTFW_Columns', 'sortable_columns') ); 200 // AJAX handlers 201 add_action( 'wp_ajax_iptfw_save_order', array('IPTFW_Ajax', 'save_order') ); 202 add_action( 'wp_ajax_iptfw_search_products', array('IPTFW_Ajax', 'search_products') ); 292 203 } 293 204 294 205 public function load_textdomain() { 295 load_plugin_textdomain( 296 'product-tabs-woocommerce', 297 false, 298 dirname(plugin_basename(IPTFW_FILE)) . '/languages' 299 ); 300 } 301 302 public function register_post_type() { 303 $labels = array( 304 'name' => esc_html__('Product Tabs', 'product-tabs-for-woo'), 305 'singular_name' => esc_html__('Product Tab', 'product-tabs-for-woo'), 306 'add_new' => esc_html__('Add New', 'product-tabs-for-woo'), 307 'add_new_item' => esc_html__('Add New Product Tab', 'product-tabs-for-woo'), 308 'edit_item' => esc_html__('Edit Product Tab', 'product-tabs-for-woo'), 309 'new_item' => esc_html__('New Product Tab', 'product-tabs-for-woo'), 310 'view_item' => esc_html__('View Product Tab', 'product-tabs-for-woo'), 311 'search_items' => esc_html__('Search Product Tabs', 'product-tabs-for-woo'), 312 'not_found' => esc_html__('No product tabs found', 'product-tabs-for-woo'), 313 'not_found_in_trash' => esc_html__('No product tabs found in trash', 'product-tabs-for-woo'), 314 'menu_name' => esc_html__('Product Tabs', 'product-tabs-for-woo') 315 ); 316 317 $args = array( 318 'labels' => $labels, 319 'public' => false, 320 'show_ui' => true, 321 'show_in_menu' => false, 322 'capability_type' => 'post', 323 'hierarchical' => false, 324 'supports' => array('title', 'editor'), 325 'show_in_rest' => true, 326 'rewrite' => false 327 ); 328 329 register_post_type('iptfw_product_tab', $args); 330 } 331 332 public function admin_init() { 333 global $parent_file, $submenu_file; 334 335 // Verify nonce for admin actions 336 if (isset($_GET['_wpnonce']) && !wp_verify_nonce(sanitize_text_field(wp_unslash($_GET['_wpnonce'])), 'iptfw_admin_nonce')) { 337 return; 338 } 339 340 // Keep Product Tabs menu active when viewing settings 341 if (isset($_GET['page']) && sanitize_text_field(wp_unslash($_GET['page'])) === 'iptfw-settings') { 342 $parent_file = 'edit.php?post_type=product'; 343 $submenu_file = 'edit.php?post_type=iptfw_product_tab'; 344 } 345 } 346 347 public function add_meta_boxes() { 348 add_meta_box( 349 'iptfw_tab_settings', 350 esc_html__('Tab Settings', 'product-tabs-for-woo'), 351 array($this, 'render_tab_settings'), 352 'iptfw_product_tab', 353 'side', 354 'high' 355 ); 356 } 357 358 public function render_tab_settings($post) { 359 wp_nonce_field('iptfw_save_meta', 'iptfw_meta_nonce'); 360 361 $priority = get_post_meta($post->ID, '_iptfw_tab_priority', true); 362 $display_type = get_post_meta($post->ID, '_iptfw_display_type', true) ?: 'all'; 363 $products = get_post_meta($post->ID, '_iptfw_products', true); 364 $categories = get_post_meta($post->ID, '_iptfw_categories', true); 365 $tags = get_post_meta($post->ID, '_iptfw_tags', true); 366 ?> 367 <div class="iptfw-settings-wrapper"> 368 <div class="components-base-control"> 369 <label class="components-base-control__label" for="tab_priority"> 370 <?php esc_html_e('Priority', 'product-tabs-for-woo'); ?> 371 </label> 372 <input type="number" 373 id="tab_priority" 374 name="tab_priority" 375 value="<?php echo esc_attr($priority); ?>" 376 class="components-text-control__input" /> 377 <p class="components-base-control__help"> 378 <?php esc_html_e('Lower numbers appear first', 'product-tabs-for-woo'); ?> 379 </p> 380 </div> 381 382 <div class="components-base-control"> 383 <label class="components-base-control__label"> 384 <?php esc_html_e('Display Conditions', 'product-tabs-for-woo'); ?> 385 </label> 386 <select name="display_type" 387 id="display_type" 388 class="components-select-control__input"> 389 <option value="all" <?php selected($display_type, 'all'); ?>> 390 <?php esc_html_e('All Products', 'product-tabs-for-woo'); ?> 391 </option> 392 <option value="specific" <?php selected($display_type, 'specific'); ?>> 393 <?php esc_html_e('Specific Products', 'product-tabs-for-woo'); ?> 394 </option> 395 <option value="categories" <?php selected($display_type, 'categories'); ?>> 396 <?php esc_html_e('Product Categories', 'product-tabs-for-woo'); ?> 397 </option> 398 <option value="tags" <?php selected($display_type, 'tags'); ?>> 399 <?php esc_html_e('Product Tags', 'product-tabs-for-woo'); ?> 400 </option> 401 </select> 402 </div> 403 404 <div id="specific_products" class="components-base-control" style="display: <?php echo $display_type === 'specific' ? 'block' : 'none'; ?>"> 405 <label class="components-base-control__label" for="tab_products"> 406 <?php esc_html_e('Product IDs', 'product-tabs-for-woo'); ?> 407 </label> 408 <input type="text" 409 id="tab_products" 410 name="tab_products" 411 value="<?php echo esc_attr($products); ?>" 412 class="components-text-control__input" /> 413 <p class="components-base-control__help"> 414 <?php esc_html_e('Enter product IDs separated by commas', 'product-tabs-for-woo'); ?> 415 </p> 416 </div> 417 418 <div id="product_categories" class="components-base-control" style="display: <?php echo $display_type === 'categories' ? 'block' : 'none'; ?>"> 419 <label class="components-base-control__label" for="tab_categories"> 420 <?php esc_html_e('Select Categories', 'product-tabs-for-woo'); ?> 421 </label> 422 <select name="tab_categories[]" 423 id="tab_categories" 424 class="components-select-control__input" 425 multiple> 426 <?php 427 $product_categories = get_terms(array( 428 'taxonomy' => 'product_cat', 429 'hide_empty' => false 430 )); 431 $selected_cats = explode(',', $categories); 432 foreach ($product_categories as $category) { 433 printf( 434 '<option value="%s" %s>%s</option>', 435 esc_attr($category->term_id), 436 selected(in_array($category->term_id, $selected_cats), true, false), 437 esc_html($category->name) 438 ); 439 } 440 ?> 441 </select> 442 </div> 443 444 <div id="product_tags" class="components-base-control" style="display: <?php echo $display_type === 'tags' ? 'block' : 'none'; ?>"> 445 <label class="components-base-control__label" for="tab_tags"> 446 <?php esc_html_e('Select Tags', 'product-tabs-for-woo'); ?> 447 </label> 448 <select name="tab_tags[]" 449 id="tab_tags" 450 class="components-select-control__input" 451 multiple> 452 <?php 453 $product_tags = get_terms(array( 454 'taxonomy' => 'product_tag', 455 'hide_empty' => false 456 )); 457 $selected_tags = explode(',', $tags); 458 foreach ($product_tags as $tag) { 459 printf( 460 '<option value="%s" %s>%s</option>', 461 esc_attr($tag->term_id), 462 selected(in_array($tag->term_id, $selected_tags), true, false), 463 esc_html($tag->name) 464 ); 465 } 466 ?> 467 </select> 468 </div> 469 </div> 470 </div> 471 <?php 472 } 473 474 public function save_meta_boxes($post_id) { 475 if (!isset($_POST['iptfw_meta_nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['iptfw_meta_nonce'])), 'iptfw_save_meta')) { 476 return; 477 } 478 479 if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) { 480 return; 481 } 482 483 if (!current_user_can('edit_post', $post_id)) { 484 return; 485 } 486 487 if (isset($_POST['tab_priority'])) { 488 update_post_meta($post_id, '_iptfw_tab_priority', sanitize_text_field(wp_unslash($_POST['tab_priority']))); 489 } 490 491 if (isset($_POST['display_type'])) { 492 update_post_meta($post_id, '_iptfw_display_type', sanitize_text_field(wp_unslash($_POST['display_type']))); 493 } 494 495 if (isset($_POST['tab_products'])) { 496 update_post_meta($post_id, '_iptfw_products', sanitize_text_field(wp_unslash($_POST['tab_products']))); 497 } 498 499 if (isset($_POST['tab_categories'])) { 500 $categories = isset($_POST['tab_categories']) ? array_map('absint', wp_unslash($_POST['tab_categories'])) : array(); 501 update_post_meta($post_id, '_iptfw_categories', implode(',', $categories)); 502 } 503 504 if (isset($_POST['tab_tags'])) { 505 $tags = isset($_POST['tab_tags']) ? array_map('absint', wp_unslash($_POST['tab_tags'])) : array(); 506 update_post_meta($post_id, '_iptfw_tags', implode(',', $tags)); 507 } 508 } 509 510 public function set_custom_columns($columns) { 511 $new_columns = array(); 512 $new_columns['cb'] = $columns['cb']; 513 $new_columns['title'] = esc_html__('Tab Title', 'product-tabs-for-woo'); 514 $new_columns['priority'] = esc_html__('Priority', 'product-tabs-for-woo'); 515 $new_columns['display_rules'] = esc_html__('Display Rules', 'product-tabs-for-woo'); 516 $new_columns['date'] = $columns['date']; 517 return $new_columns; 518 } 519 520 public function custom_column_content($column, $post_id) { 521 switch ($column) { 522 case 'priority': 523 $priority = get_post_meta($post_id, '_iptfw_tab_priority', true); 524 echo esc_html($priority ?: '50'); 525 break; 526 527 case 'display_rules': 528 $display_type = get_post_meta($post_id, '_iptfw_display_type', true) ?: 'all'; 529 switch ($display_type) { 530 case 'all': 531 esc_html_e('All Products', 'product-tabs-for-woo'); 532 break; 533 case 'specific': 534 $products = get_post_meta($post_id, '_iptfw_products', true); 535 echo esc_html__('Specific Products', 'product-tabs-for-woo') . ': ' . esc_html($products); 536 break; 537 case 'categories': 538 $categories = get_post_meta($post_id, '_iptfw_categories', true); 539 $cat_names = array(); 540 foreach (explode(',', $categories) as $cat_id) { 541 $term = get_term($cat_id, 'product_cat'); 542 if ($term) { 543 $cat_names[] = $term->name; 544 } 545 } 546 echo esc_html__('Categories', 'product-tabs-for-woo') . ': ' . esc_html(implode(', ', $cat_names)); 547 break; 548 case 'tags': 549 $tags = get_post_meta($post_id, '_iptfw_tags', true); 550 $tag_names = array(); 551 foreach (explode(',', $tags) as $tag_id) { 552 $term = get_term($tag_id, 'product_tag'); 553 if ($term) { 554 $tag_names[] = $term->name; 555 } 556 } 557 echo esc_html__('Tags', 'product-tabs-for-woo') . ': ' . esc_html(implode(', ', $tag_names)); 558 break; 559 } 560 break; 561 } 562 } 563 564 public function sortable_columns($columns) { 565 $columns['priority'] = 'priority'; 566 return $columns; 567 } 568 569 public function add_product_tabs($tabs) { 570 // Handle default tab visibility 571 $options = get_option('iptfw_settings', array()); 572 $default_tabs = array('description', 'additional_information', 'reviews'); 573 574 foreach ($default_tabs as $tab_id) { 575 $show_key = 'show_' . $tab_id; 576 // Check if the setting exists and is explicitly set to 0/false 577 if (isset($options[$show_key]) && !$options[$show_key]) { 578 unset($tabs[$tab_id]); 579 } 580 } 581 582 // Add custom tabs - Using a more efficient query 583 $args = array( 584 'post_type' => 'iptfw_product_tab', 585 'posts_per_page' => -1, 586 'post_status' => 'publish', 587 'orderby' => 'menu_order', 588 'order' => 'ASC' 589 ); 590 591 // Get all tabs first 592 $custom_tabs = get_posts($args); 593 $product_id = get_the_ID(); 594 595 // Cache priorities to avoid multiple meta queries 596 $tab_priorities = array(); 597 foreach ($custom_tabs as $tab) { 598 $tab_priorities[$tab->ID] = get_post_meta($tab->ID, '_iptfw_tab_priority', true); 599 } 600 601 // Sort tabs by priority 602 usort($custom_tabs, function($a, $b) use ($tab_priorities) { 603 $priority_a = empty($tab_priorities[$a->ID]) ? 50 : intval($tab_priorities[$a->ID]); 604 $priority_b = empty($tab_priorities[$b->ID]) ? 50 : intval($tab_priorities[$b->ID]); 605 return $priority_a - $priority_b; 606 }); 607 608 foreach ($custom_tabs as $tab) { 609 if ($this->should_display_tab($tab->ID, $product_id)) { 610 $priority = empty($tab_priorities[$tab->ID]) ? 50 : intval($tab_priorities[$tab->ID]); 611 612 $tabs['iptfw_tab_' . $tab->ID] = array( 613 'title' => $tab->post_title, 614 'priority' => $priority, 615 'callback' => array($this, 'render_tab_content'), 616 'tab_id' => $tab->ID 617 ); 618 } 619 } 620 621 return $tabs; 622 } 623 624 public function render_tab_content($key, $tab) { 625 $content = get_post_field('post_content', $tab['tab_id']); 626 echo wp_kses_post(apply_filters('the_content', $content)); 627 } 628 629 private function should_display_tab($tab_id, $product_id) { 630 $display_type = get_post_meta($tab_id, '_iptfw_display_type', true) ?: 'all'; 631 632 switch ($display_type) { 633 case 'all': 634 return true; 635 636 case 'specific': 637 $products = get_post_meta($tab_id, '_iptfw_products', true); 638 if (empty($products)) { 639 return false; 640 } 641 $product_ids = array_map('trim', explode(',', $products)); 642 return in_array($product_id, $product_ids); 643 644 case 'categories': 645 $tab_categories = get_post_meta($tab_id, '_iptfw_categories', true); 646 if (empty($tab_categories)) { 647 return false; 648 } 649 $tab_cat_ids = array_map('trim', explode(',', $tab_categories)); 650 $product_cats = wp_get_post_terms($product_id, 'product_cat', array('fields' => 'ids')); 651 return !empty(array_intersect($tab_cat_ids, $product_cats)); 652 653 case 'tags': 654 $tab_tags = get_post_meta($tab_id, '_iptfw_tags', true); 655 if (empty($tab_tags)) { 656 return false; 657 } 658 $tab_tag_ids = array_map('trim', explode(',', $tab_tags)); 659 $product_tags = wp_get_post_terms($product_id, 'product_tag', array('fields' => 'ids')); 660 return !empty(array_intersect($tab_tag_ids, $product_tags)); 661 662 default: 663 return false; 664 } 665 } 666 667 /** 668 * Add settings link to plugin action links 669 */ 670 public function add_plugin_action_links($links) { 671 $settings_link = sprintf( 672 '<a href="%s">%s</a>', 673 esc_url(admin_url('admin.php?page=iptfw-settings')), 674 esc_html__('Settings', 'product-tabs-for-woo') 675 ); 676 677 // Add settings link to the beginning of the array 678 array_unshift($links, $settings_link); 679 680 return $links; 681 } 682 } 683 206 load_plugin_textdomain( 'product-tabs-for-woo', false, dirname( plugin_basename( IPTFW_FILE ) ) . '/languages' ); 207 } 208 209 /** 210 * Add settings link to plugin action links 211 */ 212 public function add_plugin_action_links( $links ) { 213 $settings_link = sprintf( '<a href="%s">%s</a>', esc_url( admin_url( 'edit.php?post_type=iptfw_product_tab&tab=settings' ) ), esc_html__( 'Settings', 'product-tabs-for-woo' ) ); 214 array_unshift( $links, $settings_link ); 215 return $links; 216 } 217 218 } 219 220 } 684 221 // Initialize the plugin 685 222 function IPTFW() { … … 688 225 689 226 // Start the plugin 690 add_action('plugins_loaded', 'IPTFW'); 691 } 227 add_action( 'plugins_loaded', 'IPTFW' ); -
product-tabs-for-woo/tags/1.0.2/readme.txt
r3294575 r3405807 1 === Product Tabs for Woo ===2 Contributors: ilmosys, stackwc, mahdiali1 === Product Tabs for WooCommerce === 2 Contributors: stackwc, ilmosys, mahdiali 3 3 Tags: woocommerce, product tabs, custom tabs, product tabs for WooCommerce 4 4 Requires at least: 5.0 5 Tested up to: 6. 86 Requires PHP: 7. 27 Stable tag: 1.0. 15 Tested up to: 6.9 6 Requires PHP: 7.4 7 Stable tag: 1.0.2 8 8 License: GPLv3 9 9 License URI: https://www.gnu.org/licenses/gpl-3.0.html 10 10 WC requires at least: 7.2 11 WC tested up to: 9.8.511 WC tested up to: 10.3.5 12 12 13 Add custom product tabs to your WooCommerce products with advanced display conditions and priority settings.13 Add custom product tabs to your WooCommerce products with advanced display conditions, priority settings, and powerful customization options. 14 14 15 15 == Description == 16 16 17 Custom Product Tabs for WooCommerce is a lightweight yet powerful plugin that lets you create, edit, and manage product tabs effortlessly. With a built-in block editor, you can design custom tabs or import ready-made patterns for a seamless experience. The plugin also includes a settings page where you can control tab priorities and show or hide WooCommerce default tabs like Description, Additional Information, and Reviews. Enhance your product pages with structured, engaging content and improve the shopping experience with flexible tab management! 17 <a href="https://stackwc.com/plugins/product-tabs-for-woo/?utm_source=wprepo&utm_medium=link&utm_campaign=plugin" title="StackWC - Product Tabs for WooCommerce Plugin"> Product Tabs for WooCommerce </a> is a powerful and flexible plugin that allows you to create unlimited custom tabs for your WooCommerce products. With an intuitive interface, advanced display conditions, and comprehensive customization options, you can enhance your product pages with rich, engaging content that improves the shopping experience. 18 18 19 = Key Features = 19 The plugin features a modern block editor integration, allowing you to design beautiful tab content using WordPress blocks. You can also use the classic editor for quick content creation. With Pro features, you get access to advanced display conditions, tab reordering, icon customization, and much more. 20 20 21 * Create unlimited custom tabs for your products 22 * Set display conditions based on specific products, categories, or tags 23 * Control tab priority and order 24 * Rich text editor for tab content 25 * SEO-friendly tab implementation 21 <p>⭐ <a href="https://stackwc.com/plugins/product-tabs-for-woo/?utm_source=wprepo&utm_medium=link&utm_campaign=upgrade-pro"> Upgrade to Pro </a> | 🚀 <a href="https://stackwc.com/plugins/product-tabs-for-woo/"> Try the Demo< /a> | 🛟 <a href="https://stackwc.com/support/"> Get Support </a> </p> 22 23 = Key Features (Free Version) = 24 25 * Create unlimited custom product tabs 26 * Block editor support for rich content creation 27 * Classic editor support for quick editing 28 * Display conditions: All products, Specific products, Product types 29 * Tab priority system for controlling display order 30 * Show/hide default WooCommerce tabs (Description, Additional Information, Reviews) 31 * SEO-friendly implementation 26 32 * Responsive design compatible 27 * Easy to use interface 33 * Easy-to-use interface 34 * Product search for selecting specific products 35 * Support for multiple product types (Simple, Variable, Grouped, External, Downloadable, Virtual) 36 37 = Pro Features = 38 39 * Advanced Display Conditions: 40 * Product Categories - Show tabs based on product categories 41 * Product Tags - Display tabs for products with specific tags 42 * User Roles - Control tab visibility based on user roles (including guest users) 43 * Price Range - Show tabs for products within specific price ranges 44 * Stock Status - Display tabs based on product stock status (In Stock, Out of Stock, On Backorder) 45 * Sale Status - Show tabs for products on sale or regular price 46 * Date Range (Seasonal) - Display tabs only within specific date ranges 47 * Shipping Classes - Show tabs based on product shipping classes 48 * Combine multiple conditions for precise control 49 50 * Tab Management: 51 * Drag & Drop Reorder - Visually reorder tabs with drag and drop interface 52 * Tab Icons - Add Dashicons or custom CSS classes to tab titles 53 * Rename Default Tabs - Customize names of Description, Additional Information, and Reviews tabs 54 * Icon Support for Default Tabs - Add icons to default WooCommerce tabs 28 55 29 56 = Use Cases = 30 57 31 * Add product specifications 32 * Display shipping information 33 * Show size guides 34 * Include warranty information 35 * Add custom product features 36 * Display related products 37 * Show product comparisons 38 * Include video tutorials 39 * Design and customize any content you want using the block editor 58 * Product Specifications - Add detailed technical specifications 59 * Shipping Information - Display shipping policies and delivery times 60 * Size Guides - Include size charts and measurement guides 61 * Warranty Information - Show warranty terms and conditions 62 * Custom Product Features - Highlight unique selling points 63 * Related Products - Display related or complementary products 64 * Product Comparisons - Show comparison tables 65 * Video Tutorials - Embed instructional videos 66 * Customer Reviews - Add custom review sections 67 * FAQs - Include frequently asked questions 68 * Installation Guides - Provide setup instructions 69 * Seasonal Promotions - Show time-limited offers 70 * Role-based Content - Display different content for different user types 71 * Price-based Tabs - Show special tabs for products in specific price ranges 72 73 = Display Conditions Explained = 74 75 The plugin offers flexible display conditions to control when tabs appear: 76 77 * All Products - Tab appears on all product pages 78 * Specific Products - Select individual products by ID 79 * Product Type - Show tabs for specific product types (Simple, Variable, Grouped, External, Downloadable, Virtual) 80 * Product Categories (Pro) - Display tabs for products in selected categories 81 * Product Tags (Pro) - Show tabs for products with specific tags 82 * Advanced Conditions (Pro) - Combine multiple conditions: 83 * User Roles - Show tabs to specific user roles or guests 84 * Price Range - Display tabs for products within min/max price 85 * Stock Status - Show tabs based on product availability 86 * Sale Status - Display tabs for products on sale or regular price 87 * Date Range - Show tabs only during specific date periods (seasonal content) 88 * Shipping Classes - Display tabs based on product shipping class 89 40 90 41 91 == Installation == 42 1. Upload the plugin files to the /wp-content/plugins/product-tabs-woocommerce directory or install the plugin directly from the WordPress Plugins screen. 43 2. Activate the plugin through the Plugins screen in WordPress. 44 3. Navigate to WooCommerce > Products > Product Tabs to access the page. 45 4. Click the “Add New Product Tab” button to create your first custom tab. 92 93 1. Upload the plugin files to the `/wp-content/plugins/product-tabs-for-woo` directory, or install the plugin through the WordPress plugins screen directly. 94 2. Activate the plugin through the 'Plugins' screen in WordPress. 95 3. Ensure WooCommerce is installed and activated (required). 96 4. Navigate to WooCommerce > Products > Product Tabs to access the management page. 97 5. Click "Add New Product Tab" to create your first custom tab. 98 6. Configure display conditions and priority settings. 99 7. Add your content using the block editor or classic editor. 100 8. Visit a product page to see your custom tabs in action. 46 101 47 102 == Frequently Asked Questions == 48 103 49 = Does this plugin require WooCommerce? =50 Yes, this plugin requires WooCommerce to be installed and activated on your WordPress site.51 52 104 = Can I create multiple product tabs for different products? = 53 Yes, you can create unlimited custom tabs and set specific display conditions for each tab based on products, categories, or tags.105 Yes, you can create unlimited custom tabs and set specific display conditions for each tab. You can show tabs on all products, specific products, or based on categories, tags, and other advanced conditions (Pro). 54 106 55 107 = Can I control the order of tabs? = 56 Yes, you can set a priority number for each tab. Lower numbers appear first in the tab order. 108 Yes, you can set a priority number for each tab. Lower numbers appear first in the tab order. Pro users can also use the drag-and-drop reorder interface for visual tab management. 57 109 58 110 = Can I use HTML in tab content? = 59 Yes, the tab content editor supports HTML and rich text formatting.111 Yes, the tab content editor supports HTML, rich text formatting, and WordPress blocks. You can use the block editor to create rich, structured content. 60 112 61 113 = Is this plugin compatible with my theme? = 62 This plugin is compatible with most WooCommerce themes. It uses standard WooCommerce hooks and filters for tab integration .114 This plugin is compatible with most WooCommerce themes. It uses standard WooCommerce hooks and filters for tab integration, ensuring compatibility with the WooCommerce ecosystem. 63 115 64 116 = Does this plugin affect my site's performance? = 65 The plugin is optimized for performance and only loads tab content when needed. It uses WordPress caching mechanisms and efficient database queries. 117 The plugin is optimized for performance and only loads tab content when needed. It uses efficient database queries, WordPress caching mechanisms, and lazy loading where appropriate. 118 119 = Can I hide default WooCommerce tabs? = 120 Yes, you can show or hide the default WooCommerce tabs (Description, Additional Information, Reviews) from the Settings page. 121 122 = Can I rename default WooCommerce tabs? = 123 Yes, Pro users can rename the default WooCommerce tabs (Description, Additional Information, Reviews) and add icons to them. 124 125 = Can I show different tabs to different user roles? = 126 Yes, Pro users can set user role-based display conditions, allowing different tabs to be shown to different user types, including guest users. 127 128 = Can I create seasonal or time-limited tabs? = 129 Yes, Pro users can set date range conditions to display tabs only within specific date periods, perfect for seasonal promotions or limited-time offers. 130 131 = Does the plugin work with variable products? = 132 Yes, the plugin works with all WooCommerce product types including Simple, Variable, Grouped, External, Downloadable, and Virtual products. 133 134 = Can I use custom icons for tabs? = 135 Yes, Pro users can add Dashicons or custom CSS classes to tab titles for enhanced visual presentation. 66 136 67 137 == Screenshots == 68 138 69 1. Product Tabs Management Screen 70 2. Tab Settings Interface 71 3. Display Conditions Settings 72 4. Product Page with Custom Tabs 139 1. Product Tabs Management Screen - Overview of all custom tabs 140 2. Tab Settings Interface - Configure display conditions and priority 141 3. Display Conditions Settings - Advanced condition options (Pro) 142 4. Block Editor Integration - Rich content creation with blocks 143 5. Tab Reorder Interface - Drag and drop tab ordering (Pro) 144 6. Product Page with Custom Tabs - Tabs displayed on product page 145 7. Settings Page - Control default tabs and global settings 146 8. Advanced Conditions - Multiple condition types (Pro) 73 147 74 148 == Changelog == 75 149 150 = 1.0.2 - 29 Nov, 2025 = 151 * Updated: Tested up to WordPress 6.9 152 * Updated: WooCommerce compatibility tested up to 10.3.5 153 * Updated: Minimum PHP requirement to 7.4 154 * Added: Freemius SDK integration 155 * Added: Advanced tab conditions system with custom rules 156 * Added: New admin interface with improved UX 157 * Improved: Codebase restructured for better maintainability 158 * Improved: Security enhancements and capability checks 159 * Improved: Performance optimizations and caching 160 * Fixed: Various UI/UX issues 161 * Removed: Outdated code and deprecated functions 162 76 163 = 1.0.1 - 16 May, 2025 = 77 * Updated: Tested up to WordPress 6.8. 164 * Updated: Tested up to WordPress 6.8 165 * Improved: WooCommerce compatibility declarations 78 166 79 167 = 1.0.0 = 80 168 * Initial release 169 * Basic tab creation and management 170 * Display conditions (All, Specific Products, Product Types) 171 * Priority system 172 * Block editor support 81 173 82 174 == Upgrade Notice == 83 84 == Privacy Policy ==85 This plugin does not collect or store any personal data. It only uses WordPress and WooCommerce core functionality to display custom tabs on product pages.86 87 == Disclaimer ==88 This plugin is not affiliated with or endorsed by WooCommerce or Automattic. "WooCommerce" is a trademark of Automattic Inc. This plugin is an independent product developed to extend WooCommerce functionality. -
product-tabs-for-woo/trunk/assets/css/admin.css
r3270803 r3405807 23 23 color: #757575; 24 24 font-style: italic; 25 } 26 27 /* Select2 WordPress Admin Styling */ 28 .select2-container { 29 max-width: 400px; 30 } 31 .select2-container--default .select2-selection--multiple { 32 border: 1px solid #8c8f94; 33 border-radius: 4px; 34 min-height: 30px; 35 padding: 2px; 36 } 37 .select2-container--default.select2-container--focus .select2-selection--multiple { 38 border-color: #2271b1; 39 box-shadow: 0 0 0 1px #2271b1; 40 } 41 .select2-container--default .select2-selection--multiple .select2-selection__choice { 42 background-color: #2271b1; 43 border: 1px solid #2271b1; 44 color: #fff; 45 padding: 2px 8px; 46 margin: 2px; 47 } 48 .select2-container--default .select2-selection--multiple .select2-selection__choice__remove { 49 color: #fff; 50 margin-right: 5px; 51 } 52 .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover { 53 color: #f0f0f1; 25 54 } -
product-tabs-for-woo/trunk/assets/js/admin.js
r3270803 r3405807 1 1 jQuery(document).ready(function($) { 2 // --- Shared icon picker modal helpers (used on Settings and Post Editor) --- 3 var $iptfwIconModal = null, iptfwAllIcons = null, iptfwIconTarget = null; 4 function iptfwGetAllDashicons(){ 5 if (iptfwAllIcons) return iptfwAllIcons; 6 try { 7 var classes = new Set(); 8 for (var i=0;i<document.styleSheets.length;i++){ 9 var ss = document.styleSheets[i]; 10 if (!ss || !ss.href || ss.href.indexOf('dashicons') === -1) continue; 11 var rules = ss.cssRules || ss.rules; if (!rules) continue; 12 for (var j=0;j<rules.length;j++){ 13 var sel = rules[j].selectorText || ''; 14 if (sel && sel.indexOf('.dashicons-') !== -1){ 15 sel.split(',').forEach(function(part){ 16 var m = (part.trim().match(/\.dashicons-([a-z0-9\-]+)/)); 17 if (m) classes.add('dashicons-' + m[1]); 18 }); 19 } 20 } 21 } 22 iptfwAllIcons = Array.from(classes).sort(); 23 } catch(e){ 24 iptfwAllIcons = ['dashicons-admin-generic','dashicons-star-filled','dashicons-yes','dashicons-heart','dashicons-cart','dashicons-products','dashicons-format-video','dashicons-clipboard','dashicons-analytics','dashicons-download']; 25 } 26 return iptfwAllIcons; 27 } 28 function iptfwEnsureIconModal(){ 29 if ($iptfwIconModal) return $iptfwIconModal; 30 $iptfwIconModal = $('<div id="iptfw-icon-modal" class="iptfw-modal" style="display:none;position:fixed;inset:0;background:rgba(0,0,0,.4);z-index:100000;">\ 31 <div class="iptfw-modal-inner" style="background:#fff;max-width:700px;margin:60px auto;padding:16px;border-radius:6px;">\ 32 <div style="display:flex;justify-content:space-between;align-items:center;">\ 33 <h2 style="margin:0 0 12px;">Select an Icon</h2>\ 34 <button type="button" class="button" id="iptfw-icon-close">Close</button>\ 35 </div>\ 36 <input type="text" id="iptfw-icon-search" placeholder="Search..." class="regular-text" style="width:100%;margin-bottom:12px;"/>\ 37 <div class="iptfw-icon-grid" style="display:grid;grid-template-columns:repeat(6,1fr);gap:10px;max-height:360px;overflow:auto;"></div>\ 38 </div>\ 39 </div>'); 40 $('body').append($iptfwIconModal); 41 function renderIcons(filter){ 42 var icons = iptfwGetAllDashicons(); 43 var $grid = $iptfwIconModal.find('.iptfw-icon-grid').empty(); 44 icons.forEach(function(cls){ 45 if (filter && cls.toLowerCase().indexOf(filter.toLowerCase()) === -1) return; 46 var $item = $('<button type="button" class="button" style="height:48px;display:flex;align-items:center;justify-content:center;">\ 47 <span class="dashicons '+cls+'"></span></button>').data('icon', cls); 48 $grid.append($item); 49 }); 50 } 51 $iptfwIconModal.on('click', '#iptfw-icon-close', function(){ $iptfwIconModal.hide(); }); 52 $iptfwIconModal.on('click', function(e){ if (e.target === $iptfwIconModal[0]) { $iptfwIconModal.hide(); } }); 53 $iptfwIconModal.on('keyup', '#iptfw-icon-search', function(){ renderIcons($(this).val()); }); 54 $iptfwIconModal.on('click', '.iptfw-icon-grid .button', function(){ if (!iptfwIconTarget) return; iptfwIconTarget.val($(this).data('icon')).trigger('change'); $iptfwIconModal.hide(); }); 55 // Initial fill 56 renderIcons(''); 57 return $iptfwIconModal; 58 } 2 59 // Display type toggle functionality 3 60 $('#display_type').on('change', function() { 4 $('#specific_products, #product_ categories, #product_tags').hide();61 $('#specific_products, #product_type, #product_categories, #product_tags, #advanced_conditions').hide(); 5 62 switch($(this).val()) { 6 63 case 'specific': 7 64 $('#specific_products').show(); 8 65 break; 66 case 'product_type': 67 $('#product_type').show(); 68 break; 9 69 case 'categories': 10 70 $('#product_categories').show(); … … 13 73 $('#product_tags').show(); 14 74 break; 75 case 'advanced': 76 $('#advanced_conditions').show(); 77 break; 15 78 } 16 79 }); … … 19 82 $('#display_type').trigger('change'); 20 83 21 // Settings button functionality 22 function initSettingsButton() { 23 var $addNewButton = $('.wrap h1.wp-heading-inline').next('.page-title-action'); 24 if ($addNewButton.length) { 25 $addNewButton.after( 26 '<a href="' + iptfw_admin.settings_url + '" class="page-title-action" style="margin-left: 10px;">' + 27 iptfw_admin.settings_text + '</a>' 28 ); 29 } 30 } 31 32 if (iptfw_admin.is_product_tab_page) { 33 initSettingsButton(); 84 // Initialize Select2 for product search 85 if ($('.iptfw-product-search').length) { 86 $('.iptfw-product-search').select2({ 87 ajax: { 88 url: iptfw_admin.ajax_url, 89 dataType: 'json', 90 delay: 250, 91 data: function (params) { 92 return { 93 q: params.term, 94 action: 'iptfw_search_products', 95 nonce: iptfw_admin.admin_nonce 96 }; 97 }, 98 processResults: function (data) { 99 return { 100 results: data.results 101 }; 102 }, 103 cache: true 104 }, 105 minimumInputLength: 2, 106 placeholder: 'Search for products...', 107 allowClear: true 108 }); 109 } 110 111 // Settings and Reorder are now handled by tabs navigation 112 113 // Global icon picker handler (works on settings page and block editor) 114 $(document).on('click', '.iptfw-icon-picker', function(e){ 115 e.preventDefault(); 116 if (!iptfw_admin.has_premium) { window.alert('This is a Pro feature.'); return; } 117 iptfwEnsureIconModal(); 118 iptfwIconTarget = $('#'+$(this).data('target')); 119 $('#iptfw-icon-search').val(''); 120 $iptfwIconModal.show(); 121 $('#iptfw-icon-search').trigger('focus'); 122 }); 123 124 // Settings: icon picker modal 125 if (iptfw_admin.is_settings_page) { 126 127 // Add picker buttons and preview next to icon fields; hide raw input 128 ['icon_description','icon_additional_information','icon_reviews'].forEach(function(id){ 129 var $input = $('#'+id); 130 if ($input.length) { 131 var $btn = $('<button type="button" class="button iptfw-icon-picker" style="margin-left:8px;">Select Icon</button>').attr('data-target', id); 132 var $preview = $('<span class="iptfw-icon-preview" style="display:inline-flex;align-items:center;justify-content:center;margin-left:8px;min-width:28px;position:relative;height:28px;width:28px;"></span>'); 133 function syncPreview(){ 134 var cls = $input.val(); 135 $preview.empty(); 136 if (cls){ 137 $preview.append($('<span/>').addClass('dashicons').addClass(cls)); 138 // Simple clear button (×) at top-right 139 var $clear = $('<button type="button" class="iptfw-remove-icon" aria-label="Remove icon">×</button>').attr('title','Remove icon'); 140 $clear.css({ 141 position:'absolute', 142 top:'-6px', 143 right:'-6px', 144 width:'18px', 145 height:'18px', 146 lineHeight:'14px', 147 textAlign:'center', 148 background:'#f0f0f1', 149 border:'1px solid #ccd0d4', 150 borderRadius:'50%', 151 cursor:'pointer', 152 padding:0, 153 fontWeight:'bold' 154 }); 155 $preview.append($clear); 156 } 157 } 158 $input.css({width:'0',padding:'0',border:'0',opacity:0,position:'absolute'}); 159 $input.after($btn).after($preview); 160 syncPreview(); 161 $input.on('change', syncPreview); 162 } 163 }); 164 165 // Remove icon handler 166 $(document).on('click', '.iptfw-remove-icon', function(e){ 167 e.preventDefault(); 168 var $wrap = $(this).closest('.iptfw-icon-preview'); 169 var $input = $wrap.prevAll('input[type="text"]').first(); 170 if ($input.length){ 171 $input.val('').trigger('change'); 172 } 173 }); 174 } 175 176 // Post editor: apply picker/preview to custom tab sidebar field if present 177 (function(){ 178 var $input = $('#iptfw_icon'); 179 if ($input.length) { 180 var $btn = $('.iptfw-icon-picker[data-target="iptfw_icon"]'); 181 var $preview = $input.siblings('.iptfw-icon-preview'); 182 // Hide the raw input; keep value for submission 183 $input.css({width:'0',padding:'0',border:'0',opacity:0,position:'absolute'}); 184 // Click handler to open modal on editor 185 $(document).on('click', '.iptfw-icon-picker[data-target="iptfw_icon"]', function(e){ 186 e.preventDefault(); 187 if (!iptfw_admin.has_premium) { window.alert('This is a Pro feature.'); return; } 188 iptfwEnsureIconModal(); 189 iptfwIconTarget = $input; 190 $('#iptfw-icon-search').val(''); 191 $iptfwIconModal.show(); 192 $('#iptfw-icon-search').trigger('focus'); 193 }); 194 function syncPreview(){ 195 var cls = $input.val(); 196 $preview.empty(); 197 if (cls){ 198 $preview.append($('<span/>').addClass('dashicons').addClass(cls)); 199 var $clear = $('<button type="button" class="iptfw-remove-icon" aria-label="Remove icon">×</button>').attr('title','Remove icon'); 200 $clear.css({position:'absolute',top:'-6px',right:'-6px',width:'18px',height:'18px',lineHeight:'14px',textAlign:'center',background:'#f0f0f1',border:'1px solid #ccd0d4',borderRadius:'50%',cursor:'pointer',padding:0,fontWeight:'bold'}); 201 $preview.append($clear); 202 } 203 } 204 $input.on('change', syncPreview); 205 syncPreview(); 206 207 $(document).on('click', '.iptfw-remove-icon', function(e){ 208 if ($preview.has(this).length || $preview.is($(this).parent())) { 209 e.preventDefault(); 210 $input.val('').trigger('change'); 211 } 212 }); 213 } 214 })(); 215 216 // Reorder page sortable + save 217 if (iptfw_admin.is_reorder_page) { 218 var $tbody = $('#iptfw-reorder-list'); 219 if (iptfw_admin.can_reorder) { 220 $tbody.sortable({ 221 items: 'tr', 222 cursor: 'move', 223 axis: 'y', 224 containment: 'parent' 225 }); 226 } else { 227 // Disable interaction 228 $tbody.find('tr').addClass('iptfw-disabled'); 229 } 230 231 $('#iptfw-save-order').on('click', function(e) { 232 e.preventDefault(); 233 var $btn = $(this); 234 var order = []; 235 $tbody.find('tr').each(function() { 236 order.push($(this).data('id').toString()); 237 }); 238 $btn.prop('disabled', true).text('Saving...'); 239 if (!iptfw_admin.can_reorder) { 240 return false; 241 } 242 $.post(iptfw_admin.ajax_url, { 243 action: 'iptfw_save_order', 244 nonce: iptfw_admin.nonce, 245 order: order 246 }).done(function(resp) { 247 var ok = resp && (resp.success === true || resp === '1'); 248 var $notice = $('<div class="notice notice-success is-dismissible"><p>Order saved.</p></div>'); 249 if (!ok) { 250 $notice = $('<div class="notice notice-error is-dismissible"><p>Failed to save order.</p></div>'); 251 } 252 $('.wrap h1').after($notice); 253 }).fail(function(xhr) { 254 var msg = 'Failed to save order'; 255 if (xhr && xhr.responseJSON && xhr.responseJSON.data && xhr.responseJSON.data.message) { 256 msg = xhr.responseJSON.data.message; 257 } 258 var $notice = $('<div class="notice notice-error is-dismissible"><p>'+ msg +'</p></div>'); 259 $('.wrap h1').after($notice); 260 }).always(function(){ 261 $btn.prop('disabled', false).text('Save Order'); 262 }); 263 }); 34 264 } 35 265 }); -
product-tabs-for-woo/trunk/product-tabs-for-woo.php
r3294577 r3405807 1 1 <?php 2 2 3 /** 3 * Plugin Name: Product Tabs for Woo 4 * Plugin Name: Product Tabs for WooCommerce 4 5 * Plugin URI: https://stackwc.com/plugins/product-tabs-for-woo/ 5 * Description: Add s custom product tabs to WooCommerce products.6 * Version: 1.0. 16 * Description: Add custom product tabs to your WooCommerce products with advanced display conditions, priority settings, and powerful customization options. 7 * Version: 1.0.2 7 8 * Author: StackWC 8 9 * Author URI: https://stackwc.com/ … … 10 11 * Domain Path: /languages 11 12 * Requires at least: 5.0 12 * Requires PHP: 7. 213 * Requires PHP: 7.4 13 14 * License: GPLv3 14 15 * License URI: https://www.gnu.org/licenses/gpl-3.0.html 15 16 * WC requires at least: 7.2 16 * WC tested up to: 9.8.517 * WC tested up to: 10.3.5 17 18 * Requires Plugins: woocommerce 18 19 * 19 20 * Disclaimer: This plugin is not affiliated with or endorsed by WooCommerce or Automattic. 20 21 * "WooCommerce" is a trademark of Automattic Inc. This is an independent plugin. 21 * Copyright: ilmosys infotech LLP22 * Copyright: StackWC brand by ilmosys infotech LLP 22 23 */ 23 24 if (!defined('ABSPATH')) { 24 if ( !defined( 'ABSPATH' ) ) { 25 25 exit; 26 26 } 27 28 // Define plugin constants first 29 define('IPTFW_VERSION', '1.0.1'); 30 define('IPTFW_FILE', __FILE__); 31 define('IPTFW_PATH', plugin_dir_path(__FILE__)); 32 define('IPTFW_URL', plugin_dir_url(__FILE__)); 33 34 // Check if WooCommerce is active 35 function iptfw_is_woocommerce_active() { 36 return in_array('woocommerce/woocommerce.php', apply_filters('active_plugins', get_option('active_plugins'))); 37 } 38 39 // Declare WooCommerce compatibility 40 function iptfw_declare_woocommerce_compatibility() { 41 if (class_exists('\Automattic\WooCommerce\Utilities\FeaturesUtil')) { 42 \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility('custom_order_tables', __FILE__, true); 43 \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility('cart_checkout_blocks', __FILE__, true); 44 \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility('analytics', __FILE__, true); 45 \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility('product_block_editor', __FILE__, true); 46 \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility('product_block_templates', __FILE__, true); 47 \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility('product_editor', __FILE__, true); 48 \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility('product_custom_fields', __FILE__, true); 49 } 50 } 51 add_action('before_woocommerce_init', 'iptfw_declare_woocommerce_compatibility'); 52 53 // Check WooCommerce compatibility 54 function iptfw_check_woocommerce_compatibility() { 55 if (!iptfw_is_woocommerce_active()) { 56 return; 57 } 58 59 // Get WooCommerce version 60 $wc_version = WC()->version; 61 62 // Get required WooCommerce version from plugin header 63 $required_wc_version = '7.2'; 64 65 // Check if WooCommerce version meets requirements 66 if (version_compare($wc_version, $required_wc_version, '<')) { 67 add_action('admin_notices', function() use ($required_wc_version, $wc_version) { 68 ?> 69 <div class="notice notice-error"> 70 <p><?php 71 /* translators: 1: Required WooCommerce version, 2: Current WooCommerce version */ 72 printf( 73 /* translators: 1: Required WooCommerce version, 2: Current WooCommerce version */ 74 esc_html__('Custom Product Tabs for WooCommerce requires WooCommerce version %1$s or higher. You are running version %2$s. Please update WooCommerce to use this plugin.', 'product-tabs-for-woo'), 75 esc_html($required_wc_version), 76 esc_html($wc_version) 77 ); 78 ?></p> 79 </div> 80 <?php 81 }); 82 return false; 83 } 84 85 return true; 86 } 87 add_action('admin_init', 'iptfw_check_woocommerce_compatibility'); 88 89 // Disable activation if WooCommerce is not active 90 function iptfw_disable_activation() { 91 if (!iptfw_is_woocommerce_active()) { 92 // Get the plugin file 93 $plugin_file = plugin_basename(IPTFW_FILE); 94 95 // Add a notice about WooCommerce requirement 96 add_action('admin_notices', function() { 97 ?> 98 <div class="notice notice-error"> 99 <p><?php esc_html_e('Product Tabs for WooCommerce requires WooCommerce to be installed and activated.', 'product-tabs-for-woo'); ?></p> 100 </div> 101 <?php 102 }); 103 104 // Add a notice in the plugins list 105 add_action('after_plugin_row_' . $plugin_file, function() { 106 ?> 107 <tr class="plugin-update-tr"> 108 <td colspan="4" class="plugin-update colspanchange"> 109 <div class="notice notice-error inline"> 110 <p><?php esc_html_e('This plugin cannot be activated because required plugins are missing or inactive.', 'product-tabs-for-woo'); ?></p> 111 <p><?php esc_html_e('Requires: WooCommerce', 'product-tabs-for-woo'); ?></p> 112 </div> 113 </td> 114 </tr> 115 <?php 116 }); 117 118 // Disable the activate link 119 add_filter('plugin_action_links_' . $plugin_file, function($actions) { 120 if (isset($actions['activate'])) { 121 $actions['activate'] = '<span class="button button-disabled">' . esc_html__('Activate', 'product-tabs-for-woo') . '</span>'; 122 } 123 return $actions; 124 }); 125 126 // Prevent activation 127 add_filter('plugin_action_links_' . $plugin_file, function($actions) { 128 if (isset($actions['activate'])) { 129 unset($actions['activate']); 130 } 131 return $actions; 132 }); 133 } 134 } 135 add_action('admin_init', 'iptfw_disable_activation'); 136 137 // Prevent activation if WooCommerce is not active 138 function iptfw_prevent_activation($plugin) { 139 if ($plugin === plugin_basename(IPTFW_FILE) && !iptfw_is_woocommerce_active()) { 140 wp_die( 141 esc_html__('This plugin requires WooCommerce to be installed and activated.', 'product-tabs-for-woo'), 142 esc_html__('Plugin Activation Error', 'product-tabs-for-woo'), 143 array('back_link' => true) 144 ); 145 } 146 } 147 add_action('activate_plugin', 'iptfw_prevent_activation'); 148 149 // Add compatibility information to WooCommerce status 150 function iptfw_add_compatibility_info($sections) { 151 // Verify nonce for admin actions 152 if (isset($_GET['_wpnonce']) && !wp_verify_nonce(sanitize_text_field(wp_unslash($_GET['_wpnonce'])), 'iptfw_compatibility_nonce')) { 153 return $sections; 154 } 155 156 $sections['iptfw_compatibility'] = array( 157 'title' => esc_html__('Custom Product Tabs Compatibility', 'product-tabs-for-woo'), 158 'callback' => 'iptfw_compatibility_section' 159 ); 160 return $sections; 161 } 162 add_filter('woocommerce_admin_status_tabs', 'iptfw_add_compatibility_info'); 163 164 function iptfw_compatibility_section() { 165 // Verify nonce for admin actions 166 if (isset($_GET['_wpnonce']) && !wp_verify_nonce(sanitize_text_field(wp_unslash($_GET['_wpnonce'])), 'iptfw_compatibility_nonce')) { 167 return; 168 } 169 170 ?> 171 <h2><?php esc_html_e('Custom Product Tabs Compatibility', 'product-tabs-for-woo'); ?></h2> 172 <table class="widefat" cellspacing="0"> 173 <thead> 174 <tr> 175 <th><?php esc_html_e('Feature', 'product-tabs-for-woo'); ?></th> 176 <th><?php esc_html_e('Status', 'product-tabs-for-woo'); ?></th> 177 </tr> 178 </thead> 179 <tbody> 180 <tr> 181 <td><?php esc_html_e('WooCommerce Version', 'product-tabs-for-woo'); ?></td> 182 <td> 183 <?php 184 $wc_version = WC()->version; 185 $required_wc_version = '7.2'; 186 if (version_compare($wc_version, $required_wc_version, '>=')) { 187 echo '<mark class="yes">' . esc_html($wc_version) . '</mark>'; 188 } else { 189 echo '<mark class="error">' . esc_html($wc_version) . ' (' . esc_html__('Update Required', 'product-tabs-for-woo') . ')</mark>'; 190 } 191 ?> 192 </td> 193 </tr> 194 <?php if (class_exists('WC_Admin_Features')): ?> 195 <tr> 196 <td><?php esc_html_e('Analytics', 'product-tabs-for-woo'); ?></td> 197 <td> 198 <?php 199 $features = WC_Admin_Features::get_compatible_features(); 200 if (isset($features['analytics']) && $features['analytics']) { 201 echo '<mark class="warning">' . esc_html__('Enabled - May need testing', 'product-tabs-for-woo') . '</mark>'; 202 } else { 203 echo '<mark class="yes">' . esc_html__('Disabled', 'product-tabs-for-woo') . '</mark>'; 204 } 205 ?> 206 </td> 207 </tr> 208 <?php endif; ?> 209 </tbody> 210 </table> 211 <?php 212 } 213 214 // Then include required files 215 require_once IPTFW_PATH . 'includes/class-iptfw-settings.php'; 216 require_once IPTFW_PATH . 'includes/class-iptfw-admin.php'; 217 218 if (!class_exists('IPTFW_Product_Tabs')) { 27 // Freemius Integration 28 if ( !function_exists( 'iptfw_fs' ) ) { 29 // Create a helper function for easy SDK access. 30 function iptfw_fs() { 31 global $iptfw_fs; 32 if ( !isset( $iptfw_fs ) ) { 33 // Include Freemius SDK. 34 require_once dirname( __FILE__ ) . '/vendor/freemius/start.php'; 35 $iptfw_fs = fs_dynamic_init( array( 36 'id' => '19118', 37 'slug' => 'product-tabs-for-woo', 38 'premium_slug' => 'product-tabs-for-woo-pro', 39 'type' => 'plugin', 40 'public_key' => 'pk_16d450f47372bb8f7243e970e61b8', 41 'is_premium' => false, 42 'premium_suffix' => 'Pro', 43 'has_addons' => true, 44 'has_paid_plans' => true, 45 'menu' => array( 46 'first-path' => 'plugins.php', 47 'support' => false, 48 ), 49 'is_live' => true, 50 ) ); 51 } 52 return $iptfw_fs; 53 } 54 55 // Init Freemius. 56 iptfw_fs(); 57 // Signal that SDK was initiated. 58 do_action( 'iptfw_fs_loaded' ); 59 } 60 // Define plugin constants 61 define( 'IPTFW_VERSION', '1.0.2' ); 62 define( 'IPTFW_FILE', __FILE__ ); 63 define( 'IPTFW_PATH', plugin_dir_path( __FILE__ ) ); 64 define( 'IPTFW_URL', plugin_dir_url( __FILE__ ) ); 65 // Include core files 66 require_once IPTFW_PATH . 'stackwc-core/stackwc-core.php'; 67 require_once IPTFW_PATH . 'includes/class-stackwc-compatibility.php'; 68 require_once IPTFW_PATH . 'includes/class-stackwc-post-type.php'; 69 require_once IPTFW_PATH . 'includes/class-stackwc-settings.php'; 70 require_once IPTFW_PATH . 'includes/class-stackwc-admin.php'; 71 require_once IPTFW_PATH . 'includes/class-stackwc-meta-boxes.php'; 72 require_once IPTFW_PATH . 'includes/class-stackwc-tabs.php'; 73 require_once IPTFW_PATH . 'includes/class-stackwc-conditions.php'; 74 require_once IPTFW_PATH . 'includes/class-stackwc-ajax.php'; 75 require_once IPTFW_PATH . 'includes/class-stackwc-columns.php'; 76 // WooCommerce Compatibility 77 add_action( 'before_woocommerce_init', array('IPTFW_Compatibility', 'declare_woocommerce_compatibility') ); 78 add_action( 'admin_init', array('IPTFW_Compatibility', 'check_woocommerce_compatibility') ); 79 add_action( 'admin_init', array('IPTFW_Compatibility', 'disable_activation') ); 80 add_action( 'activate_plugin', array('IPTFW_Compatibility', 'prevent_activation') ); 81 add_filter( 'woocommerce_admin_status_tabs', array('IPTFW_Compatibility', 'add_compatibility_info') ); 82 // Register plugin core menu 83 add_action( 'init', function () { 84 stackwc_register_plugin_menu( array( 85 'menu_title' => __( 'Product Tabs for Woo', 'product-tabs-for-woo' ), 86 'page_title' => __( 'Product Tabs for WooCommerce Settings', 'product-tabs-for-woo' ), 87 'capability' => 'manage_woocommerce', 88 'menu_slug' => 'iptfw_product_tab', 89 'description' => __( 'Adds custom product tabs to WooCommerce products.', 'product-tabs-for-woo' ), 90 'menu_url' => admin_url( 'edit.php?post_type=iptfw_product_tab&tab=settings' ), 91 'callback' => function () { 92 // Check if user has permission to manage WooCommerce 93 if ( !current_user_can( 'manage_woocommerce' ) ) { 94 wp_die( __( 'Sorry, you are not allowed to access this page.', 'product-tabs-for-woo' ) ); 95 } 96 // Redirect immediately 97 wp_safe_redirect( admin_url( 'edit.php?post_type=iptfw_product_tab&tab=settings' ) ); 98 exit; 99 }, 100 ) ); 101 } ); 102 // Modify submenu URL to point directly to the post type edit page 103 // Run on both admin_menu (late) and admin_init (late) to catch menu registration 104 add_action( 'admin_menu', function () { 105 global $submenu; 106 if ( isset( $submenu['stackwc'] ) ) { 107 foreach ( $submenu['stackwc'] as $key => $item ) { 108 if ( isset( $item[2] ) && $item[2] === 'iptfw_product_tab' ) { 109 // Replace the menu slug with the direct URL 110 $submenu['stackwc'][$key][2] = 'edit.php?post_type=iptfw_product_tab&tab=settings'; 111 break; 112 } 113 } 114 } 115 }, 999 ); 116 add_action( 'admin_init', function () { 117 global $submenu; 118 if ( isset( $submenu['stackwc'] ) ) { 119 foreach ( $submenu['stackwc'] as $key => $item ) { 120 if ( isset( $item[2] ) && $item[2] === 'iptfw_product_tab' ) { 121 // Replace the menu slug with the direct URL 122 $submenu['stackwc'][$key][2] = 'edit.php?post_type=iptfw_product_tab'; 123 break; 124 } 125 } 126 } 127 }, 999 ); 128 // Early redirect hook to catch the page load before WordPress permission checks 129 add_action( 'admin_init', function () { 130 if ( isset( $_GET['page'] ) && $_GET['page'] === 'iptfw_product_tab' && !isset( $_GET['post_type'] ) ) { 131 // Check if user has permission 132 if ( current_user_can( 'manage_woocommerce' ) ) { 133 wp_safe_redirect( admin_url( 'edit.php?post_type=iptfw_product_tab&tab=settings' ) ); 134 exit; 135 } 136 } 137 }, 1 ); 138 // Provide plugin-specific template path 139 add_filter( 140 'stackwc_plugin_template_path', 141 function ( $template_path, $current_page ) { 142 if ( $current_page === 'wc-settings&tab=stackwc_qbnbfw' ) { 143 return STACKWC_QBNBFW_PATH . 'includes/templates/wc-settings&tab=stackwc_qbnbfw.php'; 144 } 145 return $template_path; 146 }, 147 10, 148 2 149 ); 150 /** 151 * Main Plugin Class 152 */ 153 if ( !class_exists( 'IPTFW_Product_Tabs' ) ) { 219 154 class IPTFW_Product_Tabs { 220 155 private static $instance = null; 156 221 157 public $settings; 158 222 159 public $admin; 223 private $current_page = '';224 private $current_tab = '';225 160 226 161 public static function instance() { 227 if ( null === self::$instance) {162 if ( null === self::$instance ) { 228 163 self::$instance = new self(); 229 164 } … … 232 167 233 168 public function __construct() { 234 if ( !iptfw_is_woocommerce_active()) {169 if ( !IPTFW_Compatibility::is_woocommerce_active() ) { 235 170 return; 236 171 } 237 238 172 $this->init_hooks(); 239 $this->set_current_page();240 $this->set_current_tab();241 }242 243 private function set_current_page() {244 // Verify nonce for admin actions245 if (isset($_GET['_wpnonce']) && !wp_verify_nonce(sanitize_text_field(wp_unslash($_GET['_wpnonce'])), 'iptfw_page_nonce')) {246 return;247 }248 $this->current_page = isset($_GET['page']) ? sanitize_text_field(wp_unslash($_GET['page'])) : '';249 }250 251 private function set_current_tab() {252 // Verify nonce for admin actions253 if (isset($_GET['_wpnonce']) && !wp_verify_nonce(sanitize_text_field(wp_unslash($_GET['_wpnonce'])), 'iptfw_tab_nonce')) {254 return;255 }256 $this->current_tab = isset($_GET['tab']) ? sanitize_text_field(wp_unslash($_GET['tab'])) : 'list';257 }258 259 public function is_woocommerce_active() {260 return in_array('woocommerce/woocommerce.php', apply_filters('active_plugins', get_option('active_plugins')));261 }262 263 public function woocommerce_missing_notice() {264 ?>265 <div class="notice notice-error">266 <p><?php esc_html_e('Custom Product Tabs requires WooCommerce to be installed and active.', 'product-tabs-for-woo'); ?></p>267 </div>268 <?php269 173 } 270 174 … … 273 177 $this->admin = new IPTFW_Admin($this); 274 178 $this->settings = new IPTFW_Settings(); 275 276 179 // Add text domain loading 277 add_action('init', array($this, 'load_textdomain')); 278 279 add_action('init', array($this, 'register_post_type')); 280 add_action('admin_init', array($this, 'admin_init')); 281 add_action('add_meta_boxes', array($this, 'add_meta_boxes')); 282 add_action('save_post', array($this, 'save_meta_boxes')); 283 add_filter('woocommerce_product_tabs', array($this, 'add_product_tabs')); 284 285 // Add plugin action links 286 add_filter('plugin_action_links_' . plugin_basename(IPTFW_FILE), array($this, 'add_plugin_action_links')); 287 288 // Add these column filters back 289 add_filter('manage_iptfw_product_tab_posts_columns', array($this, 'set_custom_columns')); 290 add_action('manage_iptfw_product_tab_posts_custom_column', array($this, 'custom_column_content'), 10, 2); 291 add_filter('manage_edit-iptfw_product_tab_sortable_columns', array($this, 'sortable_columns')); 180 add_action( 'init', array($this, 'load_textdomain') ); 181 // Post type and meta fields 182 add_action( 'init', array('IPTFW_Post_Type', 'register_post_type') ); 183 add_action( 'init', array('IPTFW_Post_Type', 'register_meta_fields') ); 184 // Meta boxes 185 add_action( 'add_meta_boxes', array('IPTFW_Meta_Boxes', 'add_meta_boxes') ); 186 add_action( 'save_post', array('IPTFW_Meta_Boxes', 'save_meta_boxes') ); 187 // Product tabs 188 add_filter( 'woocommerce_product_tabs', array('IPTFW_Tabs', 'add_product_tabs') ); 189 // Plugin action links 190 add_filter( 'plugin_action_links_' . plugin_basename( IPTFW_FILE ), array($this, 'add_plugin_action_links') ); 191 // Admin columns 192 add_filter( 'manage_iptfw_product_tab_posts_columns', array('IPTFW_Columns', 'set_custom_columns') ); 193 add_action( 194 'manage_iptfw_product_tab_posts_custom_column', 195 array('IPTFW_Columns', 'custom_column_content'), 196 10, 197 2 198 ); 199 add_filter( 'manage_edit-iptfw_product_tab_sortable_columns', array('IPTFW_Columns', 'sortable_columns') ); 200 // AJAX handlers 201 add_action( 'wp_ajax_iptfw_save_order', array('IPTFW_Ajax', 'save_order') ); 202 add_action( 'wp_ajax_iptfw_search_products', array('IPTFW_Ajax', 'search_products') ); 292 203 } 293 204 294 205 public function load_textdomain() { 295 load_plugin_textdomain( 296 'product-tabs-woocommerce', 297 false, 298 dirname(plugin_basename(IPTFW_FILE)) . '/languages' 299 ); 300 } 301 302 public function register_post_type() { 303 $labels = array( 304 'name' => esc_html__('Product Tabs', 'product-tabs-for-woo'), 305 'singular_name' => esc_html__('Product Tab', 'product-tabs-for-woo'), 306 'add_new' => esc_html__('Add New', 'product-tabs-for-woo'), 307 'add_new_item' => esc_html__('Add New Product Tab', 'product-tabs-for-woo'), 308 'edit_item' => esc_html__('Edit Product Tab', 'product-tabs-for-woo'), 309 'new_item' => esc_html__('New Product Tab', 'product-tabs-for-woo'), 310 'view_item' => esc_html__('View Product Tab', 'product-tabs-for-woo'), 311 'search_items' => esc_html__('Search Product Tabs', 'product-tabs-for-woo'), 312 'not_found' => esc_html__('No product tabs found', 'product-tabs-for-woo'), 313 'not_found_in_trash' => esc_html__('No product tabs found in trash', 'product-tabs-for-woo'), 314 'menu_name' => esc_html__('Product Tabs', 'product-tabs-for-woo') 315 ); 316 317 $args = array( 318 'labels' => $labels, 319 'public' => false, 320 'show_ui' => true, 321 'show_in_menu' => false, 322 'capability_type' => 'post', 323 'hierarchical' => false, 324 'supports' => array('title', 'editor'), 325 'show_in_rest' => true, 326 'rewrite' => false 327 ); 328 329 register_post_type('iptfw_product_tab', $args); 330 } 331 332 public function admin_init() { 333 global $parent_file, $submenu_file; 334 335 // Verify nonce for admin actions 336 if (isset($_GET['_wpnonce']) && !wp_verify_nonce(sanitize_text_field(wp_unslash($_GET['_wpnonce'])), 'iptfw_admin_nonce')) { 337 return; 338 } 339 340 // Keep Product Tabs menu active when viewing settings 341 if (isset($_GET['page']) && sanitize_text_field(wp_unslash($_GET['page'])) === 'iptfw-settings') { 342 $parent_file = 'edit.php?post_type=product'; 343 $submenu_file = 'edit.php?post_type=iptfw_product_tab'; 344 } 345 } 346 347 public function add_meta_boxes() { 348 add_meta_box( 349 'iptfw_tab_settings', 350 esc_html__('Tab Settings', 'product-tabs-for-woo'), 351 array($this, 'render_tab_settings'), 352 'iptfw_product_tab', 353 'side', 354 'high' 355 ); 356 } 357 358 public function render_tab_settings($post) { 359 wp_nonce_field('iptfw_save_meta', 'iptfw_meta_nonce'); 360 361 $priority = get_post_meta($post->ID, '_iptfw_tab_priority', true); 362 $display_type = get_post_meta($post->ID, '_iptfw_display_type', true) ?: 'all'; 363 $products = get_post_meta($post->ID, '_iptfw_products', true); 364 $categories = get_post_meta($post->ID, '_iptfw_categories', true); 365 $tags = get_post_meta($post->ID, '_iptfw_tags', true); 366 ?> 367 <div class="iptfw-settings-wrapper"> 368 <div class="components-base-control"> 369 <label class="components-base-control__label" for="tab_priority"> 370 <?php esc_html_e('Priority', 'product-tabs-for-woo'); ?> 371 </label> 372 <input type="number" 373 id="tab_priority" 374 name="tab_priority" 375 value="<?php echo esc_attr($priority); ?>" 376 class="components-text-control__input" /> 377 <p class="components-base-control__help"> 378 <?php esc_html_e('Lower numbers appear first', 'product-tabs-for-woo'); ?> 379 </p> 380 </div> 381 382 <div class="components-base-control"> 383 <label class="components-base-control__label"> 384 <?php esc_html_e('Display Conditions', 'product-tabs-for-woo'); ?> 385 </label> 386 <select name="display_type" 387 id="display_type" 388 class="components-select-control__input"> 389 <option value="all" <?php selected($display_type, 'all'); ?>> 390 <?php esc_html_e('All Products', 'product-tabs-for-woo'); ?> 391 </option> 392 <option value="specific" <?php selected($display_type, 'specific'); ?>> 393 <?php esc_html_e('Specific Products', 'product-tabs-for-woo'); ?> 394 </option> 395 <option value="categories" <?php selected($display_type, 'categories'); ?>> 396 <?php esc_html_e('Product Categories', 'product-tabs-for-woo'); ?> 397 </option> 398 <option value="tags" <?php selected($display_type, 'tags'); ?>> 399 <?php esc_html_e('Product Tags', 'product-tabs-for-woo'); ?> 400 </option> 401 </select> 402 </div> 403 404 <div id="specific_products" class="components-base-control" style="display: <?php echo $display_type === 'specific' ? 'block' : 'none'; ?>"> 405 <label class="components-base-control__label" for="tab_products"> 406 <?php esc_html_e('Product IDs', 'product-tabs-for-woo'); ?> 407 </label> 408 <input type="text" 409 id="tab_products" 410 name="tab_products" 411 value="<?php echo esc_attr($products); ?>" 412 class="components-text-control__input" /> 413 <p class="components-base-control__help"> 414 <?php esc_html_e('Enter product IDs separated by commas', 'product-tabs-for-woo'); ?> 415 </p> 416 </div> 417 418 <div id="product_categories" class="components-base-control" style="display: <?php echo $display_type === 'categories' ? 'block' : 'none'; ?>"> 419 <label class="components-base-control__label" for="tab_categories"> 420 <?php esc_html_e('Select Categories', 'product-tabs-for-woo'); ?> 421 </label> 422 <select name="tab_categories[]" 423 id="tab_categories" 424 class="components-select-control__input" 425 multiple> 426 <?php 427 $product_categories = get_terms(array( 428 'taxonomy' => 'product_cat', 429 'hide_empty' => false 430 )); 431 $selected_cats = explode(',', $categories); 432 foreach ($product_categories as $category) { 433 printf( 434 '<option value="%s" %s>%s</option>', 435 esc_attr($category->term_id), 436 selected(in_array($category->term_id, $selected_cats), true, false), 437 esc_html($category->name) 438 ); 439 } 440 ?> 441 </select> 442 </div> 443 444 <div id="product_tags" class="components-base-control" style="display: <?php echo $display_type === 'tags' ? 'block' : 'none'; ?>"> 445 <label class="components-base-control__label" for="tab_tags"> 446 <?php esc_html_e('Select Tags', 'product-tabs-for-woo'); ?> 447 </label> 448 <select name="tab_tags[]" 449 id="tab_tags" 450 class="components-select-control__input" 451 multiple> 452 <?php 453 $product_tags = get_terms(array( 454 'taxonomy' => 'product_tag', 455 'hide_empty' => false 456 )); 457 $selected_tags = explode(',', $tags); 458 foreach ($product_tags as $tag) { 459 printf( 460 '<option value="%s" %s>%s</option>', 461 esc_attr($tag->term_id), 462 selected(in_array($tag->term_id, $selected_tags), true, false), 463 esc_html($tag->name) 464 ); 465 } 466 ?> 467 </select> 468 </div> 469 </div> 470 </div> 471 <?php 472 } 473 474 public function save_meta_boxes($post_id) { 475 if (!isset($_POST['iptfw_meta_nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['iptfw_meta_nonce'])), 'iptfw_save_meta')) { 476 return; 477 } 478 479 if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) { 480 return; 481 } 482 483 if (!current_user_can('edit_post', $post_id)) { 484 return; 485 } 486 487 if (isset($_POST['tab_priority'])) { 488 update_post_meta($post_id, '_iptfw_tab_priority', sanitize_text_field(wp_unslash($_POST['tab_priority']))); 489 } 490 491 if (isset($_POST['display_type'])) { 492 update_post_meta($post_id, '_iptfw_display_type', sanitize_text_field(wp_unslash($_POST['display_type']))); 493 } 494 495 if (isset($_POST['tab_products'])) { 496 update_post_meta($post_id, '_iptfw_products', sanitize_text_field(wp_unslash($_POST['tab_products']))); 497 } 498 499 if (isset($_POST['tab_categories'])) { 500 $categories = isset($_POST['tab_categories']) ? array_map('absint', wp_unslash($_POST['tab_categories'])) : array(); 501 update_post_meta($post_id, '_iptfw_categories', implode(',', $categories)); 502 } 503 504 if (isset($_POST['tab_tags'])) { 505 $tags = isset($_POST['tab_tags']) ? array_map('absint', wp_unslash($_POST['tab_tags'])) : array(); 506 update_post_meta($post_id, '_iptfw_tags', implode(',', $tags)); 507 } 508 } 509 510 public function set_custom_columns($columns) { 511 $new_columns = array(); 512 $new_columns['cb'] = $columns['cb']; 513 $new_columns['title'] = esc_html__('Tab Title', 'product-tabs-for-woo'); 514 $new_columns['priority'] = esc_html__('Priority', 'product-tabs-for-woo'); 515 $new_columns['display_rules'] = esc_html__('Display Rules', 'product-tabs-for-woo'); 516 $new_columns['date'] = $columns['date']; 517 return $new_columns; 518 } 519 520 public function custom_column_content($column, $post_id) { 521 switch ($column) { 522 case 'priority': 523 $priority = get_post_meta($post_id, '_iptfw_tab_priority', true); 524 echo esc_html($priority ?: '50'); 525 break; 526 527 case 'display_rules': 528 $display_type = get_post_meta($post_id, '_iptfw_display_type', true) ?: 'all'; 529 switch ($display_type) { 530 case 'all': 531 esc_html_e('All Products', 'product-tabs-for-woo'); 532 break; 533 case 'specific': 534 $products = get_post_meta($post_id, '_iptfw_products', true); 535 echo esc_html__('Specific Products', 'product-tabs-for-woo') . ': ' . esc_html($products); 536 break; 537 case 'categories': 538 $categories = get_post_meta($post_id, '_iptfw_categories', true); 539 $cat_names = array(); 540 foreach (explode(',', $categories) as $cat_id) { 541 $term = get_term($cat_id, 'product_cat'); 542 if ($term) { 543 $cat_names[] = $term->name; 544 } 545 } 546 echo esc_html__('Categories', 'product-tabs-for-woo') . ': ' . esc_html(implode(', ', $cat_names)); 547 break; 548 case 'tags': 549 $tags = get_post_meta($post_id, '_iptfw_tags', true); 550 $tag_names = array(); 551 foreach (explode(',', $tags) as $tag_id) { 552 $term = get_term($tag_id, 'product_tag'); 553 if ($term) { 554 $tag_names[] = $term->name; 555 } 556 } 557 echo esc_html__('Tags', 'product-tabs-for-woo') . ': ' . esc_html(implode(', ', $tag_names)); 558 break; 559 } 560 break; 561 } 562 } 563 564 public function sortable_columns($columns) { 565 $columns['priority'] = 'priority'; 566 return $columns; 567 } 568 569 public function add_product_tabs($tabs) { 570 // Handle default tab visibility 571 $options = get_option('iptfw_settings', array()); 572 $default_tabs = array('description', 'additional_information', 'reviews'); 573 574 foreach ($default_tabs as $tab_id) { 575 $show_key = 'show_' . $tab_id; 576 // Check if the setting exists and is explicitly set to 0/false 577 if (isset($options[$show_key]) && !$options[$show_key]) { 578 unset($tabs[$tab_id]); 579 } 580 } 581 582 // Add custom tabs - Using a more efficient query 583 $args = array( 584 'post_type' => 'iptfw_product_tab', 585 'posts_per_page' => -1, 586 'post_status' => 'publish', 587 'orderby' => 'menu_order', 588 'order' => 'ASC' 589 ); 590 591 // Get all tabs first 592 $custom_tabs = get_posts($args); 593 $product_id = get_the_ID(); 594 595 // Cache priorities to avoid multiple meta queries 596 $tab_priorities = array(); 597 foreach ($custom_tabs as $tab) { 598 $tab_priorities[$tab->ID] = get_post_meta($tab->ID, '_iptfw_tab_priority', true); 599 } 600 601 // Sort tabs by priority 602 usort($custom_tabs, function($a, $b) use ($tab_priorities) { 603 $priority_a = empty($tab_priorities[$a->ID]) ? 50 : intval($tab_priorities[$a->ID]); 604 $priority_b = empty($tab_priorities[$b->ID]) ? 50 : intval($tab_priorities[$b->ID]); 605 return $priority_a - $priority_b; 606 }); 607 608 foreach ($custom_tabs as $tab) { 609 if ($this->should_display_tab($tab->ID, $product_id)) { 610 $priority = empty($tab_priorities[$tab->ID]) ? 50 : intval($tab_priorities[$tab->ID]); 611 612 $tabs['iptfw_tab_' . $tab->ID] = array( 613 'title' => $tab->post_title, 614 'priority' => $priority, 615 'callback' => array($this, 'render_tab_content'), 616 'tab_id' => $tab->ID 617 ); 618 } 619 } 620 621 return $tabs; 622 } 623 624 public function render_tab_content($key, $tab) { 625 $content = get_post_field('post_content', $tab['tab_id']); 626 echo wp_kses_post(apply_filters('the_content', $content)); 627 } 628 629 private function should_display_tab($tab_id, $product_id) { 630 $display_type = get_post_meta($tab_id, '_iptfw_display_type', true) ?: 'all'; 631 632 switch ($display_type) { 633 case 'all': 634 return true; 635 636 case 'specific': 637 $products = get_post_meta($tab_id, '_iptfw_products', true); 638 if (empty($products)) { 639 return false; 640 } 641 $product_ids = array_map('trim', explode(',', $products)); 642 return in_array($product_id, $product_ids); 643 644 case 'categories': 645 $tab_categories = get_post_meta($tab_id, '_iptfw_categories', true); 646 if (empty($tab_categories)) { 647 return false; 648 } 649 $tab_cat_ids = array_map('trim', explode(',', $tab_categories)); 650 $product_cats = wp_get_post_terms($product_id, 'product_cat', array('fields' => 'ids')); 651 return !empty(array_intersect($tab_cat_ids, $product_cats)); 652 653 case 'tags': 654 $tab_tags = get_post_meta($tab_id, '_iptfw_tags', true); 655 if (empty($tab_tags)) { 656 return false; 657 } 658 $tab_tag_ids = array_map('trim', explode(',', $tab_tags)); 659 $product_tags = wp_get_post_terms($product_id, 'product_tag', array('fields' => 'ids')); 660 return !empty(array_intersect($tab_tag_ids, $product_tags)); 661 662 default: 663 return false; 664 } 665 } 666 667 /** 668 * Add settings link to plugin action links 669 */ 670 public function add_plugin_action_links($links) { 671 $settings_link = sprintf( 672 '<a href="%s">%s</a>', 673 esc_url(admin_url('admin.php?page=iptfw-settings')), 674 esc_html__('Settings', 'product-tabs-for-woo') 675 ); 676 677 // Add settings link to the beginning of the array 678 array_unshift($links, $settings_link); 679 680 return $links; 681 } 682 } 683 206 load_plugin_textdomain( 'product-tabs-for-woo', false, dirname( plugin_basename( IPTFW_FILE ) ) . '/languages' ); 207 } 208 209 /** 210 * Add settings link to plugin action links 211 */ 212 public function add_plugin_action_links( $links ) { 213 $settings_link = sprintf( '<a href="%s">%s</a>', esc_url( admin_url( 'edit.php?post_type=iptfw_product_tab&tab=settings' ) ), esc_html__( 'Settings', 'product-tabs-for-woo' ) ); 214 array_unshift( $links, $settings_link ); 215 return $links; 216 } 217 218 } 219 220 } 684 221 // Initialize the plugin 685 222 function IPTFW() { … … 688 225 689 226 // Start the plugin 690 add_action('plugins_loaded', 'IPTFW'); 691 } 227 add_action( 'plugins_loaded', 'IPTFW' ); -
product-tabs-for-woo/trunk/readme.txt
r3294575 r3405807 1 === Product Tabs for Woo ===2 Contributors: ilmosys, stackwc, mahdiali1 === Product Tabs for WooCommerce === 2 Contributors: stackwc, ilmosys, mahdiali 3 3 Tags: woocommerce, product tabs, custom tabs, product tabs for WooCommerce 4 4 Requires at least: 5.0 5 Tested up to: 6. 86 Requires PHP: 7. 27 Stable tag: 1.0. 15 Tested up to: 6.9 6 Requires PHP: 7.4 7 Stable tag: 1.0.2 8 8 License: GPLv3 9 9 License URI: https://www.gnu.org/licenses/gpl-3.0.html 10 10 WC requires at least: 7.2 11 WC tested up to: 9.8.511 WC tested up to: 10.3.5 12 12 13 Add custom product tabs to your WooCommerce products with advanced display conditions and priority settings.13 Add custom product tabs to your WooCommerce products with advanced display conditions, priority settings, and powerful customization options. 14 14 15 15 == Description == 16 16 17 Custom Product Tabs for WooCommerce is a lightweight yet powerful plugin that lets you create, edit, and manage product tabs effortlessly. With a built-in block editor, you can design custom tabs or import ready-made patterns for a seamless experience. The plugin also includes a settings page where you can control tab priorities and show or hide WooCommerce default tabs like Description, Additional Information, and Reviews. Enhance your product pages with structured, engaging content and improve the shopping experience with flexible tab management! 17 <a href="https://stackwc.com/plugins/product-tabs-for-woo/?utm_source=wprepo&utm_medium=link&utm_campaign=plugin" title="StackWC - Product Tabs for WooCommerce Plugin"> Product Tabs for WooCommerce </a> is a powerful and flexible plugin that allows you to create unlimited custom tabs for your WooCommerce products. With an intuitive interface, advanced display conditions, and comprehensive customization options, you can enhance your product pages with rich, engaging content that improves the shopping experience. 18 18 19 = Key Features = 19 The plugin features a modern block editor integration, allowing you to design beautiful tab content using WordPress blocks. You can also use the classic editor for quick content creation. With Pro features, you get access to advanced display conditions, tab reordering, icon customization, and much more. 20 20 21 * Create unlimited custom tabs for your products 22 * Set display conditions based on specific products, categories, or tags 23 * Control tab priority and order 24 * Rich text editor for tab content 25 * SEO-friendly tab implementation 21 <p>⭐ <a href="https://stackwc.com/plugins/product-tabs-for-woo/?utm_source=wprepo&utm_medium=link&utm_campaign=upgrade-pro"> Upgrade to Pro </a> | 🚀 <a href="https://stackwc.com/plugins/product-tabs-for-woo/"> Try the Demo< /a> | 🛟 <a href="https://stackwc.com/support/"> Get Support </a> </p> 22 23 = Key Features (Free Version) = 24 25 * Create unlimited custom product tabs 26 * Block editor support for rich content creation 27 * Classic editor support for quick editing 28 * Display conditions: All products, Specific products, Product types 29 * Tab priority system for controlling display order 30 * Show/hide default WooCommerce tabs (Description, Additional Information, Reviews) 31 * SEO-friendly implementation 26 32 * Responsive design compatible 27 * Easy to use interface 33 * Easy-to-use interface 34 * Product search for selecting specific products 35 * Support for multiple product types (Simple, Variable, Grouped, External, Downloadable, Virtual) 36 37 = Pro Features = 38 39 * Advanced Display Conditions: 40 * Product Categories - Show tabs based on product categories 41 * Product Tags - Display tabs for products with specific tags 42 * User Roles - Control tab visibility based on user roles (including guest users) 43 * Price Range - Show tabs for products within specific price ranges 44 * Stock Status - Display tabs based on product stock status (In Stock, Out of Stock, On Backorder) 45 * Sale Status - Show tabs for products on sale or regular price 46 * Date Range (Seasonal) - Display tabs only within specific date ranges 47 * Shipping Classes - Show tabs based on product shipping classes 48 * Combine multiple conditions for precise control 49 50 * Tab Management: 51 * Drag & Drop Reorder - Visually reorder tabs with drag and drop interface 52 * Tab Icons - Add Dashicons or custom CSS classes to tab titles 53 * Rename Default Tabs - Customize names of Description, Additional Information, and Reviews tabs 54 * Icon Support for Default Tabs - Add icons to default WooCommerce tabs 28 55 29 56 = Use Cases = 30 57 31 * Add product specifications 32 * Display shipping information 33 * Show size guides 34 * Include warranty information 35 * Add custom product features 36 * Display related products 37 * Show product comparisons 38 * Include video tutorials 39 * Design and customize any content you want using the block editor 58 * Product Specifications - Add detailed technical specifications 59 * Shipping Information - Display shipping policies and delivery times 60 * Size Guides - Include size charts and measurement guides 61 * Warranty Information - Show warranty terms and conditions 62 * Custom Product Features - Highlight unique selling points 63 * Related Products - Display related or complementary products 64 * Product Comparisons - Show comparison tables 65 * Video Tutorials - Embed instructional videos 66 * Customer Reviews - Add custom review sections 67 * FAQs - Include frequently asked questions 68 * Installation Guides - Provide setup instructions 69 * Seasonal Promotions - Show time-limited offers 70 * Role-based Content - Display different content for different user types 71 * Price-based Tabs - Show special tabs for products in specific price ranges 72 73 = Display Conditions Explained = 74 75 The plugin offers flexible display conditions to control when tabs appear: 76 77 * All Products - Tab appears on all product pages 78 * Specific Products - Select individual products by ID 79 * Product Type - Show tabs for specific product types (Simple, Variable, Grouped, External, Downloadable, Virtual) 80 * Product Categories (Pro) - Display tabs for products in selected categories 81 * Product Tags (Pro) - Show tabs for products with specific tags 82 * Advanced Conditions (Pro) - Combine multiple conditions: 83 * User Roles - Show tabs to specific user roles or guests 84 * Price Range - Display tabs for products within min/max price 85 * Stock Status - Show tabs based on product availability 86 * Sale Status - Display tabs for products on sale or regular price 87 * Date Range - Show tabs only during specific date periods (seasonal content) 88 * Shipping Classes - Display tabs based on product shipping class 89 40 90 41 91 == Installation == 42 1. Upload the plugin files to the /wp-content/plugins/product-tabs-woocommerce directory or install the plugin directly from the WordPress Plugins screen. 43 2. Activate the plugin through the Plugins screen in WordPress. 44 3. Navigate to WooCommerce > Products > Product Tabs to access the page. 45 4. Click the “Add New Product Tab” button to create your first custom tab. 92 93 1. Upload the plugin files to the `/wp-content/plugins/product-tabs-for-woo` directory, or install the plugin through the WordPress plugins screen directly. 94 2. Activate the plugin through the 'Plugins' screen in WordPress. 95 3. Ensure WooCommerce is installed and activated (required). 96 4. Navigate to WooCommerce > Products > Product Tabs to access the management page. 97 5. Click "Add New Product Tab" to create your first custom tab. 98 6. Configure display conditions and priority settings. 99 7. Add your content using the block editor or classic editor. 100 8. Visit a product page to see your custom tabs in action. 46 101 47 102 == Frequently Asked Questions == 48 103 49 = Does this plugin require WooCommerce? =50 Yes, this plugin requires WooCommerce to be installed and activated on your WordPress site.51 52 104 = Can I create multiple product tabs for different products? = 53 Yes, you can create unlimited custom tabs and set specific display conditions for each tab based on products, categories, or tags.105 Yes, you can create unlimited custom tabs and set specific display conditions for each tab. You can show tabs on all products, specific products, or based on categories, tags, and other advanced conditions (Pro). 54 106 55 107 = Can I control the order of tabs? = 56 Yes, you can set a priority number for each tab. Lower numbers appear first in the tab order. 108 Yes, you can set a priority number for each tab. Lower numbers appear first in the tab order. Pro users can also use the drag-and-drop reorder interface for visual tab management. 57 109 58 110 = Can I use HTML in tab content? = 59 Yes, the tab content editor supports HTML and rich text formatting.111 Yes, the tab content editor supports HTML, rich text formatting, and WordPress blocks. You can use the block editor to create rich, structured content. 60 112 61 113 = Is this plugin compatible with my theme? = 62 This plugin is compatible with most WooCommerce themes. It uses standard WooCommerce hooks and filters for tab integration .114 This plugin is compatible with most WooCommerce themes. It uses standard WooCommerce hooks and filters for tab integration, ensuring compatibility with the WooCommerce ecosystem. 63 115 64 116 = Does this plugin affect my site's performance? = 65 The plugin is optimized for performance and only loads tab content when needed. It uses WordPress caching mechanisms and efficient database queries. 117 The plugin is optimized for performance and only loads tab content when needed. It uses efficient database queries, WordPress caching mechanisms, and lazy loading where appropriate. 118 119 = Can I hide default WooCommerce tabs? = 120 Yes, you can show or hide the default WooCommerce tabs (Description, Additional Information, Reviews) from the Settings page. 121 122 = Can I rename default WooCommerce tabs? = 123 Yes, Pro users can rename the default WooCommerce tabs (Description, Additional Information, Reviews) and add icons to them. 124 125 = Can I show different tabs to different user roles? = 126 Yes, Pro users can set user role-based display conditions, allowing different tabs to be shown to different user types, including guest users. 127 128 = Can I create seasonal or time-limited tabs? = 129 Yes, Pro users can set date range conditions to display tabs only within specific date periods, perfect for seasonal promotions or limited-time offers. 130 131 = Does the plugin work with variable products? = 132 Yes, the plugin works with all WooCommerce product types including Simple, Variable, Grouped, External, Downloadable, and Virtual products. 133 134 = Can I use custom icons for tabs? = 135 Yes, Pro users can add Dashicons or custom CSS classes to tab titles for enhanced visual presentation. 66 136 67 137 == Screenshots == 68 138 69 1. Product Tabs Management Screen 70 2. Tab Settings Interface 71 3. Display Conditions Settings 72 4. Product Page with Custom Tabs 139 1. Product Tabs Management Screen - Overview of all custom tabs 140 2. Tab Settings Interface - Configure display conditions and priority 141 3. Display Conditions Settings - Advanced condition options (Pro) 142 4. Block Editor Integration - Rich content creation with blocks 143 5. Tab Reorder Interface - Drag and drop tab ordering (Pro) 144 6. Product Page with Custom Tabs - Tabs displayed on product page 145 7. Settings Page - Control default tabs and global settings 146 8. Advanced Conditions - Multiple condition types (Pro) 73 147 74 148 == Changelog == 75 149 150 = 1.0.2 - 29 Nov, 2025 = 151 * Updated: Tested up to WordPress 6.9 152 * Updated: WooCommerce compatibility tested up to 10.3.5 153 * Updated: Minimum PHP requirement to 7.4 154 * Added: Freemius SDK integration 155 * Added: Advanced tab conditions system with custom rules 156 * Added: New admin interface with improved UX 157 * Improved: Codebase restructured for better maintainability 158 * Improved: Security enhancements and capability checks 159 * Improved: Performance optimizations and caching 160 * Fixed: Various UI/UX issues 161 * Removed: Outdated code and deprecated functions 162 76 163 = 1.0.1 - 16 May, 2025 = 77 * Updated: Tested up to WordPress 6.8. 164 * Updated: Tested up to WordPress 6.8 165 * Improved: WooCommerce compatibility declarations 78 166 79 167 = 1.0.0 = 80 168 * Initial release 169 * Basic tab creation and management 170 * Display conditions (All, Specific Products, Product Types) 171 * Priority system 172 * Block editor support 81 173 82 174 == Upgrade Notice == 83 84 == Privacy Policy ==85 This plugin does not collect or store any personal data. It only uses WordPress and WooCommerce core functionality to display custom tabs on product pages.86 87 == Disclaimer ==88 This plugin is not affiliated with or endorsed by WooCommerce or Automattic. "WooCommerce" is a trademark of Automattic Inc. This plugin is an independent product developed to extend WooCommerce functionality.
Note: See TracChangeset
for help on using the changeset viewer.