| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595 |
- <?php
- /**
- * Plugin Name: QDR - Studiou WC Export/Import Product Categories
- * Plugin URI: https://www.quadarax.com/plugins/studiou-wc-product-cat-manage
- * Description: Allows batch import and export WooCommerce product categories and products
- * Version: 1.7.1
- * Requires at least: 6.8.1
- * Requires PHP: 8.2
- * Author: Dalibor Votruba
- * Author URI: https://www.quadarax.com
- * License: GPL v2 or later
- * License URI: https://www.gnu.org/licenses/gpl-2.0.html
- * Text Domain: studiou-wc-product-cat-manage
- * Domain Path: /languages
- * WC requires at least: 9.8
- * WC tested up to: 9.8
- * Woo: 12345:342928dfsfhsf8429842374wdf4234sfd
- */
- // If this file is called directly, abort.
- if (!defined('WPINC')) {
- die;
- }
- // Define plugin constants
- if (!defined('STUDIOU_WCPCM_VERSION')) define('STUDIOU_WCPCM_VERSION', '1.7.1');
- if (!defined('STUDIOU_WCPCM_PLUGIN_DIR')) define('STUDIOU_WCPCM_PLUGIN_DIR', plugin_dir_path(__FILE__));
- if (!defined('STUDIOU_WCPCM_PLUGIN_URL')) define('STUDIOU_WCPCM_PLUGIN_URL', plugin_dir_url(__FILE__));
- if (!defined('STUDIOU_WCPCM_PLUGIN_BASENAME')) define('STUDIOU_WCPCM_PLUGIN_BASENAME', plugin_basename(__FILE__));
- /**
- * Main plugin class
- */
- class Studiou_WC_Product_Cat_Manage {
-
- /**
- * @var Studiou_WC_Product_Cat_Manage_Export
- */
- private $exporter;
-
- /**
- * @var Studiou_WC_Product_Cat_Manage_Import
- */
- private $importer;
-
- /**
- * @var Studiou_WC_Product_Cat_Manage_DB
- */
- private $db;
-
- /**
- * @var Studiou_WC_Product_Cat_Manage_Manipulator
- */
- private $manipulator;
- /**
- * @var Studiou_WC_Product_Manage_Product_DB
- */
- private $product_db;
- /**
- * @var Studiou_WC_Product_Manage_Product_Import
- */
- private $product_importer;
- /**
- * @var Studiou_WC_Product_Manage_Product_Export
- */
- private $product_exporter;
-
- /**
- * Initialize the plugin
- */
- public function __construct() {
- // Load dependencies
- $this->load_dependencies();
-
- // Check required plugins
- add_action('admin_init', array($this, 'check_required_plugins'));
-
- // Add admin menu items
- add_action('admin_menu', array($this, 'add_admin_menu'), 99);
-
- // Register admin assets
- add_action('admin_enqueue_scripts', array($this, 'register_admin_assets'));
-
- // Initialize components
- $this->init_components();
-
- // Declare WooCommerce HPOS compatibility
- add_action('before_woocommerce_init', array($this, 'declare_hpos_compatibility'));
- }
-
- /**
- * Declare compatibility with WooCommerce HPOS (High-Performance Order Storage)
- */
- public function declare_hpos_compatibility() {
- if (class_exists('Automattic\WooCommerce\Utilities\FeaturesUtil')) {
- \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility(
- 'custom_order_tables',
- __FILE__,
- true
- );
- }
- }
-
- /**
- * Load required dependencies
- */
- private function load_dependencies() {
- require_once STUDIOU_WCPCM_PLUGIN_DIR . 'includes/class-studiou-wc-product-cat-manage-db.php';
- require_once STUDIOU_WCPCM_PLUGIN_DIR . 'includes/class-studiou-wc-product-cat-manage-export.php';
- require_once STUDIOU_WCPCM_PLUGIN_DIR . 'includes/class-studiou-wc-product-cat-manage-import.php';
- require_once STUDIOU_WCPCM_PLUGIN_DIR . 'includes/class-studiou-wc-product-cat-manage-manipulator.php';
- require_once STUDIOU_WCPCM_PLUGIN_DIR . 'includes/class-studiou-wc-product-manage-product-db.php';
- require_once STUDIOU_WCPCM_PLUGIN_DIR . 'includes/class-studiou-wc-product-manage-product-import.php';
- require_once STUDIOU_WCPCM_PLUGIN_DIR . 'includes/class-studiou-wc-product-manage-product-export.php';
- }
-
- /**
- * Initialize components
- */
- private function init_components() {
- $this->db = new Studiou_WC_Product_Cat_Manage_DB();
- $this->exporter = new Studiou_WC_Product_Cat_Manage_Export($this->db);
- $this->importer = new Studiou_WC_Product_Cat_Manage_Import($this->db);
- $this->manipulator = new Studiou_WC_Product_Cat_Manage_Manipulator($this->db);
- $this->product_db = new Studiou_WC_Product_Manage_Product_DB();
- $this->product_importer = new Studiou_WC_Product_Manage_Product_Import($this->product_db);
- $this->product_exporter = new Studiou_WC_Product_Manage_Product_Export($this->product_db);
- // Register AJAX handlers
- add_action('wp_ajax_studiou_wcpcm_product_import_parse', array($this, 'handle_product_import_parse'));
- add_action('wp_ajax_studiou_wcpcm_product_import_batch', array($this, 'handle_product_import_batch'));
- add_action('wp_ajax_studiou_wcpcm_product_export', array($this, 'handle_product_export'));
- }
-
- /**
- * Check if required plugins are active
- */
- public function check_required_plugins() {
- if (!is_admin() || isset($_GET['activate-multi'])) {
- return;
- }
-
- // Check for WooCommerce
- if (!class_exists('WooCommerce') || !defined('WC_VERSION') || version_compare(WC_VERSION, '9.8', '<')) {
- add_action('admin_notices', array($this, 'woocommerce_missing_notice'));
- deactivate_plugins(STUDIOU_WCPCM_PLUGIN_BASENAME);
- if (isset($_GET['activate'])) {
- unset($_GET['activate']);
- }
- }
- }
-
- /**
- * WooCommerce missing notice
- */
- public function woocommerce_missing_notice() {
- ?>
- <div class="error">
- <p><?php echo sprintf(
- __('QDR - Studiou WC Export/Import Product Categories requires WooCommerce version 9.8 or higher. Please %sinstall or update WooCommerce%s.', 'studiou-wc-product-cat-manage'),
- '<a href="' . admin_url('plugin-install.php?s=woocommerce&tab=search&type=term') . '">',
- '</a>'
- ); ?></p>
- </div>
- <?php
- }
-
- /**
- * Add admin menu items
- */
- public function add_admin_menu() {
- // Make sure WooCommerce menu exists
- if (class_exists('WooCommerce')) {
- add_submenu_page(
- 'edit.php?post_type=product',
- __('Import Product Categories', 'studiou-wc-product-cat-manage'),
- __('Import Product Categories', 'studiou-wc-product-cat-manage'),
- 'manage_woocommerce',
- 'studiou-import-product-categories',
- array($this, 'render_import_page')
- );
-
- add_submenu_page(
- 'edit.php?post_type=product',
- __('Export Product Categories', 'studiou-wc-product-cat-manage'),
- __('Export Product Categories', 'studiou-wc-product-cat-manage'),
- 'manage_woocommerce',
- 'studiou-export-product-categories',
- array($this, 'render_export_page')
- );
-
- add_submenu_page(
- 'edit.php?post_type=product',
- __('Category Manipulations', 'studiou-wc-product-cat-manage'),
- __('Category Manipulations', 'studiou-wc-product-cat-manage'),
- 'manage_woocommerce',
- 'studiou-category-manipulations',
- array($this, 'render_manipulations_page')
- );
- add_submenu_page(
- 'edit.php?post_type=product',
- __('Product Import', 'studiou-wc-product-cat-manage'),
- __('Product Import', 'studiou-wc-product-cat-manage'),
- 'manage_woocommerce',
- 'studiou-product-import',
- array($this, 'render_product_import_page')
- );
- add_submenu_page(
- 'edit.php?post_type=product',
- __('Product Export', 'studiou-wc-product-cat-manage'),
- __('Product Export', 'studiou-wc-product-cat-manage'),
- 'manage_woocommerce',
- 'studiou-product-export',
- array($this, 'render_product_export_page')
- );
- }
- }
-
- /**
- * Register admin assets
- */
- public function register_admin_assets($hook) {
- // Debug: Log hook being checked
- if (defined('WP_DEBUG') && WP_DEBUG) {
- error_log('STUDIOU WC: Checking admin assets for hook: ' . $hook);
- }
-
- if ('product_page_studiou-import-product-categories' === $hook ||
- 'product_page_studiou-export-product-categories' === $hook ||
- 'product_page_studiou-category-manipulations' === $hook ||
- 'product_page_studiou-product-import' === $hook ||
- 'product_page_studiou-product-export' === $hook) {
-
- if (defined('WP_DEBUG') && WP_DEBUG) {
- error_log('STUDIOU WC: Loading admin assets for hook: ' . $hook);
- }
-
- wp_enqueue_style(
- 'studiou-wcpcm-admin',
- $this->get_cache_busted_asset_url('assets/css/admin', 'css'),
- array(),
- STUDIOU_WCPCM_VERSION
- );
- wp_enqueue_script(
- 'studiou-wcpcm-admin',
- $this->get_cache_busted_asset_url('assets/js/admin', 'js'),
- array('jquery'),
- STUDIOU_WCPCM_VERSION,
- true
- );
-
- wp_localize_script('studiou-wcpcm-admin', 'studiouWcpcm', array(
- 'ajaxUrl' => admin_url('admin-ajax.php'),
- 'nonce' => wp_create_nonce('studiou-wcpcm-nonce'),
- 'i18n' => array(
- 'importSuccess' => __('Import successful', 'studiou-wc-product-cat-manage'),
- 'importError' => __('Import error', 'studiou-wc-product-cat-manage'),
- 'exportSuccess' => __('Export successful', 'studiou-wc-product-cat-manage'),
- 'exportError' => __('Export error', 'studiou-wc-product-cat-manage'),
- 'moveSuccess' => __('Move operation successful', 'studiou-wc-product-cat-manage'),
- 'moveError' => __('Move operation error', 'studiou-wc-product-cat-manage'),
- 'selectDifferentCategories' => __('Please select different source and target categories', 'studiou-wc-product-cat-manage'),
- 'batchDescriptionSuccess' => __('Batch description operation successful', 'studiou-wc-product-cat-manage'),
- 'batchDescriptionError' => __('Batch description operation error', 'studiou-wc-product-cat-manage'),
- 'deleteSuccess' => __('Delete operation successful', 'studiou-wc-product-cat-manage'),
- 'deleteError' => __('Delete operation error', 'studiou-wc-product-cat-manage'),
- 'confirmDelete' => __('Are you sure you want to permanently delete ALL products in the selected categories? This action cannot be undone!', 'studiou-wc-product-cat-manage'),
- 'selectAtLeastOneCategory' => __('Please select at least one category', 'studiou-wc-product-cat-manage'),
- 'noProductsFound' => __('No products found in selected categories', 'studiou-wc-product-cat-manage'),
- 'confirmClearMedia' => __('Are you sure you want to permanently delete ALL media files in the selected media categories? This action cannot be undone!', 'studiou-wc-product-cat-manage'),
- 'clearMediaSuccess' => __('Clear media operation successful', 'studiou-wc-product-cat-manage'),
- 'clearMediaError' => __('Clear media operation error', 'studiou-wc-product-cat-manage'),
- 'noMediaFound' => __('No media files found in selected categories', 'studiou-wc-product-cat-manage'),
- 'confirmRemoveUnassigned' => __('Are you sure you want to permanently delete ALL unused uncategorized media files? This action cannot be undone!', 'studiou-wc-product-cat-manage'),
- 'removeUnassignedSuccess' => __('Remove unassigned media operation successful', 'studiou-wc-product-cat-manage'),
- 'removeUnassignedError' => __('Remove unassigned media operation error', 'studiou-wc-product-cat-manage'),
- 'noUnassignedMedia' => __('No unused uncategorized media files found', 'studiou-wc-product-cat-manage'),
- 'upvpSelectAttribute' => __('-- Select attribute --', 'studiou-wc-product-cat-manage'),
- 'upvpSelectValue' => __('-- Select value --', 'studiou-wc-product-cat-manage'),
- 'upvpInvalidPrice' => __('Please enter a valid new price', 'studiou-wc-product-cat-manage'),
- 'upvpLoadingAttributes' => __('Loading attributes...', 'studiou-wc-product-cat-manage'),
- 'upvpLoadingValues' => __('Loading values...', 'studiou-wc-product-cat-manage'),
- 'upvpDryRunComplete' => __('Dry run complete: %d variations would be updated to %s.', 'studiou-wc-product-cat-manage'),
- 'upvpApplySuccess' => __('Variant prices updated successfully', 'studiou-wc-product-cat-manage'),
- 'upvpApplyError' => __('Variant prices update error', 'studiou-wc-product-cat-manage'),
- 'upvpNoMatches' => __('No variations match the current filter.', 'studiou-wc-product-cat-manage'),
- 'upvpConfirmApply' => __('Are you sure you want to update %d variations?', 'studiou-wc-product-cat-manage'),
- 'tabLoading' => __('Loading...', 'studiou-wc-product-cat-manage'),
- 'tabLoadError' => __('Failed to load tab', 'studiou-wc-product-cat-manage'),
- // v1.7.1 — L1: keys for JS strings that were previously hardcoded English.
- 'selectBothCategories' => __('Please select both source and target categories', 'studiou-wc-product-cat-manage'),
- 'moveNoProducts' => __('No products to move.', 'studiou-wc-product-cat-manage'),
- 'importInitializing' => __('Initializing...', 'studiou-wc-product-cat-manage'),
- 'importComplete' => __('Import complete', 'studiou-wc-product-cat-manage'),
- 'deletionSummary' => __('Deletion Summary', 'studiou-wc-product-cat-manage'),
- 'importSummary' => __('Import Summary', 'studiou-wc-product-cat-manage'),
- )
- ));
-
- if (defined('WP_DEBUG') && WP_DEBUG) {
- error_log('STUDIOU WC: Admin assets loaded successfully');
- }
- }
- }
- /**
- * Build a cache-busting URL for a plugin asset.
- *
- * studiou.cz (and similar host optimisers) strip the `?ver=` query string
- * from static assets, so a version bump alone does not bust the browser
- * cache. To work around that, this serves the asset under a filename that
- * embeds the plugin version, e.g. assets/js/admin-1.6.0.js. The versioned
- * copy is generated automatically from the source file and refreshed
- * whenever the source changes, so no manual file renaming is needed.
- *
- * Falls back to the plain source URL if the copy cannot be written.
- *
- * @param string $rel_no_ext Asset path relative to the plugin dir, without
- * extension, e.g. 'assets/js/admin'.
- * @param string $ext File extension without the dot, e.g. 'js'.
- * @return string Absolute URL to the asset.
- */
- private function get_cache_busted_asset_url($rel_no_ext, $ext) {
- $source_rel = $rel_no_ext . '.' . $ext;
- $source_abs = STUDIOU_WCPCM_PLUGIN_DIR . $source_rel;
- // Source missing - nothing we can do, return the plain URL.
- if (!file_exists($source_abs)) {
- return STUDIOU_WCPCM_PLUGIN_URL . $source_rel;
- }
- // v1.7.1 — L4: write the versioned copies under uploads/ instead of
- // the plugin directory. The plugin dir is conventionally read-only
- // on managed hosts and trips some security scanners when files
- // appear in it at runtime; uploads/ is the right place for runtime-
- // generated assets. The original source files in the plugin dir
- // are still the canonical edit targets — only the versioned copies
- // moved.
- $upload_dir = wp_upload_dir();
- if (empty($upload_dir['error'])) {
- $cache_dir = trailingslashit($upload_dir['basedir']) . 'studiou-wcpcm-cache/' . dirname($rel_no_ext) . '/';
- $cache_url = trailingslashit($upload_dir['baseurl']) . 'studiou-wcpcm-cache/' . dirname($rel_no_ext) . '/';
- if (!file_exists($cache_dir)) {
- wp_mkdir_p($cache_dir);
- @file_put_contents(dirname($cache_dir) . '/index.php', '<?php // Silence is golden.');
- @file_put_contents($cache_dir . 'index.php', '<?php // Silence is golden.');
- }
- $basename = basename($rel_no_ext) . '-' . STUDIOU_WCPCM_VERSION . '.' . $ext;
- $versioned_abs = $cache_dir . $basename;
- $versioned_url = $cache_url . $basename;
- if (!file_exists($versioned_abs) || filemtime($source_abs) > filemtime($versioned_abs)) {
- // Remove stale versioned copies of this asset in the uploads cache.
- $old_files = glob($cache_dir . basename($rel_no_ext) . '-*.' . $ext);
- if (is_array($old_files)) {
- foreach ($old_files as $old_file) {
- @unlink($old_file);
- }
- }
- if (!@copy($source_abs, $versioned_abs)) {
- return STUDIOU_WCPCM_PLUGIN_URL . $source_rel;
- }
- }
- return $versioned_url;
- }
- // wp_upload_dir() failed — fall back to the plain source URL.
- return STUDIOU_WCPCM_PLUGIN_URL . $source_rel;
- }
- /**
- * Render import page
- */
- public function render_import_page() {
- include STUDIOU_WCPCM_PLUGIN_DIR . 'views/import.php';
- }
-
- /**
- * Render export page
- */
- public function render_export_page() {
- include STUDIOU_WCPCM_PLUGIN_DIR . 'views/export.php';
- }
-
- /**
- * Render manipulations page
- */
- public function render_manipulations_page() {
- include STUDIOU_WCPCM_PLUGIN_DIR . 'views/manipulations.php';
- }
- /**
- * Render product import page
- */
- public function render_product_import_page() {
- include STUDIOU_WCPCM_PLUGIN_DIR . 'views/product-import.php';
- }
- /**
- * Render product export page
- */
- public function render_product_export_page() {
- include STUDIOU_WCPCM_PLUGIN_DIR . 'views/product-export.php';
- }
- /**
- * Handle product import parse AJAX request (step 1: parse CSV)
- */
- public function handle_product_import_parse() {
- // Clean any output buffers
- while (ob_get_level()) {
- ob_end_clean();
- }
- // Verify nonce
- if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'studiou-wcpcm-nonce')) {
- wp_send_json_error(array('message' => __('Security check failed', 'studiou-wc-product-cat-manage')));
- return;
- }
- // Check permissions
- if (!current_user_can('manage_woocommerce')) {
- wp_send_json_error(array('message' => __('Insufficient permissions', 'studiou-wc-product-cat-manage')));
- return;
- }
- // Check if file was uploaded
- if (!isset($_FILES['import_file']) || $_FILES['import_file']['error'] !== UPLOAD_ERR_OK) {
- wp_send_json_error(array('message' => __('No file uploaded or upload error', 'studiou-wc-product-cat-manage')));
- return;
- }
- try {
- // Parse CSV and store in transient
- $result = $this->product_importer->parse_csv_for_batch($_FILES['import_file']['tmp_name']);
- if (isset($result['error'])) {
- wp_send_json_error(array('message' => $result['error']));
- return;
- }
- wp_send_json_success(array(
- 'transient_key' => $result['transient_key'],
- 'total' => $result['total']
- ));
- } catch (\Throwable $e) {
- // \Throwable (not Exception) — array_combine, type errors, and
- // other Error subclasses would otherwise escape as bare 500s.
- error_log('STUDIOU WC Product Import Parse Error: ' . $e->getMessage());
- wp_send_json_error(array('message' => $e->getMessage()));
- }
- }
- /**
- * Handle product import batch AJAX request (step 2: process batch)
- */
- public function handle_product_import_batch() {
- // Clean any output buffers
- while (ob_get_level()) {
- ob_end_clean();
- }
- // Verify nonce
- if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'studiou-wcpcm-nonce')) {
- wp_send_json_error(array('message' => __('Security check failed', 'studiou-wc-product-cat-manage')));
- return;
- }
- // Check permissions
- if (!current_user_can('manage_woocommerce')) {
- wp_send_json_error(array('message' => __('Insufficient permissions', 'studiou-wc-product-cat-manage')));
- return;
- }
- // Get parameters
- $transient_key = isset($_POST['transient_key']) ? sanitize_text_field($_POST['transient_key']) : '';
- $offset = isset($_POST['offset']) ? intval($_POST['offset']) : 0;
- if (empty($transient_key)) {
- wp_send_json_error(array('message' => __('Invalid request', 'studiou-wc-product-cat-manage')));
- return;
- }
- try {
- // Process batch
- $result = $this->product_importer->process_batch($transient_key, $offset, 5);
- if (isset($result['error'])) {
- wp_send_json_error(array('message' => $result['error']));
- return;
- }
- // Generate failed CSV if this is the last batch and there are failed items
- $failed_csv = '';
- if ($result['processed'] >= $result['total'] && !empty($result['failed_items'])) {
- $failed_csv = $this->product_importer->generate_failed_csv($result['failed_items']);
- }
- wp_send_json_success(array(
- 'success' => $result['success'],
- 'failed' => $result['failed'],
- 'skipped' => $result['skipped'],
- 'messages' => $result['messages'],
- 'processed' => $result['processed'],
- 'total' => $result['total'],
- 'failed_csv' => $failed_csv
- ));
- } catch (\Throwable $e) {
- error_log('STUDIOU WC Product Import Batch Error: ' . $e->getMessage());
- wp_send_json_error(array('message' => $e->getMessage()));
- }
- }
- /**
- * Handle product export AJAX request
- */
- public function handle_product_export() {
- // Clean any output buffers
- while (ob_get_level()) {
- ob_end_clean();
- }
- // Verify nonce
- if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'studiou-wcpcm-nonce')) {
- wp_send_json_error(array('message' => __('Security check failed', 'studiou-wc-product-cat-manage')));
- return;
- }
- // Check permissions
- if (!current_user_can('manage_woocommerce')) {
- wp_send_json_error(array('message' => __('Insufficient permissions', 'studiou-wc-product-cat-manage')));
- return;
- }
- // Get selected categories
- $category_ids = isset($_POST['export_categories']) ? array_map('intval', $_POST['export_categories']) : array();
- if (empty($category_ids)) {
- wp_send_json_error(array('message' => __('No categories selected', 'studiou-wc-product-cat-manage')));
- return;
- }
- try {
- // Generate export file
- $result = $this->product_exporter->generate_export_file($category_ids);
- if ($result['success']) {
- wp_send_json_success(array(
- 'message' => $result['message'],
- 'download_url' => $result['download_url']
- ));
- } else {
- wp_send_json_error(array('message' => $result['message']));
- }
- } catch (\Throwable $e) {
- error_log('STUDIOU WC Product Export Error: ' . $e->getMessage());
- wp_send_json_error(array('message' => $e->getMessage()));
- }
- }
- }
- /**
- * Begin execution of the plugin
- */
- function run_studiou_wc_product_cat_manage() {
- // Only initialize once
- static $initialized = false;
- if ($initialized) {
- return;
- }
- $initialized = true;
-
- // Load text domain first
- load_plugin_textdomain(
- 'studiou-wc-product-cat-manage',
- false,
- dirname(plugin_basename(__FILE__)) . '/languages/'
- );
-
- // Create instance
- new Studiou_WC_Product_Cat_Manage();
- }
- // Run the plugin after plugins are loaded to ensure WooCommerce is available
- add_action('plugins_loaded', 'run_studiou_wc_product_cat_manage');
|