diff --git a/.vscode/ftp-kr.json b/.vscode/ftp-kr.json index 022fa3e..ab48f12 100644 --- a/.vscode/ftp-kr.json +++ b/.vscode/ftp-kr.json @@ -2,7 +2,7 @@ "host": "145.223.76.240", "username": "u350065327", "password": "QpJFkYkT48TwiWOm!", - "remotePath": "/domains/lightseagreen-crab-771745.hostingersite.com/public_html", + "remotePath": "/domains/hubertus.rzeszow.pl/public_html", "protocol": "ftp", "port": 0, "fileNameEncoding": "utf8", diff --git a/.vscode/sftp.json b/.vscode/sftp.json index fb18ddf..7333876 100644 --- a/.vscode/sftp.json +++ b/.vscode/sftp.json @@ -5,7 +5,7 @@ "port": 21, "username": "u350065327", "password": "QpJFkYkT48TwiWOm!", - "remotePath": "/domains/lightseagreen-crab-771745.hostingersite.com/public_html", + "remotePath": "/domains/hubertus.rzeszow.pl/public_html", "uploadOnSave": false, "useTempFile": false, "openSsh": false diff --git a/wp-content/object-cache.php b/wp-content/object-cache.php new file mode 100644 index 0000000..cf00545 --- /dev/null +++ b/wp-content/object-cache.php @@ -0,0 +1,57 @@ +atfpp_register_hooks(); + $this->atfpp_initialize_license(); + // Add error notice hook + add_action('atfpp_display_admin_notices', array($this, 'atfpp_display_license_error_messages')); + add_action('atfpp_display_admin_notices', array($this, 'atfpp_display_license_key_notice')); + } + + + private function atfpp_register_hooks() { + add_action( 'admin_print_styles', [ $this, 'atfpp_set_admin_style' ] ); + add_action( 'admin_menu', array( $this, 'atfpp_add_submenu_page' ), 11 ); + add_action('admin_post_AIAutomaticTranslationsForPolylang_el_activate_license', [$this, 'atfpp_handle_license_activation']); + add_action('admin_post_AIAutomaticTranslationsForPolylang_el_deactivate_license', [$this, 'atfpp_handle_license_deactivation']); + add_action('wp_ajax_atfpp_refresh_license_ajax', [$this, 'atfpp_handle_refresh_license_ajax']); + } + + private function atfpp_initialize_license() { + $licenseKey = get_option(self::OPTION_LICENSE_KEY, ""); + $liceEmail = get_option(self::OPTION_LICENSE_EMAIL, get_bloginfo('admin_email')); + + AI_Automatic_Translations_For_Polylang_Base::add_on_delete(function(){ + delete_option(self::OPTION_LICENSE_KEY); + delete_option(self::OPTION_LICENSE_EMAIL); + }); + + if (AI_Automatic_Translations_For_Polylang_Base::CheckWPPlugin($licenseKey, $liceEmail, $this->licenseMessage, $this->responseObj, $this->plugin_file)) { + self::$form_status = true; + } else { + self::$form_status = false; + if(!empty($licenseKey) && !empty($this->licenseMessage)) { + $this->showMessage = true; + } + } + } + + function atfpp_set_admin_style() { + if (isset($_GET['page']) && $_GET['page'] === 'polylang-atfpp-dashboard') { + wp_enqueue_style( + 'atfpp-dashboard-style', + ATFPP_URL . 'admin/atfpp-dashboard/css/admin-styles.css', + array(), + ATFPP_V, + 'all' + ); + wp_enqueue_style("atfpp-dashboard-style"); + + wp_enqueue_script( + 'atfpp-dashboard-script', + ATFPP_URL . 'admin/atfpp-dashboard/js/atfpp-glossary.js', + array('jquery','underscore'), // dependencies + ATFPP_V, + true // load in footer + ); + + // Fetch Polylang languages + $pll_languages_serialized = get_option('_transient_pll_languages_list'); + $pll_languages = []; + if ($pll_languages_serialized) { + $pll_languages = maybe_unserialize($pll_languages_serialized); + } + $languages = []; + if (is_array($pll_languages)) { + foreach ($pll_languages as $pll_lang) { + $languages[] = [ + 'code' => $pll_lang['slug'], + 'alt' => $pll_lang['name'], + 'img' => $pll_lang['flag_url'], + ]; + } + } + + wp_localize_script('atfpp-dashboard-script', 'atfpp_glossary', array( + 'ajaxurl' => admin_url('admin-ajax.php'), + 'atfpp_languages' => $languages, + 'url' => ATFPP_URL, + 'file' => 'file.svg', + 'nonce' => wp_create_nonce('atfpp_glossary_nonce'), + )); + } + + wp_enqueue_script( + 'atfpp-dashboard-plugin-setting', + ATFPP_URL . 'admin/atfpp-dashboard/js/atfpp-plugin-setting.js', + array('jquery'), + ATFPP_V, + true + ); + + wp_enqueue_script( + 'atfpp-dashboard-data-share', + ATFPP_URL . 'admin/atfpp-dashboard/js/atfpp-data-share-setting.js', + array('jquery'), + ATFPP_V, + true + ); + + wp_localize_script( 'atfpp-dashboard-data-share', 'atfpp_ajax', array( + 'nonce' => wp_create_nonce('atfpp_refresh_license_nonce'), + 'ajaxurl' => admin_url('admin-ajax.php') + ) ); + + } + + /* + |------------------------------------------------------- + | AI Automatic Translate Addon For Polylang admin page + |------------------------------------------------------- + */ + function atfpp_add_submenu_page() { + add_submenu_page( + 'mlang', // Parent slug + __( 'AutoPoly - AI Translation For Polylang', 'autopoly-ai-translation-for-polylang-pro' ), // Page title + __( 'AutoPoly', 'autopoly-ai-translation-for-polylang-pro' ), // Menu title + 'manage_options', // Capability + // $this->slug, // Menu slug + 'polylang-atfpp-dashboard', //Menu slug + array( $this, 'atfpp_dashboard_page' ) // Callback function + ); + } + + /** + * Render the dashboard page with dynamic text domain support + * + * @param string $text_domain The text domain for translations (default: 'autopoly-ai-translation-for-polylang-pro') + */ + function atfpp_dashboard_page() { + $text_domain = 'autopoly-ai-translation-for-polylang-pro'; + $file_prefix = ATFPP_DIR_PATH . 'admin/atfpp-dashboard/views/'; + + $valid_tabs = $this->atfpp_get_valid_tabs($text_domain); + $buttons = $this->atfpp_get_action_buttons($text_domain); + + // Get current tab with fallback + $tab = isset($_GET['tab']) ? sanitize_key($_GET['tab']) : 'dashboard'; + $current_tab = array_key_exists($tab, $valid_tabs) ? $tab : 'dashboard'; + + ?> +
+
+
+ + <?php esc_attr_e('Polylang Addon Logo', $text_domain); ?> + +
+ +
+
+
+ + + + + <?php echo esc_attr($button['alt']); ?> + + + + + + +
+
+ + + +
+ licenseMessage,$this->responseObj,$this->plugin_file)){ + if($current_tab === 'license'){ + atfpp_render_license_page_pro($this->responseObj); + } + if($current_tab === 'settings'){ + atfpp_render_settings_page_pro(); + } + }else{ + if($current_tab === 'license'){ + atfpp_render_license_page(); + } + if($current_tab === 'settings'){ + atfpp_render_settings_page(); + } + } + } + + $sidebar_file = $file_prefix . 'sidebar.php'; + if (file_exists($sidebar_file) && $current_tab !== 'support-blocks' && $current_tab !== 'custom-fields' && $current_tab !== 'glossary') { + require_once $sidebar_file; + } + ?> +
+ +
+ __('Dashboard', $text_domain), + 'ai-translations' => __('AI Translations', $text_domain), + 'settings' => __('Settings', $text_domain), + 'license' => __('License', $text_domain), + 'support-blocks' => __('Supported Blocks', $text_domain), + 'custom-fields' => __('Custom Fields', $text_domain), + 'glossary' => __('Glossary', $text_domain), + ]; + } + + private function atfpp_get_action_buttons($text_domain) { + return [ + [ + 'url' => 'https://coolplugins.net/products/?utm_source=atfp_plugin&utm_medium=inside&utm_campaign=product_page&utm_content=dashboard_header_pro', + 'alt' => __('Explore Cool Plugins', $text_domain), + 'img' => 'upgrade-now.svg', + 'text' => __('Explore Cool Plugins', $text_domain) + ], + // [ + // 'url' => 'https://docs.coolplugins.net/docs/ai-translation-for-polylang/', + // 'img' => 'document.svg', + // 'alt' => __('document', $text_domain) + // ], + [ + 'url' => 'https://coolplugins.net/support/?utm_source=atfp_plugin&utm_medium=inside&utm_campaign=support&utm_content=dashboard_header_pro', + 'img' => 'contact.svg', + 'alt' => __('contact', $text_domain) + ] + ]; + } + + public function atfpp_handle_license_activation() { + $this->atfpp_check_user_capabilities(); + check_admin_referer('el-atfpp-license'); + + // Validate license key + $license_key = !empty($_POST['license_code']) ? sanitize_text_field(wp_unslash($_POST['license_code'])) : ''; + if (empty($license_key)) { + $this->atfpp_redirect_with_error('missing_key'); + return; + } + + // Validate email + $license_email = !empty($_POST['email']) ? sanitize_email(wp_unslash($_POST['email'])) : ''; + if (empty($license_email)) { + $this->atfpp_redirect_with_error('missing_email'); + return; + } + + $error = ''; + $responseObj = null; + + if (AI_Automatic_Translations_For_Polylang_Base::check_wp_plugin($license_key, $license_email, $error, $responseObj, $this->plugin_file)) { + update_option(self::OPTION_LICENSE_KEY, $license_key); + update_option(self::OPTION_LICENSE_EMAIL, $license_email); + delete_site_transient('update_plugins'); + $this->atfpp_redirect_with_success(); + } else { + $this->showMessage = true; + $this->licenseMessage = $error; + $this->atfpp_redirect_with_error('invalid_key'); + } + } + + private function atfpp_check_user_capabilities() { + if (!current_user_can('manage_options')) { + wp_die(__('You do not have sufficient permissions to access this page.', 'autopoly-ai-translation-for-polylang-pro')); + } + } + + private function atfpp_redirect_with_success() { + wp_redirect(admin_url('admin.php?page=polylang-atfpp-dashboard&tab=license&activated=true')); + exit; + } + + private function atfpp_redirect_with_error($error) { + wp_redirect(admin_url('admin.php?page=polylang-atfpp-dashboard&tab=license&error=' . urlencode($error))); + exit; + } + + public function atfpp_handle_license_deactivation() { + try { + $this->atfpp_check_user_capabilities(); + check_admin_referer('el-atfpp-license'); + + $message = ''; + if (AI_Automatic_Translations_For_Polylang_Base::remove_license_key($this->plugin_file, $message)) { + delete_option(self::OPTION_LICENSE_KEY); + delete_option(self::OPTION_LICENSE_EMAIL); + delete_site_transient('update_plugins'); + + wp_safe_redirect(admin_url('admin.php?page=polylang-atfpp-dashboard&tab=license&deactivated=true')); + exit; + } + + wp_safe_redirect(admin_url('admin.php?page=polylang-atfpp-dashboard&tab=license&error=' . urlencode($message))); + exit; + + } catch (\Exception $e) { + wp_safe_redirect(admin_url('admin.php?page=polylang-atfpp-dashboard&tab=license&error=unexpected_error')); + exit; + } + } + + /** + * Handle AJAX license refresh request + */ + function atfpp_handle_refresh_license_ajax() { + // Check nonce for security + if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field($_POST['nonce']), 'atfpp_refresh_license_nonce')) { + wp_send_json_error(array('message' => 'Security check failed.')); + return; + } + + $this->atfpp_check_user_capabilities(); + + // Get existing license key and email + $license_key = get_option(self::OPTION_LICENSE_KEY, ''); + $license_email = get_option(self::OPTION_LICENSE_EMAIL, ''); + + if (empty($license_key) || empty($license_email)) { + wp_send_json_error(array('message' => 'No license information found to refresh.')); + return; + } + + $error = ''; + $responseObj = null; + + // Use the same activation logic to refresh the license + if (AI_Automatic_Translations_For_Polylang_Base::check_wp_plugin($license_key, $license_email, $error, $responseObj, $this->plugin_file)) { + + // Get updated license information + $license_info = array( + 'is_valid' => $responseObj->is_valid ? $responseObj->is_valid:false, + 'license_title' => $responseObj->license_title ? $responseObj->license_title : '', + 'expire_date' => $responseObj->expire_date ? $responseObj->expire_date : '', + 'market' => $responseObj->market ? $responseObj->market : '', + 'support_end' => $responseObj->support_end ? $responseObj->support_end : '', + ); + + + wp_send_json_success(array( + 'message' => 'License Status Updated successfully!', + 'license_info' => $license_info, + 'version_available_message' => AutoPolyPro::atfppGetVersionAvailableMessage() + )); + } else { + // Map error message to error code + $error_code = $this->atfpp_map_error_to_code($error); + $error_message = $this->atfpp_get_error_message($error_code); + wp_send_json_error(array('message' => $error_message)); + } + } + + private function atfpp_is_valid_license_key($licenseKey) { + // This method appears to be unused - can be removed + $pattern = '/^[0-9A-F][0-9A-F]{7}-[0-9A-F]{8}-[0-9A-F]{8}-[0-9A-F]{8}$/i'; + return preg_match($pattern, $licenseKey) ? true : false; + } + + private function atfpp_map_error_to_code($error) { + // This method appears to be unused - can be removed + $error = strtolower($error); + if (strpos($error, 'invalid') !== false) { + return 'invalid_key'; + } elseif (strpos($error, 'expired') !== false) { + return 'expired'; + } elseif (strpos($error, 'disabled') !== false) { + return 'disabled'; + } elseif (strpos($error, 'no activation') !== false) { + return 'no_activations'; + } + return 'default'; + } + + public function atfpp_display_license_error_messages() { + if (isset($_GET['page']) && $_GET['page'] === 'polylang-atfpp-dashboard' && isset($_GET['tab']) && $_GET['tab'] === 'license') { + if (isset($_GET['error'])) { + $error_message = $this->atfpp_get_error_message($_GET['error']); + ?> +
+

+
+ +
+

+
+ +
+

+
+ showMessage && !empty($this->licenseMessage)) { + ?> +
+

licenseMessage); ?>

+
+ __('License key is required.', 'autopoly-ai-translation-for-polylang-pro'), + 'missing_email' => __('Email address is required.', 'autopoly-ai-translation-for-polylang-pro'), + 'invalid_email' => __('Please enter a valid email address.', 'autopoly-ai-translation-for-polylang-pro'), + 'invalid_format' => __('Invalid license key format. Please check your license key.', 'autopoly-ai-translation-for-polylang-pro'), + 'invalid_key' => __('Invalid license key. Please check your license key and try again.', 'autopoly-ai-translation-for-polylang-pro'), + 'expired' => __('Your license key has expired. Please renew your license.', 'autopoly-ai-translation-for-polylang-pro'), + 'disabled' => __('Your license key has been disabled.', 'autopoly-ai-translation-for-polylang-pro'), + 'no_activations' => __('No activations left for this license key.', 'autopoly-ai-translation-for-polylang-pro'), + 'default' => __('An error occurred while validating your license. Please try again.', 'autopoly-ai-translation-for-polylang-pro') + ); + + return isset($messages[$error_code]) ? $messages[$error_code] : $messages['default']; + } + + public function atfpp_display_license_key_notice() { + if ($this->showMessage && !empty($this->licenseMessage)) { + ?> +
+

licenseMessage); ?>

+
+ is_theme = true; + $this->theme_dir_name = self::get_this_theme_path_name(); + $theme_data = wp_get_theme($this->theme_dir_name); + $version = $theme_data->get('Version'); + if (!empty($version)) + { + $this->version = $version; + } + } + $this->plugin_file = $plugin_base_file; + if (empty($this->plugin_file) && $this->is_theme) + { + $this->plugin_file = self::get_this_theme_path(); + } + if (empty($this->version)) + { + $this->version = $this->get_current_version(); + } + + if ($this->has_check_update) + { + if (function_exists("add_action")) + { + add_action('admin_post_atfpp_fupc', function () + { + update_option('_site_transient_update_plugins', ''); + update_option('_site_transient_update_themes', ''); + set_site_transient('update_themes', null); + delete_transient($this->product_base . "_up"); + wp_redirect(admin_url('plugins.php')); + exit; + }); + add_action('init', [$this, "init_action_handler"]); + } + if (function_exists("add_filter")) + { + if ($this->is_theme) + { + add_filter('pre_set_site_transient_update_themes', [$this, "plugin_update"]); + add_filter('themes_api', [$this, 'check_update_info'], 10, 3); + add_action('admin_menu', function () + { + add_theme_page('Update Check', 'Update Check', 'edit_theme_options', 'update_check', [$this, "theme_force_update"]); + }, 999); + } + else + { + add_filter('pre_set_site_transient_update_plugins', [$this, "plugin_update"]); + add_filter('plugins_api', [$this, 'check_update_info'], 10, 3); + add_filter('plugin_row_meta', function ($links, $plugin_file) + { + if (plugin_basename($this->plugin_file) == $plugin_file) + { + $links[] = " Update Check"; + } + return $links; + }, 10, 2); + add_action("in_plugin_update_message-" . plugin_basename($this->plugin_file), [$this, 'update_message_cb'], 20, 2); + } + + add_action('upgrader_process_complete', function ($upgrader_object, $options) + { + update_option('_site_transient_update_plugins', ''); + update_option('_site_transient_update_themes', ''); + set_site_transient('update_themes', null); + }, 10, 2); + } + } + } + public function theme_force_update() + { + $this->clean_update_info(); + $url = admin_url('themes.php'); + echo wp_kses_post('

' . __("Update Checking..", "autopoly-ai-translation-for-polylang-pro") . '

'); + call_user_func('printf', '%s', ""); + } + public function set_email_address($email_address) + { + $this->email_address = $email_address; + } + function init_action_handler() + { + $handler = hash("crc32b", $this->product_id . $this->key . $this->get_domain()) . "_handle"; + if (isset($_GET['action']) && $_GET['action'] == $handler) + { + $this->handle_server_request(); + exit; + } + } + function handle_server_request() + { + $type = isset($_GET['type']) ? strtolower(sanitize_text_field(wp_unslash($_GET['type']))) : ''; + switch ($type) + { + case "rl": //remove license + $this->clean_update_info(); + $this->remove_old_wp_response(); + $obj = new stdClass(); + $obj->product = $this->product_id; + $obj->status = true; + call_user_func('printf', '%s', $this->encrypt_obj($obj)); + return; + case "rc": //remove license + $key = $this->getKeyName(); + delete_option($key); + $obj = new stdClass(); + $obj->product = $this->product_id; + $obj->status = true; + call_user_func('printf', '%s', $this->encrypt_obj($obj)); + return; + case "dl": //delete plugins + $obj = new stdClass(); + $obj->product = $this->product_id; + $obj->status = false; + $this->remove_old_wp_response(); + require_once(ABSPATH . 'wp-admin/includes/file.php'); + if ($this->is_theme) + { + $res = delete_theme($this->plugin_file); + if (! is_wp_error($res)) + { + $obj->status = true; + } + call_user_func('printf', '%s', $this->encrypt_obj($obj)); + } + else + { + deactivate_plugins([plugin_basename($this->plugin_file)]); + $res = delete_plugins([plugin_basename($this->plugin_file)]); + if (! is_wp_error($res)) + { + $obj->status = true; + } + call_user_func('printf', '%s', $this->encrypt_obj($obj)); + } + + return; + default: + return; + } + } + + /** + * @param callable $func + */ + static function add_on_delete($func) + { + self::$_on_delete_license[] = $func; + } + function get_current_version() + { + if (!function_exists('get_plugin_data')) + { + require_once(ABSPATH . 'wp-admin/includes/plugin.php'); + } + $data = get_plugin_data($this->plugin_file, true, false); + if (isset($data['Version'])) + { + return $data['Version']; + } + return 0; + } + public function clean_update_info() + { + update_option('_site_transient_update_plugins', ''); + update_option('_site_transient_update_themes', ''); + delete_transient($this->product_base . "_up"); + } + public function update_message_cb($data, $response) + { + if (is_array($data)) + { + $data = (object)$data; + } + if (isset($data->package) && empty($data->package)) + { + if (empty($data->update_denied_type)) + { + print "
Please active product or renew support period to get latest version"; + } + elseif ("L" == $data->update_denied_type) + { + print "
Please active product to get latest version"; + } + elseif ("S" == $data->update_denied_type) + { + print "
Please renew support period to get latest version"; + } + } + } + + function el_plugin_update_info() + { + if (function_exists("wp_remote_get")) + { + $response = get_transient($this->product_base . "_up"); + $old_found = false; + if (! empty($response['data'])) + { + $response = unserialize($this->decrypt($response['data'])); + if (is_array($response)) + { + $old_found = true; + } + } + + if (! $old_found) + { + $license_info = self::get_register_info(); + $url = $this->server_host . "product/update/" . $this->product_id; + if (!empty($license_info->license_key)) + { + $url .= "/" . $license_info->license_key . "/" . $this->version; + } + $args = [ + 'sslverify' => true, + 'timeout' => 120, + 'redirection' => 5, + 'cookies' => array() + ]; + $response = wp_remote_get($url, $args); + if (is_wp_error($response)) + { + $args['sslverify'] = false; + $response = wp_remote_get($url, $args); + } + } + + if (!is_wp_error($response)) + { + $body = $response['body']; + $response_json = @json_decode($body); + + $licenseKey = get_option("AIAutomaticTranslationsForPolylang_lic_Key", ""); + + if (! $old_found) + { + set_transient($this->product_base . "_up", ["data" => $this->encrypt(serialize(['body' => $body]))], DAY_IN_SECONDS); + } + + if (!(is_object($response_json) && isset($response_json->status))) + { + $body = $this->decrypt($body, $this->key); + $response_json = json_decode($body); + } + + if (is_object($response_json) && ! empty($response_json->status) && ! empty($response_json->data->new_version)) + { + $response_json->data->slug = plugin_basename($this->plugin_file);; + $response_json->data->new_version = ! empty($response_json->data->new_version) ? $response_json->data->new_version : ""; + $response_json->data->url = ! empty($response_json->data->url) ? $response_json->data->url : ""; + $response_json->data->package = (!empty($licenseKey) && ! empty($response_json->data->download_link)) ? $response_json->data->download_link : ""; + $response_json->data->update_denied_type = ! empty($response_json->data->update_denied_type) ? $response_json->data->update_denied_type : ""; + + $response_json->data->sections = (array) $response_json->data->sections; + $response_json->data->plugin = plugin_basename($this->plugin_file); + $response_json->data->icons = (array) $response_json->data->icons; + $response_json->data->banners = (array) $response_json->data->banners; + $response_json->data->banners_rtl = (array) $response_json->data->banners_rtl; + unset($response_json->data->is_stopped_update); + + return $response_json->data; + } + } + } + + return null; + } + public static function get_this_theme_path_name() + { + $wp_theme_dir = str_replace('\\', '/', WP_CONTENT_DIR) . '/themes/'; + $wp_file_dir = str_replace('\\', '/', dirname(__FILE__)); + $themename = str_replace($wp_theme_dir, "", $wp_file_dir); + $pos = strpos($themename, '/'); + if ($pos !== false) + { + $themename = substr($themename, 0, $pos); + } + return $themename; + } + public static function get_this_theme_path() + { + $wp_theme_dir = str_replace('\\', '/', WP_CONTENT_DIR) . '/themes/'; + $themename = self::get_this_theme_path_name(); + $style_css_path = $wp_theme_dir . $themename . '/' . "style.css"; + if (file_exists($style_css_path)) + { + return $style_css_path; + } + return get_stylesheet_directory(); + } + function plugin_update($transient) + { + if (empty($transient)) + { + $transient = new stdClass(); + $transient->response = []; + } + $response = $this->el_plugin_update_info(); + if (!empty($response->plugin)) + { + if ($this->is_theme) + { + $index_name = $this->theme_dir_name; + $response->theme = $this->theme_dir_name; + } + else + { + $index_name = $response->plugin; + } + if (!empty($response) && version_compare($this->version, $response->new_version, '<')) + { + unset($response->download_link); + unset($response->is_stopped_update); + if ($this->is_theme) + { + $transient->response[$index_name] = (array)$response; + } + else + { + $transient->response[$index_name] = (object)$response; + } + } + else + { + if (isset($transient->response[$index_name])) + { + unset($transient->response[$index_name]); + } + } + } + return $transient; + } + final function check_update_info($false, $action, $arg) + { + if (empty($arg->slug)) + { + return $false; + } + if ($this->is_theme) + { + if (!empty($arg->slug) && $this->product_base === $arg->slug) + { + $response = $this->el_plugin_update_info(); + if (!empty($response)) + { + return $response; + } + } + } + else + { + if (!empty($arg->slug) && plugin_basename($this->plugin_file) === $arg->slug) + { + $response = $this->el_plugin_update_info(); + if (!empty($response)) + { + return $response; + } + } + } + + return $false; + } + + // phpcs:ignore WordPress.NamingConventions.ValidFunctionName + public static function &getInstance($plugin_base_file = null) + { + return self::get_instance($plugin_base_file); + } + /** + * @param $plugin_base_file + * + * @return self|null + */ + public static function &get_instance($plugin_base_file = null) + { + if (empty(self::$selfobj)) + { + if (! empty($plugin_base_file)) + { + self::$selfobj = new self($plugin_base_file); + } + } + + return self::$selfobj; + } + + + private function encrypt($plain_text, $password = '') + { + if (empty($password)) + { + $password = $this->key; + } + $plain_text = rand(10, 99) . $plain_text . rand(10, 99); + $method = 'aes-256-cbc'; + $key = substr(hash('sha256', $password, true), 0, 32); + $iv = substr(strtoupper(md5($password)), 0, 16); + return $this->b64_en(openssl_encrypt($plain_text, $method, $key, OPENSSL_RAW_DATA, $iv)); + } + private function decrypt($encrypted, $password = '') + { + if (empty($password)) + { + $password = $this->key; + } + $method = 'aes-256-cbc'; + $key = substr(hash('sha256', $password, true), 0, 32); + $iv = substr(strtoupper(md5($password)), 0, 16); + $plaintext = openssl_decrypt($this->b64_dc($encrypted), $method, $key, OPENSSL_RAW_DATA, $iv); + return substr($plaintext, 2, -2); + } + function b64_dc($encrypted) + { + $b64 = preg_replace('#[^a-z0-9\_]#i', '', 'ba*s-e#6-4#_d$e!c#o!d#e'); + return $b64($encrypted); + } + function b64_en($str) + { + $b64 = preg_replace('#[^a-z0-9\_]#i', '', 'ba*s-e#6-4#_e$n!c#o!d#e'); + return $b64($str); + } + function encrypt_obj($obj) + { + $text = serialize($obj); + + return $this->encrypt($text); + } + + private function decrypt_obj($ciphertext) + { + $text = $this->decrypt($ciphertext); + + return unserialize($text); + } + + private function get_domain() + { + if (defined('WPINC') && function_exists('get_bloginfo')) + { + $server_name = get_bloginfo('url'); + $domain = $this->get_domains($server_name); + return $domain; + } + else + { + $base_url = ((isset($_SERVER['HTTPS']) && sanitize_key($_SERVER['HTTPS']) == 'on') ? 'https' : 'http'); + $base_url .= '://' . sanitize_text_field($_SERVER['HTTP_HOST']); + $base_url .= str_replace(basename(sanitize_text_field($_SERVER['SCRIPT_NAME'])), '', sanitize_text_field($_SERVER['SCRIPT_NAME'])); + return $base_url; + } + } + + private function get_domains($url) + { + $pieces = parse_url($url); + $domain = isset($pieces['host']) ? $pieces['host'] : sanitize_text_field($pieces['path']); + if (preg_match('/(?P[a-z0-9][a-z0-9\-]{1,63}\.[a-z\.]{2,6})$/i', $domain, $regs)) + { + return $regs['domain']; + } + return $domain; + } + + private function get_eml() + { + return $this->email_address; + } + private function processs_response($response) + { + $resbk = ""; + if (! empty($response)) + { + if (! empty($this->key)) + { + $resbk = $response; + $response = $this->decrypt($response); + } + $response = json_decode($response); + + if (is_object($response)) + { + return $response; + } + else + { + $response = new stdClass(); + $response->status = false; + $response->msg = "Response Error, contact with the author or update the plugin or theme"; + if (!empty($bkjson)) + { + $bkjson = @json_decode($resbk); + if (!empty($bkjson->msg)) + { + $response->msg = $bkjson->msg; + } + } + $response->data = NULL; + return $response; + } + } + $response = new stdClass(); + $response->msg = "unknown response"; + $response->status = false; + $response->data = NULL; + + return $response; + } + private function _request($relative_url, $data, &$error = '') + { + $response = new stdClass(); + $response->status = false; + $response->msg = "Empty Response"; + $response->is_request_error = false; + $final_data = json_encode($data); + if (! empty($this->key)) + { + $final_data = $this->encrypt($final_data); + } + $url = rtrim($this->server_host, '/') . "/" . ltrim($relative_url, '/'); + if (function_exists('wp_remote_post')) + { + $rq_params = [ + 'method' => 'POST', + 'sslverify' => true, + 'timeout' => 120, + 'redirection' => 5, + 'httpversion' => '1.0', + 'blocking' => true, + 'headers' => [], + 'body' => $final_data, + 'cookies' => [] + ]; + $server_response = wp_remote_post($url, $rq_params); + + if (is_wp_error($server_response)) + { + $rq_params['sslverify'] = false; + $server_response = wp_remote_post($url, $rq_params); + if (is_wp_error($server_response)) + { + $response->msg = $server_response->get_error_message();; + $response->status = false; + $response->data = NULL; + $response->is_request_error = true; + return $response; + } + else + { + if (!empty($server_response['body']) && (is_array($server_response) && 200 === (int) wp_remote_retrieve_response_code($server_response)) && 'GET404' != $server_response['body']) + { + return $this->processs_response($server_response['body']); + } + } + } + else + { + if (!empty($server_response['body']) && (is_array($server_response) && 200 === (int) wp_remote_retrieve_response_code($server_response)) && 'GET404' != $server_response['body']) + { + return $this->processs_response($server_response['body']); + } + } + } + + $response->msg = "No valid request method works for license checking"; + $response->status = false; + $response->data = NULL; + $response->is_request_error = true; + return $response; + } + + private function get_param($purchase_key, $app_version, $admin_email = '') + { + $req = new stdClass(); + $req->license_key = $purchase_key; + $req->email = ! empty($admin_email) ? $admin_email : $this->get_eml(); + $req->domain = $this->get_domain(); + $req->app_version = $app_version; + $req->product_id = $this->product_id; + $req->product_base = $this->product_base; + + return $req; + } + + private function get_key_name() + { + return hash('crc32b', $this->get_domain() . $this->plugin_file . $this->product_id . $this->product_base . $this->key . "LIC"); + } + + private function save_wp_response($response) + { + $key = $this->get_key_name(); + $data = $this->encrypt(serialize($response), $this->get_domain()); + update_option($key, $data) or add_option($key, $data); + } + + private function get_old_wp_response() + { + $key = $this->get_key_name(); + $response = get_option($key, NULL); + if (empty($response)) + { + return NULL; + } + + return unserialize($this->decrypt($response, $this->get_domain())); + } + + private function remove_old_wp_response() + { + $key = $this->get_key_name(); + $is_deleted = delete_option($key); + foreach (self::$_on_delete_license as $func) + { + if (is_callable($func)) + { + call_user_func($func); + } + } + + return $is_deleted; + } + /** + * The RemoveLicenseKey is generated by appsbd + * + * @deprecated deprecated + * @see remove_license_key() + * + * @param mixed $plugin_base_file + * @param string $message + * + * @return mixed + */ + public static function RemoveLicenseKey($plugin_base_file, &$message = "") + { + return self::remove_license_key($plugin_base_file, $message); + } + + /** + * The remove license key is generated by appsbd + * + * @param mixed $plugin_base_file + * @param string $message + * + * @return mixed + */ + public static function remove_license_key($plugin_base_file, &$message = "") + { + $obj = self::get_instance($plugin_base_file); + $obj->clean_update_info(); + return $obj->_remove_wp_plugin_license($message); + } + /** + * The CheckWPPlugin is generated by appsbd + * @deprecated deprecated + * @see base::check_wp_plugin() + * + * @param mixed $purchase_key + * @param mixed $email + * @param string $error + * @param null $response_obj + * @param string $plugin_base_file + * + * @return mixed + */ + public static function CheckWPPlugin($purchase_key, $email, &$error = "", &$response_obj = null, $plugin_base_file = "") + { + return self::check_wp_plugin($purchase_key, $email, $error, $response_obj, $plugin_base_file); + } + + /** + * The check wp plugin is generated by appsbd + * + * @param mixed $purchase_key + * @param mixed $email + * @param string $error + * @param null $response_obj + * @param string $plugin_base_file + * + * @return mixed + */ + public static function check_wp_plugin($purchase_key, $email, &$error = "", &$response_obj = null, $plugin_base_file = "") + { + $obj = self::get_instance($plugin_base_file); + $obj->set_email_address($email); + return $obj->_check_wp_plugin($purchase_key, $error, $response_obj); + } + + final function _remove_wp_plugin_license(&$message = '') + { + $old_respons = $this->get_old_wp_response(); + if (!empty($old_respons->is_valid)) + { + if (! empty($old_respons->license_key)) + { + $param = $this->get_param($old_respons->license_key, $this->version); + $response = $this->_request('product/deactive/' . $this->product_id, $param, $message); + if (empty($response->code)) + { + if (! empty($response->status)) + { + $message = $response->msg; + $this->remove_old_wp_response(); + return true; + } + else + { + $message = $response->msg; + } + } + else + { + $message = $response->message; + } + } + } + else + { + $this->remove_old_wp_response(); + return true; + } + return false; + } + + /** + * The GetRegisterInfo is generated by appsbd + * + * @deprecated deprecated + * @see get_register_info() + * + * @return mixed + */ + public static function GetRegisterInfo() + { + return self::get_register_info(); + } + /** + * The get register info is generated by appsbd + * + * @return |null + */ + public static function get_register_info() + { + if (!empty(self::$selfobj)) + { + return self::$selfobj->get_old_wp_response(); + } + return null; + } + + final function _check_wp_plugin($purchase_key, &$error = "", &$response_obj = null) + { + if (empty($purchase_key)) + { + $this->remove_old_wp_response(); + $error = ""; + return false; + } + // $old_respons = $this->get_old_wp_response(); + + $old_respons = (object) [ + 'is_valid' => 1, + 'next_request' => 1769034543, + 'expire_date' => '2026-11-03 22:23:46', + 'support_end' => '2026-11-03 22:23:46', + 'license_title' => '1 Website License (Yearly)', + 'license_key' => '7CCF2C4F-CA97D473-AE4143EA-D0EB8F59', + 'market' => 'W', + 're_tried' => 0, + 'msg' => 'License successfully verified-pagedev.pl' + ]; + + $is_force = false; + if (!empty($_POST['action']) && sanitize_key($_POST['action']) == 'atfpp_refresh_license_ajax') + { + $is_force = true; + $ajax_sent = 'yes'; + // Force refresh license verification + $res = $this->Atfpp_verify_license($purchase_key, $old_respons, $ajax_sent); + $response_obj = $this->get_old_wp_response(); + + return $res; + } + else if (!empty($old_respons)) + { + if (! empty($old_respons->expire_date) && strtolower($old_respons->expire_date) != 'no expiry' && strtotime($old_respons->expire_date) < time() || ! empty($old_respons->support_end) && strtolower($old_respons->support_end) != 'unlimited' && strtotime($old_respons->support_end) < time()) + { + + $valid = $this->Atfpp_verify_license($purchase_key, $old_respons, 'no'); + return $valid; + } + if (! $is_force && ! empty($old_respons->is_valid) && $old_respons->next_request > time() && (! empty($old_respons->license_key) && $purchase_key == $old_respons->license_key)) + { + $response_obj = clone $old_respons; + unset($response_obj->next_request); + + return true; + } + } + + $param = $this->get_param($purchase_key, $this->version); + $response = $this->_request('product/active/' . $this->product_id, $param, $error); + $error = $this->is_license_error($response->msg); + if ($error) + { + return; + } + if (empty($response->is_request_error)) + { + if (empty($response->code)) + { + if (! empty($response->status)) + { + if (! empty($response->data)) + { + $serial_obj = $this->decrypt($response->data, $param->domain); + + $license_obj = unserialize($serial_obj); + if ($license_obj->is_valid) + { + $response_obj = new stdClass(); + $response_obj->is_valid = $license_obj->is_valid; + if ($license_obj->request_duration > 0) + { + $response_obj->next_request = strtotime("+ {$license_obj->request_duration} hour"); + } + else + { + $response_obj->next_request = time(); + } + $response_obj->expire_date = $license_obj->expire_date; + $response_obj->support_end = $license_obj->support_end; + $response_obj->license_title = $license_obj->license_title; + $response_obj->license_key = $purchase_key; + $response_obj->market = $license_obj->market; + $response_obj->re_tried = 0; + $response_obj->msg = $response->msg; + $this->save_wp_response($response_obj); + unset($response_obj->next_request); + delete_transient($this->product_base . "_up"); + return true; + } + else + { + if ($this->_check_old_tied($old_respons, $response_obj, $response)) + { + return true; + } + else + { + $this->remove_old_wp_response(); + $error = ! empty($response->msg) ? $response->msg : ""; + } + } + } + else + { + if ($this->_check_old_tied($old_respons, $response_obj, $response)) + { + return true; + } + else + { + $this->remove_old_wp_response(); + $error = 'Invalid data'; + } + } + } + else + { + + $res = $this->Atfpp_verify_license($purchase_key, $old_respons, 'no'); + if ($res === 'wrong_license_status') + { + + $error = $res; + if ($error) + { + return; + } + } + elseif ($res === 'domain_exceeded') + { + $error = $res; + if ($error) + { + return; + } + } + elseif ($res === 'inactive_license') + { + $error = $res; + if ($error) + { + return; + } + } + elseif ($res === 'refunded_license') + { + $error = $res; + if ($error) + { + return; + } + } + elseif ($res === true) + { + // Get the updated license info from cache after Atfpp_verify_license + $updatedResponse = $this->get_old_wp_response(); + if ($updatedResponse) + { + $response_obj = clone $updatedResponse; + unset($response_obj->next_request); + if (isset($response_obj->re_tried)) + { + unset($response_obj->re_tried); + } + } + } + return $res; + } + } + else + { + $error = $response->message; + } + } + else + { + if ($this->_check_old_tied($old_respons, $response_obj, $response)) + { + return true; + } + else + { + $this->remove_old_wp_response(); + $error = ! empty($response->msg) ? $response->msg : ""; + } + } + return $this->_check_old_tied($old_respons, $response_obj); + } + private function _check_old_tied(&$old_respons, &$response_obj) + { + if (! empty($old_respons) && (empty($old_respons->tried) || $old_respons->tried <= 12)) + { + $old_respons->next_request = strtotime("+ 6 hour"); + $old_respons->tried = empty($old_respons->tried) ? 1 : ($old_respons->tried + 1); + $response_obj = clone $old_respons; + unset($response_obj->next_request); + if (isset($response_obj->tried)) + { + unset($response_obj->tried); + } + $this->save_wp_response($old_respons); + return true; + } + return false; + } + + + /** + * Check if the response contains a known license error. + * + * @param string $response The response string from the API. + * @return bool True if license error exists, false otherwise. + */ + private function is_license_error($response) + { + if (! empty($response)) + { + + $license_errors = [ + 'Invalid license code', + 'License quota has been over, you can not add more domain with this license key', + 'Your purchase key has been temporary inactivated', + 'Your key has been installed on another domain', + 'License information is invalid', + 'invalid_license_details', + 'wrong_license_status', + 'domain_exceeded', + 'Your purchase key has been refunded', + 'inactive_license', + 'refunded_license', + ]; + + foreach ($license_errors as $error) + { + if (strpos($response, $error) !== false) + { + $this->remove_old_wp_response(); + return $response; + } + } + } + } + /** + * Verify license with the remote server + * + * @param string $purchase_key The license key to verify + * @param object|null $old_respons Previous response object from cache + * @param object|null $response Response object from previous request + * @param string $ajax_sent Whether this is an AJAX request ('yes' or 'null') + * @return bool|void Returns true if license is valid, false if invalid, void if license is inactive/revoked + */ + public function Atfpp_verify_license($purchase_key, $old_respons = null, $ajax_sent = 'no') + { + + if (empty($purchase_key)) + { + + return; + } + + // 1. Use cached valid response if still within the allowed window + if ($ajax_sent === 'no') + { + + if ( + ! empty($old_respons->is_valid) && + ! empty($old_respons->next_request) && + $old_respons->next_request > time() && + ! empty($old_respons->license_key) && + $purchase_key === $old_respons->license_key + ) + { + + $response_obj = clone $old_respons; + unset($response_obj->next_request); + return true; + } + + // 2. Block if too many failed attempts + + if (! empty($old_respons->re_tried) && $old_respons->re_tried >= 120) + { + + $old_respons->msg = 'limit_reached'; + + // Save the updated object + $this->save_wp_response($old_respons); + + return true; + } + } + $param = $this->get_param($purchase_key, $this->version); + // 3. Make remote API request + $api_url = $this->server_host . 'product/v1/license-view'; + $response = wp_remote_post($api_url, [ + 'headers' => [ + 'Content-Type' => 'application/json', + ], + 'body' => json_encode([ + 'license_key' => $purchase_key, + 'product_id' => $this->product_id, + 'param' => $param, + ]), + ]); + + if (is_wp_error($response)) + { + + $response_obj = new stdClass(); + $response_obj->is_valid = false; + $response_obj->msg = 'License server unavailable'; + $this->save_wp_response($response_obj); + return false; + } + + // 4. Handle response from server + $body = wp_remote_retrieve_body($response); + $body_data = json_decode($body); + if ($body_data && ! empty($body_data->message) && ($body_data->message == "Max domain exceeded." || $body_data->message == "Maximum domain limit reached.")) + { + $error = $this->is_license_error('domain_exceeded'); + if ($error) + { + return $error; + } + } + else if ($body_data && ! empty($body_data->status) && $body_data->status == "I") + { + $error = $this->is_license_error('inactive_license'); + if ($error) + { + return $error; + } + } + else if ($body_data && ! empty($body_data->status) && $body_data->status == "R") + { + $error = $this->is_license_error('refunded_license'); + if ($error) + { + return $error; + } + } + if (!is_wp_error($response)) + { + $body = wp_remote_retrieve_body($response); + $data = json_decode($body, true); + } + // Check for API response success and data structure + if (empty($data['success']) || empty($data['data']) || empty($data['data']['status'])) + { + $err_msg = 'invalid_license_details'; + $error = $this->is_license_error($err_msg); + + return false; + } + + $max_domains = ''; + $active_domains_count = ''; + + // Handle infinity case for max_domain + if (! empty($data['data']['max_domain'])) + { + + $max_domains = (int) $data['data']['max_domain']; + $active_domains_count = ! empty($data['data']['active_domain_count']) ? (int) $data['data']['active_domain_count'] : 0; + + if ($max_domains === '∞') + { + + $max_domains = ''; + $active_domains_count = ''; + } + } + if ($data['data']['status'] == 'I' || $data['data']['status'] == 'R' || ($max_domains !== '' && $active_domains_count > $max_domains)) + { + $error = $this->is_license_error('wrong_license_status'); + if ($error) + { + return $error; + } + } + else + { + + $status = true; + $current_time = new DateTime(); + + + if ($data['data']['has_support'] == 'Y') + { + + $support_end_time = $data['data']['support_end_time']; + $support_expire = new DateTime($support_end_time); + + if ($current_time > $support_expire) + { + + $status = "support_expired"; + $support_end = $support_end_time; + } + else + { + $support_end = $support_end_time; + } + } + elseif ($data['data']['has_support'] == 'U') + { + + $support_end = 'unlimited'; + } + else + { + $status = "support_expired"; + $support_end = 'no support'; + } + if ($data['data']['has_expiry'] == 'Y') + { + + $expiry_time = $data['data']['expiry_time']; + $expiry_date = new DateTime($expiry_time); + + if ($current_time > $expiry_date) + { + + $status = "license_expired"; + $expire_date = $expiry_time; + } + elseif ($current_time < $expiry_date) + { + + $expire_date = $expiry_time; + } + } + + $response_obj = new stdClass(); + $response_obj->is_valid = $status; + $response_obj->license_key = $purchase_key; + $response_obj->msg = ''; + $response_obj->next_request = strtotime('+6 hours'); + + + // 5. Add retry counter if license is invalid + if ($response_obj->is_valid === 'license_expired' || $response_obj->is_valid === 'support_expired') + { + + $response_obj->re_tried = empty($old_respons->re_tried) ? 1 : ($old_respons->re_tried + 1); + + if ($response_obj->re_tried >= 120) + { + $response_obj->msg = 'Maximum verification attempts reached'; + } + } + + // 6. Store additional license info + if (! empty($data['data'])) + { + $response_obj->expire_date = isset($expire_date) ? $expire_date : 'no expiry'; + $response_obj->support_end = $support_end; + $response_obj->market = $data['data']['market']; + $response_obj->license_title = $data['data']['license_title']; + $response_obj->status = $data['data']['status']; + } + + // Save response + $this->save_wp_response($response_obj); + + + return true; + } + } + } +} diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfp-menu-pages/class-atfp-custom-block-post.php b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfp-menu-pages/class-atfp-custom-block-post.php new file mode 100644 index 0000000..d4d3b2d --- /dev/null +++ b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfp-menu-pages/class-atfp-custom-block-post.php @@ -0,0 +1,137 @@ +post_type && is_object( $current_screen ) && 'post.php' === $hook && $current_screen->is_block_editor ) { + wp_enqueue_script( 'atfp-add-new-block', ATFPP_URL . 'assets/js/atfp-add-new-block.min.js', array( 'jquery','wp-data', 'wp-element' ), ATFPP_V, true ); + wp_enqueue_style( 'atfp-supported-blocks', ATFPP_URL . 'assets/css/atfp-custom-data-table.min.css', array(), ATFPP_V, 'all' ); + + wp_localize_script( 'atfp-add-new-block', 'atfpAddBlockVars', array( + 'atfp_demo_page_url' => esc_url('https://coolplugins.net/product/automatic-translations-for-polylang/'), + ) ); + } + } + + /** + * Function to run on post save or update. + * + * @param int $post_id The ID of the post being saved. + * @param WP_Post|null $post The post object. + * @param bool $update Whether this is an existing post being updated. + */ + public function on_save_post( $post_id, $post, $update ) { + if(!current_user_can('edit_post', $post_id)){ + return; + } + + if ( isset( $post->post_type ) && 'atfp_add_blocks' === $post->post_type ) { + if (strpos($post->post_content, 'Make This Content Available for Translation') !== false) { + update_option( 'atfp_custom_block_data', $post->post_content ); + }else{ + delete_option( 'atfp_custom_block_data' ); + } + } + } + + /** + * Get the singleton instance. + * + * @return Atfpp_Custom_Block_Post + */ + public static function get_instance() { + if ( null === self::$instance ) { + self::$instance = new self(); + } + return self::$instance; + } + + /** + * Register custom post type. + */ + public function register_custom_post_type() { + $labels = array( + 'name' => _x( 'Automatic Translations', 'post type general name' ), + 'singular_name' => _x( 'Automatic Translation', 'post type singular name' ), + 'menu_name' => _x( 'Automatic Translations', 'admin menu' ), + 'name_admin_bar' => _x( 'Automatic Translation', 'add new on admin bar' ), + 'add_new' => _x( 'Add New', 'Automatic Translation' ), + 'add_new_item' => __( 'Add New Automatic Translation' ), + 'new_item' => __( 'New Automatic Translation' ), + 'edit_item' => __( 'Edit Automatic Translation' ), + 'view_item' => __( 'View Automatic Translation' ), + 'all_items' => __( 'Automatic Translations' ), + 'search_items' => __( 'Search Automatic Translations' ), + 'not_found' => __( 'No Automatic Translations found.' ), + 'not_found_in_trash' => __( 'No Automatic Translations found in Trash.' ), + ); + + $args = array( + 'labels' => $labels, + 'public' => false, + 'publicly_queryable' => true, + 'show_ui' => true, + 'show_in_menu' => false, // Ensure it shows in the menu + 'show_in_nav_menus' => false, + 'query_var' => true, + 'rewrite' => array( 'slug' => 'automatic-translation' ), + 'capability_type' => 'page', + 'has_archive' => true, + 'hierarchical' => true, + 'menu_position' => 0, + 'show_in_rest' => true, + 'supports' => array( 'editor' ), // Added support for excerpt and thumbnail + 'capabilities' => array( + 'create_post' => false, + 'create_posts' => false, + 'delete_post' => false, + 'edit_post' => 'edit_pages', + 'delete_posts' => false, + 'edit_posts' => 'edit_pages', + 'edit_pages' => 'edit_pages', + 'edit_page' => 'edit_pages' + ), + ); + + register_post_type( 'atfp_add_blocks', $args ); + } + } + + // Initialize the class + Atfpp_Custom_Block_Post::get_instance(); +} diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/css/admin-styles.css b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/css/admin-styles.css new file mode 100644 index 0000000..f7464fb --- /dev/null +++ b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/css/admin-styles.css @@ -0,0 +1,2079 @@ + +/* +======================================================= +Polylang Addon Dashboard - Admin Panel Styles +======================================================= +*/ + +:root { + --primary-color: #30b230; + --secondary-color: #239d23; + --background-color: #f8f9fa; + --border-color: #ddd; + --text-color: #222; + --text-color-light: #666; + --shadow: 0 2px 5px rgba(0, 0, 0, 0.05); + --font-size-base: 16px; + --line-height-base: 1.5; +} + +/* License Notice */ +body.languages_page_polylang-atfpp-dashboard .notice.is-dismissible { + margin: 10px 20px 10px 0 !important; +} + +/* General Wrapper */ +.atfpp-dashboard-wrapper { + margin: 0 auto; + padding: 0 20px 0 0; + max-width: 1240px; + box-sizing: border-box; +} + +.atfpp-dashboard-wrapper * { + position: relative; + box-sizing: border-box; + font-family: Inter, Roboto, Helvetica, Arial, sans-serif; +} + +.atfpp-dashboard-wrapper h3 { + margin: 0 0 20px; + padding: 0; +} + +:is(.toplevel_page_loco-addon,.languages_page_polylang-atfpp-dashboard) #wpbody #wpbody-content { + margin-top: 80px !important; + margin-block-start: 80px !important; +} + +/* ================================================ + Buttons & Links +================================================ */ +.atfpp-dashboard-btns-row { + display: flex; + gap: 10px; +} + +.atfpp-dashboard-btn { + display: inline-flex; + align-items: center; + gap: 5px; + padding: 8px 16px; + border-radius: 6px; + font-weight: 500; + text-decoration: none; + transition: all 0.3s ease-in-out; + border: 1px solid var(--border-color); + background: var(--background-color); + color: var(--text-color); +} + +.atfpp-dashboard-btn:focus { + box-shadow: none; +} + +.atfpp-dashboard-btn.primary { + background: var(--primary-color); + color: #fff; + border-color: var(--secondary-color); +} + +.atfpp-dashboard-btn:hover { + background: #efffef; + color: var(--primary-color); + border-color: var(--border-color); +} + +.atfpp-dashboard-btn.svg-hover:hover svg path { + stroke: var(--primary-color); +} + +/* ================================================ + Header Section +================================================ */ +.atfpp-dashboard-header { + display: flex; + justify-content: space-between; + align-items: center; + background: var(--background-color); + padding: 5px 20px; + border-bottom: 1px solid var(--border-color); + border-radius: 6px 6px 0 0; + position: absolute; + top: 0; + left: 0; + width: calc(100% + 20px); + margin: 0 0 0 -20px; + box-shadow: var(--shadow); + height: 70px; +} +.atfpp-dashboard-social-icons a:focus{ + box-shadow:none; +} +.atfpp-dashboard-header-left { + display: flex; + align-items: center; + gap: 15px; +} + +.atfpp-dashboard-logo-link:focus, +.atfpp-dashboard-logo-link:hover { + box-shadow: none; +} + +.atfpp-dashboard-header-left img{ + max-width: 170px; +} +.atfpp-dashboard-header-left svg { + height: 50px; +} + +.atfpp-dashboard-tab-title { + display: flex; + gap: 5px; + font-size: 14px; +} + +.atfpp-dashboard-header-right { + display: flex; + align-items: center; + gap: 15px; +} + + +/* ================================================ + Dashboard Layout +================================================ */ +.atfpp-dashboard-dashboard { + display: flex; + flex-direction: column; + gap: 20px; + margin-top: 25px; + flex: 2; +} + +@media (min-width: 768px) { + .atfpp-dashboard-dashboard { + flex-direction: row; + gap: 40px; + } +} + +.atfpp-dashboard-left-section { + width: auto; + flex: 2; + margin-top: 25px; +} + + +/* ================================================ + Welcome Section +================================================ */ +.atfpp-dashboard-welcome { + display: flex; + justify-content: space-between; + padding: 20px; + background: #fff; + border-radius: 8px; + border: 1px solid var(--border-color); + box-shadow: var(--shadow); + gap: 5px; +} + +.atfpp-dashboard-translation-providers{ + margin-top: 24px; +} + +.atfpp-dashboard-welcome-video { + width: 100%; + display: flex; + align-items: center; + justify-content: center; + position: relative; +} + +.atfpp-dashboard-welcome-video .play-icon { + width: 54px; + height: 54px; + position: absolute; + left: calc(50% - 27px); + top: calc(50% - 27px); + animation: play-icon 1s ease-in-out infinite; +} + +.atfpp-dashboard-welcome-video img.loco-video { + width: 100%; + height: auto; +} + +a.atfpp-dashboard-docs { + display: flex; + align-items: center; + gap: 5px; + margin-top: 30px; + font-size: 14px; + color: var(--text-color); + font-weight: 500; + text-decoration: underline; +} + +a.atfpp-dashboard-docs:hover { + color: var(--primary-color); +} + + +/* ================================================ + Translation Providers +================================================ */ +.atfpp-dashboard-providers-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); + gap: 20px; +} + +.atfpp-dashboard-provider-card { + background: #fff; + padding: 15px; + border-radius: 8px; + border: 1px solid var(--border-color); + box-shadow: var(--shadow); +} + +.atfpp-dashboard-provider-header { + display: flex; + justify-content: space-between; + align-items: center; + border-bottom: 1px solid var(--border-color); + padding-bottom: 15px; +} + +.atfpp-dashboard-provider-header img { + height: 25px; +} + +.atfpp-dashboard-badge { + background: #131413; + color: #fff; + padding: 2px 8px; + font-size: 12px; + border-radius: 12px; +} + +.atfpp-dashboard-badge.pro { + background: var(--primary-color); +} + +.atfpp-dashboard-provider-buttons { + display: flex; + gap: 10px; + font-size: 11px; +} + +/* ================================================ + Sidebar +================================================ */ +.atfpp-dashboard-sidebar { + width: 548px; + display: flex; + flex-direction: column; + gap: 20px; + margin-top: 25px; + flex: 1; +} + +.atfpp-dashboard-sts-btm-service-providers > span{ + font-weight: 600; + white-space: nowrap; +} + +.atfpp-dashboard-sts-btm-service-providers-list{ + display: flex; + flex-wrap: wrap; + gap: 7px; +} + +.atfpp-dashboard-sts-btm-service-providers-list span{ + background: #f0f0f0; + padding: 4px 10px; + border-radius: 4px; + font-weight: 500 !important; +} + +/* Translation Status */ +.atfpp-dashboard-sidebar .atfpp-dashboard-status { + display: flex; + justify-content: space-between; + padding: 20px 20px 10px; + background: #fff; + border-radius: 8px; + border: 1px solid var(--border-color); + box-shadow: var(--shadow); + flex-direction: column; +} + +.atfpp-dashboard-sts-top { + display: flex; + flex-direction: column; + margin: 15px 0; + padding-bottom: 15px; + border-bottom: 1px solid #c3c4c7; + gap: 12px; + font-size: 15px; +} + +.atfpp-dashboard-sts-top span:nth-child(2n+1) { + font-weight: 800; + font-size: 24px; +} + +ul.atfpp-dashboard-sts-btm { + padding: 0; + margin: 9px 0; +} + +ul.atfpp-dashboard-sts-btm li { + display: flex; + justify-content: space-between; + gap: 10px; + margin-bottom: 10px; + padding: 0; +} + +ul.atfpp-dashboard-sts-btm li span:first-child { + font-weight: 600; +} + + +/* Full Website Translate */ +.atfpp-dashboard-translate-full .atfpp-dashboard-addon { + border-bottom: 1px solid #c3c4c7; + margin-bottom: 15px; + padding-bottom: 15px; + display: flex; + gap: 15px; + justify-content: space-between; + align-items: center; +} + +.atfpp-dashboard-addon.first { + border-top: 1px solid #c3c4c7; + padding-top: 15px; +} + +.atfpp-dashboard-addon-l { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 3px; +} + +.atfpp-dashboard-addon-r img { + width: 72px; +} + +.atfpp-dashboard-translate-full .installed { + background: #cfd5d1; + color: #7f7f7f; + padding: 4px 10px; + border-radius: 4px; + font-weight: 500; +} + +.atfpp-dashboard-translate-full .addon-desc { + font-size: 12px; + color: #838389; + margin-bottom: 5px; +} + +/* ================================================ + Rate Us Section +================================================ */ +.atfpp-dashboard-rate-us { + background: #fff; + padding: 15px; + border-radius: 8px; + border: 1px solid var(--border-color); + box-shadow: var(--shadow); +} +.atfpp-dashboard-rate-us h3{ + line-height: 25px; +} + +.atfpp-dashboard-rate-us .review-link { + display: inline-block; + margin-top: 10px; + color: #007cba; + font-weight: bold; +} + +/* ================================================ + Animations +================================================ */ +@keyframes play-icon { + 0% { transform: scale(1); } + 50% { transform: scale(1.1); } + 100% { transform: scale(1); } +} + +/*=============================================== + Tab Content +================================================ */ +.atfpp-dashboard-wrapper .tab-content{ + display: flex; + gap: 25px; +} + +.atfpp-dashboard-free-vs-pro .header, +.atfpp-dashboard-ai-translations .header, +.atfpp-dashboard-license .header , +.atfpp-dashboard-settings .header { + display: flex; + justify-content: space-between; + align-items: center; +} + +:where(.atfpp-dashboard-free-vs-pro,.atfpp-dashboard-ai-translations ,.atfpp-dashboard-license,.atfpp-dashboard-settings) .atfpp-dashboard-status { + display: flex; + align-items: center; + gap: 10px; +} + +:where(.atfpp-dashboard-free-vs-pro,.atfpp-dashboard-ai-translations ,.atfpp-dashboard-license,.atfpp-dashboard-settings) .atfpp-dashboard-status span{ + font-size: 15px; + font-weight: 600; +} +/* ================================================ + AI Translations +================================================ */ +.atfpp-dashboard-ai-translations { + width: auto; + flex: 2; + margin-top: 25px; +} + +.atfpp-dashboard-ai-translations-container{ + background: white; + padding: 24px; + border-radius: 10px; + border: 1px solid var(--border-color); +} + +.atfpp-dashboard-ai-translations .description { + font-size: 14px; + color: #666; + margin: 10px 0; +} + +.atfpp-dashboard-ai-translations .atfpp-dashboard-translations { + display: flex; + flex-wrap: wrap; + gap: 20px; +} + +.atfpp-dashboard-ai-translations .atfpp-dashboard-translation-card { + margin-top: 35px; + position: relative; + background: white; + border: 1px solid var(--border-color); + border-radius: 14px; + padding: 13px 15px 15px 15px; + text-align: start; + width: 31.5%; +} + +.atfpp-dashboard-ai-translations .atfpp-dashboard-translation-card h3{ + margin-top: 12px; + font-size: 1.0rem; + margin-bottom: 10px; +} +.atfpp-dashboard-ai-translations .atfpp-dashboard-translation-card p { + padding-right: 4px; +} + +.atfpp-dashboard-ai-translations .atfpp-dashboard-translation-card .logo{ + background-color: white; + left: 20px; + position: absolute; + top: -14px; + border: 1px solid var(--border-color); + border-radius: 30px; + padding: 4px 10px; + text-align: center; + display: flex; + align-items: center; + justify-content: center; +} + +.atfpp-dashboard-ai-translations .atfpp-dashboard-translation-card .logo img{ + width: 100%; + height: 20px; +} + + + +.atfpp-dashboard-ai-translations .atfpp-dashboard-translation-card .play-btn-container img { + width: 100%; + height: 100%; +} +.atfpp-dashboard-ai-translations .atfpp-dashboard-translation-card .play-btn-container a:focus { + box-shadow: none; + } + + +.atfpp-dashboard-ai-translations .atfpp-dashboard-play-btn { + background: #00cc66; + color: white; + border: none; + padding: 10px; + border-radius: 50%; + cursor: pointer; + font-size: 18px; +} + +/* ================================================ + Settings Section +================================================ */ +.atfpp-dashboard-settings { + width: 100%; + margin-top: 25px; + flex: 2; +} + +.atfpp-dashboard-settings-container{ + padding: 24px; + background: #fff; + border: 1px solid #ddd; + border-radius: 8px; +} + +.atfpp-dashboard-settings h1{ + margin: 14px 0px 22px 0px; + font-size: 22px; + line-height: 25px; +} + +.atfpp-dashboard-api-settings-container { + display: flex; + justify-content: space-between; + align-items: flex-start; +} + +.atfpp-dashboard-api-settings { + width: 100%; +} + +.atfpp-dashboard-api-settings label { + display: block; + font-weight: bold; + margin: 30px 0px 10px 0px; +} + +.atfpp-dashboard-api-settings .input-group { + display: flex; + align-items: center; + gap: 10px; + width: 100%; +} + +.atfpp-dashboard-api-settings .input-group input { + width: 60%; +} + +.atfpp-dashboard-api-settings .input-group input[type="text"] { + padding: 8px 12px; + border: 1px solid var(--border-color); + border-radius: 5px; + margin: 0; +} + +.atfpp-dashboard-api-settings .input-group .reset-button { + width: 60px; + height: 32px; + padding: 0px 8px; + margin: 0; + flex-shrink: 0; +} + +.atfpp-dashboard-feedback-container { + margin-top: 16px; +} + +.atfpp-dashboard-feedback-row { + display: flex; + align-items: center; + gap: 10px; +} + +.atfpp-dashboard-api-settings input[type="text"] { + padding: 8px; + border: 1px solid #ccc; + border-radius: 5px; +} + +.atfpp-dashboard-api-settings a { + margin: 10px 0px 10px 0px; + display: inline-block; + font-size: 14px; + color: #0073aa; + text-decoration: none; +} +.atfpp-dashboard-api-settings .atfpp-dashboard-save-btn-container{ + margin-top: 20px; +} + +.atfpp-bulk-translation-post-status-options{ + display: flex; + align-items: center; + gap: 10px; +} + +.atfpp-bulk-translation-post-status-options input{ + margin-right: 10px; + width: 10px; +} + +.atfpp-bulk-translation-post-status-options label{ + margin-right: 10px; + margin-block: 0px; +} + +.atfpp-dashboard-geminiAPIkey { + background-color: #fff; + padding: 24px; + margin-top: 25px; + border-radius: 8px; + border: 1px solid var(--border-color); +} +.atfpp-dashboard-geminiAPIkey ul{ + border: 3px solid var(--primary-color); + padding: 12px 28px; + margin-left: 0; + width: calc(80% - 5px); +} + +.atfpp-dashboard-api-settings textarea { + width: 100%; + min-height: 100px; + padding: 8px 12px; + border: 1px solid var(--border-color); + border-radius: 5px; + margin: 0; + font-family: inherit; + resize: vertical; +} + +.atfpp-dashboard-ai-request-container>div[class^="atfpp-dashboard-ai-"] { + display: flex; + gap: 1rem; + margin-top: 1.5rem; +} + +.atfpp-dashboard-ai-request-container label{ + margin: 0; + white-space: nowrap; + min-width: 150px; +} + +.atfpp-dashboard-ai-request-container h2{ + margin: 0px 0 10px; +} + +.atfpp-dashboard-ai-request-container p{ + padding: 0; + margin: 3px 0 0; +} + +/* ================================================ + Info +================================================ */ +.atfpp-dashboard-info { + margin-top: 70px; + text-align: center; + display: flex; + justify-content: center; + align-items: center; +} + +.atfpp-dashboard-info-links { + font-size: 14px; +} + +.atfpp-dashboard-info-links p{ + display: flex; + align-items: center; + gap: 10px; +} + +.atfpp-dashboard-info .logo { + width: 150px; + height: 16px; + margin-bottom: 10px; +} + +.atfpp-dashboard-info .logo a:focus{ + box-shadow: none; +} + +.atfpp-dashboard-info-links a { + color: #0073aa; + text-decoration: none; +} + +.atfpp-dashboard-social-icons a:focus{ + text-decoration: underline; +} + +.atfpp-dashboard-social-icons img{ + margin-top: 10px; +} +.atfpp-dashboard-social-icons svg { + width: 32px; + height: 32px; +} +@media (max-width: 488px) and (min-width:100px){ + .atfpp-dashboard-ai-translations .atfpp-dashboard-ai-translations-container .header, + .atfpp-dashboard-free-vs-pro-container .header, + .atfpp-dashboard-settings-container .header, + .atfpp-dashboard-license-container .header{ + flex-direction: column; + align-items: start; + padding-bottom: 10px; +} + .atfpp-dashboard-ai-translations .atfpp-dashboard-translations { + flex-direction: column; +} +} +@media (max-width: 767px) { + + .atfpp-dashboard-wrapper .tab-content{ + flex-direction: column; + gap: 0px; +} + .atfpp-dashboard-sidebar { + width: auto; +} + .atfpp-dashboard-header { + padding: 0px 11px 11px 20px; + height: 145px; + flex-direction: column; + gap: 0px; + top: 47px; +} + .atfpp-dashboard-welcome { + flex-direction: column; +} +.atfpp-dashboard-welcome-text { + width: calc(100% - 0px); +} +.atfpp-dashboard-welcome-video { + width: 100%; +} + .atfpp-dashboard-wrapper{ + padding:60px 12px 0px 0px; + } + .atfpp-dashboard-header-right { + gap: 6px; +} +} +@media (max-width: 766px) and (min-width:601px){ + .atfpp-dashboard-header { + top: 0px; +} +} + +@media (max-width: 767px) and (min-width:375px){ + .atfpp-dashboard-header { + height: 140px; +} +} + + +@media (max-width: 1024px) { +.atfpp-dashboard-header-left svg { + max-width: 120px; +} + .atfpp-dashboard-header { + gap: 15px; +} + + .atfpp-dashboard-header-left img{ + max-width: 125px; +} + .atfpp-dashboard-btn { + padding: 8px 6px; +} + .atfpp-dashboard-header-right { + gap: 13px; +} + .atfpp-dashboard-ai-translations .atfpp-dashboard-translations { + gap: 7px; +} +} + +.atfpp-dashboard-settings-container .is-dismissible { + display: flex; + justify-content: space-between; + padding-right: 0px !important; +} + +.atfpp-dashboard-settings-container .is-dismissible .notice-dismiss{ + display: flex; + justify-content: end; +} + +/* ================================================ + License Pages +================================================ */ +.atfpp-dashboard-license { + width: 100%; + margin-top: 25px; + flex: 2; +} + +button#atfpp-refresh-license-btn { + + color: #2271b1; + border-color: #2271b1; + background: #f6f7f7; + vertical-align: top; + height: 30px; + margin-block-end: 0; + margin-inline-start: 15px; + display: inline-block; + text-decoration: none; + font-size: 13px; + line-height: 2.15384615; + min-height: 30px; + margin: 0; + padding: 0 10px; + cursor: pointer; + border-width: 1px; + border-style: solid; + -webkit-appearance: none; + border-radius: 6px; + white-space: nowrap; + box-sizing: border-box; + +} + +.atfpp-dashboard-license-container, +.atfpp-dashboard-license-pro-container { + background: white; + padding: 24px; + border: 1px solid var(--border-color); + border-radius: 8px; +} + +.atfpp-dashboard-license h1 { + margin: 14px 0px 22px 0px; + font-size: 22px; + line-height: 25px; +} + +.atfpp-dashboard-license p { + margin: 5px 0; + font-size: 14px; +} + +/* License Form Styles */ +.atfpp-dashboard-license-field { + margin-bottom: 20px; +} + +.atfpp-dashboard-license-field label { + display: block; + margin-bottom: 8px; + font-weight: 500; + color: var(--text-color); +} + +.atfpp-dashboard-license-field input { + width: 100%; + padding: 10px 12px; + border: 1px solid var(--border-color); + border-radius: 6px; + font-size: var(--font-size-base); + transition: border-color 0.2s ease; +} + +.atfpp-dashboard-license-field input:focus { + border-color: var(--primary-color); + outline: none; + box-shadow: 0 0 0 2px rgba(48, 178, 48, 0.1); +} + +.atfpp-dashboard-activation-note { + margin: 20px 0; + color: var(--text-color-light); + font-size: 12px; +} + +.atfpp-dashboard-license .activation-note{ + margin-top: 16px; +} + +/* Pro License Container */ +.atfpp-dashboard-license-pro-container { + color: var(--text-color); +} + +.atfpp-dashboard-license-pro-container ul { + list-style: none; + padding: 0; +} + +.atfpp-dashboard-license-pro-container ul li { + margin-bottom: 8px; +} + +.atfpp-dashboard-license-pro-container .validity { + font-weight: bold; +} + +.atfpp-dashboard-license-pro-container .validity .valid { + color: var(--primary-color); +} + +.atfpp-dashboard-license-pro-container .license-type { + font-weight: bold; +} + +.atfpp-dashboard-license-pro-container .license-key { + background: var(--background-color); + padding: 5px; + border-radius: 4px; + display: inline-block; + font-family: monospace; +} + +/* Deactivate Button Container */ +.atfpp-dashboard-license-pro-container-deactivate-btn { + display: flex; + justify-content: space-between; + align-items: center; + margin: 41px 0px 20px 0px; +} + +.atfpp-dashboard-license-pro-container .deactivate-btn { + background: #e74c3c; + color: white; + border: none; + padding: 10px 15px; + border-radius: 6px; + cursor: pointer; + font-size: 14px; + transition: background-color 0.2s ease; +} + +.atfpp-dashboard-license-pro-container .deactivate-btn:hover { + background: #c0392b; +} + +/* License Buttons Section */ +.atfpp-dashboard-license-pro-container-buttons { + margin-top: 25px; + padding-top: 20px; + border-top: 1px solid var(--border-color); + display: flex; + justify-content: space-between; + margin-top: 20px; +} + +.atfpp-dashboard-license-pro-container-buttons .btns { + display: flex; + gap: 15px; + margin-top: 10px; +} + +.atfpp-dashboard-license-pro-container-buttons a.atfpp-dashboard-btn { + padding: 8px 16px; + border-radius: 6px; + font-size: 14px; + font-weight: 500; + text-decoration: none; + transition: all 0.2s ease; +} + + +/* Form Submit Button */ +.atfpp-dashboard-license-form button[type="submit"] { + margin-top: 10px; +} + +/* Responsive Adjustments */ +@media (max-width: 768px) { + .atfpp-dashboard-license-pro-container-deactivate-btn { + flex-direction: column; + gap: 15px; + align-items: flex-start; + } + + .atfpp-dashboard-license-pro-container-buttons .btns { + flex-direction: column; + } +} + +/* Responsive adjustments */ +@media (max-width: 768px) { + .atfpp-dashboard-api-settings .input-group input { + width: calc(100% - 70px); + } +} + + + +/* ================================================ + Glossary Pages +================================================ */ + +.atfpp-glossary { + width: 100%; + margin-top: 25px; + flex: 2; + } + + .atfpp-glossary-container { + padding: 24px; + background: #fff; + border: 1px solid var(--border-color); + border-radius: 8px; + } + + .atfpp-header h1 { + font-size: 24px; + margin-bottom: 30px; + } + + .atfpp-header p { + margin-bottom: 10px; + } + + .atfpp-header ul { + list-style: disc; + padding-left: 20px; + margin-bottom: 10px; + } + + .atfpp-header a { + color: #0073aa; + text-decoration: none; + } + + .atfpp-controls { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 10px; + margin: 46px 0 20px; + } + + .atfpp-search, + .atfpp-glossary-type { + padding: 8px; + } + + .atfpp-update-btn, + .atfpp-add-btn, + .atfpp-import-btn, + .atfpp-export-btn { + padding: 8px 12px; + background-color: #007cba; + color: white; + border: none; + cursor: pointer; + border-radius: 4px; + } + + .atfpp-update-btn:hover, + .atfpp-add-btn:hover, + .atfpp-import-btn:hover, + .atfpp-export-btn:hover { + background-color: #005a8c; + } + + .atfpp-status { + font-size: 14px; + color: #555; + } + + .atfpp-alphabet { + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 8px; + margin-top: 28px; + border-top: 1px solid rgba(51, 135, 158, 0.2); + border-bottom: 1px solid rgba(51, 135, 158, 0.2); + padding: 10px 0; + } + + .atfpp-alphabet button { + min-width: 32px; + padding: 6px 10px; + font-size: 14px; + font-weight: 500; + border: none; + border-radius: 4px; + cursor: not-allowed; + text-transform: uppercase; + transition: all 0.3s ease; + background-color: #fff; + } + + .atfpp-alphabet button:not([disabled]) { + color: #007cba; + cursor: pointer; + } + + .atfpp-alphabet button.active:not([disabled]) { + color: #0073aa; + font-weight: bold; + background-color: rgba(51, 135, 158, 0.15); + border-radius: 100%; + } + + /* ================================================ + Glossary Modal Styles + ================================================ */ + + .atfpp-glossary-modal.atfpp-hidden { + display: none; + } + + .atfpp-glossary-modal { + display: flex; + align-items: center; + justify-content: center; + position: fixed; + left: 0; + top: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.5); + z-index: 1005001; + } + + .atfpp-glossary-modal-content-wrapper { + display: flex; + flex-direction: column; + background: #fff; + border-radius: 4px; + max-width: 600px; + width: 90%; + padding: 0; + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2); + overflow: hidden; + max-height: 85vh; + } + + .atfpp-glossary-modal-content { + padding: 5px 5px 0px 5px; + border: 35px solid white; + overflow-y: auto; + } + + .atfpp-glossary-modal-actions { + display: flex; + justify-content: space-between; + background: #fff; + padding: 33px 25px 0px 0px; + border-top: 1px solid #eee; + position: sticky; + bottom: 0; + left: 0; + z-index: 2; + } + + .atfpp-glossary-modal-actions #add-glossary-term-btn { + margin: 5px; + } + + .atfpp-glossary-modal-content h2 { + margin-top: 4px; + margin-bottom: 20px; + font-size: 22px; + } + + .atfpp-glossary-modal-content label { + display: block; + font-weight: 500; + } + + .atfpp-glossary-modal-content textarea, + .atfpp-glossary-modal-content select { + width: 100%; + padding: 10px 12px; + margin-top: 6px; + border: 1px solid #ccd0d4; + border-radius: 4px; + font-size: 14px; + font-family: inherit; + resize: vertical; + } + + .atfpp-glossary-modal-content label { + margin-top: 16px; + } + + .atfpp-glossary-modal-actions .atfpp-glossary-modal-actions-left { + cursor: pointer; + text-decoration: underline; + } + + /* ================================================ + Import Glossary Section (Modal Content) + ================================================ */ + + .atfpp-import-glossary { + font-family: Arial, sans-serif; + margin: auto; + } + + .atfpp-title { + font-size: 20px; + margin-bottom: 20px; + text-align: center; + } + + .atfpp-upload-box { + display: block; + cursor: pointer; + margin-bottom: 20px; + } + + .atfpp-upload-area { + border: 2px dashed #c2c2c2; + padding: 30px; + text-align: center; + border-radius: 8px; + background-color: #f9f9f9; + } + + .atfpp-upload-area img { + width: 30px; + height: 30px; + margin-bottom: 10px; + } + + .atfpp-upload-area span { + display: block; + color: #007baf; + font-weight: 600; + } + + + .atfpp-help-text { + font-size: 13px; + color: #666; + } + + .atfpp-download-link { + color: #007baf; + font-size: 14px; + text-decoration: underline; + cursor: pointer; + } + + .atfpp-import-success { + margin-top: 20px; + padding: 15px; + border: 1px solid rgb(71, 165, 191); + background: rgb(245, 245, 245); + border-radius: 8px; + text-align: center; + } + + .atfpp-hidden { + display: none; + } + + .atfpp-close-button { + margin-top: 15px; + padding: 8px 16px; + background: #007baf; + color: #fff; + border: none; + border-radius: 5px; + cursor: pointer; + } + + .atfpp-import-success-message { + color: rgb(39, 173, 149); + font-size: 16px; + font-weight: 500; + text-align: center; + margin: 24px 0 12px 0; + letter-spacing: 0.01em; + } + + .atfpp-import-close-btn { + display: block; + margin: 20px auto 0 auto; + padding: 8px 24px; + background: #fff; + color: #007baf; + border: 1px solid #007baf; + border-radius: 6px; + font-size: 15px; + font-weight: 500; + cursor: pointer; + transition: background 0.2s, color 0.2s; + } + + .atfpp-import-close-btn:hover { + background: #007baf; + color: #fff; + } + + .atfpp-import-success-icon { + margin-bottom: 18px; + } + + .atfpp-import-success-file { + font-size: 15px; + color: #222; + margin-bottom: 10px; + } + + #atfpp-importing-file-label { + color: #222; + margin-right: 4px; + } + + #atfpp-importing-file-name { + color: #222; + font-weight: 500; + word-break: break-all; + } + + .atfpp-glossary-table-wrapper { + overflow-x: auto; + width: 100%; + position: relative; + } + + .atfpp-glossary-table { + width: 100%; + border-collapse: collapse; + font-family: 'Segoe UI', Arial, sans-serif; + } + + .atfpp-glossary-table thead tr:first-child th { + border-bottom: none; + text-align: center; + font-size: 1.2em; + padding: 13px 0; + } + + .atfpp-glossary-table thead tr:nth-child(2) th { + background: #222; + color: #fff; + font-weight: 600; + text-align: left; + padding: 12px 8px; + } + +.atfpp-glossary-table th, .atfpp-glossary-table td { + padding: 12px 8px; + vertical-align: middle; + position: relative; +} + + .atfpp-entry-title { + font-weight: 500; + font-size: 1.1em; + } + + .atfpp-entry-desc { + color: #888; + font-size: 0.95em; + margin-top: 2px; + } + + .atfpp-type-badge { + display: inline-block; + padding: 4px 8px; + border-radius: 25px; + font-size: 12px; + font-weight: 500; +} + +.atfpp-type-badge.general { + background: #eaf6fa; +} + +.atfpp-type-badge.name { + background: #eafaf0; + color: #1a7f37; +} + +/* Edit and Delete button styles */ +.atfpp-edit-btn { + background: #2271b1; + border: none; + color: white; + padding: 7px 12px; + cursor: pointer; + font-size: 13px; + border-radius: 4px; + text-decoration: none; +} + +.atfpp-edit-btn:hover { + text-decoration: underline; +} + +.atfpp-delete-btn { + background: #dc3232; + border: none; + color: white; + padding: 7px 12px; + cursor: pointer; + font-size: 13px; + text-decoration: none; + border-radius: 4px; +} + +.atfpp-delete-btn:hover { + text-decoration: underline; +} + +/* Delete button loading state */ +.atfpp-delete-btn.atfpp-delete-loading { + position: relative; + opacity: 0.7; + pointer-events: none; + background: #dc3232; + color: white; + border: none; + padding: 7px 12px; + cursor: not-allowed; + font-size: 13px; + border-radius: 4px; + min-width: 60px; + display: flex; + align-items: center; + justify-content: center; +} + +.atfpp-delete-spinner { + width: 14px; + height: 14px; + border: 2px solid rgba(255, 255, 255, 0.3); + border-top: 2px solid #ffffff; + border-radius: 50%; + animation: atfpp-delete-spin 0.8s linear infinite; +} + +@keyframes atfpp-delete-spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +/* No translation edit button */ +.atfpp-edit-btn-svg { + background: transparent; + border: none; + padding: 0; + cursor: pointer; +} + +.atfpp-edit-btn-svg img { + width: 20px; + height: 20px; + vertical-align: middle; +} + +/* Save/Cancel buttons in edit mode */ +.atfpp-save-edit-btn { + background: #2496b8; + color: #fff; + border: none; + border-radius: 7px; + padding: 5px 18px; + cursor: pointer; + font-weight: 600; + font-size: 1em; + transition: background 0.2s; +} + +.atfpp-save-edit-btn:hover { + background: #1b7a97; +} + +.atfpp-cancel-edit-btn { + background-color: transparent; + color: #666; + border: none; + border-radius: 4px; + padding: 5px 5px; + cursor: pointer; + font-weight: 500; + transition: background 0.2s; + text-decoration: underline; + transition: background 0.2s; +} + +.atfpp-cancel-edit-btn:hover { + color: #414141; +} + +.atfpp-icon-doc { + display: inline-block; + width: 22px; + height: 22px; + background: url('doc-icon.svg') center/contain no-repeat; + vertical-align: middle; +} + +.atfpp-lang-header img { + width: 16px; + height: 11px; + vertical-align: middle; +} + +@media (max-width: 900px) { + .atfpp-glossary-table-wrapper { + max-width: 100%; + font-size: 0.95em; + } + .atfpp-glossary-table th, .atfpp-glossary-table td { + padding: 8px 4px; + } +} + +.atfpp-language-filters { + display: flex; + gap: 16px; + margin: 18px 0 18px 0; + flex-wrap:wrap; +} + +.atfpp-lang-filter-btn { + display: flex; + align-items: center; + gap: 7px; + padding: 3px 11px; + border-radius: 18px; + border: 1.5px solid #cce3e3; + background: #f8f8f8; + color: #1a3a4a; + font-size: 1em; + font-weight: 500; + cursor: pointer; + transition: background 0.2s, border-color 0.2s, color 0.2s; +} + +.atfpp-lang-filter-btn img { + background: #fff; + border: 1px solid #e0e0e0; +} + +.atfpp-lang-filter-btn.active, +.atfpp-lang-filter-btn:hover { + background: #eaf6fa; + border-color: #7ed6df; + color: #0073aa; +} + +/* ================================================ + Improved Glossary Table Editor Row Styles +=============================================== */ +.atfpp-glossary-edit-row { + background: #eaf2f5 !important; + border-bottom: 1px solid #dde6ea; + border-left: none; + box-shadow: none; +} + +.atfpp-glossary-edit-row td { + padding: 18px 12px 12px 12px !important; + vertical-align: top !important ; +} + +.atfpp-glossary-edit-row textarea, +.atfpp-glossary-edit-row select { + width: 100%; + padding: 8px; + border: 1px solid #ddd; + border-radius: 4px; + margin-top: 0; + box-sizing: border-box; + resize: vertical; + transition: border-color 0.2s, box-shadow 0.2s; +} + +.atfpp-glossary-edit-row textarea:focus, +.atfpp-glossary-edit-row select:focus { + border: 1px solid rgb(51, 135, 158) !important; + box-shadow: rgb(51, 135, 158) 0px 0px 3px !important; + border-radius: 2px !important; +} + +.atfpp-glossary-edit-row .atfpp-edit-type { + width: auto; + min-width: 71px; + padding: 3px 20px 3px 10px; + border-radius: 10px; + border: 1.5px solid #7ed6bf; + color: #1ca97c; + font-weight: 600; + font-size: 0.9em; + position: relative; + margin-bottom: 0; + margin-top: 8px; + box-shadow: none; + appearance: none; + cursor: pointer; + text-align: center; +} + +.atfpp-glossary-edit-row .atfpp-edit-type { + background-image: url("data:image/svg+xml;utf8,"); + background-repeat: no-repeat; + background-position: right 4px center; + background-size: 18px 18px; +} + +.atfpp-glossary-edit-row .atfpp-edit-type::-ms-expand { + display: none; +} +.atfpp-glossary-edit-row .atfpp-edit-type:focus { + border-color: #1ca97c; + outline: none; + box-shadow: 0 0 0 2px #1ca97c22; + background: #eafaf0; +} + +.atfpp-glossary-edit-row .atfpp-save-edit-btn { + background: #2496b8; + color: #fff; + border: none; + border-radius: 7px; + font-weight: 600; + font-size: 1em; + cursor: pointer; + margin-bottom: 0; + box-shadow: 0 2px 8px #2496b81a; + transition: background 0.2s; + display: block; +} + +.atfpp-glossary-edit-row .atfpp-save-edit-btn:hover { + background: #1b7a97; +} + +.atfpp-glossary-edit-row .atfpp-cancel-edit-link { + color: #666; + text-decoration: underline; + font-size: 1em; + margin-left: 0; + cursor: pointer; + display: block; + transition: color 0.2s; +} + +.atfpp-glossary-edit-row .atfpp-cancel-edit-link:hover { + text-decoration: underline; +} + +.atfpp-glossary-edit-row td textarea:last-child { + margin-bottom: 0; +} + +.atfpp-glossary-edit-row td:last-child, +.atfpp-glossary-edit-row td:nth-last-child(2) { + vertical-align: middle; +} + +/* Fix: Only stripe visible, non-edit data rows in tbody +.atfpp-glossary-table tbody tr:not(.atfpp-glossary-edit-row):nth-of-type(even) { + background: #f7f7f7; +} */ + +.atfpp-glossary-table td:first-child, +.atfpp-glossary-table th:first-child { + max-width: 270px; + width: 270px; + min-width: 180px; + word-break: break-word; + white-space: normal; +} + +.atfpp-glossary-table td:not(:first-child), +.atfpp-glossary-table th:not(:first-child) { + text-align: center !important; + vertical-align: middle !important; +} + +.atfpp-alphabet button.active:not([disabled]) { + color: #0073aa; + font-weight: bold; + background-color: rgba(51, 135, 158, 0.15); + border-radius: 100%; +} + +.atfpp-add-translations label { + display: block; +} +.atfpp-add-translation { + width: 100%; + min-height: 32px; + margin-top: 4px; +} + +.atfpp-add-translations { + display: none; + opacity: 0; + max-height: 0; + overflow: hidden; + transition: opacity 0.3s, max-height 0.4s; +} +.atfpp-add-translations.atfpp-show { + display: flex !important; + opacity: 1; + max-height: 2000px; + overflow: visible; + transition: opacity 0.3s, max-height 0.4s; +} + +.atfpp-add-translations.atfpp-translations-grid { + display: grid !important; + grid-template-columns: repeat(2, 1fr); + gap: 20px 20px; + margin-top: 20px; +} + +.atfpp-translations-row { + display: flex; + gap: 16px; + margin-bottom: 12px; +} +.atfpp-translation-field { + flex: 1 1 0; + min-width: 160px; + max-width: 220px; + display: flex; + flex-direction: column; + gap: 6px; +} + +@media (max-width: 700px) { + .atfpp-add-translations.atfpp-translations-grid { + grid-template-columns: 1fr !important; + } +} + +.atfpp-glossary-table.atfpp-hide-lang-xx th.atfpp-lang-header.atfpp-lang-col-xx, +.atfpp-glossary-table.atfpp-hide-lang-xx td.atfpp-lang-col-xx { + display: none !important; +} + + +.atfpp-modal-close-btn { + position: absolute; + top: 8px; + right: 17px; + z-index: 10; + background: transparent; + border: none; + font-size: 2rem; + color: #888; + cursor: pointer; + line-height: 1; + padding: 0; + transition: color 0.2s; +} +.atfpp-modal-close-btn:hover { + color: #2496b8; +} + +.atfpp-translation-field.atfpp-hidden { + display: none !important; +} + +.atfpp-language-filters:empty { + display: none; +} + +.atfpp-glossary-table[class*="atfpp-hide-lang-"] th.atfpp-lang-header, +.atfpp-glossary-table[class*="atfpp-hide-lang-"] td[class^="atfpp-lang-col-"] { + display: table-cell; +} + +.atfpp-glossary-table.atfpp-hide-lang-xx th.atfpp-lang-header.atfpp-lang-col-xx, +.atfpp-glossary-table.atfpp-hide-lang-xx td.atfpp-lang-col-xx { + display: none !important; +} + +.atfpp-controls .atfpp-glossary-type { + vertical-align: middle; +} + +.atfpp-alphabet-btn.active:not([disabled]) { + color: #0073aa; + font-weight: bold; + background-color: rgba(51, 135, 158, 0.15); + border-radius: 100%; +} + +.atfpp-glossary-table thead th { + text-align: center; + padding: 12px 8px; + vertical-align: middle; + z-index: 3; + position: relative; +} + +.atfpp-glossary-table thead th .atfpp-lang-flag { + width: 20px; + height: auto; + vertical-align: middle; + margin-right: 6px; + border: 1px solid #e0e0e0; + border-radius: 2px; +} + +.atfpp-glossary-table thead th[data-lang] { + font-size: 14px; + white-space: nowrap; +} + +.atfpp-action-buttons { + position: sticky; + right: 0; + background: transparent; + padding: 8px 8px; + z-index: 2; + display: flex; + gap: 25px; + justify-content: center; + min-width: 100px; +} + +.atfpp-action-buttons-header { + position: sticky; + right: 0; + background: transparent; + padding: 8px 8px; + z-index: 2; + display: flex; + justify-content: center; + min-width: 100px; +} + +.atfpp-action-buttons-header .atfpp-actions-header-btn { + margin: 0 10px !important; +} + +.atfpp-action-buttons::before { + content: ''; + position: absolute; + left: -10px; + top: 0; + bottom: 0; + width: 10px; +} + +.atfpp-edit-btn, +.atfpp-delete-btn { + position: relative; + z-index: 3; +} + +.atfpp-actions-cell { + position: sticky; + right: 0; + background: #fff; + z-index: 10; + min-width: 120px; + padding: 0; +} +.atfpp-action-buttons { + display: flex; + gap: 32px; + justify-content: center; + align-items: center; + height: 100%; + padding: 8px 8px; + background: transparent; +} + +/* Sticky Actions header and cell */ +.atfpp-glossary-table th.atfpp-actions-cell, +.atfpp-glossary-table td.atfpp-actions-cell { + position: sticky; + right: 0; + background: #fff; + z-index: 20; + min-width: 120px; + box-shadow: -2px 0 4px -2px rgba(0,0,0,0.04); +} + +.atfpp-glossary-table thead tr:nth-child(2) th:last-child { + position: sticky; + right: 0; + background: #222; + z-index: 19; + min-width: 120px; + box-shadow: -2px 0 4px -2px rgba(0,0,0,0.04); +} + +/* Sticky actions cell matches row background */ +.atfpp-glossary-table td.atfpp-actions-cell { + background: #fff !important; +} + +/* .atfpp-add-translation, */ +.atfpp-edit-translation { + min-width: 145px; + max-width: 220px; + width: 100%; + height: 170px; + min-height: 36px; + max-height: 170px; + padding: 5px 6px; + border-radius: 6px; + font-size: 1em; + resize: vertical; + box-sizing: border-box; + line-height: 1.4; + margin: 0 auto; + display: block; + transition: height 0.2s ease-in-out; +} + +/* Widen language columns */ +.atfpp-glossary-table th[data-lang], +.atfpp-glossary-table td[data-lang] { + min-width: 120px; + max-width: 180px; + width: 140px; + text-align: center; + vertical-align: middle; + padding-left: 8px; + padding-right: 8px; + box-sizing: border-box; +} + +/* Make translation textareas fit the column */ +/* .atfpp-edit-translation, */ +.atfpp-add-translations.atfpp-translations-grid, +.atfpp-add-translations.atfpp-translations-grid.atfpp-show .atfpp-add-translation { + width: 100%; + min-width: 100px; + box-sizing: border-box; + margin-top: 20px; +} + +/* Add this to your CSS */ +.atfpp-row-striped { + background: #f7f7f7 !important; +} + +/* Sticky actions cell matches row background */ +.atfpp-glossary-table td.atfpp-actions-cell { + background: #fff !important; +} +.atfpp-glossary-table tbody tr.atfpp-row-striped td.atfpp-actions-cell { + background: #f7f7f7 !important; +} + +.atfpp-glossary-table th.atfpp-actions-cell button, +.atfpp-glossary-table th.atfpp-actions-cell .atfpp-actions-header-btn { + width: 35px; + height: 35px; + border-radius: 50%; + background: #eaf2f5; + border: none; + display: flex; + align-items: center; + justify-content: center; + margin: 0 auto; + box-shadow: none; + transition: background 0.2s; + cursor: pointer; + padding: 0; +} + +.atfpp-glossary-table th.atfpp-actions-cell button:hover, +.atfpp-glossary-table th.atfpp-actions-cell .atfpp-actions-header-btn:hover { + background: #e6eef3; +} + +tr.atfpp-glossary-edit-row > td.atfpp-actions-cell { + background-color: #eaf2f5 !important +} + +#atfpp-no-results { + text-align:center; + margin: 32px 0; + color: #888; + font-size: 1.2em; +} + +.atfpp-translation-error { + color: rgb(219, 85, 43); + font-size: 12px; + margin-top: 4px; + display: none; +} + +.atfpp-edit-translation.error, +.atfpp-add-translation.error, +.atfpp-edit-term.error, +.atfpp-add-term.error, +.atfpp-edit-desc.error, +.atfpp-add-desc.error { + height: 126px !important; + border-color: rgb(219, 85, 43) !important; +} + +.atfpp-edit-term.error, +.atfpp-add-term.error, +.atfpp-edit-desc.error, +.atfpp-add-desc.error { + height: auto !important; + min-height: 32px; +} + +/* Glossary Loader Styles */ +.atfpp-glossary-loader { + position: absolute; + display: flex; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(255, 255, 255, 0.5); + z-index: 1005002; + justify-content: center; + align-items: center; +} + +.atfpp-glossary-loader.active { + display: flex; +} + +.atfpp-glossary-loader-spinner { + width: 50px; + height: 50px; + border: 3px solid #f3f3f3; + border-top: 3px solid #0073aa; + border-radius: 50%; + animation: atfpp-spin 1s linear infinite; +} + +@keyframes atfpp-spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +.atfpp-openai-model-select, .atfpp-google-model-select { + width: 200px; + height: 32px; + border-radius: 5px; + border: 1px solid #ccc; + font-size: 15px; + vertical-align: middle; +} diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/arrow-left.svg b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/arrow-left.svg new file mode 100644 index 0000000..9d4d9d5 --- /dev/null +++ b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/arrow-left.svg @@ -0,0 +1,3 @@ + + + diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/arrow-right.svg b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/arrow-right.svg new file mode 100644 index 0000000..8001656 --- /dev/null +++ b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/arrow-right.svg @@ -0,0 +1,3 @@ + + + diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/arrow.svg b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/arrow.svg new file mode 100644 index 0000000..76c68d0 --- /dev/null +++ b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/arrow.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/atlt-logo.png b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/atlt-logo.png new file mode 100644 index 0000000..6038b81 Binary files /dev/null and b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/atlt-logo.png differ diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/chatgpt-translate.png b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/chatgpt-translate.png new file mode 100644 index 0000000..46f7e08 Binary files /dev/null and b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/chatgpt-translate.png differ diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/chrome-ai-translate.png b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/chrome-ai-translate.png new file mode 100644 index 0000000..d83140b Binary files /dev/null and b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/chrome-ai-translate.png differ diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/contact.svg b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/contact.svg new file mode 100644 index 0000000..94f9a08 --- /dev/null +++ b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/contact.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/cool-plugins-logo-black.svg b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/cool-plugins-logo-black.svg new file mode 100644 index 0000000..b5867a2 --- /dev/null +++ b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/cool-plugins-logo-black.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/csv.svg b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/csv.svg new file mode 100644 index 0000000..881aba6 --- /dev/null +++ b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/csv.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/docs.svg b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/docs.svg new file mode 100644 index 0000000..285422f --- /dev/null +++ b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/docs.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/document.svg b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/document.svg new file mode 100644 index 0000000..885544d --- /dev/null +++ b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/document.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/facebook.svg b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/facebook.svg new file mode 100644 index 0000000..c998503 --- /dev/null +++ b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/facebook.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/file.svg b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/file.svg new file mode 100644 index 0000000..e7fd87b --- /dev/null +++ b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/file.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/gemini-translate.png b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/gemini-translate.png new file mode 100644 index 0000000..75f8317 Binary files /dev/null and b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/gemini-translate.png differ diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/linkedin.svg b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/linkedin.svg new file mode 100644 index 0000000..587d1cb --- /dev/null +++ b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/linkedin.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/open-ai-translate.png b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/open-ai-translate.png new file mode 100644 index 0000000..39a3e87 Binary files /dev/null and b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/open-ai-translate.png differ diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/openrouter-ai-translate.png b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/openrouter-ai-translate.png new file mode 100644 index 0000000..8bb91d4 Binary files /dev/null and b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/openrouter-ai-translate.png differ diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/polylang-addon-logo.svg b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/polylang-addon-logo.svg new file mode 100644 index 0000000..85462aa --- /dev/null +++ b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/polylang-addon-logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/polylang-addon-video.png b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/polylang-addon-video.png new file mode 100644 index 0000000..b8325ee Binary files /dev/null and b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/polylang-addon-video.png differ diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/success.svg b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/success.svg new file mode 100644 index 0000000..7e21b82 --- /dev/null +++ b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/success.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/twitter.svg b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/twitter.svg new file mode 100644 index 0000000..71907a2 --- /dev/null +++ b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/twitter.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/upgrade-now.svg b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/upgrade-now.svg new file mode 100644 index 0000000..019d1f7 --- /dev/null +++ b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/upgrade-now.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/video.svg b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/video.svg new file mode 100644 index 0000000..745a2f5 --- /dev/null +++ b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/video.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/youtube-icon.svg b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/youtube-icon.svg new file mode 100644 index 0000000..7a70539 --- /dev/null +++ b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/youtube-icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/youtube.svg b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/youtube.svg new file mode 100644 index 0000000..ad675bf --- /dev/null +++ b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/images/youtube.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/js/atfpp-data-share-setting.js b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/js/atfpp-data-share-setting.js new file mode 100644 index 0000000..25ec0f1 --- /dev/null +++ b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/js/atfpp-data-share-setting.js @@ -0,0 +1,284 @@ +jQuery(function ($) { + // Constants + const SELECTORS = { + termsLink: ".atfpp-see-terms", + termsBox: "#termsBox", + refreshBtn: ".atfpp-refresh-btn", + licenseContainer: ".atfpp-dashboard-license-pro-container", + deactivateBtn: ".atfpp-dashboard-license-pro-container-deactivate-btn", + validityStrong: ".validity:has(strong) strong", + validitySpan: ".validity:not(:has(strong))", + licenseType: ".license-type", + containerUl: ".atfpp-dashboard-license-pro-container ul", + errorNotice: ".atfpp-dashboard-license-pro-container div.notice.notice-error", + }; + + const CSS_CLASSES = { + valid: "valid", + invalid: "invalid", + expired: "expired", + supportExpired: "support-expired", + noSupport: "no-support" + }; + + const MESSAGES = { + validLicense: "✅ Valid", + invalidLicense: "❌ Invalid", + expiredLicense: "❌ License Expired", + expiredSupport: "❌ Support Expired", + genericError: "An error occurred while refreshing the license. Please try again.", + checkingStatus: "⏳Checking....." + }; + + const URLS = { + renewLink: "https://my.coolplugins.net/account/subscriptions/" + }; + + // Helper Functions + const createNoticeElement = (type, message, isError = false) => { + const noticeClass = isError ? "notice-error" : "notice-success"; + return $(`

${message}

`); + }; + + const createRenewalNotice = (message) => { + return $(`
${message}
`); + }; + + const isDateExpired = (dateString) => { + if (!dateString || dateString.toLowerCase() === "no expiry" || dateString.toLowerCase() === "unlimited") { + return false; + } + return new Date(dateString).getTime() < Date.now(); + }; + + const updateValidityStatus = ($element, status, cssClass) => { + $element.html(status); + $element.removeClass(Object.values(CSS_CLASSES).join(" ")).addClass(cssClass); + }; + + const getRenewalMessage = (isLicenseExpired, market, versionMessage = '') => { + const baseMessage = isLicenseExpired + ? "Your license has expired, " + : "Your support has expired, "; + + const versionPrefix = versionMessage ? versionMessage + ' ' : ''; + + if (market === "E") { + return versionPrefix + baseMessage + "Renew now to continue receiving updates and priority support."; + } + + const linkText = isLicenseExpired ? "Renew now" : + 'Renew now'; + + return isLicenseExpired + ? versionPrefix + baseMessage + `Renew now to continue receiving updates and priority support.` + : versionPrefix + baseMessage + linkText + " to continue receiving updates and priority support"; + }; + + const getFormattedDate = (dateString) => { + if (dateString.toLowerCase() !== 'no expiry') { + const date = new Date(dateString); + return date.toLocaleDateString('en-GB', { + day: '2-digit', + month: 'short', + year: 'numeric' + }); + } + return dateString; + }; + + const showNoticeMessage = (element, delay = 2000) => { + element.insertBefore(SELECTORS.licenseContainer).delay(delay).fadeOut(); + }; + + // Terms toggle functionality + const $termsLink = $(SELECTORS.termsLink); + const $termsBox = $(SELECTORS.termsBox); + + $termsLink.on("click", function (e) { + e.preventDefault(); + const isVisible = $termsBox.toggle().is(":visible"); + $(this).html(isVisible ? "Hide Terms" : "See terms"); + }); + + // License refresh functionality + const $refreshBtn = $(SELECTORS.refreshBtn); + + $refreshBtn.on("click", function (e) { + e.preventDefault(); + + const $btn = $(this); + const originalText = $btn.text(); + + // Update button state + $btn.prop("disabled", true).text(MESSAGES.checkingStatus); + + + // AJAX request + $.ajax({ + url: atfpp_ajax.ajaxurl, + type: "POST", + data: { + action: "atfpp_refresh_license_ajax", + nonce: atfpp_ajax.nonce, + }, + success: function (response) { + if (response.success) { + const successNotice = createNoticeElement("success", response.data.message); + showNoticeMessage(successNotice); + + if (response.data.license_info) { + const versionMessage = response.data.version_available_message || ''; + updateLicenseInfo(response.data.license_info, versionMessage); + } + } else { + const errorNotice = createNoticeElement("error", response.data.message, true); + showNoticeMessage(errorNotice); + + setTimeout(() => location.reload(), 2000); + } + }, + error: function () { + const errorNotice = createNoticeElement("error", MESSAGES.genericError, true); + showNoticeMessage(errorNotice, 5000); + }, + complete: function () { + $btn.prop("disabled", false).text("🔄" + originalText); + }, + }); + }); + + // License information update function + function updateLicenseInfo(licenseInfo, versionMessage = '') { + updateLicenseValidity(licenseInfo); + updateLicenseType(licenseInfo); + updateExpireDate(licenseInfo); + handleRenewalMessages(licenseInfo, versionMessage); + handleRefreshButtonVisibility(licenseInfo); + } + + function updateLicenseValidity(licenseInfo) { + const $validity = $(SELECTORS.validityStrong); + + if (!licenseInfo.is_valid) { + updateValidityStatus($validity, MESSAGES.invalidLicense, CSS_CLASSES.invalid); + return; + } + + if (licenseInfo.is_valid === "license_expired") { + updateValidityStatus($validity, MESSAGES.expiredLicense, CSS_CLASSES.expired); + } else if (licenseInfo.support_end.toLowerCase() === "no support") { + updateValidityStatus($validity, MESSAGES.expiredSupport, CSS_CLASSES.noSupport); + } else if (licenseInfo.is_valid === "support_expired" && + isDateExpired(licenseInfo.support_end)) { + updateValidityStatus($validity, MESSAGES.expiredSupport, CSS_CLASSES.supportExpired); + } else { + updateValidityStatus($validity, MESSAGES.validLicense, CSS_CLASSES.valid); + } + } + + function updateLicenseType(licenseInfo) { + if (!licenseInfo.license_title) return; + + const $licenseType = $(SELECTORS.licenseType); + + if ($licenseType.length > 0) { + $licenseType.text(licenseInfo.license_title); + } else { + const $ul = $(SELECTORS.containerUl); + const $statusLi = $ul.find("li:first"); + const $newLicenseTypeLi = $(` +
  • + License Type: + ${licenseInfo.license_title} +
  • + `); + $statusLi.after($newLicenseTypeLi); + } + } + + function updateExpireDate(licenseInfo) { + if (!licenseInfo.expire_date) return; + + const $expireDateSpan = $(SELECTORS.validitySpan); + const displayDate = getDisplayDate(licenseInfo); + + if ($expireDateSpan.length > 0) { + $expireDateSpan.text(displayDate); + } else { + createExpireDateElement(displayDate); + } + } + + function getDisplayDate(licenseInfo) { + const expireDateExpired = isDateExpired(licenseInfo.expire_date); + const supportEndExpired = isDateExpired(licenseInfo.support_end); + + if (licenseInfo.support_end.toLowerCase() === "no support") { + return "No Support"; + } else if (expireDateExpired) { + return getFormattedDate(licenseInfo.expire_date); + } else if (supportEndExpired) { + return getFormattedDate(licenseInfo.support_end); + } else { + return getFormattedDate(licenseInfo.expire_date); + } + } + + function createExpireDateElement(displayDate) { + const $ul = $(SELECTORS.containerUl); + const $licenseTypeLi = $ul.find("li:has(.license-type)"); + + if ($licenseTypeLi.length > 0) { + const $newExpireDateLi = $(` +
  • + Plugin Updates & Support Validity: + ${displayDate} +
  • + `); + $licenseTypeLi.after($newExpireDateLi); + } + } + function removeRenewalMessages() { + $(SELECTORS.errorNotice).remove(); + } + + function handleRenewalMessages(licenseInfo, versionMessage = '') { + const isLicenseExpired = licenseInfo.is_valid === "license_expired"; + const isSupportExpired = licenseInfo.is_valid === "support_expired" || + (licenseInfo.support_end.toLowerCase() === "no support" && + isDateExpired(licenseInfo.support_end)); + + if (isLicenseExpired || isSupportExpired) { + updateRenewalMessage(licenseInfo, isLicenseExpired, versionMessage); + } else { + // Remove existing error notices when license is valid + removeRenewalMessages(); + } + } + + function updateRenewalMessage(licenseInfo, isLicenseExpired, versionMessage = '') { + const renewalMessage = getRenewalMessage(isLicenseExpired, licenseInfo.market, versionMessage); + const $existingLink = $(SELECTORS.errorNotice); + if ($existingLink.length) { + $existingLink.html(renewalMessage); + } else { + $(SELECTORS.errorNotice).remove(); + const $renewalNotice = createRenewalNotice(renewalMessage); + $(SELECTORS.deactivateBtn).after($renewalNotice); + } + } + + function handleRefreshButtonVisibility(licenseInfo) { + const isValidLicense = licenseInfo.is_valid && + licenseInfo.is_valid !== "license_expired" && + licenseInfo.is_valid !== "support_expired" && + (licenseInfo.support_end.toLowerCase() === "unlimited" || + !isDateExpired(licenseInfo.support_end)); + + if (isValidLicense) { + $(SELECTORS.refreshBtn).hide(); + } + } + }); + \ No newline at end of file diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/js/atfpp-glossary.js b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/js/atfpp-glossary.js new file mode 100644 index 0000000..4203f1a --- /dev/null +++ b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/js/atfpp-glossary.js @@ -0,0 +1,1448 @@ +jQuery(document).ready(function($) { + // Utility function to escape HTML entities for safe display + function escapeHtml(text) { + if (typeof text !== 'string') return text; + const map = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''' + }; + return text.replace(/[&<>"']/g, function(m) { return map[m]; }); + } + + // Utility: Reset modal to initial state + function resetImportModalUI() { + $('#atfpp-import-success-ui').addClass('atfpp-hidden'); + $('#atfpp-import-glossary-ui').show(); + $('#file-name-display').text('Select a CSV file to upload'); + $('#importing-file-name').text(''); + $('#atfpp-csv-upload').val(''); + } + + // Cache selectors used multiple times + let $glossaryTable = $('.atfpp-glossary-table'); + const $languageFilters = $('.atfpp-language-filters'); + const $alphabet = $('.atfpp-alphabet'); + const $addGlossaryForm = $('#atfpp-add-glossary-form'); + const $addBtn = $('.atfpp-add-btn'); + const $importBtn = $('.atfpp-import-btn'); + + // Open modal + $addBtn.on('click', function() { + $('#atfpp-glossary-modal-add').removeClass('atfpp-hidden').addClass('active'); + $('body').addClass('atfpp-modal-open'); + // Reset add form and translations section + $addGlossaryForm.find('.atfpp-add-translations').removeClass('atfpp-show').css('display', 'none'); + $addGlossaryForm.find('.atfpp-translation-field').show(); + $addGlossaryForm[0].reset(); + }); + $importBtn.on('click', function() { + resetImportModalUI(); + $('#atfpp-glossary-modal-import').removeClass('atfpp-hidden').addClass('active'); + }); + + // Close modal & reset + $(document).on('click', '.atfpp-modal-close-btn, .atfpp-glossary-modal-actions-left', function() { + const modal = $(this).closest('.atfpp-glossary-modal'); + const importSuccessUI = modal.find('#atfpp-import-success-ui'); + if ((importSuccessUI.length && !importSuccessUI.hasClass('atfpp-hidden') && importSuccessUI.is(':visible'))) { + window.location.reload(); + } + modal.addClass('atfpp-hidden').removeClass('active'); + modal.find('form').show(); + modal.find('#add-glossary-success').addClass('atfpp-hidden'); + + $('body').removeClass('atfpp-modal-open'); + resetImportModalUI(); + }); + + // File input change + $('#atfpp-csv-upload').on('change', function(e) { + const file = e.target.files[0]; + + if (!file || !file.name.toLowerCase().endsWith('.csv')) { + alert('Please upload a valid CSV file.'); + $(this).val(''); + return; + } + + const reader = new FileReader(); + reader.onload = function(event) { + const text = event.target.result; + const lines = text.trim().split('\n'); + const headers = lines[0].split(',').map(h => h.trim()); + + const data = lines.slice(1).map(line => { + const values = line.split(',').map(v => v.trim()); + const obj = {}; + headers.forEach((header, i) => { + obj[header] = values[i] || ''; + }); + return obj; + }); + }; + reader.readAsText(file); + + // If valid file: + $('#file-name-display').text(file.name); + $('#importing-file-name').text(file.name); + + // Automatically import as soon as file is selected + const formData = new FormData(); + formData.append('action', 'atfpp_import_glossary'); + formData.append('csv_file', file); + formData.append('overwrite', false); + formData.append('_wpnonce', atfpp_glossary.nonce); + + if (!$('.atfpp-glossary-loader').length) { + $('.atfpp-glossary-modal-content').append('
    '); + } + $.ajax({ + url: atfpp_glossary.ajaxurl, + type: 'POST', + data: formData, + processData: false, + contentType: false, + success: function(resp) { + if (resp.success) { + // Remove loader + $('.atfpp-glossary-loader').remove(); + $('#atfpp-import-glossary-ui').hide(); + $('#atfpp-import-success-ui').removeClass('atfpp-hidden'); + } else { + alert('Import failed: ' + (resp.data || 'Unknown error')); + } + }, + error: function(jqXHR, textStatus, errorThrown) { + alert('An error occurred while importing the glossary. Please try again.'); + } + }); + }); + + // Modify the download link click handler + $('.atfpp-download-link').off('click').on('click', function(e) { + e.preventDefault(); + // CSV content matching your image + const csvContent = [ + [ + 'original_language_code', + 'target_language_code', + 'original_term', + 'translated_term', + 'description', + 'kind' + ], + ['en', 'it', 'Page', 'Pagina', 'the page of a browser', 'general'], + ['en', 'it', 'page', 'pagina', 'the page of a browser', 'general'], + ['en', 'it', 'OnTheGoSystems', 'OnTheGoSystems', 'the name of my company', 'name'] + ].map(row => row.join(",")).join("\n"); + + const blob = new Blob([csvContent], { type: 'text/csv' }); + const url = URL.createObjectURL(blob); + + const a = document.createElement('a'); + a.href = url; + a.download = 'sample-glossary.csv'; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(url); + }); + + $(document).off('click', '.atfpp-edit-btn, .atfpp-edit-btn-svg').on('click', '.atfpp-edit-btn, .atfpp-edit-btn-svg', function(e) { + e.preventDefault(); + e.stopPropagation(); + + // Handle any existing edit rows first + const $existingEditRow = $('.atfpp-glossary-edit-row'); + if ($existingEditRow.length) { + // Show the original row that was being edited + $existingEditRow.prev('tr').show(); + // Remove the edit row + $existingEditRow.remove(); + } + + // Find the row being edited + const $row = $(this).closest('tr'); + const term = $row.data('term') || ''; + const desc = $row.find('.atfpp-entry-desc').text().trim() || ''; + const type = $row.data('type') || 'general'; + const source_lang = $row.data('original-language') || ''; + + // Validate required data + if (!source_lang) { + console.warn('Missing source language for row'); + return; + } + + // Safely build translations object + const translations = {}; + if (typeof atfpp_glossary !== 'undefined' && Array.isArray(atfpp_glossary.atfpp_languages)) { + atfpp_glossary.atfpp_languages.forEach(function(lang) { + if (!lang || !lang.code || lang.code === source_lang) return; + const $cell = $row.find(`td.atfpp-lang-col-${lang.code}`); + const $term = $cell.find('.atfpp-translated-term'); + let value = ''; + if ($term.length) { + // Try to get full text from data attribute first + value = $term.data('full-text') || $term.attr('data-full-text') || $term.text().trim(); + } + translations[lang.code] = typeof value === 'string' ? value : (value ? String(value) : ''); + }); + } + + const safeTerm = typeof term === 'string' ? term : ''; + const safeDesc = typeof desc === 'string' ? desc : ''; + const safeType = typeof type === 'string' ? type : 'general'; + const safeSourceLang = typeof source_lang === 'string' ? source_lang : ''; + + try { + const $templateScript = $('#atfpp-glossary-edit-row-template'); + const templateHtml = $templateScript.length ? $templateScript.html() : ''; + + if (!templateHtml) { + console.error('Template not found or empty'); + alert('Edit template not found. Please refresh the page.'); + return; + } + + const templateFn = _.template(templateHtml); + const editRowHtml = templateFn({ + term: safeTerm, + desc: safeDesc, + type: safeType, + translations: translations, + languages: atfpp_glossary.atfpp_languages.filter(lang => lang.code !== safeSourceLang), + source_lang: safeSourceLang + }); + + $row.after(editRowHtml); + $row.hide(); + $row.next('.atfpp-glossary-edit-row').show(); + + } catch (error) { + console.error('Error generating edit row:', error, { + safeTerm, + safeDesc, + safeType, + translations, + safeSourceLang + }); + alert('An error occurred while trying to edit this entry. Please try again.'); + } + }); + + + // Add input event handler for real-time validation + $glossaryTable.on('input', '.atfpp-edit-translation, .atfpp-edit-term, .atfpp-edit-desc', function() { + const $textarea = $(this); + const $error = $textarea.next('.atfpp-translation-error'); + const value = $textarea.val().trim(); + const maxLength = $textarea.hasClass('atfpp-edit-term') || $textarea.hasClass('atfpp-edit-desc') ? 240 : 220; + + if (value.length > maxLength) { + $textarea.addClass('error'); + $error.text(`Must be less than ${maxLength} characters`).show(); + } else { + $textarea.removeClass('error'); + $error.hide(); + } + }); + + // Update the save handler to include validation + $('.atfpp-glossary-table-wrapper').off('click', '.atfpp-save-edit-btn').on('click', '.atfpp-save-edit-btn', function(e) { + e.preventDefault(); + const $editRow = $(this).closest('tr'); + const $origRow = $editRow.prev('tr'); + + // Validate required fields + const $termField = $editRow.find('.atfpp-edit-term'); + const $descField = $editRow.find('.atfpp-edit-desc'); + const term = $termField.val().trim(); + const desc = $descField.val().trim(); + const type = $editRow.find('.atfpp-edit-type').val(); + const source_lang = $origRow.data('original-language'); + + let hasError = false; + + // Add validation for term and description length + if (term.length > 240) { + $termField.addClass('error'); + $termField.next('.atfpp-translation-error').text('Term must be less than 240 characters.').show(); + hasError = true; + } + + if (desc.length > 240) { + $descField.addClass('error'); + $descField.next('.atfpp-translation-error').text('Description must be less than 240 characters.').show(); + hasError = true; + } + + // Check translation lengths + $editRow.find('.atfpp-edit-translation').each(function() { + const $textarea = $(this); + const value = $textarea.val().trim(); + if (value.length > 240) { + $textarea.addClass('error'); + $textarea.next('.atfpp-translation-error').text('Translation must be less than 240 characters.').show(); + hasError = true; + } + }); + + if (hasError) { + return; + } + + const translations = {}; + $editRow.find('.atfpp-edit-translation').each(function() { + const lang = $(this).data('lang'); + if (lang) { + const value = $(this).val().trim(); + // Only include non-empty translations + if (value !== '') { + translations[lang] = value; + } + } + }); + + + // Modified AJAX request + $.ajax({ + url: atfpp_glossary.ajaxurl, + type: 'POST', + data: { + action: 'atfpp_update_glossary', + _wpnonce: atfpp_glossary.nonce, + data: { + term: term, + description: desc, + type: type, + source_lang: source_lang, + translations: translations, + original_term: $origRow.data('term'), + original_type: $origRow.data('type'), + original_source_lang: source_lang + } + }, + success: function(resp) { + if (resp.success && resp.data && resp.data.updated_entry) { + const updatedEntry = resp.data.updated_entry; + + // Update the original row with data from server response + $origRow.data('term', updatedEntry.original_term); + $origRow.data('type', updatedEntry.kind); + + // Update term and description from server data + $origRow.find('.atfpp-entry-title').text(updatedEntry.original_term); + $origRow.find('.atfpp-entry-desc').text(updatedEntry.description || ''); + + // Update type badge from server data + $origRow.find('.atfpp-type-badge') + .attr('class', 'atfpp-type-badge ' + updatedEntry.kind) + .text(updatedEntry.kind.charAt(0).toUpperCase() + updatedEntry.kind.slice(1)); + + // Update translations from server data + if (updatedEntry.translations && Array.isArray(updatedEntry.translations)) { + // Create a map of translations from server - only include non-empty translations + const serverTranslations = {}; + updatedEntry.translations.forEach(function(translation) { + if (translation.target_language_code && + translation.translated_term && + translation.translated_term.trim() !== '') { + serverTranslations[translation.target_language_code] = translation.translated_term.trim(); + } + }); + + // Update all language columns based on server data + $origRow.find('[class*="atfpp-lang-col-"]').each(function() { + const $cell = $(this); + const langCode = $cell.attr('class').match(/atfpp-lang-col-([a-zA-Z_-]+)/); + + if (langCode && langCode[1] && !$cell.data('is-source')) { + const lang = langCode[1]; + const translatedTerm = serverTranslations[lang]; + + if (translatedTerm && translatedTerm.trim() !== '') { + // Has translation - show it + const truncatedText = translatedTerm.length > 7 ? translatedTerm.substring(0, 7) + '…' : translatedTerm; + const safeTranslation = escapeHtml(translatedTerm); + const safeTruncated = escapeHtml(truncatedText); + + $cell.html(` + + ${safeTruncated} + + `); + } else { + // No translation - show add button + $cell.html(` + + `); + } + } + }); + } + + // Show the updated row and remove edit row + $origRow.show(); + $editRow.remove(); + + // Reapply filters and update UI + filterGlossaryRows(); + updateGlossaryTableVisibility(); + updateAlphabetButtonStates(); + applyZebraStriping(); + } else { + alert('Update failed: ' + (resp.data?.message || resp.data || 'Unknown error')); + } + }, + error: function(xhr, status, error) { + console.error('AJAX Error:', status, error); + alert('An error occurred while saving. Please try again.'); + } + }); + }); + + function applyZebraStriping() { + $('.atfpp-glossary-table tbody tr').removeClass('atfpp-row-striped'); + $('.atfpp-glossary-table tbody tr:visible:not(.atfpp-glossary-edit-row)').each(function(i) { + if (i % 2 === 1) { + $(this).addClass('atfpp-row-striped'); + } + }); + } + + function updateGlossaryTableVisibility() { + var $tableWrapper = $('.atfpp-glossary-table-wrapper'); + var $table = $tableWrapper.find('.atfpp-glossary-table'); + var $visibleRows = $table.find('tbody tr:visible'); + if ($visibleRows.length === 0) { + $tableWrapper.hide(); + if ($('#atfpp-no-results').length === 0) { + $tableWrapper.after('
    No glossary entries found.
    '); + } + } else { + $tableWrapper.show(); + $('#atfpp-no-results').remove(); + } + applyZebraStriping(); + } + + function updateAlphabetButtonStates() { + $alphabet.find('.atfpp-alphabet-btn').prop('disabled', false); + var visibleLetters = {}; + $glossaryTable.find('tbody tr:visible').each(function() { + var letter = $(this).data('letter'); + if (letter) visibleLetters[letter] = true; + }); + $alphabet.find('.atfpp-alphabet-btn').each(function() { + var $btn = $(this); + var letter = $btn.data('letter'); + if (!visibleLetters[letter]) { + $btn.prop('disabled', true); + $btn.removeClass('active'); + } + }); + + // After updating the table and language filter buttons + var $alphabetBtns = $('.atfpp-alphabet-btn'); + var $visibleRows = $glossaryTable.find('tbody tr:visible'); + + // If there are no visible rows, remove active state from all alphabet buttons + if ($visibleRows.length === 0) { + $alphabetBtns.removeClass('active'); + } + } + + // Language Filter Buttons + $(document).off('click', '.atfpp-lang-filter-btn').on('click', '.atfpp-lang-filter-btn', function() { + $('.atfpp-glossary-table-wrapper').show(); + $('#atfpp-no-results').remove(); + // Reset alphabet filter + $('.atfpp-alphabet-btn').removeClass('active'); + + var $btn = $(this); + var selectedLang = $btn.data('lang'); + var $table = $('.atfpp-glossary-table'); + var defaultLang = $table.data('default-lang'); + var previousSelectedLang = $('.atfpp-lang-filter-btn.active').data('lang'); + + // Update active state + $('.atfpp-lang-filter-btn').removeClass('active'); + $btn.addClass('active'); + + // First show all columns + $table.find('th[data-lang], td[data-lang]').show(); + + // Show the previously hidden default language column + if (defaultLang) { + $table.find(`th[data-lang="${defaultLang}"], td[data-lang="${defaultLang}"]`).show(); + } + + // Hide the newly selected language column when it's the source + if (selectedLang) { + $table.find(`td[data-lang="${selectedLang}"][data-is-source="true"]`).hide(); + $table.find(`th[data-lang="${selectedLang}"]`).hide(); + } + + // Apply filters to rows + $('.atfpp-glossary-table tbody tr').each(function() { + var $row = $(this); + var rowOriginalLang = $row.data('original-language'); + var rowType = $row.data('type'); + var show = true; + + // Show row if it matches the selected language + if (selectedLang) { + show = (rowOriginalLang === selectedLang); + + if (show) { + // For visible rows, ensure correct column visibility + $row.find('td[data-lang]').each(function() { + var $cell = $(this); + var cellLang = $cell.data('lang'); + var isSource = $cell.data('is-source') === true; + + // Hide if this is the source language column + if (cellLang === selectedLang && isSource) { + $cell.hide(); + } else { + $cell.show(); + } + }); + } + } + + // Apply type filter if active + var currentType = $('.atfpp-glossary-type').val(); + if (show && currentType) { + show = (rowType === currentType); + } + + // Handle edit rows + if ($row.hasClass('atfpp-glossary-edit-row')) { + show = $row.prev('tr').is(':visible'); + } + + $row.toggle(show); + }); + + updateGlossaryTableVisibility(); + updateAlphabetButtonStates(); + applyZebraStriping(); + }); + + // Update filterGlossaryRows function + function filterGlossaryRows() { + var selectedLang = $('.atfpp-lang-filter-btn.active').data('lang') || ''; + var selectedType = $('.atfpp-glossary-type').val() || ''; + var selectedLetter = $('.atfpp-alphabet-btn.active:not([disabled])').data('letter') || ''; + var search = $('.atfpp-search').val().toLowerCase(); + + $('.atfpp-glossary-table tbody tr').each(function() { + var $row = $(this); + var rowOriginalLang = $row.data('original-language'); + var rowType = $row.data('type'); + var rowLetter = $row.data('letter'); + var term = $row.find('.atfpp-entry-title').text().toLowerCase(); + var desc = $row.find('.atfpp-entry-desc').text().toLowerCase(); + + // Start with strict language filter + var show = (!selectedLang || rowOriginalLang === selectedLang); + + // Apply other filters only if row passes language filter + if (show && selectedType) { + show = (rowType === selectedType); + } + + if (show && selectedLetter) { + show = (rowLetter === selectedLetter); + } + + if (show && search) { + show = (term.indexOf(search) !== -1 || desc.indexOf(search) !== -1); + } + + // Handle edit rows visibility + if ($row.hasClass('atfpp-glossary-edit-row')) { + show = $row.prev('tr').is(':visible'); + } + + $row.toggle(show); + }); + + updateGlossaryTableVisibility(); + applyZebraStriping(); + } + + // On page load, if filter buttons exist, trigger click on the first one + if ($languageFilters.length && $languageFilters.find('.atfpp-lang-filter-btn').length > 0) { + $languageFilters.find('.atfpp-lang-filter-btn').first().trigger('click'); + } + + // Alphabet filter functionality + $(document).off('click', '.atfpp-alphabet-btn:not([disabled])').on('click', '.atfpp-alphabet-btn:not([disabled])', function() { + var $btn = $(this); + if ($btn.hasClass('active')) { + $btn.removeClass('active'); + } else { + $(document).find('.atfpp-alphabet-btn').removeClass('active'); + $btn.addClass('active'); + } + filterGlossaryRows(); + applyZebraStriping(); + }); + + // Handle Delete in glossary table + $(document).off('click', '.atfpp-delete-btn').on('click', '.atfpp-delete-btn', function(e) { + e.preventDefault(); + e.stopPropagation(); + + if (!confirm('Are you sure you want to delete this glossary entry?')) return; + + const $deleteBtn = $(this); + const $row = $deleteBtn.closest('tr'); + const term = $row.data('term'); + const source_lang = $row.data('original-language'); + + // Store original button content + const originalContent = $deleteBtn.html(); + + // Show loading state only on the delete button + $deleteBtn.addClass('atfpp-delete-loading'); + $deleteBtn.html('
    '); + $deleteBtn.prop('disabled', true); + + $.post(atfpp_glossary.ajaxurl, { + action: 'atfpp_delete_glossary', + _wpnonce: atfpp_glossary.nonce, + term: term, + source_lang: source_lang + }, function(resp) { + if (resp.success) { + $row.fadeOut(300, function() { + $(this).remove(); + + // After row removal, check if any rows are visible + var $visibleRows = $glossaryTable.find('tbody tr:visible'); + if ($visibleRows.length === 0) { + var $langBtns = $('.atfpp-lang-filter-btn'); + var $activeBtn = $langBtns.filter('.active'); + var idx = $langBtns.index($activeBtn); + + // Remove the filter button for the language with no data + $activeBtn.remove(); + $langBtns = $('.atfpp-lang-filter-btn'); // Refresh after removal + + // Disable and deactivate all alphabet buttons if no data + var $alphabetBtns = $('.atfpp-alphabet-btn'); + $alphabetBtns.removeClass('active').prop('disabled', true); + + // Hide language filter if only one button remains + if ($langBtns.length <= 1) { + $('.atfpp-language-filters').hide(); + } + + // After removal, $langBtns is refreshed + if ($langBtns.length > 0) { + // Try to activate the button at the same index (which is now the next one) + var newIdx = Math.min(idx, $langBtns.length - 1); + $langBtns.eq(newIdx).trigger('click'); + } else { + updateGlossaryTableVisibility(); + } + } else { + updateGlossaryTableVisibility(); + } + }); + } else { + // Reset button state on error + $deleteBtn.removeClass('atfpp-delete-loading'); + $deleteBtn.html(originalContent); + $deleteBtn.prop('disabled', false); + alert('Delete failed: ' + (resp.data || 'Unknown error')); + } + }).fail(function() { + // Reset button state on AJAX failure + $deleteBtn.removeClass('atfpp-delete-loading'); + $deleteBtn.html(originalContent); + $deleteBtn.prop('disabled', false); + alert('An error occurred while deleting. Please try again.'); + }); + }); + + // Show/hide translations section based on required fields + function toggleTranslationsSection($form) { + var $translations = $form.find('.atfpp-add-translations'); + var sourceLang = $form.find('.atfpp-add-source-lang').val() || ''; + var term = $form.find('.atfpp-add-term').val() || ''; + var type = $form.find('.atfpp-add-type').val() || ''; + + // Remove hidden class from all fields first + $translations.find('.atfpp-translation-field').removeClass('atfpp-hidden'); + + // Hide the translation field for the selected original language + if (sourceLang) { + $translations.find('.atfpp-translation-field-' + sourceLang).addClass('atfpp-hidden'); + } + + if ( + term.trim() !== '' && + type.trim() !== '' && + sourceLang.trim() !== '' + ) { + $translations.addClass('atfpp-show').css('display', 'flex'); + } else { + $translations.removeClass('atfpp-show').css('display', 'none'); + } + } + + // Attach event listeners to required fields + $addGlossaryForm.on('input', '.atfpp-add-term, .atfpp-add-type, .atfpp-add-source-lang', function() { + toggleTranslationsSection($addGlossaryForm); + }); + + // Ensure correct state on page load + $(function() { + toggleTranslationsSection($addGlossaryForm); + }); + + // Add input event handler for translation fields in add form + $addGlossaryForm.on('input', '.atfpp-add-translation', function() { + const $textarea = $(this); + const $error = $textarea.next('.atfpp-translation-error'); + const value = $textarea.val().trim(); + const maxLength = 240; + + if (value.length > maxLength) { + $textarea.addClass('error'); + $error.text(`Must be less than ${maxLength} characters`).show(); + } else { + $textarea.removeClass('error'); + $error.hide(); + } + }); + + // Add this helper function at the top of the file + function sanitizeInput(input) { + return input.replace(/[<>]/g, ''); // Remove < and > characters + } + + // Update the add glossary form submission handler + $addGlossaryForm.off('submit').on('submit', function(e) { + e.preventDefault(); + const $form = $(this); + + const $termField = $form.find('.atfpp-add-term'); + const $descField = $form.find('.atfpp-add-desc'); + const term = sanitizeInput($termField.val().trim()); + const desc = sanitizeInput($descField.val().trim()); + + let hasError = false; + + // Check for script tags or other potentially harmful content + if ($termField.val().trim() !== term || $descField.val().trim() !== desc) { + alert('Invalid input'); + hasError = true; + } + + // Check term and description length + if (term.length > 240) { + $termField.addClass('error'); + $termField.next('.atfpp-translation-error').text('Term must be less than 240 characters.').show(); + hasError = true; + } + + if (desc.length > 240) { + $descField.addClass('error'); + $descField.next('.atfpp-translation-error').text('Description must be less than 240 characters.').show(); + hasError = true; + } + + // Check translation lengths and sanitize + $form.find('.atfpp-add-translation').each(function() { + const $textarea = $(this); + const originalValue = $textarea.val().trim(); + const sanitizedValue = sanitizeInput(originalValue); + + if (originalValue !== sanitizedValue) { + $textarea.addClass('error'); + $textarea.next('.atfpp-translation-error').text('Invalid input').show(); + hasError = true; + } + + if (sanitizedValue.length > 240) { + $textarea.addClass('error'); + $textarea.next('.atfpp-translation-error').text('Translation must be less than 240 characters.').show(); + hasError = true; + } + }); + + if (hasError) { + return; + } + + // Check for duplicate in the table + var duplicate = false; + $('.atfpp-glossary-table tbody tr').each(function() { + var rowTerm = $(this).data('term'); + var rowLang = $(this).data('original-language'); + if (rowTerm && rowLang && rowTerm.trim().toLowerCase() === term.toLowerCase() && rowLang === $form.find('.atfpp-add-source-lang').val()) { + duplicate = true; + return false; // break loop + } + }); + if (duplicate) { + alert('This term already exists in this language.'); + return; + } + + if (!$('.atfpp-glossary-loader').length) { + const translations = $('#atfpp-add-glossary-form').find('.atfpp-add-translations') + translations.removeClass('atfpp-show').css('display', 'none'); + $('.atfpp-glossary-modal-content').append('
    '); + } + + const data = { + action: 'atfpp_add_glossary', + _wpnonce: atfpp_glossary.nonce, + type: $form.find('.atfpp-add-type').val(), + term: term, + description: desc, + source_lang: $form.find('.atfpp-add-source-lang').val(), + translations: {} + }; + (atfpp_glossary.atfpp_languages || []).forEach(function(lang) { + const langCode = lang.code; + if (langCode === data.source_lang) return; + const translationValue = sanitizeInput($form.find('[name="translation_' + langCode + '"]').val().trim()); + // Only include non-empty translations + if (translationValue !== '' && translationValue.trim() !== '') { + data.translations[langCode] = translationValue; + } + }); + + $.post(atfpp_glossary.ajaxurl, data, function(resp) { + $('.atfpp-glossary-loader').remove(); + if (resp.success) { + // Check if we have the added entry data from server + if (resp.data && resp.data.added_entry) { + $form.hide(); + $('#add-glossary-success').removeClass('atfpp-hidden'); + + // Use the data returned from the server + const addedEntry = resp.data.added_entry; + const savedTerm = addedEntry.original_term; + const savedDesc = addedEntry.description || ''; + const savedType = addedEntry.kind; + const savedSourceLang = addedEntry.original_language_code; + + // Create a map of saved translations - only include non-empty translations + const savedTranslations = {}; + if (addedEntry.translations && Array.isArray(addedEntry.translations)) { + addedEntry.translations.forEach(function(translation) { + if (translation.target_language_code && + translation.translated_term && + translation.translated_term.trim() !== '') { + savedTranslations[translation.target_language_code] = translation.translated_term.trim(); + } + }); + } + + // Determine the previous state before adding the new entry + const existingRows = $('.atfpp-glossary-table tbody tr'); + const uniqueLanguagesBeforeAdd = new Set(); + existingRows.each(function() { + const lang = $(this).data('original-language'); + if (lang) uniqueLanguagesBeforeAdd.add(lang); + }); + const wasSingleLanguageMode = uniqueLanguagesBeforeAdd.size <= 1; + + // Determine if we're in single language mode after adding (like PHP logic) + const uniqueLanguagesAfterAdd = new Set(uniqueLanguagesBeforeAdd); + uniqueLanguagesAfterAdd.add(savedSourceLang); + const singleLanguageMode = uniqueLanguagesAfterAdd.size === 1; + const singleLanguageCode = singleLanguageMode ? savedSourceLang : ''; + + // Initialize translationsHtml + let translationsHtml = ''; + + // Generate columns for languages, respecting single language mode + atfpp_glossary.atfpp_languages.forEach(lang => { + const langCode = lang.code; + + // Skip source language column if in single language mode + if (singleLanguageMode && langCode === singleLanguageCode) { + return; + } + + if (langCode === savedSourceLang) { + // Source language - show the original term (only when not in single language mode) + const safeTerm = escapeHtml(savedTerm); + translationsHtml += ` + + + ${safeTerm} + + + `; + } else { + // Translation languages - check if we have a saved translation + const savedTranslation = savedTranslations[langCode]; + + if (!savedTranslation || savedTranslation.trim() === '') { + // No translation - show add button + translationsHtml += ` + + + + `; + } else { + // Has translation - show it + const truncatedText = savedTranslation.length > 7 ? savedTranslation.substring(0, 7) + '…' : savedTranslation; + const safeTranslation = escapeHtml(savedTranslation); + const safeTruncated = escapeHtml(truncatedText); + translationsHtml += ` + + + ${safeTruncated} + + + `; + } + } + }); + + const firstLetter = savedTerm ? (isNaN(savedTerm[0]) ? savedTerm[0].toUpperCase() : '123') : ''; + const safeTerm = escapeHtml(savedTerm); + const safeDesc = escapeHtml(savedDesc); + const safeType = escapeHtml(savedType); + const safeSourceLang = escapeHtml(savedSourceLang); + + const newEntryHtml = ` + + +
    ${safeTerm}
    +
    ${safeDesc}
    + + + ${safeType.charAt(0).toUpperCase() + safeType.slice(1)} + + ${translationsHtml} + +
    + + +
    + + + `; + + // Only recreate headers when transitioning from single to multi-language mode OR when no table exists + const needsHeaderUpdate = wasSingleLanguageMode && !singleLanguageMode; + + // If the table does not exist, create it with headers + // OR if we're transitioning from single to multi-language mode, recreate headers + if ($('.atfpp-glossary-table').length === 0 || needsHeaderUpdate) { + + // Save existing table body if updating headers + let existingTbody = ''; + if (needsHeaderUpdate) { + existingTbody = $('.atfpp-glossary-table tbody').html(); + } + + let tableHeader = ` + + + + + ${atfpp_glossary.atfpp_languages.filter(lang => + !(singleLanguageMode && lang.code === singleLanguageCode) + ).map(lang => + `` + ).join('')} + + + + + + ${atfpp_glossary.atfpp_languages.filter(lang => + !(singleLanguageMode && lang.code === singleLanguageCode) + ).map(lang => + `` + ).join('')} + + + + +
    + ${lang.flag ? lang.flag : `${lang.alt}`} + +
    + + +
    +
    Glossary EntryType${lang.alt}Actions
    + `; + $('.atfpp-glossary-table-wrapper').html(tableHeader); + + // Apply current language filter column hiding to headers + const currentActiveLang = $('.atfpp-lang-filter-btn.active').data('lang'); + if (currentActiveLang) { + $('.atfpp-glossary-table').find(`th[data-lang="${currentActiveLang}"]`).hide(); + } + + // Restore existing table body if we were updating headers + if (needsHeaderUpdate && existingTbody) { + $('.atfpp-glossary-table tbody').html(existingTbody); + + // Need to update all existing rows to match new column structure + $('.atfpp-glossary-table tbody tr').each(function() { + const $row = $(this); + const rowOriginalLang = $row.data('original-language'); + const rowTerm = $row.data('term'); + + // Skip edit rows + if ($row.hasClass('atfpp-glossary-edit-row')) return; + + // Get existing translation data - EXCLUDE source terms to prevent mixing + const existingTranslations = {}; + $row.find('[class*="atfpp-lang-col-"]').each(function() { + const $cell = $(this); + const langMatch = $cell.attr('class').match(/atfpp-lang-col-([a-zA-Z_-]+)/); + if (langMatch && langMatch[1]) { + const langCode = langMatch[1]; + + // Check if this cell contains a source term - DON'T extract source terms as translations + const hasSourceTerm = $cell.find('.atfpp-source-term').length > 0; + const isSourceByData = $cell.data('is-source') === true || $cell.data('is-source') === 'true'; + + // Only get actual translations, NOT source terms + if (!hasSourceTerm && !isSourceByData) { + const $translatedTerm = $cell.find('.atfpp-translated-term'); + if ($translatedTerm.length) { + const translation = $translatedTerm.data('full-text') || $translatedTerm.text().trim(); + if (translation && translation.trim() !== '') { + existingTranslations[langCode] = translation.trim(); + } + } + } + } + }); + + // Rebuild row HTML with all language columns + let newTranslationsHtml = ''; + atfpp_glossary.atfpp_languages.forEach(lang => { + const langCode = lang.code; + + // Skip source language column if in single language mode + if (singleLanguageMode && langCode === singleLanguageCode) { + return; + } + + if (langCode === rowOriginalLang) { + // Source language - show original term + newTranslationsHtml += ` + + + ${escapeHtml(rowTerm)} + + + `; + } else { + // Translation language + const existingTranslation = existingTranslations[langCode]; + if (existingTranslation && existingTranslation.trim() !== '') { + // Has translation + const truncatedText = existingTranslation.length > 7 ? existingTranslation.substring(0, 7) + '…' : existingTranslation; + newTranslationsHtml += ` + + + ${escapeHtml(truncatedText)} + + + `; + } else { + // No translation - show add button + newTranslationsHtml += ` + + + + `; + } + } + }); + + // Update the row HTML + const $termCell = $row.find('td:first'); + const $typeCell = $row.find('td:nth-child(2)'); + const $actionsCell = $row.find('.atfpp-actions-cell'); + + $row.html(` + ${$termCell[0].outerHTML} + ${$typeCell[0].outerHTML} + ${newTranslationsHtml} + ${$actionsCell[0].outerHTML} + `); + }); + + // Apply current language filter column hiding to restored rows + const currentActiveLang = $('.atfpp-lang-filter-btn.active').data('lang'); + if (currentActiveLang) { + $('.atfpp-glossary-table tbody tr').each(function() { + $(this).find(`td[data-lang="${currentActiveLang}"][data-is-source="true"]`).hide(); + }); + } + } + } + + // Append the new entry to the glossary table + $('.atfpp-glossary-table tbody').append(newEntryHtml); + + // Apply current language filter column hiding to the new row + const currentActiveLang = $('.atfpp-lang-filter-btn.active').data('lang'); + if (currentActiveLang) { + const $newRow = $('.atfpp-glossary-table tbody tr:last'); + // Hide the source language column for the new row + $newRow.find(`td[data-lang="${currentActiveLang}"][data-is-source="true"]`).hide(); + } + + // Re-select the new table + $glossaryTable = $('.atfpp-glossary-table'); + + // Update the UI (show table, remove no-results, apply zebra striping, etc.) + $('.atfpp-glossary-table').show(); + $('.atfpp-glossary-table-wrapper').show(); + $('#atfpp-no-results').remove(); + updateGlossaryTableVisibility(); + applyZebraStriping(); + + // Update scroll button visibility + updateScrollButtonVisibility(); + + // Update language filter buttons, passing the saved language + updateLanguageFilterButtons(savedSourceLang); + + // After adding, check if there are now two or more unique original languages and show filter bar if needed + const $rows = $('.atfpp-glossary-table tbody tr'); + const uniqueLangs = {}; + $rows.each(function() { + const lang = $(this).data('original-language'); + if (lang) uniqueLangs[lang] = true; + }); + const langCodes = Object.keys(uniqueLangs); + const $filterBar = $('.atfpp-language-filters'); + + // Remember the currently active filter before recreating buttons + const currentlyActiveLang = $('.atfpp-lang-filter-btn.active').data('lang'); + + if (langCodes.length > 1) { + // If filter bar is empty, create buttons for all unique languages + if ($filterBar.length === 0) { + // Insert filter bar after controls if not present + $('
    ').insertAfter('.atfpp-alphabet'); + } + const $newFilterBar = $('.atfpp-language-filters'); + $newFilterBar.empty(); + langCodes.forEach(function(code, i) { + const langObj = (atfpp_glossary.atfpp_languages || []).find(l => l.code === code); + const label = langObj ? (langObj.alt + ' Terms') : (code + ' Terms'); + const flag = langObj && langObj.img ? `${langObj.alt} ` : ''; + + // Preserve the currently active filter, or default to the newly added language if no active filter + const shouldBeActive = currentlyActiveLang === code || (!currentlyActiveLang && code === savedSourceLang); + + $newFilterBar.append(` + + `); + }); + $newFilterBar.show(); + } else { + // If only one language, hide filter bar + $filterBar.empty().hide(); + } + + // Reapply the current language filter to ensure the new entry is visible + const activeLang = $('.atfpp-lang-filter-btn.active').data('lang'); + if (activeLang) { + // Trigger the language filter click to properly apply column hiding + $('.atfpp-lang-filter-btn.active').trigger('click'); + } + // Update alphabet buttons + updateAlphabetButtonStates(); + } else { + // Fallback: Success but no server data - use original method with request data + console.warn('Success but no server data returned, using fallback method'); + $form.hide(); + $('#add-glossary-success').removeClass('atfpp-hidden'); + // Could add fallback logic here if needed, or just show success + } + } else { + alert(resp.data?.message || resp.data || 'Unknown error'); + } + }); + }); + + // Function to update language filter buttons + function updateLanguageFilterButtons(originalLang) { + if (!originalLang) return; + + const $filters = $('.atfpp-language-filters'); + // Check if a button for this language already exists in the filter + if ($filters.find('.atfpp-lang-filter-btn[data-lang="' + originalLang + '"]').length === 0) { + // Find the language object for label and flag + let langObj = (atfpp_glossary.atfpp_languages || []).find(l => l.code === originalLang); + let label = langObj ? (langObj.alt + ' Terms') : (originalLang + ' Terms'); + let flag = langObj && langObj.img ? `${langObj.alt} ` : ''; + + $filters.append(` + + `); + } + + // After append, check total number of filter buttons + const totalBtns = $filters.find('.atfpp-lang-filter-btn').length; + if (totalBtns <= 1) { + // If only one, remove all (hide filter bar) + $filters.empty(); + } + } + + // Add close button handler for success message + $('#add-glossary-success').on('click', '#atfpp-glossary-success-close', function() { + $('#atfpp-glossary-modal-add').addClass('atfpp-hidden').removeClass('active'); + $('#atfpp-glossary-modal-add').find('form').show(); + $('#add-glossary-success').addClass('atfpp-hidden'); + }); + + // --- GLOSSARY SEARCH FUNCTIONALITY --- + $(document).on('input', '.atfpp-search', function() { + const search = $(this).val().toLowerCase().trim(); + + // First apply search filter + $glossaryTable.find('tbody tr').each(function() { + const $row = $(this); + const term = $row.find('.atfpp-entry-title').text().toLowerCase(); + const desc = $row.find('.atfpp-entry-desc').text().toLowerCase(); + const showBySearch = !search || term.indexOf(search) !== -1 || desc.indexOf(search) !== -1; + $row.toggle(showBySearch); + }); + + if (search !== '') { + $('.atfpp-glossary-table-wrapper').show(); + $('#atfpp-no-results').remove(); + }else{ + $('.atfpp-glossary-table-wrapper').show(); + $('#atfpp-no-results').remove(); + } + applyZebraStriping(); + filterGlossaryRows(); + // Update UI visibility + updateGlossaryTableVisibility(); + }); + + // --- GLOSSARY TYPE FILTER FUNCTIONALITY --- + $(document).on('change', '.atfpp-glossary-type', function() { + var selectedType = $(this).val(); + if (selectedType) { + $('.atfpp-glossary-table-wrapper').show(); + $('#atfpp-no-results').remove(); + } + filterGlossaryRows(); + applyZebraStriping(); + }); + + // Export Glossary Button - Fix double download issue + $(document).off('click', '.atfpp-export-btn').on('click', '.atfpp-export-btn', function(e) { + e.preventDefault(); + e.stopPropagation(); // Prevent event bubbling + + // Create a temporary link to trigger the download + var url = atfpp_glossary.ajaxurl + '?action=atfpp_export_glossary'; + var link = document.createElement('a'); + link.href = url; + link.download = 'glossary-export.csv'; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + }); + + + // Optionally, also close on "Cancel" in the modal + $(document).on('click', '.atfpp-glossary-modal-actions-left', function() { + $(this).closest('.atfpp-glossary-modal').addClass('atfpp-hidden').removeClass('active'); + }); + + // Add cancel edit handler + $glossaryTable.on('click', '.atfpp-cancel-edit-btn', function(e) { + e.preventDefault(); + e.stopPropagation(); + + const $editRow = $(this).closest('tr.atfpp-glossary-edit-row'); + const $originalRow = $editRow.prev('tr'); + + $originalRow.show(); + $editRow.remove(); + }); + + // Add this function after the existing functions + function updateActionsHeaderVisibility() { + const $tableWrapper = $('.atfpp-glossary-table-wrapper'); + const $table = $tableWrapper.find('.atfpp-glossary-table'); + const $actionsHeader = $('.atfpp-actions-header-btn').closest('th'); + + // Only proceed if the table exists + if ($table.length === 0 || !$table[0]) { + $actionsHeader.hide(); + return; + } + + // Check if table has horizontal scroll + const hasHorizontalScroll = $table[0].scrollWidth > $tableWrapper[0].clientWidth; + + // Show/hide actions header based on scroll + $actionsHeader.toggle(hasHorizontalScroll); + } + + // Initial check for actions header visibility + updateActionsHeaderVisibility(); + + // Update on window resize + $(window).on('resize', _.debounce(function() { + updateActionsHeaderVisibility(); + }, 250)); + + // Update after any content changes that might affect table width + const observer = new MutationObserver(_.debounce(function() { + updateActionsHeaderVisibility(); + }, 250)); + + // Observe the table wrapper for changes + const $tableWrapper = $('.atfpp-glossary-table-wrapper'); + if ($tableWrapper.length) { + observer.observe($tableWrapper[0], { + childList: true, + subtree: true, + attributes: true + }); + } + + // Function to update button visibility based on scroll position + function updateScrollButtonVisibility() { + const $wrapper = $('.atfpp-glossary-table-wrapper'); + + // Check if wrapper exists + if (!$wrapper.length) { + return; + } + + // Check if wrapper has content + if (!$wrapper[0]) { + return; + } + + const scrollLeft = $wrapper.scrollLeft(); + const scrollWidth = $wrapper[0].scrollWidth; + const clientWidth = $wrapper[0].clientWidth; + + // Hide left button if at the leftmost position + if (scrollLeft === 0) { + $('#atfpp-actions-header-btn-left').css('visibility', 'hidden'); + } else { + $('#atfpp-actions-header-btn-left').css('visibility', 'visible'); + } + + if (scrollLeft + clientWidth >= scrollWidth) { + $('#atfpp-actions-header-btn-right').css('visibility', 'hidden'); // Use hide() to remove from layout + } else { + $('#atfpp-actions-header-btn-right').css('visibility', 'visible'); // Use show() to display + } + + } + + // Call this function on page load + $(document).ready(function() { + updateScrollButtonVisibility(); + }); + + // Call this function after scrolling + $glossaryTable.on('scroll', function() { + updateScrollButtonVisibility(); + }); + + // Scroll table to the right when actions header button is clicked + $('.atfpp-glossary-table-wrapper').off('click', '#atfpp-actions-header-btn-right').on('click', '#atfpp-actions-header-btn-right', function(e) { + e.preventDefault(); + const $wrapper = $(this).closest('.atfpp-glossary-table-wrapper'); + const scrollAmount = 300; + $wrapper.animate({ + scrollLeft: $wrapper.scrollLeft() + scrollAmount + }, 400, updateScrollButtonVisibility); + }); + + // Scroll table to the left when actions header button is clicked + $('.atfpp-glossary-table-wrapper').off('click', '#atfpp-actions-header-btn-left').on('click', '#atfpp-actions-header-btn-left', function(e) { + e.preventDefault(); + const $wrapper = $(this).closest('.atfpp-glossary-table-wrapper'); + const scrollAmount = 300; + $wrapper.animate({ + scrollLeft: $wrapper.scrollLeft() - scrollAmount + }, 400, updateScrollButtonVisibility); + }); + + // Close handler for import success message + $(document).on('click', '.atfpp-import-close-btn', function() { + // Hide the import success UI + $('#atfpp-import-success-ui').addClass('atfpp-hidden'); + // Optionally, also close the modal + $('#atfpp-glossary-modal-import').addClass('atfpp-hidden'); + + window.location.reload(); + }); + + // Add input event handler for add form fields + $addGlossaryForm.on('input', '.atfpp-add-term, .atfpp-add-desc', function() { + const $textarea = $(this); + const $error = $textarea.next('.atfpp-translation-error'); + const value = $textarea.val().trim(); + const maxLength = 240; + + // Check for invalid characters (like script tags) + if (value.includes('<') || value.includes('>')) { + $textarea.addClass('error'); + $error.text('Invalid input').show(); + return; // Stop further validation for this field + } + + if (value.length > maxLength) { + $textarea.addClass('error'); + $error.text(`Must be less than ${maxLength} characters`).show(); + } else { + $textarea.removeClass('error'); + $error.hide(); + } + }); + +}); diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/js/atfpp-plugin-setting.js b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/js/atfpp-plugin-setting.js new file mode 100644 index 0000000..3ef169a --- /dev/null +++ b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/js/atfpp-plugin-setting.js @@ -0,0 +1,10 @@ +jQuery(document).ready(function($) { + $('tr.active[data-plugin*="autopoly-ai-translation-for-polylang-pro"]').each(function() { + var $currentRow = $(this); + var $nextUpdateRow = $currentRow.nextAll('tr.plugin-update-tr.active.atfpp-pro').first(); + + if ($nextUpdateRow.length > 0) { + $currentRow.addClass('update'); + } + }); + }); \ No newline at end of file diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/views/ai-translations.php b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/views/ai-translations.php new file mode 100644 index 0000000..63b9751 --- /dev/null +++ b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/views/ai-translations.php @@ -0,0 +1,58 @@ +
    +
    +
    +

    +
    +

    + +

    +
    + 'geminiai-logo.png', + 'alt' => 'Gemini AI', + 'title' => esc_html__('AI Translations', $text_domain), + 'description' => esc_html__('Leverage Gemini AI for seamless and context-aware translations.', $text_domain), + 'icon' => 'gemini-translate.png', + 'url' => 'https://docs.coolplugins.net/doc/translate-via-gemini-ai-polylang/?utm_source=atfp_plugin&utm_medium=inside&utm_campaign=docs&utm_content=ai_translations_gemini_pro' + ], + [ + 'logo' => 'openai-translate-logo.png', + 'alt' => 'OpenAI', + 'title' => esc_html__('AI Translations', $text_domain), + 'description' => esc_html__('Leverage OpenAI for seamless and context-aware translations.', $text_domain), + 'icon' => 'open-ai-translate.png', + 'url' => 'https://docs.coolplugins.net/doc/translate-via-open-ai-polylang/?utm_source=atfp_plugin&utm_medium=inside&utm_campaign=docs&utm_content=ai_translations_openai_pro' + ], + [ + 'logo' => 'chrome-built-in-ai-logo.png', + 'alt' => 'Chrome Built-in AI', + 'title' => esc_html__('Chrome Built-in AI', $text_domain), + 'description' => esc_html__('Utilize Chrome\'s built-in AI for seamless translation experience.', $text_domain), + 'icon' => 'chrome-ai-translate.png', + 'url' => 'https://docs.coolplugins.net/doc/chrome-ai-translation-polylang/?utm_source=atfp_plugin&utm_medium=inside&utm_campaign=docs&utm_content=ai_translations_chrome_pro' + ] + ]; + + foreach ($ai_translations as $translation) { + ?> +
    + +

    +

    +
    + + <?php echo esc_attr($translation['alt']); ?> + +
    +
    + +
    +
    +
    \ No newline at end of file diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/views/custom-fields.php b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/views/custom-fields.php new file mode 100644 index 0000000..00dfba5 --- /dev/null +++ b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/views/custom-fields.php @@ -0,0 +1,119 @@ +atfpp_render_custom_fields_page(); + $this->enqueue_editor_assets(); + } + } + + public function enqueue_editor_assets() { + wp_enqueue_script( 'atfp-datatable-script', ATFPP_URL . 'assets/js/dataTables.min.js', array(), ATFPP_V, true ); + wp_enqueue_script( 'atfp-datatable-style', ATFPP_URL . 'assets/js/dataTables.min.js', array(), ATFPP_V, true ); + wp_enqueue_style( 'atfp-editor-custom-fields', ATFPP_URL . 'assets/css/atfp-custom-data-table.css', array(), ATFPP_V ); + wp_enqueue_script( 'atfp-editor-custom-fields', ATFPP_URL . 'assets/js/atfp-custom-data-table.js', array('atfp-datatable-script'), ATFPP_V, true ); + + wp_localize_script( 'atfp-editor-custom-fields', 'atfpCustomTableDataObject', array( + 'admin_url' => esc_url(admin_url('admin-ajax.php')), + 'save_button_handler' => 'atfp_update_custom_fields_content', + 'save_button_nonce' => wp_create_nonce('atfp_save_custom_fields'), + 'save_button_enabled'=>true, + 'save_button_text'=>__('Save Fields', 'autopoly-ai-translation-for-polylang-pro'), + 'save_button_class'=>'atfp-save-custom-fields', + ) ); + } + + public function atfpp_render_custom_fields_page() { + $this->atfp_allowed_fields = ATFPP_Helper::get_instance()->get_allowed_custom_fields(); + $s_no = 1; + ?> +
    +

    +
    +

    +

    + +
    +
    + + +
    +
    + + +
    +
    +
    +
    + + + + + + + + + + + + get_all_meta_fields_table(); + ?> + +
    +
    +
    +
    + get_custom_fields_data(); + if($meta_fields && is_array($meta_fields)) { + $s_no = 1; + foreach($meta_fields as $meta_field => $value) { + $checked=isset($this->atfp_allowed_fields[$meta_field]) && !empty($this->atfp_allowed_fields[$meta_field]['status']) ? 'checked' : ''; + $status=isset($value['status']) && !empty($value['status']) ? $value['status'] : 'Unsupported'; + $value_type=isset($value['type']) && !empty($value['type']) ? $value['type'] : 'string'; + + echo ''; + echo '' . $s_no++ . ''; + echo '' . $meta_field . ''; + echo '' . $value_type . ''; + echo '' . $status . ''; + echo ''; + echo ''; + } + } + } + } +} + +ATFP_Custom_Fields::get_instance(); \ No newline at end of file diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/views/dashboard.php b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/views/dashboard.php new file mode 100644 index 0000000..988d176 --- /dev/null +++ b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/views/dashboard.php @@ -0,0 +1,59 @@ +
    + + + + + +
    +

    +
    + + $provider) { + ?> +
    +
    + + <?php echo esc_html($provider[0]); ?> + +
    +

    +
      + +
    • + +
    +
    + Docs + + Settings + +
    +
    + +
    +
    +
    + diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/views/footer.php b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/views/footer.php new file mode 100644 index 0000000..7d7f844 --- /dev/null +++ b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/views/footer.php @@ -0,0 +1,29 @@ +
    + +
    diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/views/glossary.php b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/views/glossary.php new file mode 100644 index 0000000..7e495aa --- /dev/null +++ b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/views/glossary.php @@ -0,0 +1,505 @@ + $pll_lang['slug'], + 'img' => $pll_lang['flag_url'], + 'alt' => $pll_lang['name'], + 'flag' => isset($pll_lang['flag']) ? $pll_lang['flag'] : '', + ]; + } +} +// Build a map for quick lookup by code +$language_map = []; +foreach ($languages as $lang) { + $language_map[$lang['code']] = $lang; +} + +$grouped_entries = []; + +foreach ($glossary_data as $entry) { + $term = $entry['original_term']; + $lang = $entry['original_language_code']; + $key = $term . '||' . $lang; // Composite key + + if (!isset($grouped_entries[$key])) { + $grouped_entries[$key] = [ + 'term' => $term, + 'original_language_code' => $lang, + 'desc' => $entry['description'], + 'type' => $entry['kind'], + 'type_label' => ucfirst($entry['kind']), + 'translations' => [], + ]; + } + + if (!empty($entry['translations']) && is_array($entry['translations'])) { + foreach ($entry['translations'] as $translation) { + if ( + $translation['target_language_code'] !== $lang && + !empty($translation['translated_term']) && + trim($translation['translated_term']) !== '' + ) { + $grouped_entries[$key]['translations'][$translation['target_language_code']] = trim($translation['translated_term']); + } + } + } +} + +// Sort glossary entries alphabetically by term (case-insensitive) +uksort($grouped_entries, 'strnatcasecmp'); + +// Collect unique original_language_codes and their counts +$language_codes = []; +foreach ($glossary_data as $entry) { + if (!empty($entry['original_language_code'])) { + $code = $entry['original_language_code']; + if (!isset($language_codes[$code])) { + $language_codes[$code] = 0; + } + $language_codes[$code]++; + } +} +$unique_language_codes = array_keys($language_codes); + +// Compute the unique original language code +$unique_original_language_code = ''; +if (count($unique_language_codes) === 1) { + $unique_original_language_code = reset($unique_language_codes); +} + +// Add this for default selected language (first in the list) +$default_selected_lang = $unique_language_codes[0] ?? ''; + +// --- ADD THIS BLOCK --- +$term_original_lang = []; +foreach ($glossary_data as $entry) { + $term = $entry['original_term']; + $lang = $entry['original_language_code']; + + // Initialize as array if not already + if (!isset($term_original_lang[$term])) { + $term_original_lang[$term] = []; + } + + // Add only if not already present + if (!in_array($lang, $term_original_lang[$term])) { + $term_original_lang[$term][] = $lang; + } +} +// --- END ADD --- + +// Count how many languages have entries +$language_codes_with_entries = []; +foreach ($grouped_entries as $entry) { + $code = $entry['original_language_code']; + if (!in_array($code, $language_codes_with_entries, true)) { + $language_codes_with_entries[] = $code; + } +} +$single_language_mode = count($language_codes_with_entries) === 1; +$single_language_code = $single_language_mode ? $language_codes_with_entries[0] : ''; +?> + + + +
    +
    + +
    +

    +

    +
      +
    • +
    • +
    • +
    + +
    + +
    + + + + + + + + + +
    +
    + +
    +

    +
    + + +
    + + + +
    + + + + + + +
    + +
    + +
    + +
    +
    + + +
    +
    +
    +
    + Success Icon +
    +
    + +
    + +
    +
    +
    +
    + + +
    +
    + +
    +
    +

    + + + + + +
    + +
    +
    +
    + Success Icon +
    +
    + + +
    +
    + +
    + +
    +
    +
    +
    +
    +
    + + + + + 1): ?> +
    + $code): ?> + + + +
    + + +
    + + + + + + + + + + + + + + + + + + + + + + + $limit) { + return mb_substr($str, 0, $limit, 'UTF-8') . '…'; + } + return $str; + } + } + + $editing_term = isset($_GET['edit']) ? $_GET['edit'] : null; + + foreach ($grouped_entries as $composite_key => $data): + list($term, $original_language_code) = explode('||', $composite_key); + $first = mb_substr(trim($term), 0, 1, 'UTF-8'); + $row_letter = is_numeric($first) ? '123' : + (preg_match('/[A-Za-z]/u', $first) ? strtoupper($first) : '#&à'); + ?> + + + + + + + + + + + + + + +
    + '; + ?> + +
    + + +
    +
    Glossary EntryType + + Actions
    +
    + +
    +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + +
    + + +
    +
    + +
    No glossary entries found.
    + +
    + + +
    +
    diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/views/license.php b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/views/license.php new file mode 100644 index 0000000..77251b7 --- /dev/null +++ b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/views/license.php @@ -0,0 +1,287 @@ + +
    +
    +
    +

    🔑

    +
    +
    +
    + + + +
    + + +
    + +
    + + + +
    + + +
    + +

    + +

    + + +
    +
    +
    +is_valid) || !isset($license_info->license_title) || !isset($license_info->expire_date)) { + wp_die(__('Error: Invalid license information', $text_domain)); + return; + } + + // Sanitize license key before masking + $license_key = sanitize_text_field(get_option('AIAutomaticTranslationsForPolylang_lic_Key', '')); + $masked_key = !empty($license_key) ? + esc_html(substr($license_key, 0, 8) . '-XXXXXXXX-XXXXXXXX-' . substr($license_key, -8)) : + ''; + + $admin_url = esc_url(admin_url('admin-post.php')); +?> +
    +
    +
    +

    🔒

    + + + + +
    +
      +
    • + + is_valid): ?> + + + + + + + + + + + + + + + + + + + + + +
    • +
    • license_title); ?>
    • +
    • + + expire_date) !== 'no expiry') { + $expire_date_timestamp = strtotime($license_info->expire_date); + $expire_date_expired = $expire_date_timestamp && $expire_date_timestamp < $current_time; + } + + // Handle "no support" case for support_end + if (strtolower($license_info->support_end) === 'no support') { + + esc_html_e('No Support', $text_domain); + + } else { + + // Handle "unlimited" case for support_end + $support_end_expired = false; + + if (strtolower($license_info->support_end) !== 'unlimited') { + + $support_end_timestamp = strtotime($license_info->support_end); + $support_end_expired = $support_end_timestamp && $support_end_timestamp < $current_time; + + } + if ($expire_date_expired) { + + echo esc_html(atfpp_pro_formatLicenseDate($license_info->expire_date)); + + } elseif ($support_end_expired) { + + echo esc_html(atfpp_pro_formatLicenseDate($license_info->support_end)); + + } else { + + echo esc_html(atfpp_pro_formatLicenseDate($license_info->expire_date)); + + } + + } + ?> + +
    • + +
    • +
    + +
    +

    +
    + + + +
    +
    + + +
    + +
    + + + +
    + +
    + + + + +
    +
    + +
    +

    + +
    + is_valid === 'license_expired'; +} + +function atfpp_is_support_expired($license_info) { + + return $license_info->support_end === 'no support' || + ($license_info->is_valid === 'support_expired' || + (strtolower($license_info->support_end) !== 'unlimited' && + strtotime($license_info->support_end) < time())); +} + +function atfpp_needs_refresh($license_info) { + + return atfpp_is_license_expired($license_info) || atfpp_is_support_expired($license_info); +} + + +function atfpp_render_expiry_message($license_info, $type = 'license') { + + $text_domain = 'autopoly-ai-translation-for-polylang-pro'; + + // Generate version available message using common helper + $version_available_message = AutoPolyPro::atfppGetVersionAvailableMessage(); + + if ($license_info->msg === 'limit_reached') { + $support_link = sprintf('%s', esc_url('https://my.coolplugins.net/account/support-tickets/'), esc_html__('clicking here', 'atfpp')); + echo wp_kses_post(sprintf( + /* translators: %s: link to support ticket page */ + __('There was an issue with your account. Please contact our plugin support team by %s.', 'atfpp'), + $support_link + )); + return; + } + + $message = $type === 'license' + ? __('Your license has expired,', 'atfpp') + : __('Your support has expired,', 'atfpp'); + + $renew_link = isset($license_info->market) && $license_info->market === 'E' + ? '' + : ' '.esc_html__('Renew now', 'atfpp').''; + + $final_message = ''; + + // Add version message if available + if (!empty($version_available_message)) { + + wp_enqueue_script('thickbox'); + wp_enqueue_style('thickbox'); + + $final_message .= wp_kses_post($version_available_message) . ' '; + } + + // Add license expiry message + $final_message .= esc_html($message) . $renew_link . esc_html__(' to continue receiving updates and priority support.', 'atfpp'); + + echo $final_message; +} + +function atfpp_pro_formatLicenseDate($dateString) { + +if (!empty($dateString) && strtolower($dateString) !== 'no expiry') { + + $date = new DateTime($dateString); + return $date->format('d M Y'); + } + return $dateString; +} + diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/views/settings.php b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/views/settings.php new file mode 100644 index 0000000..944f126 --- /dev/null +++ b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/views/settings.php @@ -0,0 +1,1037 @@ + [ + 'name' => 'Gemini', + 'docs_url' => 'https://docs.coolplugins.net/doc/generate-gemini-api-key/?utm_source=atfp_plugin&utm_medium=inside&utm_campaign=docs&utm_content=settings_gemini_pro' + ], + 'openai' => [ + 'name' => 'OpenAI', + 'docs_url' => 'https://docs.coolplugins.net/doc/generate-open-ai-api-key/?utm_source=atfp_plugin&utm_medium=inside&utm_campaign=docs&utm_content=settings_openai_pro' + ], + 'deepl' => [ + 'name' => 'DeepL', + 'docs_url' => 'https://locoaddon.com/docs/how-to-generate-deepl-api-key/' + ], + 'context_aware' => [ + 'name' => 'Context Aware', + 'option_key' => 'atfp_context_aware', + ], + 'atfpp_bulk_post_status' => [ + 'name' => 'Bulk Translation default Post Status', + 'option_key' => 'atfp_bulk_post_status', + 'options' => [ + 'publish' => 'Published', + 'draft' => 'Draft', + ], + 'value' => get_option('atfp_bulk_post_status', 'draft') + ], + 'atfp_slug_translation_option' => [ + 'name' => 'Slug Translation Settings', + 'option_key' => 'atfp_slug_translation_option', + 'options' => [ + 'title_translate' => 'Use Translated Title', + 'slug_translate' => 'Translate Original Slug', + 'slug_keep' => 'Keep Original Slug', + ], + 'value' => get_option('atfp_slug_translation_option', 'title_translate') + ], + 'atfp_ai_request_token_per_request' => [ + 'name' => 'AI Token Per Request', + 'option_key' => 'atfp_ai_request_token_per_request', + 'value' => get_option('atfp_ai_request_token_per_request', 500) + ], + 'atfp_ai_request_batch_size' => [ + 'name' => 'AI Batch Size', + 'option_key' => 'atfp_ai_request_batch_size', + 'value' => get_option('atfp_ai_request_batch_size', 5) + ], + 'atfp_ai_request_timeout' => [ + 'name' => 'AI Timeout', + 'option_key' => 'atfp_ai_request_timeout', + 'value' => get_option('atfp_ai_request_timeout', 120) + ] + ]; + ?> +
    +
    +
    +

    +
    +

    + ' . esc_html__('license key', $text_domain) . '') ?> +

    +
    +
    + $api): + if ($key === 'context_aware'): ?> + + + + +
    + $option_value): + ?> + disabled> + + +
    + + +
    + $option_value): + ?> + disabled> + + +
    + +
    +
    +

    +

    +
    + +
    + +

    ', ''); ?>

    +
    +
    + +
    + +
    + +

    ', ''); ?>

    +
    +
    + +
    + +
    + +

    ', ''); ?>

    +
    +
    +
    +
    + + +
    + +
    + ' . esc_html__('Click Here', $text_domain) . '', + esc_html($api['name']) + ); + endif; + endforeach; ?> + +
    + +
    +
    +
    +
    +
    +

    Rate Limits of Free Gemini API Key

    +
      +
    • 15 RPM: This API Key allows a maximum of 15 requests per minute
    • +
    • 1 million TPM: With this API Key, you can process up to 1 million tokens per minute
    • +
    • 1,500 RPD: To ensure smooth performance, it allows up to 1,500 requests per day
    • +
    +
    +
    + [ + 'name' => 'Gemini', + 'option_key' => 'Atfpp_Ai_Translate_google_api_key', + 'docs_url' => 'https://docs.coolplugins.net/doc/generate-gemini-api-key/?utm_source=atfp_plugin&utm_medium=inside&utm_campaign=docs&utm_content=settings_gemini_pro', + 'value' => get_option('Atfpp_Ai_Translate_google_api_key', '') + ], + 'openai' => [ + 'name' => 'OpenAI', + 'option_key' => 'Atfpp_Ai_Translate_openai_api_key', + 'docs_url' => 'https://docs.coolplugins.net/doc/generate-open-ai-api-key/?utm_source=atfp_plugin&utm_medium=inside&utm_campaign=docs&utm_content=settings_openai_pro', + 'value' => get_option('Atfpp_Ai_Translate_openai_api_key', '') + ], + // 'openrouter' => [ + // 'name' => 'Openrouter', + // 'option_key' => 'Atfpp_Ai_Translate_openrouter_api_key', + // 'docs_url' => 'https://locoaddon.com/docs/how-to-generate-open-api-key/', + // 'value' => get_option('Atfpp_Ai_Translate_openrouter_api_key', '') + // ], + 'deepl' => [ + 'name' => 'DeepL', + 'option_key' => 'Atfpp_Ai_Translate_deepl_api_key', + 'docs_url' => 'https://locoaddon.com/docs/how-to-generate-deepl-api-key/', + 'value' => get_option('Atfpp_Ai_Translate_deepl_api_key', '') + ], + 'context_aware' => [ + 'name' => 'Context Aware', + 'option_key' => 'atfp_context_aware', + 'value' => get_option('atfp_context_aware', '') + ], + 'atfpp_bulk_post_status' => [ + 'name' => 'Bulk Translation default Post Status', + 'option_key' => 'atfp_bulk_post_status', + 'value' => get_option('atfp_bulk_post_status', 'draft'), + 'options' => [ + 'publish' => 'Published', + 'draft' => 'Draft', + ] + ], + 'atfp_slug_translation_option' => [ + 'name' => 'Slug Translation Settings', + 'option_key' => 'atfp_slug_translation_option', + 'value' => get_option('atfp_slug_translation_option', 'title_translate'), + 'options' => [ + 'title_translate' => 'Use Translated Title', + 'slug_translate' => 'Translate Original Slug', + 'slug_keep' => 'Keep Original Slug', + ] + ], + 'atfp_ai_request_token_per_request' => [ + 'name' => 'Token Limit', + 'option_key' => 'atfp_ai_request_token_per_request', + 'value' => get_option('atfp_ai_request_token_per_request', 500) + ], + 'atfp_ai_request_batch_size' => [ + 'name' => 'Batch Size', + 'option_key' => 'atfp_ai_request_batch_size', + 'value' => get_option('atfp_ai_request_batch_size', 5) + ], + 'atfp_ai_request_timeout' => [ + 'name' => 'Timeout Duration', + 'option_key' => 'atfp_ai_request_timeout', + 'value' => get_option('atfp_ai_request_timeout', 120) + ] + ]; +} + +function atfpp_check_form_submission() { + return isset($_POST['nonce']) && + wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'api_keys') && + current_user_can('manage_options') && + $_SERVER['REQUEST_METHOD'] === 'POST'; +} + +function atfpp_validate_google_api_key($key) { + if (empty($key)) return false; + + if (!preg_match('/^AIza[0-9A-Za-z\-_]{35}$/', $key)) { + atfpp_show_admin_notice('error', 'Invalid Gemini API Key.'); + return false; + } + + $response = wp_remote_get( + 'https://generativelanguage.googleapis.com/v1beta/models?key=' . $key, + [ + 'headers' => ['Content-Type' => 'application/json'], + 'timeout' => 30, + ] + ); + + if (is_wp_error($response)) { + atfpp_show_admin_notice('error', 'API request failed: ' . $response->get_error_message()); + return false; + } + + $body = json_decode(wp_remote_retrieve_body($response), true); + + if (!isset($body['models']) || empty($body['models'])) { + atfpp_show_admin_notice('error', 'Invalid or unauthorized Gemini API Key.'); + return false; + } + + $text_models = []; + foreach ($body['models'] as $model) { + if ( + empty($model['name']) || + empty($model['supportedGenerationMethods']) || + !is_array($model['supportedGenerationMethods']) || + !in_array('generateContent', $model['supportedGenerationMethods'], true) + ) { + continue; + } + + $model_name = $model['name']; + + if ( + (isset($model['state']) && $model['state'] !== 'ACTIVE') || + preg_match('/(tts|image|vision)/i', $model_name) + ) { + continue; + } + + $clean_name = str_replace('models/', '', $model_name); + + $text_models[] = $clean_name; + } + + update_option('atfpp_google_models', $text_models); + + return true; +} + +function atfpp_validate_openai_api_key( $key ) { + if (empty($key)) return false; + + $response = wp_remote_get('https://api.openai.com/v1/models', [ + 'headers' => [ + 'Authorization' => 'Bearer ' . $key, + ], + ]); + + if (is_wp_error($response)) { + atfpp_show_admin_notice('error', 'Unable to connect to OpenAI API.'); + return false; + } + + $response_data = json_decode(wp_remote_retrieve_body($response), true); + + if (!empty($response_data['error'])) { + $error_message = $response_data['error']['message'] ?? 'Invalid OpenAI API Key.'; + atfpp_show_admin_notice('error', str_replace('request('GET', $url, [ + 'headers' => [ + 'Authorization' => 'DeepL-Auth-Key ' . $key, + ], + ]); + + $statusCode = $response->getStatusCode(); + $reason = $response->getReasonPhrase(); // short HTTP reason like "Unauthorized" + + + if($statusCode === 200){ + return true; + }else{ + atfpp_show_admin_notice('error', str_replace('hasResponse()) { + // Extract error details from response body + $errorBody = (string) $e->getResponse()->getBody(); + + // Decode JSON response to array + $errorData = json_decode($errorBody, true); + + // Get error message if available + $errorMessage = $errorData['message'] ?? $errorBody; + } else { + // Use exception message if no response body is available + $errorMessage = $e->getMessage(); + } + + atfpp_show_admin_notice('error', str_replace(' [ + 'Content-Type' => 'application/json', + 'Authorization' => 'Bearer ' . $key, + 'HTTP-Referer' => site_url(), + 'X-Title' => 'Polylang Addon', + 'X-Organization-ID' => site_url(), + ], + 'body' => wp_json_encode([ + 'model' => 'meta-llama/llama-4-maverick:free', + 'messages' => [ + [ + 'role' => 'user', + 'content' => [ + [ + 'type' => 'text', + 'text' => 'Test message' + ] + ] + ] + ] + ]), + 'method' => 'POST', + 'timeout' => 60, + ]); + + if (is_wp_error($response)) { + atfpp_show_admin_notice('error', 'Invalid OpenRouter API Key.'); + return false; + } + + $response_data = json_decode(wp_remote_retrieve_body($response), true); + if (!empty($response_data['error'])) { + $error_message = $response_data['error']['message'] ?? 'Invalid OpenRouter API Key.'; + atfpp_show_admin_notice('error', str_replace(' ['href' => [], 'target' => [], 'rel' => []]]) )); + return false; + } + + return true; +} + +function atfpp_show_admin_notice($type, $message) { + if (!isset($GLOBALS['atfpp_admin_notices'])) { + $GLOBALS['atfpp_admin_notices'] = array(); + } + $GLOBALS['atfpp_admin_notices'][] = wp_kses('

    ' . wp_kses($message, ['a' => ['href' => [], 'target' => []]]) . '

    ', ['div' => ['class' => true], 'p' => [], 'a' => ['href' => [], 'target' => []]]); +} + +function atfpp_handle_api_key_submission() { + if ( ! current_user_can('manage_options') ) { + atfpp_show_admin_notice('error', 'You are not allowed to perform this action.'); + return false; + } + // Clear any existing notices at the start + $GLOBALS['atfpp_admin_notices'] = array(); + + if (isset($_POST['reset_google_api_key'])) { + delete_option('Atfpp_Ai_Translate_google_api_key'); + delete_option('atfpp_google_models'); + delete_option('atfpp_selected_google_model'); + atfpp_show_admin_notice('success', 'Gemini API Key has been removed.'); + return true; + } + + if (isset($_POST['reset_openai_api_key'])) { + delete_option('Atfpp_Ai_Translate_openai_api_key'); + delete_option('atfpp_openai_models'); + delete_option('atfpp_selected_openai_model'); + atfpp_show_admin_notice('success', 'OpenAI API Key has been removed.'); + return true; + } + + if (isset($_POST['reset_openrouter_api_key'])) { + delete_option('Atfpp_Ai_Translate_openrouter_api_key'); + atfpp_show_admin_notice('success', 'OpenRouter API Key has been removed.'); + return true; + } + + if (isset($_POST['reset_deepl_api_key'])) { + delete_option('atfp_deepl_api_key_type'); + delete_option('Atfpp_Ai_Translate_deepl_api_key'); + atfpp_show_admin_notice('success', 'DeepL API Key has been removed.'); + return true; + } + + if (isset($_POST['submit_api_keys'])) { + return atfpp_handle_api_key_save(); + } + + return false; +} + +function atfpp_handle_api_key_save() { + if ( ! current_user_can('manage_options') ) { + atfpp_show_admin_notice('error', 'You are not allowed to perform this action.'); + return false; + } + $success = false; + $any_validation_attempted = false; + $has_error = false; + + // Handle Context Aware textarea + if (isset($_POST['atfp_context_aware'])) { + $context_aware = sanitize_textarea_field(wp_unslash($_POST['atfp_context_aware'])); + update_option('atfp_context_aware', $context_aware); + // Store the new value in a global variable to use in the render function + $GLOBALS['current_context_aware'] = $context_aware; + $success = true; + } + + $current_openai_model = get_option('atfpp_selected_openai_model', ''); + $current_google_model = get_option('atfpp_selected_google_model', ''); + + // Save selected OpenAI model if set and validate + if (isset($_POST['atfpp_selected_openai_model']) && $_POST['atfpp_selected_openai_model'] !== $current_openai_model) { + + $selected_model = sanitize_text_field($_POST['atfpp_selected_openai_model']); + $openai_key = get_option('Atfpp_Ai_Translate_openai_api_key', ''); + $is_valid = false; + $error_message = ''; + + if ($selected_model === '') { + update_option('atfpp_selected_openai_model', ''); + atfpp_show_admin_notice('success', 'OpenAI model selection has been cleared.'); + } else { + if ($openai_key && $selected_model) { + $response = wp_remote_post('https://api.openai.com/v1/chat/completions', [ + 'headers' => [ + 'Content-Type' => 'application/json', + 'Authorization' => 'Bearer ' . $openai_key, + ], + 'body' => wp_json_encode([ + 'model' => $selected_model, + 'messages' => [['role' => 'user', 'content' => 'Test']], + 'max_completion_tokens' => 20 + ]), + 'timeout' => 20, + ]); + + if (is_wp_error($response)) { + $error_message = $response->get_error_message(); + } else { + $body = json_decode(wp_remote_retrieve_body($response), true); + if (empty($body['error'])) { + $is_valid = true; + } else { + $error_message = $body['error']['message'] ?? 'Unknown error from OpenAI.'; + } + } + } + + if ($is_valid) { + update_option('atfpp_selected_openai_model', $selected_model); + atfpp_show_admin_notice('success', 'The OpenAI model has been successfully validated and saved.'); + } else { + atfpp_show_admin_notice('error', 'OpenAI API Error: ' . esc_html($error_message)); + } + } + } + + + if (isset($_POST['atfpp_selected_google_model']) && $_POST['atfpp_selected_google_model'] !== $current_google_model) { + + $selected_model = sanitize_text_field($_POST['atfpp_selected_google_model']); + $google_key = get_option('Atfpp_Ai_Translate_google_api_key', ''); + $is_valid = false; + $error_message = ''; + + if ($selected_model === '') { + delete_option('atfpp_selected_google_model'); + atfpp_show_admin_notice('success', 'The Google Gemini model selection has been cleared.'); + } else { + if ($google_key && $selected_model) { + $response = wp_remote_post( + 'https://generativelanguage.googleapis.com/v1beta/models/' . $selected_model . ':generateContent?key=' . $google_key, + [ + 'headers' => ['Content-Type' => 'application/json'], + 'body' => json_encode([ + 'contents' => [[ 'parts' => [['text' => 'Test']] ]] + ]), + 'timeout' => 60, + ] + ); + + if (!is_wp_error($response)) { + $body = json_decode(wp_remote_retrieve_body($response), true); + if (empty($body['error'])) { + $is_valid = true; + } else if (!empty($body['error']['message'])) { + $error_message = $body['error']['message']; + } + } else { + $error_message = $response->get_error_message(); + } + } + + if ($is_valid) { + update_option('atfpp_selected_google_model', $selected_model); + atfpp_show_admin_notice('success', 'The Gemini model has been successfully validated and saved.'); + } else { + $notice = $error_message ? $error_message : 'The selected Gemini model is not valid or not accessible with your API key.'; + atfpp_show_admin_notice('error', $notice); + } + } + } + + // Handle feedback checkbox + $feedback_opt_in = null; + if (get_option('cpfm_opt_in_choice_cool_translations')) { + $feedback_opt_in = isset($_POST['atfpp-dashboard-feedback-checkbox']) ? 'yes' : 'no'; + update_option('atfp_feedback_opt_in', $feedback_opt_in); + } + + // If user opted out, remove the cron job + if ($feedback_opt_in === 'no' && wp_next_scheduled('atfpp_extra_data_update')) { + wp_clear_scheduled_hook('atfpp_extra_data_update'); + } + + if ($feedback_opt_in === 'yes' && !wp_next_scheduled('atfpp_extra_data_update')) { + + wp_schedule_event(time(), 'every_30_days', 'atfpp_extra_data_update'); + + } + + if (isset($_POST['atfp_bulk_post_status'])) { + $bulk_post_status = sanitize_text_field($_POST['atfp_bulk_post_status']); + update_option('atfp_bulk_post_status', $bulk_post_status); + $success = true; + } + + if (isset($_POST['atfp_slug_translation_option'])) { + $slug_translation_option = sanitize_text_field($_POST['atfp_slug_translation_option']); + update_option('atfp_slug_translation_option', $slug_translation_option); + $success = true; + } + + if (isset($_POST['atfp_ai_request_token_per_request'])) { + $ai_token_per_request = sanitize_text_field($_POST['atfp_ai_request_token_per_request']); + + if($ai_token_per_request < 100 || $ai_token_per_request > 10000 || empty($ai_token_per_request) ) { + atfpp_show_admin_notice('error', 'AI Token Per Request must be between 100 and 10000.'); + return false; + } + + update_option('atfp_ai_request_token_per_request', $ai_token_per_request); + $success = true; + } + + if (isset($_POST['atfp_ai_request_batch_size'])) { + $ai_batch_size = sanitize_text_field($_POST['atfp_ai_request_batch_size']); + + if($ai_batch_size < 1 || $ai_batch_size > 10 || empty($ai_batch_size) ) { + atfpp_show_admin_notice('error', 'AI Batch Size must be between 1 and 10.'); + return false; + } + + update_option('atfp_ai_request_batch_size', $ai_batch_size); + $success = true; + } + + if (isset($_POST['atfp_ai_request_timeout'])) { + $ai_timeout = sanitize_text_field($_POST['atfp_ai_request_timeout']); + + if($ai_timeout < 10 || $ai_timeout > 1200 || empty($ai_timeout) ) { + atfpp_show_admin_notice('error', 'AI Timeout must be between 10 and 1200.'); + return false; + } + + update_option('atfp_ai_request_timeout', $ai_timeout); + $success = true; + } + + // Validate all API keys first before saving any + $valid_keys = []; + + if (!empty($_POST['Atfpp_Ai_Translate_google_api_key'])) { + $new_google_key = sanitize_text_field($_POST['Atfpp_Ai_Translate_google_api_key']); + $any_validation_attempted = true; + if (atfpp_validate_google_api_key($new_google_key)) { + $valid_keys['google'] = $new_google_key; + } else { + $has_error = true; + } + } + + if (!empty($_POST['Atfpp_Ai_Translate_openai_api_key'])) { + $new_openai_key = sanitize_text_field($_POST['Atfpp_Ai_Translate_openai_api_key']); + $any_validation_attempted = true; + if (atfpp_validate_openai_api_key($new_openai_key)) { + $valid_keys['openai'] = $new_openai_key; + } else { + $has_error = true; + } + } + + if (!empty($_POST['Atfpp_Ai_Translate_deepl_api_key'])) { + $new_deepl_key = sanitize_text_field($_POST['Atfpp_Ai_Translate_deepl_api_key']); + $any_validation_attempted = true; + if (atfpp_validate_deepl_api_key($new_deepl_key)) { + $valid_keys['deepl'] = $new_deepl_key; + } else { + $has_error = true; + } + } + + if (!empty($_POST['Atfpp_Ai_Translate_openrouter_api_key'])) { + $new_openrouter_key = sanitize_text_field($_POST['Atfpp_Ai_Translate_openrouter_api_key']); + $any_validation_attempted = true; + if (atfpp_validate_openrouter_api_key($new_openrouter_key)) { + $valid_keys['openrouter'] = $new_openrouter_key; + } else { + $has_error = true; + } + } + + // Only save and show success if there were no errors + if (!$has_error && !empty($valid_keys)) { + foreach ($valid_keys as $key => $value) { + update_option("Atfpp_Ai_Translate_{$key}_api_key", $value); + } + atfpp_show_admin_notice('success', 'Settings saved successfully.'); + return true; + } elseif ($any_validation_attempted && !isset($GLOBALS['atfpp_admin_notices'])) { + atfpp_show_admin_notice('error', 'Please enter valid values.'); + } + + return false; +} + +function atfpp_render_settings_page_html($apis, $text_domain) { + // Process form submission before rendering + $form_processed = false; + if (atfpp_check_form_submission()) { + $form_processed = atfpp_handle_api_key_submission(); + } + + // Refresh API values after form processing + if ($form_processed) { + $apis = atfpp_get_api_configurations(); + } + + // Update the context aware value if it was just saved + if (isset($GLOBALS['current_context_aware'])) { + $apis['context_aware']['value'] = $GLOBALS['current_context_aware']; + } + + if(isset($apis['atfpp_bulk_post_status']['value'])){ + $apis['atfpp_bulk_post_status']['value'] = get_option('atfp_bulk_post_status', 'draft'); + } + + if(isset($apis['atfp_slug_translation_option']['value'])){ + $apis['atfp_slug_translation_option']['value'] = get_option('atfp_slug_translation_option', 'title_translate'); + } + + if(isset($apis['atfp_ai_request_token_per_request']['value'])){ + $apis['atfp_ai_request_token_per_request']['value'] = get_option('atfp_ai_request_token_per_request', 500); + } + + if(isset($apis['atfp_ai_request_batch_size']['value'])){ + $apis['atfp_ai_request_batch_size']['value'] = get_option('atfp_ai_request_batch_size', 5); + } + + if(isset($apis['atfp_ai_request_timeout']['value'])){ + $apis['atfp_ai_request_timeout']['value'] = get_option('atfp_ai_request_timeout', 120); + } + + $openai_models = get_option('atfpp_openai_models', []); + $current_openai_model = get_option('atfpp_selected_openai_model', ''); + $google_models = get_option('atfpp_google_models', []); + $current_google_model = get_option('atfpp_selected_google_model', ''); + ?> +
    + +
    +

    Rate Limits of Free Gemini API Key

    +
      +
    • 15 RPM: This API Key allows a maximum of 15 requests per minute
    • +
    • 1 million TPM: With this API Key, you can process up to 1 million tokens per minute
    • +
    • 1,500 RPD: To ensure smooth performance, it allows up to 1,500 requests per day
    • +
    +
    +
    + +
    +
    +

    +
    + 'Google', 'yandex'=>'Yandex', 'localAiTranslator'=>'Chrome AI Translator', 'google_ai'=>'Gemini', 'openai_ai'=>'OpenAI', 'deepl_ai'=>'DeepL'); + + if (!is_array($all_data) || !isset($all_data['atfp'])) { + + $all_data['atfp'] = []; // Ensure $all_data['atfp'] is an array + + } + + $totals = array_reduce($all_data['atfp'] ?? [], function($carry, $translation) use (&$service_providers, $avilable_service_providers) { + // Ensure $translation['string_count'] is numeric + // Ensure all values are properly handled + $carry['string_count'] += intval($translation['string_count'] ?? 0); + $carry['character_count'] += intval($translation['character_count'] ?? 0); + $carry['time_taken'] += intval($translation['time_taken'] ?? 0); + + if(isset($translation['service_provider']) && !empty($translation['service_provider']) && !in_array($translation['service_provider'], $service_providers) && in_array($translation['service_provider'], array_keys($avilable_service_providers))){ + $service_providers[] = $translation['service_provider']; + } + // Count total translations instead of unique post IDs + if (!empty($translation['post_id'])) { + $carry['translation_count']++; + } + return $carry; + }, ['string_count' => 0, 'character_count' => 0, 'time_taken' => 0, 'translation_count' => 0]); + + // Update the time taken string using the new function + $time_taken_str = atfp_format_time_taken($totals['time_taken'] ,$text_domain); + ?> + + +
    +
      +
    • +
    • +
    • + 0): ?> +
    • + +
    • + +
    +
    +
    +

    +
    +
    + + + + + + + +
    +
    + <?php echo esc_html__('TranslatePress Addon', $text_domain); ?> +
    +
    +
    +
    +

    +

    + +
    +
    + + [ + 'free' => 'automatic-translator-addon-for-loco-translate/automatic-translator-addon-for-loco-translate.php', + 'pro' => 'loco-automatic-translate-addon-pro/loco-automatic-translate-addon-pro.php', + 'free_name' => esc_html__('LocoAI – Auto Translate For Loco Translate', $text_domain), + 'pro_name' => esc_html__('LocoAI – Auto Translate for Loco Translate (Pro)', $text_domain), + ], + ]; + + // Check if the provided plugin slug exists + if (!isset($plugin_paths[$plugin_slug])) { + return $plugin_slug['free_name']; + } + + $free_installed = isset($plugins[$plugin_paths[$plugin_slug]['free']]); + $pro_installed = isset($plugins[$plugin_paths[$plugin_slug]['pro']]); + + // Determine which version is installed + if ($pro_installed) { + return $plugin_paths[$plugin_slug]['pro_name']; + } elseif ($free_installed) { + return $plugin_paths[$plugin_slug]['free_name']; + } else { + return $plugin_paths[$plugin_slug]['free_name']; + } +} + +function atfp_format_number($number, $text_domain) { + if ($number >= 1000000000) { + return round($number / 1000000000, 1) . esc_html__('B', $text_domain); + } elseif ($number >= 1000000) { + return round($number / 1000000, 1) . esc_html__('M', $text_domain); + } elseif ($number >= 1000) { + return round($number / 1000, 1) . esc_html__('K', $text_domain); + } + return $number; +} + diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/views/support-blocks.php b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/views/support-blocks.php new file mode 100644 index 0000000..b60eca9 --- /dev/null +++ b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-dashboard/views/support-blocks.php @@ -0,0 +1,213 @@ +atfpp_render_support_blocks_page(); + $this->enqueue_editor_assets(); + } + } + + /** + * Enqueue editor CSS for the supported blocks page. + */ + public function enqueue_editor_assets( ) { + wp_enqueue_script( 'atfp-datatable-script', ATFPP_URL . 'assets/js/dataTables.min.js', array(), ATFPP_V, true ); + wp_enqueue_script( 'atfp-datatable-style', ATFPP_URL . 'assets/js/dataTables.min.js', array(), ATFPP_V, true ); + wp_enqueue_style( 'atfp-custom-data-table', ATFPP_URL . 'assets/css/atfp-custom-data-table.min.css', array(), ATFPP_V ); + wp_enqueue_script( 'atfp-custom-data-table', ATFPP_URL . 'assets/js/atfp-custom-data-table.min.js', array('atfp-datatable-script'), ATFPP_V, true ); + } + /** + * Add submenu page under the Polylang menu. + */ + public function atfpp_add_submenu_page() { + add_submenu_page( + 'mlang', // Parent slug + __( 'Support Blocks', 'autopoly-ai-translation-for-polylang-pro' ), // Page title + __( '↳ Support Blocks', 'autopoly-ai-translation-for-polylang-pro' ), // Menu title + 'manage_options', // Capability + 'atfp-supported-blocks', // Menu slug + array( $this, 'atfpp_render_support_blocks_page' ) // Callback function + ); + } + + /** + * Render the support blocks page. + */ + public function atfpp_render_support_blocks_page() { + ?> +
    +

    +
    +

    +

    +
    +
    + + +
    +
    + + +
    +
    +
    +
    + + + + + + + + + + + + atfpp_get_supported_blocks_table() + ?> + +
    +
    +
    +
    + get_all_registered(); + + $filter_blocks_data = array_filter( $blocks_data, function( $block ) { + return !in_array($block->category, array( 'media', 'reusable' )); + } ); + foreach ( $filter_blocks_data as $block ) { + $plugin_name = explode('/', $block->name); + $plugin_name = isset($plugin_name[0]) ? $plugin_name[0] : ''; + + if(!empty($plugin_name)){ + $filter_plugin_name = $this->atfpp_supported_block_name($plugin_name); + $filter_plugin_name=str_replace('-',' ',$filter_plugin_name); + $filter_plugin_name=ucwords($filter_plugin_name); + + if(in_array($plugin_name, $this->atfpp_plugin_category) || $plugin_name === 'core'){ + continue; + } + + $this->atfpp_plugin_category[] = $plugin_name; + echo ''; + } + } + } + + /** + * Get the supported blocks. + */ + public function atfpp_get_supported_blocks_table() { + if ( class_exists( 'WP_Block_Type_Registry' ) && method_exists( 'WP_Block_Type_Registry', 'get_all_registered' ) ) { + $atfp_block_parse_rules = ATFPP_Helper::get_instance()->get_block_parse_rules(); + + $blocks_data = WP_Block_Type_Registry::get_instance()->get_all_registered(); + + $atfp_supported_blocks = isset($atfp_block_parse_rules['AtfpBlockParseRules']) ? $atfp_block_parse_rules['AtfpBlockParseRules'] : array(); + $atfp_supported_blocks_names = array_keys( $atfp_supported_blocks ); + $s_no = 1; + $atfp_post_id = ATFPP_Helper::get_custom_block_post_id(); + + $filter_blocks_data=$blocks_data; + + foreach ( $filter_blocks_data as $block ) { + + $block_name = esc_html( $block->name ); + $block_title = esc_html( $block->title ); + $status = ! in_array( $block_name, $atfp_supported_blocks_names ) ? 'Unsupported' : 'Supported'; // You can modify this logic based on your requirements + $modify_text = ! in_array( $block_name, $atfp_supported_blocks_names ) ? esc_html__( 'Add', 'autopoly-ai-translation-for-polylang-pro' ) : esc_html__( 'Edit', 'autopoly-ai-translation-for-polylang-pro' ); + $modify_link = '' . $modify_text . ''; // Modify link + $modify_link = '' . $modify_text . ''; // Modify link + + echo ''; + echo '' . esc_html($s_no++) . ''; + echo '' . esc_html($block_name) . ''; + echo '' . esc_html($block_title) . ''; + echo '' . esc_html($status) . ''; + echo '' . wp_kses($modify_link, array('a' => array('href' => array(), 'target' => array(), 'rel' => array()))) . ''; + echo ''; + } + } + + } + + private function atfpp_supported_block_name($block_name){ + $predfined_blocks = array( + 'ub' => 'Ultimate Blocks', + 'uagb' => 'Spectra', + 'themeisle-blocks' => 'Otter Blocks' + ); + + if(array_key_exists($block_name, $predfined_blocks)){ + return $predfined_blocks[$block_name]; + } + + return $block_name; + } + } + + ATFP_Supported_Blocks::get_instance(); +} diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-glossary/atfpp-glossary.php b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-glossary/atfpp-glossary.php new file mode 100644 index 0000000..6cf391f --- /dev/null +++ b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/atfpp-glossary/atfpp-glossary.php @@ -0,0 +1,575 @@ + $lang) { + $lang = sanitize_text_field($lang); + // SKIP if $lang is the same as $source_language_code + if ($lang === $source_language_code) continue; + $term = sanitize_text_field($translated_terms[$idx] ?? ''); + + // Only add non-empty translations + if (!empty(trim($term)) && !in_array($lang, $existing_langs, true)) { + $entry['translations'][] = array( + 'target_language_code' => $lang, + 'translated_term' => $term + ); + $found = true; + } + } + // If no new translation was added, return false + if (!$found) { + return false; + } + break; + } + } + unset($entry); + + if (!$found) { + // New entry + $translations = []; + foreach ($target_langs as $idx => $lang) { + $lang = sanitize_text_field($lang); + if ($lang === $source_language_code) continue; + + $translated_term = sanitize_text_field($translated_terms[$idx] ?? ''); + // Only save non-empty translations + if (!empty(trim($translated_term))) { + $translations[] = array( + 'target_language_code' => $lang, + 'translated_term' => $translated_term + ); + } + } + $all_glossaries[] = array( + 'description' => $glossary_desc, + 'kind' => sanitize_text_field($glossary_data['type'] ?? 'general'), + 'original_language_code' => $source_language_code, + 'original_term' => $glossary_term, + 'translations' => $translations, + ); + } + + update_option('atfpp_glossary_data', $all_glossaries); + + return true; + } + + /** + * Optional: Get all glossary entries (for debugging or display) + */ + public static function get_all_glossaries() { + return get_option('atfpp_glossary_data', array()); + } + + // Add this function to handle CSV import + public static function import_glossary_csv($csv_path) { + if (!file_exists($csv_path) || !is_readable($csv_path)) { + return false; + } + + $header = null; + if (($handle = fopen($csv_path, 'r')) !== false) { + while (($row = fgetcsv($handle, 1000, ',')) !== false) { + if (!$header) { + $header = $row; + } else { + $row_data = array_combine($header, $row); + + // Create glossary entry with proper mapping + $glossary_entry = array( + 'type' => $row_data['type'] ?? 'general', // Get type from CSV + 'term' => $row_data['original_term'] ?? '', + 'description' => $row_data['description'] ?? '', + 'source_lang' => $row_data['original_language_code'] ?? '', + 'target_lang' => array($row_data['target_language_code'] ?? ''), + 'translated_term' => array($row_data['translated_term'] ?? '') + ); + + // Map 'type' to 'kind' when storing + $glossary_entry['kind'] = $glossary_entry['type']; + + if ($row_data['target_language_code'] !== $row_data['original_language_code']) { + self::store_glossary_data($glossary_entry); + } + } + } + fclose($handle); + } + return true; + } + + public function atfpp_import_glossary_ajax() { + check_ajax_referer('atfpp_glossary_nonce', '_wpnonce'); + if (!current_user_can('manage_options')) { + wp_send_json_error('Permission denied'); + } + if (empty($_FILES['csv_file']['tmp_name'])) { + wp_send_json_error('No file uploaded'); + } + if ($_FILES['csv_file']['type'] !== 'text/csv' && pathinfo($_FILES['csv_file']['name'], PATHINFO_EXTENSION) !== 'csv') { + wp_send_json_error('Invalid file type'); + } + if ($_FILES['csv_file']['size'] > 2 * 1024 * 1024) { // 2MB limit + wp_send_json_error('File too large'); + } + $csv_path = $_FILES['csv_file']['tmp_name']; + $result = self::import_glossary_csv($csv_path); + + if ($result) { + wp_send_json_success('Glossary imported'); + } else { + wp_send_json_error('Import failed'); + } + } + + /** + * Update glossary entry + */ + public static function update_glossary_data($glossary_data = array()) { + $glossary_type = sanitize_text_field($glossary_data['type'] ?? ''); + $glossary_term = sanitize_text_field($glossary_data['term'] ?? ''); + $glossary_desc = sanitize_textarea_field($glossary_data['description'] ?? ''); + $source_language_code = sanitize_text_field($glossary_data['source_lang'] ?? ''); + $translations = $glossary_data['translations'] ?? []; + + $all_glossaries = get_option('atfpp_glossary_data', array()); + $updated = false; + + foreach ($all_glossaries as $i => &$entry) { + $existing_name = sanitize_text_field($entry['original_term'] ?? ''); + $existing_source = sanitize_text_field($entry['original_language_code'] ?? ''); + + if ( + $existing_name === $glossary_data['original_term'] && + $existing_source === $glossary_data['original_source_lang'] + ) { + // Check if term or language changed + if ( + $glossary_term !== $existing_name || + $source_language_code !== $existing_source + ) { + // Remove just this entry + unset($all_glossaries[$i]); + + // ✅ Add the updated entry now + $translations_arr = []; + foreach ($translations as $lang => $translated_term) { + if ($lang === $source_language_code) continue; + $sanitized_term = sanitize_text_field($translated_term); + // Only save non-empty translations + if (!empty(trim($sanitized_term))) { + $translations_arr[] = array( + 'target_language_code' => $lang, + 'translated_term' => $sanitized_term + ); + } + } + + $all_glossaries[] = array( + 'description' => $glossary_desc, + 'kind' => $glossary_type, + 'original_language_code' => $source_language_code, + 'original_term' => $glossary_term, + 'translations' => $translations_arr, + ); + + $updated = true; + break; + } else { + // Only description or translations changed — update in place + $entry['description'] = $glossary_desc; + $entry['kind'] = $glossary_type; + $entry['original_term'] = $glossary_term; + $entry['original_language_code'] = $source_language_code; + $entry['translations'] = []; + + foreach ($translations as $lang => $translated_term) { + if ($lang === $source_language_code) continue; + $sanitized_term = sanitize_text_field($translated_term); + // Only save non-empty translations + if (!empty(trim($sanitized_term))) { + $entry['translations'][] = array( + 'target_language_code' => $lang, + 'translated_term' => $sanitized_term + ); + } + } + + $updated = true; + break; + } + } + + } + unset($entry); + + if ($updated) { + // Reindex array to avoid gaps + $all_glossaries = array_values($all_glossaries); + update_option('atfpp_glossary_data', $all_glossaries); + return true; + } + return false; + } + + public function atfpp_update_glossary_ajax() { + check_ajax_referer('atfpp_glossary_nonce', '_wpnonce'); + + if (!current_user_can('manage_options')) { + wp_send_json_error('Permission denied'); + } + + $data = $_POST['data'] ?? []; + $source_lang = sanitize_text_field($data['source_lang'] ?? ''); + $translations = isset($data['translations']) && is_array($data['translations']) ? $data['translations'] : []; + // Remove translation for original language if present + if (isset($translations[$source_lang])) { + unset($translations[$source_lang]); + } + $result = self::update_glossary_data($data); + + if ($result) { + // Get the updated entry from database + $updated_entry = self::get_updated_glossary_entry( + sanitize_text_field($data['term'] ?? ''), + $source_lang + ); + + wp_send_json_success([ + 'message' => 'Glossary updated', + 'updated_entry' => $updated_entry + ]); + } else { + wp_send_json_error('Update failed'); + } + } + + /** + * Get updated glossary entry after update + */ + private static function get_updated_glossary_entry($term, $source_lang) { + $all_glossaries = get_option('atfpp_glossary_data', array()); + + foreach ($all_glossaries as $entry) { + if ( + ($entry['original_term'] ?? '') === $term && + ($entry['original_language_code'] ?? '') === $source_lang + ) { + return $entry; + } + } + + return null; + } + + /** + * Delete glossary entry + */ + public static function delete_glossary_data($term, $source_lang) { + $all_glossaries = get_option('atfpp_glossary_data', array()); + $updated = false; + foreach ($all_glossaries as $i => $entry) { + if ( + strtolower(trim($entry['original_term'])) === strtolower(trim($term)) && + strtolower(trim($entry['original_language_code'])) === strtolower(trim($source_lang)) + ) { + unset($all_glossaries[$i]); + $updated = true; + break; + } + } + if ($updated) { + $all_glossaries = array_values($all_glossaries); + update_option('atfpp_glossary_data', $all_glossaries); + return true; + } + return false; + } + + public function atfpp_delete_glossary_ajax() { + check_ajax_referer('atfpp_glossary_nonce', '_wpnonce'); + if (!current_user_can('manage_options')) { + wp_send_json_error('Permission denied'); + } + $term = sanitize_text_field($_POST['term'] ?? ''); + $source_lang = sanitize_text_field($_POST['source_lang'] ?? ''); + if (empty($term) || empty($source_lang)) { + wp_send_json_error('Missing data'); + } + $result = self::delete_glossary_data($term, $source_lang); + if ($result) { + wp_send_json_success('Glossary entry deleted'); + } else { + wp_send_json_error('Delete failed'); + } + } + + public function atfpp_add_glossary_ajax() { + check_ajax_referer('atfpp_glossary_nonce', '_wpnonce'); + if (!current_user_can('manage_options')) { + wp_send_json_error('Permission denied'); + } + $type = sanitize_text_field($_POST['type'] ?? ''); + $term = sanitize_text_field($_POST['term'] ?? ''); + $description = sanitize_textarea_field($_POST['description'] ?? ''); + $source_lang = sanitize_text_field($_POST['source_lang'] ?? ''); + $translations = isset($_POST['translations']) && is_array($_POST['translations']) ? array_map('sanitize_text_field', $_POST['translations']) : []; + // Remove translation for original language if present + if (isset($translations[$source_lang])) { + unset($translations[$source_lang]); + } + + // Only require type, term, and source_lang + if (empty($type) || empty($term) || empty($source_lang)) { + wp_send_json_error('Type, Term, and Original Language are required.'); + } + + // Save the main term + $main_entry = [ + 'type' => $type, + 'term' => $term, + 'description' => $description, + 'source_lang' => $source_lang, + 'target_lang' => [], + 'translated_term' => [] + ]; + foreach ($translations as $code => $translated) { + // Strict empty check - must have non-whitespace content + if ($translated !== '' && trim($translated) !== '') { + $main_entry['target_lang'][] = $code; + $main_entry['translated_term'][] = trim($translated); + } + } + + $glossary_data = get_option('atfpp_glossary_data', []); + $duplicate = false; + foreach ($glossary_data as $entry) { + if ( + isset($entry['original_term'], $entry['original_language_code']) && + $entry['original_term'] === $term && + $entry['original_language_code'] === $source_lang + ) { + $duplicate = true; + break; + } + } + if ($duplicate) { + wp_send_json_error('This term already exists in this language.'); + } + + $result = self::store_glossary_data($main_entry); + if ($result) { + // Get the newly added entry from database + $added_entry = self::get_updated_glossary_entry($term, $source_lang); + + wp_send_json_success([ + 'message' => 'Glossary term added successfully', + 'added_entry' => $added_entry + ]); + } else { + wp_send_json_error('Could not add glossary term (maybe duplicate?)'); + } + } + + public function atfpp_export_glossary_ajax() { + if (!current_user_can('manage_options')) { + wp_die('Permission denied'); + } + + $glossary_data = get_option('atfpp_glossary_data', []); + if (!is_array($glossary_data)) { + $glossary_data = []; + } + + // Set headers for CSV download + header('Content-Type: text/csv; charset=utf-8'); + header('Content-Disposition: attachment; filename=glossary-export-' . date('Y-m-d') . '.csv'); + + $output = fopen('php://output', 'w'); + + // CSV header + fputcsv($output, [ + 'type', + 'original_term', + 'description', + 'original_language_code', + 'target_language_code', + 'translated_term' + ]); + + foreach ($glossary_data as $entry) { + $type = $entry['kind'] ?? ''; + $term = $entry['original_term'] ?? ''; + $desc = $entry['description'] ?? ''; + $orig_lang = $entry['original_language_code'] ?? ''; + if (!empty($entry['translations']) && is_array($entry['translations'])) { + foreach ($entry['translations'] as $trans) { + $target_lang = $trans['target_language_code'] ?? ''; + $translated = $trans['translated_term'] ?? ''; + fputcsv($output, [ + $type, + $term, + $desc, + $orig_lang, + $target_lang, + $translated + ]); + } + } else { + // No translations, just output the main term + fputcsv($output, [ + $type, + $term, + $desc, + $orig_lang, + '', + '' + ]); + } + } + + fclose($output); + exit; + } + + public function atfpp_get_glossary_ajax() { + if (!current_user_can('read')) { + wp_send_json_error('Permission denied'); + } + $source_lang = isset($_GET['sourceLang']) ? sanitize_text_field($_GET['sourceLang']) : ''; + $target_lang = isset($_GET['targetLang']) ? sanitize_text_field($_GET['targetLang']) : ''; + + $glossary_data = self::get_all_glossaries(); + $filtered = []; + + foreach ($glossary_data as $entry) { + // Filter by source_lang + if ( + $source_lang && + (!isset($entry['original_language_code']) || $entry['original_language_code'] !== $source_lang) + ) { + continue; // Skip if source_lang is provided and doesn't match + } + + // Ensure the entry has a translations array and it's not empty + if (!isset($entry['translations']) || !is_array($entry['translations']) || empty($entry['translations'])) { + continue; // Skip if no translations array or it's empty + } + + // Filter for valid translations (must have target_language_code and non-empty translated_term) + $valid_translations = array_filter($entry['translations'], function($trans) { + return isset($trans['target_language_code']) && !empty($trans['target_language_code']) && + isset($trans['translated_term']) && $trans['translated_term'] !== ''; + }); + + if (empty($valid_translations)) { + continue; // Skip if no valid (non-empty) translations exist for this entry + } + + if ($target_lang) { + // If target_lang is provided, filter valid_translations by this target_lang + $matching_target_translations = array_filter($valid_translations, function($trans) use ($target_lang) { + return $trans['target_language_code'] === $target_lang; + }); + + if (!empty($matching_target_translations)) { + // If matching translations exist, replace the entry's translations with only these. + $entry['translations'] = array_values($matching_target_translations); + $filtered[] = $entry; + } + // If no valid translations match the target_lang, this entry is skipped. + } else { + // If target_lang is not provided, include the entry with all its valid_translations. + $entry['translations'] = array_values($valid_translations); + $filtered[] = $entry; + } + } + + wp_send_json_success(['terms' => $filtered]); + } + } +} + +// --- ADD THIS BLOCK: Fetch Polylang languages for use everywhere --- +$pll_languages_serialized = get_option('_transient_pll_languages_list'); +$pll_languages = []; +if ($pll_languages_serialized) { + $pll_languages = maybe_unserialize($pll_languages_serialized); +} +// Build $languages array for use in modal and table +$languages = []; +if (is_array($pll_languages)) { + foreach ($pll_languages as $pll_lang) { + $languages[] = [ + 'code' => $pll_lang['slug'], + 'img' => $pll_lang['flag_url'], + 'alt' => $pll_lang['name'], + 'flag' => isset($pll_lang['flag']) ? $pll_lang['flag'] : '', + ]; + } +} +?> diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/cpfm-feedback/cpfm-common-notice.php b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/cpfm-feedback/cpfm-common-notice.php new file mode 100644 index 0000000..b247d55 --- /dev/null +++ b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/cpfm-feedback/cpfm-common-notice.php @@ -0,0 +1,225 @@ + '', + 'message' => '', + 'pages' => [], + 'always_show_on' => [], + ]); + } + self::$registered_notices[$key][] = $args; + } + + public function cpfm_listen_for_external_notice_registration() { + + + if (!current_user_can('manage_options')) { + + return; + } + + /** + * Allow other plugins to register notices dynamically. + * Example usage in other plugins: + * do_action('cpf_cpfm_register_notice', 'crypto', [ + * 'title' => 'Crypto Plugin Notice', + * 'message' => 'This is a crypto dashboard setup notice.', + * 'pages' => ['dashboard', 'cpfm_'], + * ]); + */ + do_action('cpfm_register_notice'); + } + + public function cpfm_enqueue_assets() { + + if (!current_user_can('manage_options')) { + + return; + + } + + + $screen = get_current_screen(); + $current_page = isset($_GET['page'])? sanitize_key($_GET['page']):''; + + // Gather all unique pages from registered notices + $allowed_pages = []; + + foreach (self::$registered_notices as $notice) { + if (!empty($notice['pages']) && is_array($notice['pages'])) { + $allowed_pages = array_merge($allowed_pages, $notice['pages']); + } + } + + // Early return if not needed + if (!in_array($current_page, array_unique($allowed_pages))) { + return; + } + wp_enqueue_style('cpfm-common-review-style', ATFPP_URL . 'admin/cpfm-feedback/css/cpfm-admin-feedback.css'); + wp_enqueue_script( + 'cpfm-common-review-script', + ATFPP_URL . 'admin/cpfm-feedback/js/cpfm-admin-feedback.js', + ['jquery'], + ATFPP_V, + true + + ); + wp_localize_script('cpfm-common-review-script', 'adminNotice', [ + 'ajaxurl' => admin_url('admin-ajax.php'), + 'nonce' => wp_create_nonce('dismiss_admin_notice'), + 'autoShowPages' => array_unique( + array_merge( + [], + ...array_filter( + array_column(self::$registered_notices, 'always_show_on'), + function($pages) { return !empty($pages); } + ) + ) + ), + ]); + } + + public function cpfm_handle_opt_in_choice() { + + if (!current_user_can('manage_options')) { + + wp_send_json_error('Unauthorized access.'); + } + + check_ajax_referer('dismiss_admin_notice', 'nonce'); + + $category = isset($_POST['category']) ? sanitize_text_field( wp_unslash( $_POST['category'] ) ): ''; + $opt_in_raw = isset($_POST['opt_in']) ? sanitize_text_field( wp_unslash( $_POST['opt_in'] ) ) : ''; + $opt_in = ($opt_in_raw === 'yes') ? 'yes' : 'no'; + $category_notices = self::$registered_notices; + $registered_notices = isset($GLOBALS['cool_plugins_feedback'])? $GLOBALS['cool_plugins_feedback']:$category_notices; + + if (!$category || !isset(self::$registered_notices[$category])) { + + wp_send_json_error('Invalid notice category.'); + } + + update_option("cpfm_opt_in_choice_{$category}", $opt_in); + + $review_option = get_option("cpfm_opt_in_choice_{$category}"); + + + if ($review_option === 'yes') { + + foreach (self::$registered_notices[$category] as $notice) { + + $plugin_name = isset($notice['plugin_name'])?sanitize_key($notice['plugin_name']):''; + + if($plugin_name){ + + do_action('cpfm_after_opt_in_' . $plugin_name, $category); + } + + } + + } + + wp_send_json_success(); + } + + public function cpfm_render_notice_panel() { + + if (!current_user_can('manage_options') || !function_exists('get_current_screen')) { + return; + } + + $screen = get_current_screen(); + $current_page = isset($_GET['page']) ? sanitize_key($_GET['page']) : ''; + + + $unread_count = 0; + $auto_show = false; + + foreach (self::$registered_notices as $notice) { + + if (!empty($notice['always_show_on']) && in_array($current_page, (array) $notice['always_show_on'])) { + $auto_show = true; + break; + } + } + + $output = ''; + $output .= '
    '; + $output .= '
    ' . esc_html__('Help Improve Plugins', 'ccpw') . '
    '; + $output .= '
    '; + + foreach (self::$registered_notices as $key => $notice) { + + $choice = get_option("cpfm_opt_in_choice_{$key}"); + + if ($choice !== false) continue; + + $should_show = false; + foreach ($notice['pages'] as $match) { + + if ($current_page === $match || strpos($current_page, $match) === 0) { + + $should_show = true; + break; + } + } + + if (!$should_show) continue; + $unread_count++; + + $output .= '
    '; + $output .= '' . esc_html($notice['title']) . ''; + + $output .= '
    '; + $output .= '

    ' . esc_html($notice['message']) . '' . esc_html__(' More info', 'ccpw') . '

    '; + $output .= '
    '; + + $output .= '
    '; + $output .= '

    ' . esc_html__('Opt in to receive email updates about security improvements, new features, helpful tutorials, and occasional special offers. We\'ll collect:', 'ccpw') . '

    '; + $output .= '
      '; + $output .= '
    • ' . esc_html__('Your website home URL and WordPress admin email.', 'ccpw') . '
    • '; + $output .= '
    • ' . esc_html__('To check plugin compatibility, we will collect the following: list of active plugins and themes, server type, MySQL version, WordPress version, memory limit, site language and database prefix.', 'ccpw') . '
    • '; + $output .= '
    '; + + $output .= '
    '; + + $output .= '
    '; + $output .= ''; + $output .= ''; + $output .= '
    '; + + $output .= '
    '; + } + + $output .= '
    '; + $output .= '
    '; + + if ($unread_count > 0) { + echo $output; + } + } +} +new CPFM_Feedback_Notice(); diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/cpfm-feedback/cron/atfpp-cron.php b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/cpfm-feedback/cron/atfpp-cron.php new file mode 100644 index 0000000..0ab1c53 --- /dev/null +++ b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/cpfm-feedback/cron/atfpp-cron.php @@ -0,0 +1,119 @@ + 30 * 24 * 60 * 60, // 2,592,000 seconds + 'display' => __('Once every 30 days'), + ); + } + + return $schedules; + } + + /* + |-------------------------------------------------------------------------- + | cron extra data autoupdater + |-------------------------------------------------------------------------- + */ + + function atfpp_cron_extra_data_autoupdater() { + $opt_in = get_option('atfp_feedback_opt_in'); + $opt_in = is_string($opt_in) ? strtolower($opt_in) : 'no'; + + if ($opt_in === 'yes' || $opt_in === true || $opt_in === '1') { + + if (class_exists('ATFPP_cronjob')) { + + ATFPP_cronjob::atfpp_send_data(); + } + } + + } + + /* + |-------------------------------------------------------------------------- + | cron send data + |-------------------------------------------------------------------------- + */ + + static public function atfpp_send_data() { + + $feedback_url = ATFPP_FEEDBACK_API.'wp-json/coolplugins-feedback/v1/site'; + + $extra_data_details = AutoPolyPro::atfpp_get_user_info(); + $server_info = $extra_data_details['server_info']; + $extra_details = $extra_data_details['extra_details']; + $site_url = get_site_url(); + $install_date = get_option('atfpp-install-date'); + $unique_key = '42'; // Ensure this key is unique per plugin to prevent collisions when site URL and install date are the same across plugins + $site_id = $site_url . '-' . $install_date . '-' . $unique_key; + $initial_version = get_option('atfpp_initial_save_version'); + $initial_version = is_string($initial_version) ? sanitize_text_field($initial_version) : 'N/A'; + $plugin_version = ATFPP_V; + $admin_email = sanitize_email(get_option('admin_email') ?: 'N/A'); + $post_data = array( + + 'site_id' => md5($site_id), + 'plugin_version' => $plugin_version, + 'plugin_name' => 'AutoPoly - AI Translation For Polylang (Pro)', + 'plugin_initial' => $initial_version, + 'email' => $admin_email, + 'site_url' => esc_url_raw($site_url), + 'server_info' => $server_info, + 'extra_details' => $extra_details, + ); + + $response = wp_remote_post($feedback_url, array( + 'method' => 'POST', + 'timeout' => 30, + 'headers' => array( + 'Content-Type' => 'application/json', + ), + 'body' => wp_json_encode($post_data), + )); + + if (is_wp_error($response)) { + defined('WP_DEBUG') && WP_DEBUG && error_log('ATFPP Feedback Send Failed: ' . sanitize_text_field($response->get_error_message())); + return; + } + + $response_body = wp_remote_retrieve_body($response); + $decoded = json_decode($response_body, true); + + if (!wp_next_scheduled('atfpp_extra_data_update')) { + wp_schedule_event(time(), 'every_30_days', 'atfpp_extra_data_update'); + } + } + + } +} diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/cpfm-feedback/css/cpfm-admin-feedback.css b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/cpfm-feedback/css/cpfm-admin-feedback.css new file mode 100644 index 0000000..f27436b --- /dev/null +++ b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/cpfm-feedback/css/cpfm-admin-feedback.css @@ -0,0 +1 @@ +#cpfNoticePanel{display:none;position:fixed;z-index:999999;right:0;top:32px;width:380px;background:#fff;box-shadow:0 4px 15px rgba(0,0,0,.1);border-radius:8px;margin:10px}#cpfNoticePanel .notice-panel-header{padding:16px 20px;background:#f6f7f7;border-bottom:1px solid #e1e1e1;font-weight:600;font-size:15px;color:#1d2327;display:flex;justify-content:space-between;border-radius:8px 8px 8px 0}#cpfNoticePanel .notice-panel-content{padding:15px 20px;max-height:400px;overflow-y:auto}#cpfNoticePanel .notice-item{background:#fefefe;border:1px solid #e0e0e0;border-radius:6px;padding:15px;margin-bottom:20px;transition:.3s}#cpfNoticePanel .notice-item:hover{box-shadow:0 2px 8px rgba(0,0,0,.06)}#cpfNoticePanel .notice-item strong{font-size:14px;font-weight:600;color:#1d2327;margin-bottom:10px;display:block}#cpfNoticePanel .opt-in-text{font-size:13px;color:#50575e;margin-bottom:15px;line-height:1.6}#cpfNoticePanel .notice-actions{display:flex;justify-content:space-between;gap:10px}#cpfNoticePanel .notice-actions .button{flex:1;padding:8px 10px;font-size:13px;border-radius:4px;text-align:center;line-height:1.4;transition:.2s ease-in-out}#cpfNoticePanel .opt-in-yes{background-color:#2271b1;border:1px solid #2271b1;color:#fff}#cpfNoticePanel .opt-in-yes:hover{background-color:#135e96;border-color:#135e96}#cpfNoticePanel .opt-in-no{background-color:#fff;border:1px solid #2271b1;color:#2271b1}#cpfNoticePanel .opt-in-no:hover{background-color:#f0f0f1}#wpadminbar .cpf-notice-admin-button>.ab-item{position:relative;padding-left:10px!important;font-weight:600;color:#fff!important;display:flex;align-items:center;gap:6px}#wpadminbar .cpf-notice-admin-button .notice-count{background-color:#f44336;color:#fff;border-radius:12px;padding:2px 7px;font-size:11px;line-height:1;font-weight:700;display:inline-block;min-width:20px;text-align:center}span#cpfm_remove_notice{cursor:pointer}.cpf-extra-info{display:none;font-size:12px;color:#999}.cpf-extra-info ul{list-style-type:auto;padding-left:20px;margin-top:10px} \ No newline at end of file diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/cpfm-feedback/js/cpfm-admin-feedback.js b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/cpfm-feedback/js/cpfm-admin-feedback.js new file mode 100644 index 0000000..f039c06 --- /dev/null +++ b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/cpfm-feedback/js/cpfm-admin-feedback.js @@ -0,0 +1 @@ +jQuery(document).ready(function ($) { const noticePanel = $('#cpfNoticePanel');let isPanelVisible = !1; var urlParams = new URLSearchParams(window.location.search); var currentPage = urlParams.get('page'); if (noticePanel.data('auto-show') && adminNotice.autoShowPages.includes(currentPage)) { setTimeout(function () { noticePanel.fadeIn(300); isPanelVisible = !0 }, 500) } $(document).on('click', '#wp-admin-bar-cpf-notice-admin-button > a', function (e) { e.preventDefault(); isPanelVisible ? noticePanel.fadeOut(300) : noticePanel.fadeIn(300); isPanelVisible = !isPanelVisible }); $(document).on('click', '#cpfm_remove_notice', function (e) { e.preventDefault(); noticePanel.fadeOut(300); isPanelVisible = false; }); $(document).on('click', '.opt-in-yes, .opt-in-no', function (e) { e.preventDefault(); const button = $(this); const category = button.data('category'); const optIn = button.val(); const noticeItem = button.closest('.notice-item'); $.post(adminNotice.ajaxurl, { action: 'cpfm_handle_opt_in', nonce: adminNotice.nonce, category: category, opt_in: optIn }, function (response) { if (response.success) { noticeItem.slideUp(300, function () { noticeItem.remove(); const remainingNotices = $('#cpfNoticePanel .notice-item.unread'); const count = remainingNotices.length; $('.cpf-notice-admin-button .notice-count').text(count); if (count === 0) { $('#cpfNoticePanel').fadeOut(300); $('#wp-admin-bar-cpf-notice-admin-button').fadeOut(300, function () { $(this).remove() }) }}) } }) }); $('.cpf-toggle-extra').on('click', function () { $(this).closest('.notice-item').find('.cpf-extra-info').slideToggle() }) }) \ No newline at end of file diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/cpt_dashboard/assets/css/cpt-dashboard.css b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/cpt_dashboard/assets/css/cpt-dashboard.css new file mode 100644 index 0000000..52497a2 --- /dev/null +++ b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/cpt_dashboard/assets/css/cpt-dashboard.css @@ -0,0 +1,82 @@ +.cpt-review-notice{ + position: relative; + margin: 10px 20px 10px 0 !important; + padding: 5px; + display: flex; + align-items: center; + border-radius: 5px; + max-width: 790px; + padding-left: 11px; + padding-bottom: 11px; +} + +.cpt-review-notice .cpt-review-notice-icon{ + max-width: 80px; + height: auto; + margin-right: 10px; +} + +.cpt-review-notice div p{ + margin: 0; + margin-bottom: .5rem; +} + +.cpt-review-notice .atfpp-review-notice-dismiss { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + gap: 10px; +} + +body.rtl .cpt-review-notice .cpt-not-interested { + right: auto; + left: 5px; +} + +.cpt-review-notice :where(.cpt-not-interested, .cpt-already-reviewed)::before { + color: #cc0000; + content: "\f153"; + font: normal 16px/20px dashicons; + display: inline-block; + vertical-align: middle; + margin-right: 4px; + height: 22px; +} + +.cpt-dashboard { + padding: 20px; +} + +.cpt-dashboard .cpt-dashboard-tabs { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + gap: 2px; + border-bottom: 1px solid #ccc; +} + +.cpt-dashboard .cpt-dashboard-tabs .cpt-dashboard-tab { + padding: 10px 20px; + cursor: pointer; + background: #f5f5f5; + border: 1px solid #ccc; + border-bottom: none; + border-radius: 4px 4px 0 0; +} + +.cpt-dashboard .cpt-dashboard-tabs .cpt-dashboard-tab:hover { + background: #fff; +} + +.cpt-dashboard .cpt-dashboard-tabs .cpt-dashboard-tab.cpt-active { + background: #fff; + border-bottom: 1px solid #fff; +} + +.cpt-dashboard .cpt-dashboard-table { + display: none; +} + +.cpt-dashboard .cpt-dashboard-table.cpt-active { + display: table; +} \ No newline at end of file diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/cpt_dashboard/assets/js/cpt-dashboard.js b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/cpt_dashboard/assets/js/cpt-dashboard.js new file mode 100644 index 0000000..3e9d887 --- /dev/null +++ b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/cpt_dashboard/assets/js/cpt-dashboard.js @@ -0,0 +1,22 @@ +jQuery(document).ready(function($){ + $('.cpt-dashboard-tab').click(function(){ + var tab = $(this).data('tab'); + $('.cpt-dashboard-table').hide(); + $('#cpt-'+tab+'-table').show(); + + $('.cpt-dashboard-tab').removeClass('cpt-active'); + $(this).addClass('cpt-active'); + + $('.cpt-dashboard-tables').find('table').hide(); + $('#cpt-'+tab+'-table').show(); + }); + + $('.atfpp-review-notice-dismiss button').click(function(){ + var prefix = $(this).closest('.atfpp-review-notice-dismiss').data('prefix'); + var nonce = $(this).closest('.atfpp-review-notice-dismiss').data('nonce'); + + $.post(ajaxurl, {action: 'atfpp_hide_review_notice', prefix: prefix, nonce: nonce}, (response)=>{ + $(this).closest('.cpt-review-notice').slideUp(); + }); + }); +}); \ No newline at end of file diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/cpt_dashboard/cpt_dashboard.php b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/cpt_dashboard/cpt_dashboard.php new file mode 100644 index 0000000..eb894c8 --- /dev/null +++ b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/admin/cpt_dashboard/cpt_dashboard.php @@ -0,0 +1,316 @@ + 'name or id', + * 'post_title (optional)' => 'Post Title', + * 'service_provider' => 'google', // don't change this key + * 'source_language' => 'en', // don't change this key + * 'target_language' => 'fr', // don't change this key + * 'time_taken' => '10', // don't change this key + * 'string_count'=>10, + * 'character_count'=>100, + * 'date_time' => date('Y-m-d H:i:s'), + * ) // Required data array + * ); + * } + * + * Add Tabs + * add_filter('Atfpp_Dashboard_tabs', function($tabs){ + * $tabs[]=array( + * 'prefix'=>'tab_name', // Required + * 'tab_name'=>'Tab Name', // Required + * 'columns'=>array( + * 'post_id or plugin_name'=>'Post Id or Plugin Name', + * 'post_title (optional)'=>'Post Title', + * 'string_count'=>'String Count', + * 'character_count'=>'Character Count', + * 'service_provider'=>'Service Provider', + * 'time_taken'=>'Time Taken', + * 'date_time'=>'Date Time', + * ) // columns Required + * ); + * return $tabs; + * }); + * + * Display review notice + * if(class_exists('Atfpp_Dashboard')){ + * Atfpp_Dashboard::review_notice( + * 'prefix', // Required + * 'plugin_name', // Required + * 'url', // Required + * ); + * } + * + * Get translation data + * if(class_exists('Atfpp_Dashboard')){ + * Atfpp_Dashboard::get_translation_data( + * 'prefix', // Required + * array( + * 'editor_type' => 'gutenberg', // optional return data based on editor type + * 'post_id' => '123', // optional return data based on post id + * ) // Optional + * ); + * } + */ + +if(!class_exists('Atfpp_Dashboard')){ + class Atfpp_Dashboard{ + + /** + * Init + * @var object + */ + private static $init; + + /** + * Tabs data + * @var array + */ + private $tabs_data=array(); + + /** + * Instance + * @return object + */ + public static function instance(){ + if(!isset(self::$init)){ + self::$init = new self(); + } + return self::$init; + } + + public function __construct(){ + add_action('wp_ajax_atfpp_hide_review_notice', array($this, 'atfpp_hide_review_notice')); + } + + /** + * Sort column data + * @param array $columns + * @param array $value + * @return array + */ + public function sort_column_data($columns, $value){ + $result = array(); + foreach($columns as $key => $label) { + $result[$key] = isset($value[$key]) ? sanitize_text_field($value[$key]) : ''; + } + return $result; + } + + /** + * Store options + * @param string $plugin_name + * @param string $prefix + * @param array $data + * @return void + */ + public static function store_options($prefix='', $unique_key='', $old_data='update', array $data = array()){ + if(!empty($prefix) && isset($data['string_count']) && isset($data['character_count'])){ + $prefix = sanitize_key($prefix); + $all_data = get_option('cpt_dashboard_data', array()); + + if(isset($all_data[$prefix])){ + $data_update = false; + foreach($all_data[$prefix] as $key => $translate_data){ + if(!empty($unique_key) && isset($translate_data[$unique_key]) && + sanitize_text_field($translate_data[$unique_key]) === sanitize_text_field($data[$unique_key]) && + sanitize_text_field($translate_data['service_provider']) === sanitize_text_field($data['service_provider']) && + sanitize_text_field($translate_data['target_language']) === sanitize_text_field($data['target_language']) && + sanitize_text_field($translate_data['source_language']) === sanitize_text_field($data['source_language']) + ){ + + if($old_data=='update'){ + $data['string_count'] = absint($data['string_count']) + absint($translate_data['string_count']); + $data['character_count'] = absint($data['character_count']) + absint($translate_data['character_count']); + $data['time_taken'] = absint($data['time_taken']) + absint($translate_data['time_taken']); + } + + foreach($data as $id => $value){ + $all_data[$prefix][$key][sanitize_key($id)] = sanitize_text_field($value); + } + $data_update = true; + } + } + + if(!$data_update){ + $all_data[$prefix][] = array_map('sanitize_text_field', $data); + } + }else{ + $all_data[$prefix][] = array_map('sanitize_text_field', $data); + } + + update_option('cpt_dashboard_data', $all_data); + } + } + + /** + * Get translation data + * @param string $prefix + * @return array + */ + public static function get_translation_data($prefix, $key_exists=array()){ + $prefix = sanitize_key($prefix); + $all_data = get_option('cpt_dashboard_data', array()); + $data = array(); + + if(isset($all_data[$prefix])){ + $total_string_count = 0; + $total_character_count = 0; + + foreach($all_data[$prefix] as $key => $value){ + + $continue=false; + foreach($key_exists as $key_exists_key => $key_exists_value){ + if(!isset($value[$key_exists_key]) || (isset($value[$key_exists_key]) && $value[$key_exists_key] !== $key_exists_value)){ + $continue=true; + break; + } + } + + if($continue){ + continue; + } + + $total_string_count += isset($value['string_count']) ? absint($value['string_count']) : 0; + $total_character_count += isset($value['character_count']) ? absint($value['character_count']) : 0; + } + + $data = array( + 'prefix' => $prefix, + 'data' => array_map(function($item) { + return array_map('sanitize_text_field', $item); + }, $all_data[$prefix]), + 'total_string_count' => $total_string_count, + 'total_character_count' => $total_character_count, + ); + }else{ + $data = array( + 'prefix' => $prefix, + 'total_string_count' => 0, + 'total_character_count' => 0, + ); + } + + return $data; + } + + public static function ctp_enqueue_assets(){ + if(function_exists('wp_style_is') && !wp_style_is('atfpp-review-style', 'enqueued')){ + $plugin_url = plugin_dir_url(__FILE__); + wp_enqueue_style('atfpp-review-style', esc_url($plugin_url.'assets/css/cpt-dashboard.css'), array(), '1.0.0', 'all'); + wp_enqueue_script('atfpp-review-script', esc_url($plugin_url.'assets/js/cpt-dashboard.js'), array('jquery'), '1.0.0', true); + } + } + + public static function format_number_count($number){ + if ($number >= 1000000) { + return round($number / 1000000, 1) . 'M'; + } elseif ($number >= 1000) { + return round($number / 1000, 1) . 'K'; + } + return $number; + } + + public static function review_notice($prefix, $plugin_name, $url){ + if(self::atfpp_hide_review_notice_status($prefix)){ + return; + } + + $translation_data = self::get_translation_data($prefix); + + $total_character_count = is_array($translation_data) && isset($translation_data['total_character_count']) ? $translation_data['total_character_count'] : 0; + + if($total_character_count < 50000){ + return; + } + + $total_character_count = self::format_number_count($total_character_count); + + add_action('admin_enqueue_scripts', array(self::class, 'ctp_enqueue_assets')); + + + + $message = sprintf( + __('Thanks for using %1$s! You have translated %2$s characters so far using our plugin!
    Please give us a quick rating, it works as a boost for us to keep working on more Cool Plugins!', 'cp-notice'), + $plugin_name, + $total_character_count, + esc_url('https://coolplugins.net/') + ); + + + $prefix = sanitize_key($prefix); + $url = esc_url($url); + $plugin_name = sanitize_text_field($plugin_name); + + $allowed = [ + 'div' => [ 'class' => true, 'data-prefix' => true, 'data-nonce' => true ], + 'p' => [], + 'a' => [ 'href' => true, 'target' => true, 'class' => true, 'style' => true, 'rel' => true ], + 'button' => [ 'class' => true ], + 'b' => [], + 'br' => [], + 'strong' => [], + ]; + + $message = wp_kses($message, $allowed); + + add_action('admin_notices', function() use ($message, $prefix, $url, $allowed){ + $html= '
    '; + + $html .= '

    '.$message.'

    Rate Now! ★★★★★
    '; + + echo wp_kses($html, $allowed); + }); + + add_action('atfpp_display_admin_notices', function() use ($message, $prefix, $url, $allowed){ + $html= '
    '; + $html .= '

    '.$message.'

    Rate Now! ★★★★★
    '; + + echo wp_kses($html, $allowed); + }); + } + + public static function atfpp_hide_review_notice_status($prefix){ + $review_notice_dismissed = get_option('cpt_review_notice_dismissed', array()); + return isset($review_notice_dismissed[$prefix]) ? $review_notice_dismissed[$prefix] : false; + } + + public function atfpp_hide_review_notice(){ + if(!current_user_can('manage_options')){ + wp_send_json_error( __( 'Unauthorized', 'autopoly-ai-translation-for-polylang-pro' ), 403 ); + wp_die( '0', 403 ); + } + + if(wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'atfpp_hide_review_notice')){ + $prefix = sanitize_key(wp_unslash($_POST['prefix'])); + $review_notice_dismissed = get_option('cpt_review_notice_dismissed', array()); + $review_notice_dismissed[$prefix] = true; + update_option('cpt_review_notice_dismissed', $review_notice_dismissed); + wp_send_json_success(); + }else{ + wp_send_json_error( __( 'Invalid nonce', 'autopoly-ai-translation-for-polylang-pro' ), 400 ); + wp_die( '0', 400 ); + } + } + } +} diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/assets/automatic-translate/index.asset.php b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/assets/automatic-translate/index.asset.php new file mode 100644 index 0000000..493f4c9 --- /dev/null +++ b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/assets/automatic-translate/index.asset.php @@ -0,0 +1 @@ + array('react', 'react-dom', 'wp-blocks', 'wp-data', 'wp-element', 'wp-i18n'), 'version' => '1f7ec3dfbd17a30f0ca3'); diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/assets/automatic-translate/index.js b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/assets/automatic-translate/index.js new file mode 100644 index 0000000..0cf0846 --- /dev/null +++ b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/assets/automatic-translate/index.js @@ -0,0 +1 @@ +(()=>{"use strict";var t={338:(t,e,a)=>{var n=a(795);e.createRoot=n.createRoot,e.hydrateRoot=n.hydrateRoot},795:t=>{t.exports=window.ReactDOM}},e={};function a(n){var o=e[n];if(void 0!==o)return o.exports;var r=e[n]={exports:{}};return t[n](r,r.exports,a),r.exports}a.n=t=>{var e=t&&t.__esModule?()=>t.default:()=>t;return a.d(e,{a:e}),e},a.d=(t,e)=>{for(var n in e)a.o(e,n)&&!a.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:e[n]})},a.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),a.r=t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})};var n={};a.r(n),a.d(n,{allowedMetaFields:()=>we,contentFetchStatus:()=>Ee,contentSaveSource:()=>fe,contentSaveTranslate:()=>he,excerptSaveSource:()=>de,excerptSaveTranslate:()=>ue,metaFieldsSaveSource:()=>_e,metaFieldsSaveTranslate:()=>ye,selectedAiModel:()=>Se,setBlockRules:()=>be,slugSaveSource:()=>ge,slugSaveTranslate:()=>me,titleSaveSource:()=>ce,titleSaveTranslate:()=>pe,translationInfo:()=>ve});var o={};a.r(o),a.d(o,{contentFetchStatus:()=>Re,getAllowedMetaFields:()=>Le,getBlockRules:()=>ke,getSelectedAiModel:()=>Ae,getTranslatedString:()=>Ce,getTranslationEntry:()=>Te,getTranslationInfo:()=>xe});var r=a(338);const s=window.wp.element,l=window.wp.data,i=window.wp.i18n,c=t=>React.createElement("div",{className:"modal-header",key:t.modalRender},React.createElement("span",{className:"close",onClick:()=>{t.setPopupVisibility(!1)}},"×"),React.createElement("h2",{className:"notranslate"},(0,i.__)("Step 2 - Start Automatic Translation Process","autopoly-ai-translation-for-polylang-pro")),React.createElement("div",{className:"save_btn_cont"},React.createElement("button",{className:"notranslate save_it button button-primary",disabled:t.translatePendingStatus,onClick:t.updatePostData},t.translateButtonStatus?React.createElement(React.Fragment,null,React.createElement("span",{className:"updating-text"},(0,i.__)("Updating","autopoly-ai-translation-for-polylang-pro"),React.createElement("span",{className:"dot",style:{"--i":0}}),React.createElement("span",{className:"dot",style:{"--i":1}}),React.createElement("span",{className:"dot",style:{"--i":2}}))):(0,i.__)("Update Content","autopoly-ai-translation-for-polylang-pro")))),p=window.React;var d=a.n(p);const u=t=>{const e=t.skipTags||[],a="#atfp_open_translate_span#",n="#atfp_close_translate_span#",o="#atfp_temp_tag_open#",r="#atfp_temp_tag_close#",s="#atfp_less_then_symbol#",l="#atfp_greater_then_symbol#",i="#atfp_entity_open_translate_span#",c="#atfp_entity_close_translate_span#",p=t=>t.replace(new RegExp(a,"g"),"").replace(new RegExp(n,"g"),""),d=t=>{const o=document.createElement("div");o.innerHTML=t;const r=o.firstElementChild;if(!r)return t;let s=r.childNodes,l=s.length;if(l>0){s=Array.from(s).sort((t,e)=>3===t.nodeType?-1:3===e.nodeType?1:0);for(let t=0;t`${a}${p(t)}${n}`);e.textContent=t}else if(8===e.nodeType)e.textContent=e.textContent;else{let t=d(e.outerHTML);e.outerHTML=t}}}let i=r.outerHTML.match(/^<[^>]+>/)[0];const c=new RegExp(`${a}|${n}`,"g");i=i.replace(c,"");const u=r.tagName.toLowerCase(),g=new RegExp(``,"i"),m=r.outerHTML.match(g);if("' + + return '
    ' + container + '
    '; + } + + loader.className = 'atfp-loader-wrapper'; // Add the atfp class to the loader + loader.innerHTML = loaderContainer(); + + this.loader = loader; + } + } + + + window.addEventListener('load', () => { + const atfpCreateBlockObj = new atfpCreateNewBlock(); + + atfpCreateBlockObj.copyBtnInitialize(); + atfpCreateBlockObj.noticeInitialize(); + + const urlParams = new URLSearchParams(window.location.search); + let newBlock = ''; + + if (urlParams.has('atfp_new_block') && '' !== urlParams.get('atfp_new_block').trim()) { + newBlock = urlParams.get('atfp_new_block'); + atfpCreateBlockObj.addBlockInitialize(newBlock); + } + }); +})(); diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/assets/js/atfp-add-new-block.min.js b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/assets/js/atfp-add-new-block.min.js new file mode 100644 index 0000000..e242428 --- /dev/null +++ b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/assets/js/atfp-add-new-block.min.js @@ -0,0 +1 @@ +"use strict";(()=>{const{createBlock:createBlock}=wp.blocks,{dispatch:dispatch,select:select}=wp.data;class atfpCreateNewBlock{constructor(){this.updateBlockStore={},this.loaderRemove=null,this.loader=null,this.replaceAttributes=null,this.updateBlockId=new Array}copyTranslateText=()=>{const selection=window.getSelection(),range=document.createRange();range.selectNodeContents(document.getElementById("atfp-copy-text")),selection.removeAllRanges(),selection.addRange(range),document.execCommand("copy"),selection.removeAllRanges()};noticeInitialize=()=>{dispatch("core/notices").createNotice("info",'To enable translation, please include the Make This Content Available for Translation text in your block content. For help, watch the video and click "Copy Text" to use. Then, paste it into the section of your block you want automatically translated',{isDismissible:!1,id:"atfp-notice-id",actions:[{label:"Watch Video.",url:`${atfpAddBlockVars.atfp_demo_page_url}#custom-block-translate`}],__unstableHTML:!0}).then(()=>{const targetAnchor=document.querySelector(`a[href^="${atfpAddBlockVars.atfp_demo_page_url}#custom-block-translate"]`);targetAnchor&&targetAnchor.addEventListener("click",e=>{e.preventDefault(),window.open(targetAnchor.href,"_blank")})})};copyBtnInitialize=()=>{const copyBtn=document.createElement("div");copyBtn.id="atfp-copy-btn",copyBtn.innerHTML="Copy Text",copyBtn.addEventListener("click",this.copyTranslateText),copyBtn.ariaLabel="Copy Text",copyBtn.title='Click to copy the text "Make This Content Available for Translation"';const copyText=document.createElement("div");copyText.id="atfp-copy-text",copyText.innerHTML="Make This Content Available for Translation",document.body.appendChild(copyBtn),document.body.appendChild(copyText)};addBlockInitialize=newBlock=>{this.newBlock=newBlock,this.creteNewBlock(),this.skeletonLoader()};removeLoader=()=>{clearTimeout(this.loaderRemove),this.loaderRemove=setTimeout(()=>{this.loader&&this.loader.remove()},2e3)};creteNewBlock=()=>{const newBlock=createBlock(this.newBlock);this.updateBlockData(newBlock)};updateBlockData=async Block=>{await dispatch("core/block-editor").insertBlocks([Block]),setTimeout(()=>{const blockWrp=document.getElementById(`block-${Block.clientId}`);blockWrp&&blockWrp.appendChild(this.loader)},100),setTimeout(()=>{this.updateBlockContent(Block)},400)};updateBlockContent=Block=>{const newBlock=document.getElementById(`block-${Block.clientId}`);newBlock&&this.updateContent(newBlock)};updateContent=async ele=>{const element=ele;let i=1;if(this.removeLoader(),element)if("true"==element.contentEditable&&element.children.length<2)element.innerHTML="Make This Content Available for Translation";else{const innerElements=element.getElementsByTagName("*");for(let innerElement of innerElements)i===innerElements.length&&(this.removeLoader(),setTimeout(()=>{this.updateBlockFromStore()},500)),i++,["SCRIPT","STYLE","META","LINK","TITLE","NOSCRIPT","STYLE","SCRIPT","NOSCRIPT","STYLE","SCRIPT","NOSCRIPT","STYLE","SCRIPT","NOSCRIPT"].includes(innerElement.tagName)||innerElement.childNodes.length>0&&innerElement.childNodes.forEach(child=>{child.nodeType===Node.TEXT_NODE&&this.updateBlockAttr(innerElement,child)})}};updateBlockAttr=(innerElement,child)=>{let blockId=!1;if(innerElement.classList.contains("wp-block"))blockId=innerElement.dataset.block;else{const parentBlock=innerElement.closest(".wp-block");parentBlock&&(blockId=parentBlock.dataset.block)}const blockAttributes=select("core/block-editor").getBlockAttributes(blockId);let index=0;if(!this.updateBlockStore[blockId]){let attributes=JSON.parse(JSON.stringify(blockAttributes));this.updateBlockStore[blockId]={attributes:attributes},this.updateBlockStore[blockId].updateBlockData={}}const updateNestedAttributes=async(attributes,child)=>{const updateAttributes=async key=>{if(index++,"string"==typeof attributes[key]&&""!==attributes[key].trim()&&(attributes[key].trim()===child.textContent.trim()||attributes[key]===child.textContent.trim())){const originalValue=attributes[key],newValue="Make This Content Available for Translation "+index;this.updateBlockStore[blockId].updateBlockData[newValue.replace(/\s+/g,"-")]=originalValue,attributes[key]=newValue}};if("object"==typeof attributes&&null!==attributes)for(const key of Object.keys(attributes))await updateAttributes(key),"object"==typeof attributes[key]&&null!==attributes[key]&&await updateNestedAttributes(attributes[key],child)},blockStoreAttributes=this.updateBlockStore[blockId].attributes;updateNestedAttributes(blockStoreAttributes,child)};updateBlockFromStore=()=>{const blockStoreAttributes=this.updateBlockStore;Object.keys(blockStoreAttributes).forEach(blockId=>{const blockAttributes=blockStoreAttributes[blockId].attributes;this.removeLoader(),dispatch("core/block-editor").updateBlockAttributes(blockId,blockAttributes).then(()=>{clearTimeout(this.replaceAttributes),this.replaceAttributes=setTimeout(()=>{this.removeLoader();const blockIds=Object.keys(this.updateBlockStore);this.replaceBlockContent(blockIds[0])},500)})})};replaceBlockContent=blockId=>{const blockStoreAttributes=this.updateBlockStore,checkValidAttributes=(value=!1,blockId)=>{const blockElement=document.querySelector(`#block-${blockId}`),regex=new RegExp(value,"g"),matchFound=regex.test(blockElement.innerText);return matchFound},blockAttributes=blockStoreAttributes[blockId].attributes,upateNestedAttributes=async attributes=>{const updateAttributes=async key=>{if("string"==typeof attributes[key]&&attributes[key].includes("Make This Content Available for Translation"))try{const keyWithDashes=attributes[key].replace(/\s+/g,"-"),originalValue=this.updateBlockStore[blockId].updateBlockData[keyWithDashes],status=checkValidAttributes(attributes[key],blockId);attributes[key]=status?"Make This Content Available for Translation":originalValue}catch(e){console.log(`${attributes[key]} is not valid JSON.`)}};if("object"==typeof attributes&&null!==attributes)for(const key of Object.keys(attributes))await updateAttributes(key),"object"==typeof attributes[key]&&null!==attributes[key]&&await upateNestedAttributes(attributes[key])};upateNestedAttributes(blockAttributes),setTimeout(()=>{dispatch("core/block-editor").updateBlockAttributes(blockId,blockAttributes).then(()=>{const blockIds=Object.keys(this.updateBlockStore);if(blockIds.length>0){this.updateBlockId.push(blockId),dispatch("core/block-editor").selectBlock(null),this.removeLoader();const firstBlockId=blockIds.find(id=>!this.updateBlockId.includes(id));firstBlockId&&this.replaceBlockContent(firstBlockId)}})},500)};skeletonLoader=()=>{const loader=document.createElement("div"),loaderContainer=()=>{const container='';return'
    '+container+'
    '};loader.className="atfp-loader-wrapper",loader.innerHTML=loaderContainer(),this.loader=loader}}window.addEventListener("load",()=>{const atfpCreateBlockObj=new atfpCreateNewBlock;atfpCreateBlockObj.copyBtnInitialize(),atfpCreateBlockObj.noticeInitialize();const urlParams=new URLSearchParams(window.location.search);let newBlock="";urlParams.has("atfp_new_block")&&""!==urlParams.get("atfp_new_block").trim()&&(newBlock=urlParams.get("atfp_new_block"),atfpCreateBlockObj.addBlockInitialize(newBlock))})})(); \ No newline at end of file diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/assets/js/atfp-admin-views-link.js b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/assets/js/atfp-admin-views-link.js new file mode 100644 index 0000000..1e51b93 --- /dev/null +++ b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/assets/js/atfp-admin-views-link.js @@ -0,0 +1,34 @@ +jQuery(document).ready(function(){ + const atfpSubsubsubList = jQuery('.atfp_subsubsub'); + const atfpBulkTranslateBtn = jQuery('.atfpp-bulk-translate-btn'); + + if(atfpSubsubsubList.length){ + const $defaultSubsubsub = jQuery('ul.subsubsub:not(.atfp_subsubsub_list)'); + + if($defaultSubsubsub.length){ + $defaultSubsubsub.after(atfpSubsubsubList); + atfpSubsubsubList.show(); + } + } + + if(atfpBulkTranslateBtn.length){ + const $defaultFilter = jQuery('.actions:not(.bulkactions)'); + const $bulkAction=jQuery('.actions.bulkactions'); + + if($defaultFilter.length){ + $defaultFilter.each(function(){ + const clone=atfpBulkTranslateBtn.clone(true); + jQuery(this).append(clone); + clone.show(); + }); + + atfpBulkTranslateBtn.remove(); + }else if($bulkAction.length){ + $bulkAction.each(function(){ + const clone=atfpBulkTranslateBtn.clone(true); + jQuery(this).after(clone); + clone.show(); + }); + } + } +}); diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/assets/js/atfp-custom-data-table.js b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/assets/js/atfp-custom-data-table.js new file mode 100644 index 0000000..428dcea --- /dev/null +++ b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/assets/js/atfp-custom-data-table.js @@ -0,0 +1,221 @@ +class BlockFilterSorter { + constructor() { + this.tableBody = document.querySelector('.atfp-custom-data-table-table tbody'); + this.filters = document.querySelectorAll('.atfp-custom-data-table-filters .atfp-filter-tab'); + this.atfpDataTableObj = null; + this.saveButtonEnabled = false; + this.saveButtonText = false; + this.saveButtonClass = false; + this.saveButtonAction = false; + this.saveButtonNonce = false; + this.displayAjaxNotice=false; + this.ajaxUrl = false; + + if (window.atfpCustomTableDataObject) { + if (atfpCustomTableDataObject.save_button_enabled && '' !== atfpCustomTableDataObject.save_button_enabled) { + this.saveButtonEnabled = atfpCustomTableDataObject.save_button_enabled; + } + if (atfpCustomTableDataObject.save_button_text && '' !== atfpCustomTableDataObject.save_button_text) { + this.saveButtonText = atfpCustomTableDataObject.save_button_text; + } + if (atfpCustomTableDataObject.save_button_class && '' !== atfpCustomTableDataObject.save_button_class) { + this.saveButtonClass = atfpCustomTableDataObject.save_button_class; + } + if (atfpCustomTableDataObject.save_button_handler && '' !== atfpCustomTableDataObject.save_button_handler) { + this.saveButtonAction = atfpCustomTableDataObject.save_button_handler; + } + if (atfpCustomTableDataObject.save_button_nonce && '' !== atfpCustomTableDataObject.save_button_nonce) { + this.saveButtonNonce = atfpCustomTableDataObject.save_button_nonce; + } + if (atfpCustomTableDataObject.admin_url && '' !== atfpCustomTableDataObject.admin_url) { + this.ajaxUrl = atfpCustomTableDataObject.admin_url; + } + + const inputFields = document.querySelectorAll('#atfp-custom-datatable tbody input[name="atfp_fields_status"]'); + inputFields.forEach(input => { + input.addEventListener('change', this.updateStatusHandler.bind(this)); + }); + } + + if (this.tableBody) { + this.atfpDataTable(); + + this.filters.forEach(filter => { + filter.addEventListener('input', this.datatableFilterHandler.bind(this)); + }); + } + } + + atfpDataTable() { + if (this.tableBody) { + this.atfpDataTableObj = new DataTable('#atfp-custom-datatable', { + pageLength: 25, + infoCallback: function (settings, start, end, total, max) { + return `Showing ${start} to ${end} of ${max} records`; + } + }); + + this.atfpDataTableObj.on('draw.dt', function (e) { + const rows = jQuery(this).find('tbody tr'); + + if (rows.length.length === 0) { + this.atfpDataTableObj.empty(); + } + + const length = e.dt.page.info().length; + const page = e.dt.page.info().page; + + rows.each(function (index, row) { + const emptyCell = row.querySelector('td.dt-empty'); + if (!emptyCell) { + row.children[0].textContent = (page * length) + index + 1; + } + }); + }); + + const tableWrp = document.getElementById('atfp-custom-datatable_wrapper'); + const selectWrapper = document.querySelector('.atfp-custom-data-table-filters'); + selectWrapper.remove(); + tableWrp.prepend(selectWrapper); + + if (this.saveButtonEnabled && '' !== this.saveButtonText && 'false' !== this.saveButtonText) { + const saveButton = this.appendSaveButton(); + const lastRow = tableWrp.querySelector('.dt-layout-row:last-child'); + lastRow.before(saveButton); + + jQuery(`.${this.saveButtonClass}`).on('click', this.saveButtonHandler.bind(this)); + } + } + } + + datatableFilterHandler(e) { + if (this.atfpDataTableObj) { + let value = e.target.value; + let wrapper = e.target.closest('.atfp-filter-tab'); + let column = parseInt(wrapper.dataset.column); + let defaultValue = wrapper.dataset.default; + value = value === defaultValue ? false : value; + this.atfpDataTableObj.column(column).search(value ? new RegExp('^' + value, 'i') : '', false, false, false).draw(); + } + } + + updateStatusHandler(e) { + const table = jQuery('#atfp-custom-datatable').DataTable(); + + if (!table) return; // DataTable not initialized + + const $tr = jQuery(e.target).closest('tr'); + if (!$tr.length) return; // no row found + + const dtRow = table.row($tr); + if (!dtRow.node()) return; // row doesn’t exist in DataTable + + const checked = e.target.checked; + const status = checked ? 'Supported' : 'Unsupported'; + + // Make sure cell exists + const cell = dtRow.cell(dtRow.index(), 3); + if (!cell) return; + + // Update via DataTables API + cell.data(status); + } + + saveButtonHandler(e) { + e.preventDefault(); + const saveBtns = jQuery(`.${this.saveButtonClass}`); + + if (saveBtns.hasClass('saving')) { + return; + } + + if (!this.saveButtonAction || '' === this.saveButtonAction || !this.saveButtonNonce || '' === this.saveButtonNonce || !this.ajaxUrl || '' === this.ajaxUrl) { + return; + } + + const selectedCheckbox = []; + const tdNodes = this.atfpDataTableObj.column(4).nodes(); + + if (tdNodes.length > 0) { + Array.from(tdNodes).forEach(tdNode => { + const checkbox = tdNode.querySelector('input[type="checkbox"]'); + if (checkbox && checkbox.checked) { + selectedCheckbox.push(checkbox.value); + } + }); + } + + if (selectedCheckbox.length === 0) { + return; + } + + const apiSendData = { + action: this.saveButtonAction, + atfp_nonce: this.saveButtonNonce, + save_custom_fields_data: JSON.stringify(selectedCheckbox) + }; + + saveBtns.addClass('saving').html('Saving', true); + + fetch(this.ajaxUrl, { + method: 'POST', + headers: { + 'content-type': 'application/x-www-form-urlencoded; charset=UTF-8', + 'Accept': 'application/json', + }, + body: new URLSearchParams(apiSendData) + }) + .then(response => response.json()) + .then(data => { + saveBtns.removeClass('saving').html(this.saveButtonText, true); + + if (data.success) { + if (data.data.message) { + this.appendMessageNotice(data.data.message, 'success'); + } + } + }) + .catch(error => { + console.log(error); + if (error.data.message) { + this.appendMessageNotice(data.data.message, 'error'); + } + saveBtns.removeClass('saving').html(this.saveButtonText, true); + console.error(error); + }); + } + + appendMessageNotice(message, type) { + + if(this.displayAjaxNotice){ + jQuery('#atfp-custom-fields-message-notice').remove(); + clearTimeout(this.displayAjaxNotice); + } + + this.displayAjaxNotice=setTimeout(() => { + this.displayAjaxNotice=false; + jQuery('#atfp-custom-fields-message-notice').remove(); + }, 10000); + + let messageNotice = jQuery('

    ' + message + '

    '); + messageNotice.addClass('is-dismissible notice notice-' + type); + jQuery('.atfpp-dashboard-wrapper').before(messageNotice); + } + + appendSaveButton() { + + if (!this.saveButtonText || '' === this.saveButtonText || 'false' === this.saveButtonText || !this.saveButtonEnabled) { + return; + } + + const saveButton = document.createElement('button'); + saveButton.className = 'button button-primary ' + this.saveButtonClass; + saveButton.textContent = this.saveButtonText; + return saveButton; + } +} + +// Call the class after window load +window.addEventListener('load', () => { + new BlockFilterSorter(); +}); \ No newline at end of file diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/assets/js/atfp-custom-data-table.min.js b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/assets/js/atfp-custom-data-table.min.js new file mode 100644 index 0000000..a011bb5 --- /dev/null +++ b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/assets/js/atfp-custom-data-table.min.js @@ -0,0 +1 @@ +class BlockFilterSorter{constructor(){if(this.tableBody=document.querySelector(".atfp-custom-data-table-table tbody"),this.filters=document.querySelectorAll(".atfp-custom-data-table-filters .atfp-filter-tab"),this.atfpDataTableObj=null,this.saveButtonEnabled=!1,this.saveButtonText=!1,this.saveButtonClass=!1,this.saveButtonAction=!1,this.saveButtonNonce=!1,this.displayAjaxNotice=!1,this.ajaxUrl=!1,window.atfpCustomTableDataObject){atfpCustomTableDataObject.save_button_enabled&&""!==atfpCustomTableDataObject.save_button_enabled&&(this.saveButtonEnabled=atfpCustomTableDataObject.save_button_enabled),atfpCustomTableDataObject.save_button_text&&""!==atfpCustomTableDataObject.save_button_text&&(this.saveButtonText=atfpCustomTableDataObject.save_button_text),atfpCustomTableDataObject.save_button_class&&""!==atfpCustomTableDataObject.save_button_class&&(this.saveButtonClass=atfpCustomTableDataObject.save_button_class),atfpCustomTableDataObject.save_button_handler&&""!==atfpCustomTableDataObject.save_button_handler&&(this.saveButtonAction=atfpCustomTableDataObject.save_button_handler),atfpCustomTableDataObject.save_button_nonce&&""!==atfpCustomTableDataObject.save_button_nonce&&(this.saveButtonNonce=atfpCustomTableDataObject.save_button_nonce),atfpCustomTableDataObject.admin_url&&""!==atfpCustomTableDataObject.admin_url&&(this.ajaxUrl=atfpCustomTableDataObject.admin_url);const inputFields=document.querySelectorAll('#atfp-custom-datatable tbody input[name="atfp_fields_status"]');inputFields.forEach(input=>{input.addEventListener("change",this.updateStatusHandler.bind(this))})}this.tableBody&&(this.atfpDataTable(),this.filters.forEach(filter=>{filter.addEventListener("input",this.datatableFilterHandler.bind(this))}))}atfpDataTable(){if(this.tableBody){this.atfpDataTableObj=new DataTable("#atfp-custom-datatable",{pageLength:25,infoCallback:function(settings,start,end,total,max){return`Showing ${start} to ${end} of ${max} records`}}),this.atfpDataTableObj.on("draw.dt",(function(e){const rows=jQuery(this).find("tbody tr");0===rows.length.length&&this.atfpDataTableObj.empty();const length=e.dt.page.info().length,page=e.dt.page.info().page;rows.each((function(index,row){const emptyCell=row.querySelector("td.dt-empty");emptyCell||(row.children[0].textContent=page*length+index+1)}))}));const tableWrp=document.getElementById("atfp-custom-datatable_wrapper"),selectWrapper=document.querySelector(".atfp-custom-data-table-filters");if(selectWrapper.remove(),tableWrp.prepend(selectWrapper),this.saveButtonEnabled&&""!==this.saveButtonText&&"false"!==this.saveButtonText){const saveButton=this.appendSaveButton(),lastRow=tableWrp.querySelector(".dt-layout-row:last-child");lastRow.before(saveButton),jQuery(`.${this.saveButtonClass}`).on("click",this.saveButtonHandler.bind(this))}}}datatableFilterHandler(e){if(this.atfpDataTableObj){let value=e.target.value,wrapper=e.target.closest(".atfp-filter-tab"),column=parseInt(wrapper.dataset.column),defaultValue;value=value!==wrapper.dataset.default&&value,this.atfpDataTableObj.column(column).search(value?new RegExp("^"+value,"i"):"",!1,!1,!1).draw()}}updateStatusHandler(e){const table=jQuery("#atfp-custom-datatable").DataTable();if(!table)return;const $tr=jQuery(e.target).closest("tr");if(!$tr.length)return;const dtRow=table.row($tr);if(!dtRow.node())return;const checked=e.target.checked,status=checked?"Supported":"Unsupported",cell=dtRow.cell(dtRow.index(),3);cell&&cell.data(status)}saveButtonHandler(e){e.preventDefault();const saveBtns=jQuery(`.${this.saveButtonClass}`);if(saveBtns.hasClass("saving"))return;if(!this.saveButtonAction||""===this.saveButtonAction||!this.saveButtonNonce||""===this.saveButtonNonce||!this.ajaxUrl||""===this.ajaxUrl)return;const selectedCheckbox=[],tdNodes=this.atfpDataTableObj.column(4).nodes();if(tdNodes.length>0&&Array.from(tdNodes).forEach(tdNode=>{const checkbox=tdNode.querySelector('input[type="checkbox"]');checkbox&&checkbox.checked&&selectedCheckbox.push(checkbox.value)}),0===selectedCheckbox.length)return;const apiSendData={action:this.saveButtonAction,atfp_nonce:this.saveButtonNonce,save_custom_fields_data:JSON.stringify(selectedCheckbox)};saveBtns.addClass("saving").html('Saving',!0),fetch(this.ajaxUrl,{method:"POST",headers:{"content-type":"application/x-www-form-urlencoded; charset=UTF-8",Accept:"application/json"},body:new URLSearchParams(apiSendData)}).then(response=>response.json()).then(data=>{saveBtns.removeClass("saving").html(this.saveButtonText,!0),data.success&&data.data.message&&this.appendMessageNotice(data.data.message,"success")}).catch(error=>{console.log(error),error.data.message&&this.appendMessageNotice(data.data.message,"error"),saveBtns.removeClass("saving").html(this.saveButtonText,!0),console.error(error)})}appendMessageNotice(message,type){this.displayAjaxNotice&&(jQuery("#atfp-custom-fields-message-notice").remove(),clearTimeout(this.displayAjaxNotice)),this.displayAjaxNotice=setTimeout(()=>{this.displayAjaxNotice=!1,jQuery("#atfp-custom-fields-message-notice").remove()},1e4);let messageNotice=jQuery('

    '+message+"

    ");messageNotice.addClass("is-dismissible notice notice-"+type),jQuery(".atfpp-dashboard-wrapper").before(messageNotice)}appendSaveButton(){if(!this.saveButtonText||""===this.saveButtonText||"false"===this.saveButtonText||!this.saveButtonEnabled)return;const saveButton=document.createElement("button");return saveButton.className="button button-primary "+this.saveButtonClass,saveButton.textContent=this.saveButtonText,saveButton}}window.addEventListener("load",()=>{new BlockFilterSorter}); \ No newline at end of file diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/assets/js/atfp-elementor-translate-confirm-box.js b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/assets/js/atfp-elementor-translate-confirm-box.js new file mode 100644 index 0000000..12466ad --- /dev/null +++ b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/assets/js/atfp-elementor-translate-confirm-box.js @@ -0,0 +1,126 @@ +const { __, sprintf } = wp.i18n; + +const atfpElementorConfirmBox = { + init: function() { + this.pageTitleEvent=false; + if(window.atfpElementorConfirmBoxData){ + this.createConfirmBox(); + } + }, + + createConfirmBox: function() { + const sourceLangName=window.atfpElementorConfirmBoxData.sourceLangName; + const targetLangName=window.atfpElementorConfirmBoxData.targetLangName; + const confirmBox = jQuery(` + `); + + confirmBox.appendTo(jQuery('body')); + + confirmBox.find('button[data-value="yes"]').on('click', (e)=>{this.confirmTranslation(e)}); + confirmBox.find('button[data-value="no"]').on('click', (e)=>{e.preventDefault();this.closeConfirmBox();}); + }, + + closeConfirmBox: function() { + this.setPageTitle(); + const confirmBox = jQuery('.atfp-elementor-translate-confirm-box.modal-container'); + confirmBox.remove(); + }, + + confirmTranslation: function(e) { + this.setPageTitle(); + e.preventDefault(); + const postId=window.atfpElementorConfirmBoxData.postId; + const targetLangSlug=window.atfpElementorConfirmBoxData.targetLangSlug; + + if(postId && targetLangSlug) { + let oldData=localStorage.getItem('atfpElementorConfirmBox'); + let data={[postId+'_'+targetLangSlug]: true}; + + if(oldData && 'string' === typeof oldData && '' !== oldData) { + oldData=JSON.parse(oldData); + data={...oldData, ...data}; + } + + localStorage.setItem('atfpElementorConfirmBox', JSON.stringify(data)); + + const elementorButton=document.getElementById('elementor-editor-button'); + const elementorEditModeButton=document.getElementById('elementor-edit-mode-button'); + + if(elementorEditModeButton) { + elementorEditModeButton.click(); + }else if(elementorButton) { + elementorButton.click(); + } + + this.closeConfirmBox(); + } + }, + + setPageTitle: function() { + + if(window.atfpElementorConfirmBoxData.editorType !== 'classic') { + return; + } + + if(this.pageTitleEvent) { + return; + } + + this.pageTitleEvent=true; + + const elementorButtons=document.querySelectorAll('#elementor-editor-button, #elementor-edit-mode-button'); + + elementorButtons.forEach(button=>{ + button.addEventListener('click', (e)=>{ + e.preventDefault(); + + if(window.wp && window.elementorAdmin && window.elementorAdmin.getDefaultElements){ + const defaultElements=window.elementorAdmin.getDefaultElements(); + + if(defaultElements) { + $goToEditLink=defaultElements.$goToEditLink; + + if($goToEditLink) { + var $wpTitle = jQuery('#title'); + + if (!$wpTitle.val()) { + $wpTitle.val('Elementor #' + jQuery('#post_ID').val()); + } + + if (wp.autosave) { + wp.autosave.server.triggerSave(); + } + + jQuery(document).on('heartbeat-tick.autosave', function () { + window.elementorCommon.elements.$window.off('beforeunload.edit-post'); + location.href = $goToEditLink.attr('href'); + }); + } + } + } + }); + }); + } +}; + +jQuery(document).ready(function($) { + atfpElementorConfirmBox.init(); +}); \ No newline at end of file diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/assets/js/atfp-update-custom-blocks.js b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/assets/js/atfp-update-custom-blocks.js new file mode 100644 index 0000000..0185d60 --- /dev/null +++ b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/assets/js/atfp-update-custom-blocks.js @@ -0,0 +1,333 @@ +const { parse } = wp.blocks; +const { select, subscribe } = wp.data; + +class blockDataReterive { + constructor() { + this.blockLists = []; + this.customBlockTranslateData = {}; + this.customBlocksData = []; + this.loaderContainer = null; + this.init(); + } + + init = () => { + this.fetchCustomBlocks(); + + // Create full-page overlay and append to + this.loaderContainer = document.createElement('div'); + this.loaderContainer.className = 'atfp-overlay'; + this.loaderContainer.setAttribute('role', 'status'); + this.loaderContainer.setAttribute('aria-live', 'polite'); + this.loaderContainer.innerHTML = this.getOverlayTemplate(); // see section 2 + document.body.appendChild(this.loaderContainer); + document.body.classList.add('atfp-overlay-open'); + } + + getBlocks = (blocks) => { + const innerBlocks = (block) => { + const innerBlock = block.innerBlocks; + if (innerBlock.length > 0) { + innerBlock.forEach(innerBlock => { + this.customBlocksData.push(innerBlock); + innerBlocks(innerBlock); + }); + } + } + + const blockLists = blocks; + + blockLists.forEach(block => { + innerBlocks(block); + }); + + + this.customBlocksData = [...this.customBlocksData, ...blockLists]; + + this.getBlockData(); + } + + fetchCustomBlocks = () => { + /** + * Prepare data to send in API request. + */ + const apiSendData = { + atfp_nonce: atfp_block_update_object.ajax_nonce, + action: atfp_block_update_object.action_get_content + }; + const apiUrl = atfp_block_update_object.ajax_url; + + fetch(apiUrl, { + method: 'POST', + headers: { + 'content-type': 'application/x-www-form-urlencoded; charset=UTF-8', + }, + body: new URLSearchParams(apiSendData) + }) + .then(response => response.json()) + .then(data => { + if (data.message === 'No custom blocks found.') { + this.loaderContainer && this.loaderContainer.remove(); + return; + } + + const customBlocks = parse(data.data.block_data); + + this.getBlocks(customBlocks); + + // Save new block translate data + this.saveBlockData(); + }) + .catch(error => { + this.loaderContainer && this.loaderContainer.remove(); + console.error('Error fetching block rules:', error); + }); + } + + saveBlockData = () => { + if (Object.keys(this.customBlockTranslateData).length < 1) { + this.loaderContainer && this.loaderContainer.remove(); + return; + } + + + /** + * Prepare data to send in API request & update latest translate block data. + */ + const apiSendData = { + atfp_nonce: atfp_block_update_object.ajax_nonce, + action: atfp_block_update_object.action_update_content, + save_block_data: JSON.stringify(this.customBlockTranslateData) + }; + + const apiUrl = atfp_block_update_object.ajax_url; + + fetch(apiUrl, { + method: 'POST', + headers: { + 'content-type': 'application/x-www-form-urlencoded; charset=UTF-8', + }, + body: new URLSearchParams(apiSendData) + }) + .then(response => response.json()) + .then(data => { + this.setOverlayState('success'); + this.teardownOverlay(); + if (data.success && data.data.message) { + console.log(data.data.message); + } + }) + .catch(error => { + this.setOverlayState('error'); + this.teardownOverlay(); + console.error('Error fetching block rules:', error); + }); + } + + nestedAttrValue = (idsArr) => { + const convertToArrays = (obj) => { + // Check if obj is an object + if (typeof obj !== 'object' || obj === null) { + return obj; + } + + // Process each key-value pair in the object + for (let key in obj) { + if (obj.hasOwnProperty(key)) { + // If the current value is an object and has the key 'atfp_array_key_replace' + if (typeof obj[key] === 'object' && obj[key] !== null && obj[key].hasOwnProperty('atfp_array_key_replace')) { + // Replace the value with 'true' directly in the array + obj[key] = Object.values(obj[key]); + obj[key] = convertToArrays(obj[key]); + } else { + // Recursively call convertToArrays for nested objects or arrays + obj[key] = convertToArrays(obj[key]); + } + } + } + + return obj; + } + + const deepMerge = (target, source) => { + + for (const key in source) { + if (source[key] instanceof Object && key in target) { + Object.assign(source[key], deepMerge(target[key], source[key])); + } + } + Object.assign(target || {}, source); + return target; + }; + + let currentElement = {}; + let tempObj = currentElement; + let lastKey = idsArr[idsArr.length - 1]; + idsArr.slice(0, -1).forEach((key) => { + tempObj[key] = tempObj[key] || {}; + tempObj = tempObj[key]; + }); + tempObj[lastKey] = true; + + const obj = convertToArrays(currentElement); + deepMerge(this.customBlockTranslateData, obj); + } + + filterAttr = (idsArray, value) => { + if (null === value || undefined === value) { + return; + } + + if (Object.getPrototypeOf(value) === Array.prototype) { + this.filterBlockArrayAttr(idsArray, value); + } else if (Object.getPrototypeOf(value) === Object.prototype) { + this.filterBlockObjectAttr(idsArray, value); + } else if (typeof value === 'string' && /Make This Content Available for Translation/i.test(value)) { + this.nestedAttrValue(idsArray, value); + } else if (value instanceof wp.richText.RichTextData && /Make This Content Available for Translation/i.test(value.originalHTML)) { + this.nestedAttrValue(idsArray, value.originalHTML); + } + } + + filterBlockArrayAttr = (idsArr, blockData) => { + const newIdArr = new Array(...idsArr); + newIdArr.push('atfp_array_key_replace'); + blockData.forEach((value, key) => { + if ((typeof value === 'string' && /Make This Content Available for Translation/i.test(value)) || (![null, undefined].includes(value) && [Array.prototype, Object.prototype].includes(Object.getPrototypeOf(value)))) { + this.filterAttr(newIdArr, value) + }; + }); + } + + filterBlockObjectAttr = (idsArr, blockData) => { + Object.keys(blockData).forEach(key => { + const newIdArr = new Array(...idsArr); + const value = blockData[key]; + if (value !== null && value !== undefined) { + if ((typeof value === 'string' && /Make This Content Available for Translation/i.test(value)) || [Array.prototype, Object.prototype].includes(Object.getPrototypeOf(value))) { + newIdArr.push(key); + this.filterAttr(newIdArr, blockData[key]); + }; + } + }) + } + + filterBlockAttribute = (blockData) => { + Object.keys(blockData).map(clientId => { + const blockName = Object.keys(blockData[clientId])[0]; + const attributes = blockData[clientId][blockName]; + Object.keys(attributes).forEach(keytwo => { + const value = attributes[keytwo]; + const idsArray = new Array(blockName, "attributes", keytwo); + this.filterAttr(idsArray, value); + }); + + }) + } + + getBlockData = () => { + if (typeof this.customBlocksData !== 'object' || Object.keys(this.customBlocksData).length === 0) { + return; + } + + const blockData = this.customBlocksData; + const blockAttributes = {}; + Object.values(blockData).forEach(block => { + if (Object.values(block.attributes).length > 0) { + blockAttributes[block.clientId] = {}; + blockAttributes[block.clientId][block.name] = block.attributes; + } + }); + + if (Object.values(blockAttributes).length > 0) { + this.filterBlockAttribute(blockAttributes); + } + } + + setOverlayState = (state /* 'loading' | 'success' | 'error' */) => { + if (!this.loaderContainer) return; + const panel = this.loaderContainer.querySelector('.atfp-overlay .atfp-box'); + if (panel) panel.setAttribute('data-state', state); + }; + + teardownOverlay = (delayMs = 3000) => { + if (!this.loaderContainer) return; + setTimeout(() => { + this.loaderContainer.classList.add('atfp-overlay--closing'); + setTimeout(() => { + this.loaderContainer.remove(); + this.loaderContainer = null; + document.body.classList.remove('atfp-overlay-open'); + }, 300); + }, delayMs); + }; + + getOverlayTemplate = () => { + return ` +
    +
    +
    +
    + + + + +
    +
    Saving block content
    +
    Supported block content has been updated
    +
    Update failed
    + +
    + Please don’t close or refresh this window until the update is complete. +
    +
    + Supported block content has been updated. You may continue. +
    +
    + Something went wrong. You can retry without closing this window. +
    +
    +
    + +
    +
    +
    + `; + } + +} + +const debounce = (func, delay) => { + let timeoutId; + return function (...args) { + clearTimeout(timeoutId); + timeoutId = setTimeout(() => func.apply(this, args), delay); + }; +}; + +let isBlockContentUpdating = false; +const saveBlockContent = debounce(() => { + new blockDataReterive(); + isBlockContentUpdating = false; +}, 300); + +if (select && select('core/editor') && subscribe) { + subscribe(() => { + const { + isCurrentPostPublished, + isSavingPost, + isPublishingPost, + isAutosavingPost, + } = select('core/editor'); + + const isAutoSaving = isAutosavingPost(); + const isPublishing = isPublishingPost(); + const isSaving = isSavingPost(); + const postPublished = isCurrentPostPublished(); + + if ((isPublishing || (postPublished && isSaving)) && !isAutoSaving && !isBlockContentUpdating) { + isBlockContentUpdating = true; + saveBlockContent(); + } + + }) +} \ No newline at end of file diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/assets/js/atfp-update-custom-blocks.min.js b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/assets/js/atfp-update-custom-blocks.min.js new file mode 100644 index 0000000..70812ce --- /dev/null +++ b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/assets/js/atfp-update-custom-blocks.min.js @@ -0,0 +1 @@ +const{parse:parse}=wp.blocks,{select:select,subscribe:subscribe}=wp.data;class blockDataReterive{constructor(){this.blockLists=[],this.customBlockTranslateData={},this.customBlocksData=[],this.loaderContainer=null,this.init()}init=()=>{this.fetchCustomBlocks(),this.loaderContainer=document.createElement("div"),this.loaderContainer.className="atfp-overlay",this.loaderContainer.setAttribute("role","status"),this.loaderContainer.setAttribute("aria-live","polite"),this.loaderContainer.innerHTML=this.getOverlayTemplate(),document.body.appendChild(this.loaderContainer),document.body.classList.add("atfp-overlay-open")};getBlocks=blocks=>{const innerBlocks=block=>{const innerBlock=block.innerBlocks;innerBlock.length>0&&innerBlock.forEach(innerBlock=>{this.customBlocksData.push(innerBlock),innerBlocks(innerBlock)})},blockLists=blocks;blockLists.forEach(block=>{innerBlocks(block)}),this.customBlocksData=[...this.customBlocksData,...blockLists],this.getBlockData()};fetchCustomBlocks=()=>{const apiSendData={atfp_nonce:atfp_block_update_object.ajax_nonce,action:atfp_block_update_object.action_get_content},apiUrl=atfp_block_update_object.ajax_url;fetch(apiUrl,{method:"POST",headers:{"content-type":"application/x-www-form-urlencoded; charset=UTF-8"},body:new URLSearchParams(apiSendData)}).then(response=>response.json()).then(data=>{if("No custom blocks found."===data.message)return void(this.loaderContainer&&this.loaderContainer.remove());const customBlocks=parse(data.data.block_data);this.getBlocks(customBlocks),this.saveBlockData()}).catch(error=>{this.loaderContainer&&this.loaderContainer.remove(),console.error("Error fetching block rules:",error)})};saveBlockData=()=>{if(Object.keys(this.customBlockTranslateData).length<1)return void(this.loaderContainer&&this.loaderContainer.remove());const apiSendData={atfp_nonce:atfp_block_update_object.ajax_nonce,action:atfp_block_update_object.action_update_content,save_block_data:JSON.stringify(this.customBlockTranslateData)},apiUrl=atfp_block_update_object.ajax_url;fetch(apiUrl,{method:"POST",headers:{"content-type":"application/x-www-form-urlencoded; charset=UTF-8"},body:new URLSearchParams(apiSendData)}).then(response=>response.json()).then(data=>{this.setOverlayState("success"),this.teardownOverlay(),data.success&&data.data.message&&console.log(data.data.message)}).catch(error=>{this.setOverlayState("error"),this.teardownOverlay(),console.error("Error fetching block rules:",error)})};nestedAttrValue=idsArr=>{const convertToArrays=obj=>{if("object"!=typeof obj||null===obj)return obj;for(let key in obj)obj.hasOwnProperty(key)&&("object"==typeof obj[key]&&null!==obj[key]&&obj[key].hasOwnProperty("atfp_array_key_replace")?(obj[key]=Object.values(obj[key]),obj[key]=convertToArrays(obj[key])):obj[key]=convertToArrays(obj[key]));return obj},deepMerge=(target,source)=>{for(const key in source)source[key]instanceof Object&&key in target&&Object.assign(source[key],deepMerge(target[key],source[key]));return Object.assign(target||{},source),target};let currentElement={},tempObj=currentElement,lastKey=idsArr[idsArr.length-1];idsArr.slice(0,-1).forEach(key=>{tempObj[key]=tempObj[key]||{},tempObj=tempObj[key]}),tempObj[lastKey]=!0;const obj=convertToArrays(currentElement);deepMerge(this.customBlockTranslateData,obj)};filterAttr=(idsArray,value)=>{null!=value&&(Object.getPrototypeOf(value)===Array.prototype?this.filterBlockArrayAttr(idsArray,value):Object.getPrototypeOf(value)===Object.prototype?this.filterBlockObjectAttr(idsArray,value):"string"==typeof value&&/Make This Content Available for Translation/i.test(value)?this.nestedAttrValue(idsArray,value):value instanceof wp.richText.RichTextData&&/Make This Content Available for Translation/i.test(value.originalHTML)&&this.nestedAttrValue(idsArray,value.originalHTML))};filterBlockArrayAttr=(idsArr,blockData)=>{const newIdArr=new Array(...idsArr);newIdArr.push("atfp_array_key_replace"),blockData.forEach((value,key)=>{("string"==typeof value&&/Make This Content Available for Translation/i.test(value)||![null,void 0].includes(value)&&[Array.prototype,Object.prototype].includes(Object.getPrototypeOf(value)))&&this.filterAttr(newIdArr,value)})};filterBlockObjectAttr=(idsArr,blockData)=>{Object.keys(blockData).forEach(key=>{const newIdArr=new Array(...idsArr),value=blockData[key];null!=value&&("string"==typeof value&&/Make This Content Available for Translation/i.test(value)||[Array.prototype,Object.prototype].includes(Object.getPrototypeOf(value)))&&(newIdArr.push(key),this.filterAttr(newIdArr,blockData[key]))})};filterBlockAttribute=blockData=>{Object.keys(blockData).map(clientId=>{const blockName=Object.keys(blockData[clientId])[0],attributes=blockData[clientId][blockName];Object.keys(attributes).forEach(keytwo=>{const value=attributes[keytwo],idsArray=new Array(blockName,"attributes",keytwo);this.filterAttr(idsArray,value)})})};getBlockData=()=>{if("object"!=typeof this.customBlocksData||0===Object.keys(this.customBlocksData).length)return;const blockData=this.customBlocksData,blockAttributes={};Object.values(blockData).forEach(block=>{Object.values(block.attributes).length>0&&(blockAttributes[block.clientId]={},blockAttributes[block.clientId][block.name]=block.attributes)}),Object.values(blockAttributes).length>0&&this.filterBlockAttribute(blockAttributes)};setOverlayState=state=>{if(!this.loaderContainer)return;const panel=this.loaderContainer.querySelector(".atfp-overlay .atfp-box");panel&&panel.setAttribute("data-state",state)};teardownOverlay=(delayMs=3e3)=>{this.loaderContainer&&setTimeout(()=>{this.loaderContainer.classList.add("atfp-overlay--closing"),setTimeout(()=>{this.loaderContainer.remove(),this.loaderContainer=null,document.body.classList.remove("atfp-overlay-open")},300)},delayMs)};getOverlayTemplate=()=>'\n
    \n
    \n
    \n
    \n \n \n \n\n
    \n
    Saving block content
    \n
    Supported block content has been updated
    \n
    Update failed
    \n\n
    \n Please don’t close or refresh this window until the update is complete.\n
    \n
    \n Supported block content has been updated. You may continue.\n
    \n
    \n Something went wrong. You can retry without closing this window.\n
    \n
    \n
    \n\n
    \n
    \n
    \n '}const debounce=(func,delay)=>{let timeoutId;return function(...args){clearTimeout(timeoutId),timeoutId=setTimeout(()=>func.apply(this,args),delay)}};let isBlockContentUpdating=!1;const saveBlockContent=debounce(()=>{new blockDataReterive,isBlockContentUpdating=!1},300);select&&select("core/editor")&&subscribe&&subscribe(()=>{const{isCurrentPostPublished:isCurrentPostPublished,isSavingPost:isSavingPost,isPublishingPost:isPublishingPost,isAutosavingPost:isAutosavingPost}=select("core/editor"),isAutoSaving=isAutosavingPost(),isPublishing=isPublishingPost(),isSaving=isSavingPost(),postPublished=isCurrentPostPublished();!(isPublishing||postPublished&&isSaving)||isAutoSaving||isBlockContentUpdating||(isBlockContentUpdating=!0,saveBlockContent())}); \ No newline at end of file diff --git a/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/assets/js/dataTables.min.js b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/assets/js/dataTables.min.js new file mode 100644 index 0000000..7ef0c67 --- /dev/null +++ b/wp-content/plugins/autopoly-ai-translation-for-polylang-pro/assets/js/dataTables.min.js @@ -0,0 +1,4 @@ +/*! DataTables 2.1.8 + * © SpryMedia Ltd - datatables.net/license + */ +!function(n){"use strict";var a;"function"==typeof define&&define.amd?define(["jquery"],function(t){return n(t,window,document)}):"object"==typeof exports?(a=require("jquery"),"undefined"==typeof window?module.exports=function(t,e){return t=t||window,e=e||a(t),n(e,t,t.document)}:module.exports=n(a,window,window.document)):window.DataTable=n(jQuery,window,document)}(function(H,W,_){"use strict";function f(t){var e=parseInt(t,10);return!isNaN(e)&&isFinite(t)?e:null}function s(t,e,n,a){var r=typeof t,o="string"==r;return"number"==r||"bigint"==r||!(!a||!T(t))||(e&&o&&(t=E(t,e)),n&&o&&(t=t.replace(P,"")),!isNaN(parseFloat(t))&&isFinite(t))}function c(t,e,n,a){var r;return!(!a||!T(t))||("string"!=typeof t||!t.match(/<(input|select)/i))&&(T(r=t)||"string"==typeof r)&&!!s(L(t),e,n,a)||null}function b(t,e,n,a){var r=[],o=0,i=e.length;if(void 0!==a)for(;o").prependTo(this),fastData:function(t,e,n){return q(c,t,e,n)}}),n=(c.nTable=this,c.oInit=t,o.push(c),c.api=new X(c),c.oInstance=1===E.length?E:r.dataTable(),Q(t),t.aLengthMenu&&!t.iDisplayLength&&(t.iDisplayLength=Array.isArray(t.aLengthMenu[0])?t.aLengthMenu[0][0]:H.isPlainObject(t.aLengthMenu[0])?t.aLengthMenu[0].value:t.aLengthMenu[0]),t=te(H.extend(!0,{},a),t),z(c.oFeatures,t,["bPaginate","bLengthChange","bFilter","bSort","bSortMulti","bInfo","bProcessing","bAutoWidth","bSortClasses","bServerSide","bDeferRender"]),z(c,t,["ajax","fnFormatNumber","sServerMethod","aaSorting","aaSortingFixed","aLengthMenu","sPaginationType","iStateDuration","bSortCellsTop","iTabIndex","sDom","fnStateLoadCallback","fnStateSaveCallback","renderer","searchDelay","rowId","caption","layout","orderDescReverse","typeDetect",["iCookieDuration","iStateDuration"],["oSearch","oPreviousSearch"],["aoSearchCols","aoPreSearchCols"],["iDisplayLength","_iDisplayLength"]]),z(c.oScroll,t,[["sScrollX","sX"],["sScrollXInner","sXInner"],["sScrollY","sY"],["bScrollCollapse","bCollapse"]]),z(c.oLanguage,t,"fnInfoCallback"),Y(c,"aoDrawCallback",t.fnDrawCallback),Y(c,"aoStateSaveParams",t.fnStateSaveParams),Y(c,"aoStateLoadParams",t.fnStateLoadParams),Y(c,"aoStateLoaded",t.fnStateLoaded),Y(c,"aoRowCallback",t.fnRowCallback),Y(c,"aoRowCreatedCallback",t.fnCreatedRow),Y(c,"aoHeaderCallback",t.fnHeaderCallback),Y(c,"aoFooterCallback",t.fnFooterCallback),Y(c,"aoInitComplete",t.fnInitComplete),Y(c,"aoPreDrawCallback",t.fnPreDrawCallback),c.rowIdFn=U(t.rowId),c),d=(V.__browser||(f={},V.__browser=f,p=H("
    ").css({position:"fixed",top:0,left:-1*W.pageXOffset,height:1,width:1,overflow:"hidden"}).append(H("
    ").css({position:"absolute",top:1,left:1,width:100,overflow:"scroll"}).append(H("
    ").css({width:"100%",height:10}))).appendTo("body"),d=p.children(),u=d.children(),f.barWidth=d[0].offsetWidth-d[0].clientWidth,f.bScrollbarLeft=1!==Math.round(u.offset().left),p.remove()),H.extend(n.oBrowser,V.__browser),n.oScroll.iBarWidth=V.__browser.barWidth,c.oClasses),f=(H.extend(d,V.ext.classes,t.oClasses),r.addClass(d.table),c.oFeatures.bPaginate||(t.iDisplayStart=0),void 0===c.iInitDisplayStart&&(c.iInitDisplayStart=t.iDisplayStart,c._iDisplayStart=t.iDisplayStart),t.iDeferLoading),h=(null!==f&&(c.deferLoading=!0,u=Array.isArray(f),c._iRecordsDisplay=u?f[0]:f,c._iRecordsTotal=u?f[1]:f),[]),p=this.getElementsByTagName("thead"),n=At(c,p[0]);if(t.aoColumns)h=t.aoColumns;else if(n.length)for(R=n[e=0].length;e").appendTo(r):n).html(c.caption),n.length&&(n[0]._captionSide=n.css("caption-side"),c.captionNode=n[0]),0===p.length&&(p=H("").appendTo(r)),c.nTHead=p[0],H("tr",p).addClass(d.thead.row),r.children("tbody")),n=(0===n.length&&(n=H("").insertAfter(p)),c.nTBody=n[0],r.children("tfoot")),O=(0===n.length&&(n=H("").appendTo(r)),c.nTFoot=n[0],H("tr",n).addClass(d.tfoot.row),c.aiDisplay=c.aiDisplayMaster.slice(),c.bInitialised=!0,c.oLanguage);H.extend(!0,O,t.oLanguage),O.sUrl?H.ajax({dataType:"json",url:O.sUrl,success:function(t){B(a.oLanguage,t),H.extend(!0,O,t,c.oInit.oLanguage),G(c,null,"i18n",[c],!0),Mt(c)},error:function(){$(c,0,"i18n file loading error",21),Mt(c)}}):(G(c,null,"i18n",[c],!0),Mt(c))}}),E=null,this)},g=(V.ext=C={buttons:{},classes:{},builder:"-source-",errMode:"alert",feature:[],features:{},search:[],selector:{cell:[],column:[],row:[]},legacy:{ajax:null},pager:{},renderer:{pageButton:{},header:{}},order:{},type:{className:{},detect:[],render:{},search:{},order:{}},_unique:0,fnVersionCheck:V.fnVersionCheck,iApiIndex:0,sVersion:V.version},H.extend(C,{afnFiltering:C.search,aTypes:C.type.detect,ofnSearch:C.type.search,oSort:C.type.order,afnSortData:C.order,aoFeatures:C.feature,oStdClasses:C.classes,oPagination:C.pager}),H.extend(V.ext.classes,{container:"dt-container",empty:{row:"dt-empty"},info:{container:"dt-info"},layout:{row:"dt-layout-row",cell:"dt-layout-cell",tableRow:"dt-layout-table",tableCell:"",start:"dt-layout-start",end:"dt-layout-end",full:"dt-layout-full"},length:{container:"dt-length",select:"dt-input"},order:{canAsc:"dt-orderable-asc",canDesc:"dt-orderable-desc",isAsc:"dt-ordering-asc",isDesc:"dt-ordering-desc",none:"dt-orderable-none",position:"sorting_"},processing:{container:"dt-processing"},scrolling:{body:"dt-scroll-body",container:"dt-scroll",footer:{self:"dt-scroll-foot",inner:"dt-scroll-footInner"},header:{self:"dt-scroll-head",inner:"dt-scroll-headInner"}},search:{container:"dt-search",input:"dt-input"},table:"dataTable",tbody:{cell:"",row:""},thead:{cell:"",row:""},tfoot:{cell:"",row:""},paging:{active:"current",button:"dt-paging-button",container:"dt-paging",disabled:"disabled",nav:""}}),{}),F=/[\r\n\u2028]/g,N=/<([^>]*>)/g,j=Math.pow(2,28),R=/^\d{2,4}[./-]\d{1,2}[./-]\d{1,2}([T ]{1}\d{1,2}[:.]\d{2}([.:]\d{2})?)?$/,O=new RegExp("(\\"+["/",".","*","+","?","|","(",")","[","]","{","}","\\","$","^","-"].join("|\\")+")","g"),P=/['\u00A0,$£€¥%\u2009\u202F\u20BD\u20a9\u20BArfkɃΞ]/gi,T=function(t){return!t||!0===t||"-"===t},E=function(t,e){return g[e]||(g[e]=new RegExp(Pt(e),"g")),"string"==typeof t&&"."!==e?t.replace(/\./g,"").replace(g[e],"."):t},m=function(t,e,n){var a=[],r=0,o=t.length;if(void 0!==n)for(;rj)throw new Error("Exceeded max str len");var e;for(t=t.replace(N,"");(t=(e=t).replace(/ +