self::CAP); add_filter('plugin_action_links_' . plugin_basename(GERSYNC_FILE), [self::class, 'links']); add_filter('plugin_row_meta', [self::class, 'rowMeta'], 10, 2); add_action('woocommerce_product_options_sku', [self::class, 'productGuid']); add_action('woocommerce_variation_options', [self::class, 'variationGuid'], 10, 3); add_action('woocommerce_admin_process_product_object', [self::class, 'saveLock']); } public static function menu(): void { self::$hook = (string) add_submenu_page('woocommerce', 'Обмен с 1С', 'Обмен с 1С', self::CAP, self::PAGE, [self::class, 'render']); } /** * Стили и скрипт — только на странице настроек плагина. */ public static function assets(string $hook): void { if ($hook !== self::$hook || self::$hook === '') { return; } wp_enqueue_style('gersync-admin', plugins_url('assets/admin.css', GERSYNC_FILE), [], GERSYNC_VERSION); wp_enqueue_script('gersync-admin', plugins_url('assets/admin.js', GERSYNC_FILE), [], GERSYNC_VERSION, ['in_footer' => true]); } public static function settings(): void { register_setting(self::GROUP, Settings::OPTION, [ 'type' => 'array', 'sanitize_callback' => [Settings::class, 'sanitize'], 'default' => [], ]); } public static function links(array $links): array { $url = admin_url('admin.php?page=' . self::PAGE); array_unshift($links, '' . esc_html__('Настройки', 'gersync') . ''); return $links; } /** * Ссылка «Поддержать автора» в строке плагина (под описанием). */ public static function rowMeta(array $meta, string $file): array { if ($file !== plugin_basename(GERSYNC_FILE)) { return $meta; } if (GERSYNC_DONATE_URL !== '') { $meta[] = self::link(self::donateUrl(GERSYNC_DONATE_DEFAULT), '☕ ' . __('Кофе автору', 'gersync')); } if (GERSYNC_AUTHOR_URL !== '') { $meta[] = self::link(GERSYNC_AUTHOR_URL, __('Об авторе и проектах', 'gersync')); } return $meta; } /** * Ссылка на донат с подставленной суммой ({amount} — «1.99», {cents} — «199»). */ public static function donateUrl(string $amount): string { $value = max(1.0, min(500.0, (float) $amount)); return str_replace( ['{amount}', '{cents}'], [number_format($value, 2, '.', ''), (string) (int) round($value * 100)], GERSYNC_DONATE_URL ); } private static function link(string $url, string $label): string { return sprintf('%s', esc_url($url), esc_html($label)); } /** * GUID из 1С на вкладке «Запасы» товара (только в админке). */ public static function productGuid(): void { global $product_object; if (!$product_object instanceof \WC_Product) { return; } self::guidField($product_object, '

%s

'); if ($product_object->get_meta(Repo::META) !== '' || $product_object->get_meta(Repo::LEGACY_META) !== '') { echo ''; woocommerce_wp_checkbox([ 'id' => Products::LOCK_META, 'value' => $product_object->get_meta(Products::LOCK_META) === 'yes' ? 'yes' : 'no', 'label' => __('Не обновлять из 1С', 'gersync'), 'description' => __('Название, описание, категории, картинки, атрибуты, артикул и вес этого товара 1С менять не будет. Цены и остатки обновляются.', 'gersync'), ]); } } /** * Сохранение галочки «Не обновлять из 1С». Nonce и права проверяет WooCommerce при сохранении товара. */ public static function saveLock(\WC_Product $product): void { if (empty($_POST['gersync_lock_field'])) { return; // поля не было в форме } $nonce = isset($_POST['woocommerce_meta_nonce']) ? sanitize_text_field(wp_unslash($_POST['woocommerce_meta_nonce'])) : ''; if (!wp_verify_nonce($nonce, 'woocommerce_save_data') || !current_user_can('edit_post', $product->get_id())) { return; } $product->update_meta_data(Products::LOCK_META, empty($_POST[Products::LOCK_META]) ? 'no' : 'yes'); } /** * GUID у каждой вариации, рядом с флажками «Включено / Виртуальный». */ public static function variationGuid(int $loop, array $data, \WP_Post $variation): void { $product = wc_get_product($variation->ID); if ($product instanceof \WC_Product) { self::guidField($product, 'Ид в 1С: %s'); } } private static function guidField(\WC_Product $product, string $wrapper): void { $guid = (string) $product->get_meta(Repo::META); $legacy = (string) $product->get_meta(Repo::LEGACY_META); if ($guid === '' && $legacy === '') { return; } $html = '' . esc_html($guid !== '' ? $guid : $legacy) . ''; if ($guid === '') { $html .= ' ' . esc_html__('(из прежнего плагина, привяжется при первом обмене)', 'gersync') . ''; } echo wp_kses(sprintf($wrapper, $html), [ 'p' => ['class' => true], 'label' => [], 'span' => ['class' => true, 'style' => true], 'code' => [], ]); } public static function tool(): void { if (!current_user_can(self::CAP)) { wp_die(esc_html__('Недостаточно прав.', 'gersync'), 403); } check_admin_referer('gersync_tool'); $tool = sanitize_key((string) ($_POST['tool'] ?? '')); switch ($tool) { case 'clear': Storage::clear(); State::reset(); break; case 'reset_maps': delete_option(Catalog::PROPS_OPTION); delete_option(Catalog::PRICES_OPTION); break; case 'force_full': Session::requestForced(true); Log::warning('Запланирован принудительный полный обмен (пользователь #' . get_current_user_id() . ')'); break; case 'cancel_force': Session::requestForced(false); break; case 'cleanup_images': if (!MediaCleanup::start()) { $tool = 'cleanup_busy'; } break; default: $tool = ''; } wp_safe_redirect(add_query_arg(['page' => self::PAGE, 'gersync_done' => $tool], admin_url('admin.php')) . '#service'); exit; } public static function render(): void { if (current_user_can(self::CAP)) { (new AdminPage())->render(); } } }