modules = $modules; } public function register(): void { add_action('admin_menu', [$this, 'addMenuPage']); add_action('admin_init', [$this, 'registerSettings']); add_action('admin_enqueue_scripts', [$this, 'enqueueAssets']); add_action('admin_notices', [$this, 'adminNotices']); } // Branding private function getMenuIcon(): string { $svg = ''; return 'data:image/svg+xml;base64,' . base64_encode($svg); } // Menu & settings registration public function addMenuPage(): void { add_menu_page( __('Aikanen', 'aikanen'), __('Aikanen', 'aikanen'), 'manage_woocommerce', 'aikanen', [$this, 'render'], $this->getMenuIcon(), 56 ); add_submenu_page( 'aikanen', __('Settings', 'aikanen'), __('Settings', 'aikanen'), 'manage_woocommerce', 'aikanen', [$this, 'render'], ); add_submenu_page( 'aikanen', __('Products', 'aikanen'), __('Products', 'aikanen'), 'manage_woocommerce', 'aikanen-products', [$this, 'renderProducts'], ); add_submenu_page( 'aikanen', __('Categories', 'aikanen'), __('Categories', 'aikanen'), 'manage_woocommerce', 'aikanen-categories', [$this, 'renderCategories'], ); } public function registerSettings(): void { $requiresToken = fn($v) => get_option('aikanen_api_token') ? rest_sanitize_boolean($v) : false; // Connection (API and Webhook) register_setting('aikanen', 'aikanen_api_token', [ 'type' => 'string', 'sanitize_callback' => function ($value) { $value = sanitize_text_field($value); return $value !== '' ? $value : get_option('aikanen_api_token', ''); }, ]); register_setting('aikanen', 'aikanen_webhook_secret', [ 'type' => 'string', 'sanitize_callback' => function ($value) { $value = trim($value); return $value !== '' ? $value : get_option('aikanen_webhook_secret', ''); }, ]); // VAT mapping register_setting('aikanen', ProductImportMapper::OPTION_VAT_MAP, [ 'type' => 'array', 'sanitize_callback' => [$this, 'sanitizeVatMap'], ]); // Automatic sync toggles foreach ([ 'aikanen_auto_export_category', 'aikanen_auto_export_product', 'aikanen_auto_import_category', 'aikanen_auto_import_product', 'aikanen_auto_stock_sync', ] as $option) { register_setting('aikanen', $option, [ 'type' => 'boolean', 'default' => false, 'sanitize_callback' => $requiresToken, ]); } register_setting('aikanen', 'aikanen_stock_source_of_truth', [ 'type' => 'string', 'default' => 'aikanen', 'sanitize_callback' => fn ($v) => in_array($v, ['aikanen', 'woocommerce', 'bidirectional']) ? $v : 'aikanen', ]); register_setting('aikanen', 'aikanen_stock_full_sync_interval', [ 'type' => 'string', 'default' => 'disabled', 'sanitize_callback' => fn($v) => in_array($v, ['disabled', 'hourly', 'daily'], true) ? $v : 'disabled', ]); } public function sanitizeVatMap(mixed $input): array { if (!is_array($input)) { return ProductImportMapper::DEFAULT_VAT_MAP; } $validClasses = array_merge([''], \WC_Tax::get_tax_class_slugs()); $map = []; foreach (['1', '2', '3', '4'] as $vatId) { $value = (string) ($input[$vatId] ?? ''); $map[$vatId] = in_array($value, $validClasses, true) ? $value : (ProductImportMapper::DEFAULT_VAT_MAP[$vatId] ?? ''); } return $map; } // Assets public function enqueueAssets(string $hook): void { if (!in_array($hook, ['toplevel_page_aikanen', 'aikanen_page_aikanen-products', 'aikanen_page_aikanen-categories'], true)) return; wp_enqueue_style('aikanen-admin', plugins_url('assets/css/admin.css', AIKANEN_PLUGIN_FILE), [], AIKANEN_VERSION); wp_enqueue_script('aikanen-admin', plugins_url('assets/js/admin.js', AIKANEN_PLUGIN_FILE), [], AIKANEN_VERSION, true); if ($hook === 'toplevel_page_aikanen') { wp_add_inline_script('aikanen-admin', ' (function () { var interval = document.querySelector("[name=\"aikanen_stock_full_sync_interval\"]"); var sourceRow = document.getElementById("aikanen-stock-source-row"); if (!interval || !sourceRow) return; interval.addEventListener("change", function () { sourceRow.hidden = this.value === "disabled"; }); })(); '); } if ($hook === 'aikanen_page_aikanen-products') { wp_add_inline_script('aikanen-admin', ' document.getElementById("aikanen-select-all")?.addEventListener("change", function () { document.querySelectorAll("input[name=\"services[]\"]").forEach(function (cb) { cb.checked = this.checked; }, this); }); '); } } // Admin notices public function adminNotices(): void { $notice = get_transient('aikanen_notice'); if (!$notice) { return; } delete_transient('aikanen_notice'); printf( '
%s