Add PSR HTTP Message Interfaces and Dependencies

- Implemented StreamInterface, UploadedFileInterface, and UriInterface as per PSR standards.
- Added getallheaders function to retrieve HTTP headers in a compatible manner.
- Included LICENSE files for ralouphie/getallheaders and symfony/deprecation-contracts.
- Introduced function for triggering deprecation notices in Symfony.
This commit is contained in:
2025-12-28 12:44:00 +01:00
parent cf600ae727
commit cd264483f8
410 changed files with 60841 additions and 16 deletions

View File

@@ -0,0 +1,491 @@
<?php
/*
Plugin Name: AutoPoly - AI Translation For Polylang
Plugin URI:
Description: simple description
Version: 1.0.0.0
*/
require_once "class-ai-automatic-translations-for-polylang-base.php";
class AIAutomaticTranslationsForPolylang {
public $plugin_file=ATFPP_FILE;
public $response_obj;
public $license_message;
public $slug="atfpp";
public static $form_status = false;
private const OPTION_LICENSE_KEY = 'AIAutomaticTranslationsForPolylang_lic_Key';
private const OPTION_LICENSE_EMAIL = 'AIAutomaticTranslationsForPolylang_lic_email';
private $showMessage = false;
private $licenseMessage = '';
private $responseObj = null;
function __construct() {
$this->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';
?>
<div class="atfpp-dashboard-wrapper">
<div class="atfpp-dashboard-header">
<div class="atfpp-dashboard-header-left">
<a href="?page=polylang-atfpp-dashboard&tab=dashboard" class="atfpp-dashboard-logo-link">
<img src="<?php echo esc_url(ATFPP_URL . 'admin/atfpp-dashboard/images/polylang-addon-logo.svg'); ?>"
alt="<?php esc_attr_e('Polylang Addon Logo', $text_domain); ?>">
</a>
<div class="atfpp-dashboard-tab-title">
<span>↳</span> <?php echo esc_html($valid_tabs[$current_tab]); ?>
</div>
</div>
<div class="atfpp-dashboard-header-right">
<span><?php esc_html('AutoPoly - AI Translation For Polylang'); ?></span>
<?php foreach ($buttons as $button): ?>
<a href="<?php echo esc_url($button['url']); ?>"
class="atfpp-dashboard-btn"
target="_blank"
aria-label="<?php echo isset($button['alt']) ? esc_attr($button['alt']) : ''; ?>">
<?php if (isset($button['img'])): ?>
<img src="<?php echo esc_url(ATFPP_URL . 'admin/atfpp-dashboard/images/' . $button['img']); ?>"
alt="<?php echo esc_attr($button['alt']); ?>">
<?php endif; ?>
<?php if (isset($button['text'])): ?>
<span><?php echo esc_html($button['text']); ?></span>
<?php endif; ?>
</a>
<?php endforeach; ?>
</div>
</div>
<nav class="nav-tab-wrapper" aria-label="<?php esc_attr_e('Dashboard navigation', $text_domain); ?>">
<?php foreach ($valid_tabs as $tab_key => $tab_title): ?>
<a href="?page=polylang-atfpp-dashboard&tab=<?php echo esc_attr($tab_key); ?>"
class="nav-tab <?php echo esc_attr($tab === $tab_key ? 'nav-tab-active' : ''); ?>">
<?php echo esc_html($tab_title); ?>
</a>
<?php endforeach; ?>
</nav>
<div class="tab-content">
<?php
$allowed_tabs = ['dashboard', 'ai-translations', 'settings', 'license', 'support-blocks', 'custom-fields','glossary'];
if(!in_array($current_tab, $allowed_tabs)) {
$current_tab = 'dashboard';
}
$view_file = $file_prefix . $current_tab . '.php';
if (file_exists($view_file)) {
require_once $view_file;
}
if($current_tab === 'license' || $current_tab === 'settings') {
$licenseKey = get_option(self::OPTION_LICENSE_KEY,"");
$liceEmail = get_option( self::OPTION_LICENSE_EMAIL,"");
AI_Automatic_Translations_For_Polylang_Base::add_on_delete(function(){
delete_option(self::OPTION_LICENSE_KEY);
});
if(AI_Automatic_Translations_For_Polylang_Base::CheckWPPlugin($licenseKey,$liceEmail,$this->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;
}
?>
</div>
<?php
$footer_file = $file_prefix . 'footer.php';
if (file_exists($footer_file)) {
require_once $footer_file;
}
?>
</div>
<?php
}
private function atfpp_get_valid_tabs($text_domain) {
return [
'dashboard' => __('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']);
?>
<div class="notice notice-error is-dismissible">
<p><?php echo esc_html($error_message); ?></p>
</div>
<?php
}
if (isset($_GET['activated']) && $_GET['activated'] === 'true') {
?>
<div class="notice notice-success is-dismissible">
<p><?php _e('License activated successfully!', 'autopoly-ai-translation-for-polylang-pro'); ?></p>
</div>
<?php
}
if (isset($_GET['deactivated']) && $_GET['deactivated'] === 'true') {
?>
<div class="notice notice-info is-dismissible">
<p><?php _e('License deactivated successfully!', 'autopoly-ai-translation-for-polylang-pro'); ?></p>
</div>
<?php
}
if ($this->showMessage && !empty($this->licenseMessage)) {
?>
<div class="notice notice-error is-dismissible">
<p><?php echo esc_html($this->licenseMessage); ?></p>
</div>
<?php
}
}
}
private function atfpp_get_error_message($error_code) {
$messages = array(
'missing_key' => __('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)) {
?>
<div class="notice notice-error is-dismissible">
<p><?php echo esc_html($this->licenseMessage); ?></p>
</div>
<?php
}
}
}
new AIAutomaticTranslationsForPolylang();

View File

@@ -0,0 +1,99 @@
.el-atfpp-license-container {
margin: 20px;
padding: 35px;
background: #fff;
border-radius: 4px;
display: flex;
}
.el-atfpp-license-container .el-atfpp-license-field {
display: block;
margin-bottom: 15px;
}
.el-atfpp-license-container .el-atfpp-license-field input {
font-size: 100%;
/* padding: 8px 10px 10px; */
width: 100%;
}
.el-atfpp-license-container .notice-error,
.el-atfpp-license-container div.error {
background: rgba(220, 50, 50, 0.11);
margin: 0;
}
.el-atfpp-license-container .el-atfpp-license-title {
margin-top: 0;
/* font-size: 30px; */
}
.el-atfpp-license-container .el-atfpp-license-info li {
list-style: none;
padding: 0;
}
.el-atfpp-license-container .el-atfpp-license-info-title {
width: 150px;
display: inline-block;
position: relative;
padding-right: 5px;
}
.el-atfpp-license-container .el-atfpp-license-info-title:after {
content: ":";
position: absolute;
right: 2px;
}
.el-atfpp-license-container .el-atfpp-license-valid,
.el-atfpp-license-container .el-atfpp-license-invalid {
padding: 0 5px 2px;
color: #fff;
background-color: #8fcc77;
border-radius: 3px;
}
.el-atfpp-license-container .el-atfpp-license-invalid {
background-color: #f44336;
}
.el-atfpp-license-container .el-atfpp-license-key {
font-weight: 700;
opacity: 0.8;
}
.el-atfpp-license-container .el-cfefp-green-btn {
padding: 0 5px 2px;
color: #fff;
background-color: #8fcc77;
border-radius: 3px;
text-decoration: none;
-webkit-box-shadow: 0 0 3px -1px rgba(0, 0, 0, 0.38);
-moz-box-shadow: 0 0 3px -1px rgba(0, 0, 0, 0.38);
box-shadow: 0 0 3px -1px rgba(0, 0, 0, 0.38);
}
.el-atfpp-license-container .el-cfefp-green-btn:hover {
color: #fff;
background-color: #84bc6c;
}
.el-atfpp-license-container .el-cfefp-blue-btn {
padding: 0 5px 2px;
color: #fff;
background-color: #20b1d2;
border-radius: 3px;
text-decoration: none;
-webkit-box-shadow: 0 0 3px -1px rgba(0, 0, 0, 0.38);
-moz-box-shadow: 0 0 3px -1px rgba(0, 0, 0, 0.38);
box-shadow: 0 0 3px -1px rgba(0, 0, 0, 0.38);
}
.el-atfpp-license-container .el-cfefp-blue-btn:hover {
color: #fff;
background-color: #219dbf;
}
.el-atfpp-license-container .el-atfpp-license-field label {
display: block;
margin-bottom: 5px;
}
.el-atfpp-license-container .el-atfpp-license-active-btn {
margin-top: 25px;
}
.el-atfpp-left-cont{
border: 5px solid whitesmoke;
border-radius: 10px;
padding: 10px;
transition: border-color .5s;
}
.el-atfpp-left-cont:hover{
border-color:#4aba4a42;
}

View File

@@ -0,0 +1,137 @@
<?php
/**
* Do not access the page directly
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
if ( ! class_exists( 'ATFPP_Custom_Block_Post' ) ) {
/**
* Class ATFPP_Custom_Block_Post
*
* This class handles the custom block post type for the AutoPoly - AI Translation For Polylang plugin.
* It manages the registration of the custom post type, adds submenu pages under the Polylang menu,
* and handles post save actions.
*
* @package ATFPP
*/
class ATFPP_Custom_Block_Post {
/**
* Singleton instance.
*
* @var Atfpp_Custom_Block_Post
*/
private static $instance = null;
/**
* Constructor.
*/
private function __construct() {
add_action( 'init', array( $this, 'register_custom_post_type' ) );
add_action( 'save_post', array( $this, 'on_save_post' ), 10, 3 );
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
}
/**
* Enqueue scripts.
*/
public function enqueue_scripts( $hook ) {
$current_screen = get_current_screen();
if ( 'atfp_add_blocks' === $current_screen->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();
}

View File

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<polyline points="15 6 9 12 15 18" fill="none" stroke="#444" stroke-width="2"/>
</svg>

After

Width:  |  Height:  |  Size: 173 B

View File

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<polyline points="9 6 15 12 9 18" fill="none" stroke="#444" stroke-width="2"/>
</svg>

After

Width:  |  Height:  |  Size: 172 B

View File

@@ -0,0 +1 @@
<svg width="16" height="8" viewBox="0 0 16 8" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M12.4497 7.77152L15.8438 4.37741C16.0521 4.16913 16.0521 3.83144 15.8438 3.62317L12.4497 0.229053C12.2414 0.0207729 11.9037 0.0207729 11.6954 0.229053C11.4872 0.437332 11.4872 0.775021 11.6954 0.9833L14.1791 3.46696H0.533333C0.238781 3.46696 0 3.70574 0 4.00029C0 4.29484 0.238781 4.53362 0.533333 4.53362H14.1791L11.6954 7.01728C11.4872 7.22556 11.4872 7.56324 11.6954 7.77152C11.9037 7.9798 12.2414 7.9798 12.4497 7.77152Z" fill="#666666"></path></svg>

After

Width:  |  Height:  |  Size: 596 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 569 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 379 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><g fill="none" stroke="#222" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"><path d="M22 12.5c0-.491-.005-1.483-.016-1.976c-.065-3.065-.098-4.598-1.229-5.733c-1.131-1.136-2.705-1.175-5.854-1.254a115 115 0 0 0-5.802 0c-3.149.079-4.723.118-5.854 1.254c-1.131 1.135-1.164 2.668-1.23 5.733a69 69 0 0 0 0 2.952c.066 3.065.099 4.598 1.23 5.733c1.131 1.136 2.705 1.175 5.854 1.254q1.204.03 2.401.036"></path><path d="m7 8.5l2.942 1.74c1.715 1.014 2.4 1.014 4.116 0L17 8.5m5 9h-8m8 0c0-.7-1.994-2.008-2.5-2.5m2.5 2.5c0 .7-1.994 2.009-2.5 2.5"></path></g></svg>

After

Width:  |  Height:  |  Size: 654 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

@@ -0,0 +1 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M21.3334 17.056V21.3333H17.0667V17.056H21.3334ZM21.3334 10.6603V14.9227H17.0667V10.6603H21.3334ZM14.9334 10.6603H10.6667V14.9227H14.9334V10.6603ZM14.9334 17.056H10.6667V21.3333H14.9334V17.056Z" fill="#27AD95"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M2.1333 3.2C2.1333 2.35131 2.47044 1.53737 3.07056 0.937258C3.67068 0.337142 4.48461 0 5.3333 0L22.8416 0L29.8666 7.02507V28.8C29.8666 29.6487 29.5295 30.4626 28.9294 31.0627C28.3293 31.6629 27.5153 32 26.6666 32H5.3333C4.48461 32 3.67068 31.6629 3.07056 31.0627C2.47044 30.4626 2.1333 29.6487 2.1333 28.8V3.2ZM23.4666 8.52693H8.5333V23.4667H23.4666V8.52693Z" fill="#27AD95"></path></svg>

After

Width:  |  Height:  |  Size: 757 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="256" height="256" viewBox="0 0 256 256"><path fill="currentColor" d="M52 144H36a8 8 0 0 0-8 8v56a8 8 0 0 0 8 8h16a36 36 0 0 0 0-72m0 56h-8v-40h8a20 20 0 0 1 0 40m169.53-4.91a8 8 0 0 1 .25 11.31A30.06 30.06 0 0 1 200 216c-17.65 0-32-16.15-32-36s14.35-36 32-36a30.06 30.06 0 0 1 21.78 9.6a8 8 0 0 1-11.56 11.06A14.24 14.24 0 0 0 200 160c-8.82 0-16 9-16 20s7.18 20 16 20a14.24 14.24 0 0 0 10.22-4.66a8 8 0 0 1 11.31-.25M128 144c-17.65 0-32 16.15-32 36s14.35 36 32 36s32-16.15 32-36s-14.35-36-32-36m0 56c-8.82 0-16-9-16-20s7.18-20 16-20s16 9 16 20s-7.18 20-16 20m-80-80a8 8 0 0 0 8-8V40h88v48a8 8 0 0 0 8 8h48v16a8 8 0 0 0 16 0V88a8 8 0 0 0-2.34-5.66l-56-56A8 8 0 0 0 152 24H56a16 16 0 0 0-16 16v72a8 8 0 0 0 8 8m112-68.69L188.69 80H160Z"/></svg>

After

Width:  |  Height:  |  Size: 789 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><g fill="none" stroke="#222" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"><path d="M21 14.016c-.046-.992-.723-1.016-1.643-1.016c-1.416 0-1.651.338-1.651 1.667v1.666c0 1.329.235 1.667 1.651 1.667c.92 0 1.597-.024 1.643-1.016M10.295 15.5c0 1.38-1.106 2.5-2.47 2.5c-.309 0-.463 0-.577-.067c-.275-.16-.247-.485-.247-.766v-3.334c0-.281-.028-.606.247-.766c.114-.067.268-.067.576-.067c1.365 0 2.47 1.12 2.47 2.5M14 18c-.776 0-1.165 0-1.406-.244s-.241-.637-.241-1.423v-1.666c0-.786 0-1.179.241-1.423S13.224 13 14 13s1.165 0 1.406.244s.241.637.241 1.423v1.666c0 .786 0 1.179-.241 1.423S14.776 18 14 18"></path><path d="M15 22h-4.273c-3.26 0-4.892 0-6.024-.798a4.1 4.1 0 0 1-.855-.805C3 19.331 3 17.797 3 14.727v-2.545c0-2.963 0-4.445.469-5.628c.754-1.903 2.348-3.403 4.37-4.113C9.095 2 10.668 2 13.818 2c1.798 0 2.698 0 3.416.252c1.155.406 2.066 1.263 2.497 2.35C20 5.278 20 6.125 20 7.818V10"></path><path d="M3 12a3.333 3.333 0 0 1 3.333-3.333c.666 0 1.451.116 2.098-.057A1.67 1.67 0 0 0 9.61 7.43c.173-.647.057-1.432.057-2.098A3.333 3.333 0 0 1 13 2"></path></g></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="-2 -2 24 24"><g fill="currentColor"><path d="M8.695 6.937v1.377H7.687v1.683h1.008V15h2.072V9.997h1.39s.131-.807.194-1.69h-1.576v-1.15c0-.173.226-.404.45-.404h1.128V5h-1.535C8.644 5 8.695 6.685 8.695 6.937"/><path d="M4 2a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2zm0-2h12a4 4 0 0 1 4 4v12a4 4 0 0 1-4 4H4a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4"/></g></svg>

After

Width:  |  Height:  |  Size: 440 B

View File

@@ -0,0 +1 @@
<svg width="17" height="19" viewBox="0 0 17 19" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M7.83333 7.33529C7.83333 6.9671 8.13181 6.66862 8.5 6.66862C8.86819 6.66862 9.16667 6.9671 9.16667 7.33529V9.33529H11.1667C11.5349 9.33529 11.8333 9.63376 11.8333 10.002C11.8333 10.3701 11.5349 10.6686 11.1667 10.6686H9.16667V12.6686C9.16667 13.0368 8.86819 13.3353 8.5 13.3353C8.13181 13.3353 7.83333 13.0368 7.83333 12.6686V10.6686H5.83333C5.46514 10.6686 5.16667 10.3701 5.16667 10.002C5.16667 9.63376 5.46514 9.33529 5.83333 9.33529H7.83333V7.33529Z" fill="#373737"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M16.292 4.85151C16.288 4.84722 16.2838 4.84296 16.2796 4.83874L11.6491 0.208202L11.6435 0.202733C11.5225 0.0788515 11.3536 0.00195312 11.1667 0.00195312H1.16667C0.798477 0.00195312 0.5 0.30043 0.5 0.66862V18.002C0.5 18.3701 0.798477 18.6686 1.16667 18.6686H15.8333C16.2015 18.6686 16.5 18.3701 16.5 18.002V5.33529C16.5 5.14482 16.4201 4.973 16.292 4.85151ZM10.5 6.00195H15.1667V17.3353H1.83333V1.33529H10.5V6.00195ZM14.2239 4.66862H11.8333V2.2781L14.2239 4.66862Z" fill="#373737"></path></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 366 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="-2 -2 24 24"><g fill="currentColor"><path d="M15 11.13v3.697h-2.143v-3.45c0-.866-.31-1.457-1.086-1.457c-.592 0-.945.398-1.1.784c-.056.138-.071.33-.071.522v3.601H8.456s.029-5.842 0-6.447H10.6v.913l-.014.021h.014v-.02c.285-.44.793-1.066 1.932-1.066c1.41 0 2.468.922 2.468 2.902M6.213 5.271C5.48 5.271 5 5.753 5 6.385c0 .62.466 1.115 1.185 1.115h.014c.748 0 1.213-.496 1.213-1.115c-.014-.632-.465-1.114-1.199-1.114m-1.086 9.556h2.144V8.38H5.127z"/><path d="M4 2a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2zm0-2h12a4 4 0 0 1 4 4v12a4 4 0 0 1-4 4H4a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4"/></g></svg>

After

Width:  |  Height:  |  Size: 678 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 367 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 42 KiB

View File

@@ -0,0 +1 @@
<svg width="32" height="33" viewBox="0 0 32 33" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#clip0_46_1321)"><path fill-rule="evenodd" clip-rule="evenodd" d="M32 16.5C32 25.3366 24.8366 32.5 16 32.5C7.16344 32.5 0 25.3366 0 16.5C0 7.66344 7.16344 0.5 16 0.5C24.8366 0.5 32 7.66344 32 16.5ZM9.0733 15.6079C8.65674 15.1913 8.65674 14.5159 9.0733 14.0994C9.48986 13.6828 10.1652 13.6828 10.5818 14.0994L15.3592 18.8768L24.2713 9.96466C24.6879 9.54811 25.3633 9.54811 25.7798 9.96466C26.1964 10.3812 26.1964 11.0566 25.7798 11.4732L16.124 21.129C15.8138 21.4392 15.36 21.5184 14.9765 21.3665C14.8324 21.3151 14.6971 21.2316 14.5816 21.1162L9.0733 15.6079Z" fill="#27AD95"></path><path d="M30.9333 16.5C30.9333 24.7475 24.2475 31.4333 16 31.4333C7.75255 31.4333 1.06667 24.7475 1.06667 16.5C1.06667 8.25255 7.75255 1.56667 16 1.56667C24.2475 1.56667 30.9333 8.25255 30.9333 16.5ZM26.5341 9.21042L25.7798 9.96466L26.5341 9.21042C25.701 8.3773 24.3502 8.3773 23.5171 9.21042L15.3592 17.3683L11.336 13.3451C10.5029 12.512 9.15217 12.512 8.31905 13.3451C7.48594 14.1783 7.48593 15.529 8.31905 16.3621L13.8274 21.8705C14.0523 22.0954 14.3174 22.261 14.6007 22.3649C15.362 22.6588 16.2612 22.5003 16.8783 21.8832L26.5341 12.2274C27.3672 11.3943 27.3672 10.0435 26.5341 9.21042Z" stroke="#27AD95" stroke-width="2.13333"></path></g><defs><clipPath id="clip0_46_1321"><rect width="32" height="32" fill="white" transform="translate(0 0.5)"></rect></clipPath></defs></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" color="currentColor"><path d="M2.5 12c0-4.478 0-6.718 1.391-8.109S7.521 2.5 12.001 2.5c4.478 0 6.717 0 8.108 1.391S21.5 7.521 21.5 12c0 4.478 0 6.718-1.391 8.109S16.479 21.5 12 21.5c-4.478 0-6.717 0-8.109-1.391c-1.39-1.392-1.39-3.63-1.39-8.109"/><path d="m7 17l4.194-4.193M17 7l-4.193 4.194m0 0L9.777 7H7l4.194 5.807m1.613-1.614L17 17h-2.778l-3.028-4.193"/></g></svg>

After

Width:  |  Height:  |  Size: 553 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><g fill="none"><path fill="#30b230" d="M20.433 14.173a.75.75 0 0 0-1.491-.16zm-16.12-.08l-.746.08zm-.184-1.705l.746-.08zm4.631-1.454l.655.365zm1.79-3.209l-.655-.365zm2.9 0l-.655.366zm1.79 3.209l.655-.365zm.764 1.025l-.303.687zm1.467-.714l-.53-.531zm-1.018.777l-.102-.743zm-9.923-.777l-.53.532zm1.017.777l.102-.743zm.45-.063l.301.687zm-2.285 8.194l.5-.559zm12.576 0l-.5-.559zm1.694-9.653l-.008.75zm-1.117-.52l.567-.49zm-5.957-3.197l-.341-.668zm-1.816 0l.341-.668zm1.996 13.467h-2.176v1.5h2.176zm-8.03-6.237l-.183-1.705l-1.491.16l.183 1.705zm4.357-2.714l1.79-3.208l-1.31-.73l-1.79 3.208zm3.38-3.208l1.79 3.208l1.31-.73l-1.79-3.209zm1.79 3.208c.162.29.31.56.455.765c.149.211.351.445.662.582l.604-1.373c.056.024.046.05-.039-.071a8 8 0 0 1-.372-.633zm2.356-.585c-.258.258-.412.41-.533.507c-.115.093-.117.066-.057.058l.205 1.486c.336-.047.595-.216.796-.378c.195-.158.412-.376.648-.61zm-1.24 1.932c.269.118.565.159.855.119l-.205-1.486a.1.1 0 0 1-.045-.006zm-9.7-.87c.235.235.452.453.647.61c.201.164.46.332.796.379l.205-1.486c.06.008.058.035-.057-.058a8 8 0 0 1-.533-.507zm2.104-1.207a8 8 0 0 1-.373.633c-.084.12-.094.095-.038.07l.604 1.374c.31-.137.514-.37.662-.582c.144-.206.293-.475.455-.765zm-.661 2.196c.29.04.586-.001.854-.12l-.604-1.372a.1.1 0 0 1-.045.006zm3.468 7.485c-1.438 0-2.445-.001-3.213-.1c-.748-.095-1.17-.273-1.487-.556l-1 1.118c.63.564 1.39.81 2.296.926c.886.113 2.006.112 3.404.112zm-7.345-6.077c.148 1.378.266 2.727.466 3.821c.101.552.229 1.072.405 1.523c.175.448.417.875.774 1.195l1-1.118c-.116-.104-.248-.294-.377-.623q-.19-.488-.326-1.247c-.188-1.022-.297-2.28-.45-3.711zm15.375-.16c-.154 1.431-.264 2.689-.45 3.71q-.138.76-.327 1.248c-.129.329-.261.52-.377.623l1 1.118c.357-.32.599-.747.774-1.195c.176-.451.304-.971.405-1.523c.2-1.094.318-2.443.466-3.82zm-5.854 7.737c1.398 0 2.518.001 3.404-.112c.907-.116 1.666-.362 2.296-.926l-1-1.118c-.317.283-.739.46-1.487.556c-.768.099-1.775.1-3.213.1zM10.75 5c0-.69.56-1.25 1.25-1.25v-1.5A2.75 2.75 0 0 0 9.25 5zM12 3.75c.69 0 1.25.56 1.25 1.25h1.5A2.75 2.75 0 0 0 12 2.25zM20.75 9a.75.75 0 0 1-.75.75v1.5A2.25 2.25 0 0 0 22.25 9zm-1.5 0a.75.75 0 0 1 .75-.75v-1.5A2.25 2.25 0 0 0 17.75 9zm.75-.75a.75.75 0 0 1 .75.75h1.5A2.25 2.25 0 0 0 20 6.75zM4 9.75A.75.75 0 0 1 3.25 9h-1.5A2.25 2.25 0 0 0 4 11.25zM3.25 9A.75.75 0 0 1 4 8.25v-1.5A2.25 2.25 0 0 0 1.75 9zM4 8.25a.75.75 0 0 1 .75.75h1.5A2.25 2.25 0 0 0 4 6.75zm16 1.5h-.009l-.017 1.5H20zm-.009 0a.75.75 0 0 1-.559-.26l-1.135.98c.406.47 1.006.772 1.677.78zm-.559-.26A.74.74 0 0 1 19.25 9h-1.5c0 .561.207 1.076.547 1.47zM18 11.777c.677-.675 1.026-1.015 1.258-1.159l-.787-1.276c-.42.26-.924.768-1.53 1.372zM4.75 9a.74.74 0 0 1-.182.49l1.135.98c.34-.394.547-.909.547-1.47zm2.309 1.714c-.606-.604-1.11-1.113-1.53-1.372l-.787 1.276c.232.144.58.484 1.258 1.159zM4.568 9.49a.75.75 0 0 1-.559.26l.017 1.5a2.25 2.25 0 0 0 1.677-.78zm-.559.26H4v1.5h.026zm.866 2.558a33 33 0 0 1-.109-1.116a3 3 0 0 1-.005-.592l-1.487-.2a4.6 4.6 0 0 0-.004.898c.023.328.065.72.114 1.17zM13.25 5c0 .485-.276.907-.683 1.115l.681 1.336A2.75 2.75 0 0 0 14.75 5zm-.683 1.115c-.17.086-.361.135-.567.135v1.5a2.74 2.74 0 0 0 1.249-.3zm1.538 1.245c-.206-.37-.391-.703-.561-.975l-1.272.795c.146.234.31.53.523.91zM12 6.25c-.206 0-.398-.05-.567-.135l-.681 1.336c.375.191.8.299 1.248.299zm-.567-.135A1.25 1.25 0 0 1 10.75 5h-1.5a2.75 2.75 0 0 0 1.502 2.45zm-.228 1.976c.212-.382.377-.677.523-.91l-1.272-.796c-.17.272-.355.605-.561.975z"></path><path stroke="#30b230" stroke-linecap="round" stroke-width="1.5" d="M5 17.5h14"></path></g></svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@@ -0,0 +1,11 @@
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 26 26">
<g fill="#30B230">
<path fill-rule="evenodd" d="M13.5 26C20.404 26 26 20.404 26 13.5S20.404 1 13.5 1S1 6.596 1 13.5S6.596 26 13.5 26m0-2C19.299 24 24 19.299 24 13.5S19.299 3 13.5 3S3 7.701 3 13.5S7.701 24 13.5 24" clip-rule="evenodd" opacity="0.2" />
<g opacity="0.2">
<path d="M19.568 14.058a1 1 0 0 1-.054 1.721l-8.033 4.408A1 1 0 0 1 10 19.311V9.817a1 1 0 0 1 1.535-.845z" />
<path fill-rule="evenodd" d="M17.067 14.841L12 11.633v5.988zm2.447.938a1 1 0 0 0 .054-1.721l-8.033-5.086A1 1 0 0 0 10 9.817v9.494a1 1 0 0 0 1.481.876z" clip-rule="evenodd" />
</g>
<path fill-rule="evenodd" d="m9 18.321l9.014-4.883L9 7.804zm9.49-4.003a1 1 0 0 0 .054-1.728L9.53 6.956A1 1 0 0 0 8 7.804v10.517a1 1 0 0 0 1.476.88z" clip-rule="evenodd" />
<path fill-rule="evenodd" d="M13 24.5c6.351 0 11.5-5.149 11.5-11.5S19.351 1.5 13 1.5S1.5 6.649 1.5 13S6.649 24.5 13 24.5m0 1c6.904 0 12.5-5.596 12.5-12.5S19.904.5 13 .5S.5 6.096.5 13S6.096 25.5 13 25.5" clip-rule="evenodd" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="256" height="180" viewBox="0 0 256 180"><path fill="#f00" d="M250.346 28.075A32.18 32.18 0 0 0 227.69 5.418C207.824 0 127.87 0 127.87 0S47.912.164 28.046 5.582A32.18 32.18 0 0 0 5.39 28.24c-6.009 35.298-8.34 89.084.165 122.97a32.18 32.18 0 0 0 22.656 22.657c19.866 5.418 99.822 5.418 99.822 5.418s79.955 0 99.82-5.418a32.18 32.18 0 0 0 22.657-22.657c6.338-35.348 8.291-89.1-.164-123.134"/><path fill="#fff" d="m102.421 128.06l66.328-38.418l-66.328-38.418z"/></svg>

After

Width:  |  Height:  |  Size: 511 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="-2 -2 24 24"><g fill="currentColor"><path d="M12.923 6.526H7.077C5.93 6.526 5 7.446 5 8.58v2.89c0 1.135.93 2.054 2.077 2.054h5.846c1.147 0 2.077-.92 2.077-2.054V8.58c0-1.135-.93-2.054-2.077-2.054m-1.404 3.64l-2.735 1.29a.11.11 0 0 1-.157-.099v-2.66a.11.11 0 0 1 .16-.097l2.734 1.37a.11.11 0 0 1-.002.196"/><path d="M4 2a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2zm0-2h12a4 4 0 0 1 4 4v12a4 4 0 0 1-4 4H4a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4"/></g></svg>

After

Width:  |  Height:  |  Size: 539 B

View File

@@ -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 $(`<div class="notice ${noticeClass} is-dismissible"><p>${message}</p></div>`);
};
const createRenewalNotice = (message) => {
return $(`<div style="margin-top: 10px; color: #d63638;" class="notice notice-error">${message}</div>`);
};
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" :
'<a style="color: #0073aa; text-decoration: underline;" href="' + URLS.renewLink + '" target="_blank">Renew now</a>';
return isLicenseExpired
? versionPrefix + baseMessage + `<a href="${URLS.renewLink}" target="_blank">Renew now</a> 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 = $(`
<li>
<strong>License Type:</strong>
<span class="license-type">${licenseInfo.license_title}</span>
</li>
`);
$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 = $(`
<li>
<strong>Plugin Updates & Support Validity:</strong>
<span class="validity">${displayDate}</span>
</li>
`);
$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();
}
}
});

View File

@@ -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');
}
});
});

View File

@@ -0,0 +1,58 @@
<div class="atfpp-dashboard-ai-translations">
<div class="atfpp-dashboard-ai-translations-container">
<div class="header">
<h1><?php echo esc_html__('AI Translations', $text_domain); ?></h1>
</div>
<p class="description">
<?php echo esc_html__('Experience the power of AI for faster, more accurate translations. Choose from multiple AI providers to translate your content efficiently.', $text_domain); ?>
</p>
<div class="atfpp-dashboard-translations">
<?php
$ai_translations = [
[
'logo' => '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) {
?>
<div class="atfpp-dashboard-translation-card">
<div class="logo">
<img src="<?php echo esc_url(ATFPP_URL . 'assets/images/' . $translation['logo']); ?>"
alt="<?php echo esc_attr($translation['alt']); ?>">
</div>
<h3><?php echo esc_html($translation['title']); ?></h3>
<p><?php echo esc_html($translation['description']); ?></p>
<div class="play-btn-container">
<a href="<?php echo esc_url($translation['url']); ?>" target="_blank">
<img src="<?php echo esc_url(ATFPP_URL . 'admin/atfpp-dashboard/images/' . $translation['icon']); ?>" alt="<?php echo esc_attr($translation['alt']); ?>">
</a>
</div>
</div>
<?php
}
?>
</div>
</div>
</div>

View File

@@ -0,0 +1,119 @@
<?php
if(!defined('ABSPATH')) exit;
if(!class_exists('ATFP_Custom_Fields')) {
class ATFP_Custom_Fields {
private static $instance = null;
private $atfp_saved_fields = array();
private $atfp_allowed_fields = array();
public static function get_instance() {
if(null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
private function __construct() {
// wp:phpcs:ignore Wordpress.security Nonce verification is not required here
$tab=isset($_GET['tab']) ? sanitize_text_field(wp_unslash($_GET['tab'])) : '';
$page=isset($_GET['page']) ? sanitize_text_field(wp_unslash($_GET['page'])) : '';
if('custom-fields' === $tab && 'polylang-atfpp-dashboard' === $page){
$this->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;
?>
<div class="atfp-custom-data-table-wrapper atfp-custom-fields">
<h3><?php echo __('Custom Fields Translation Settings', 'autopoly-ai-translation-for-polylang-pro'); ?>
<br>
<p><?php echo sprintf(esc_html__('Select which custom fields will be translated by %s.', 'autopoly-ai-translation-for-polylang-pro'), 'AutoPoly'); ?></p>
</h3>
<button class="button button-primary atfp-save-custom-fields"><?php esc_html_e( 'Save Fields', 'autopoly-ai-translation-for-polylang-pro' ); ?></button>
<div class="atfp-custom-data-table-filters">
<div class="atfp-filter-tab" data-column="3" data-default="all">
<label for="atfp-fields-filter"><?php esc_html_e( 'Show Fields:', 'autopoly-ai-translation-for-polylang-pro' ); ?></label>
<select id="atfp-fields-filter" name="atfp_fields_filter">
<option value="all"><?php esc_html_e( 'All', 'autopoly-ai-translation-for-polylang-pro' ); ?></option>
<option value="supported"><?php esc_html_e( 'Translatable', 'autopoly-ai-translation-for-polylang-pro' ); ?></option>
<option value="unsupported"><?php esc_html_e( 'Non-Translatable', 'autopoly-ai-translation-for-polylang-pro' ); ?></option>
</select>
</div>
<div class="atfp-filter-tab" data-column="2" data-default="all">
<label for="atfp-fields-filter"><?php esc_html_e( 'Type:', 'autopoly-ai-translation-for-polylang-pro' ); ?></label>
<select id="atfp-fields-value-type-filter" name="atfp_fields_value_type_filter">
<option value="all"><?php esc_html_e( 'All', 'autopoly-ai-translation-for-polylang-pro' ); ?></option>
<option value="string"><?php esc_html_e( 'String', 'autopoly-ai-translation-for-polylang-pro' ); ?></option>
<option value="array"><?php esc_html_e( 'Array', 'autopoly-ai-translation-for-polylang-pro' ); ?></option>
</select>
</div>
</div>
<div class="atfp-custom-table-section">
<div class="atfp-custom-table-lists">
<table class="atfp-custom-data-table-table" id="atfp-custom-datatable">
<thead>
<tr>
<th><?php esc_html_e( 'Sr.No', 'autopoly-ai-translation-for-polylang-pro' ); ?></th>
<th><?php esc_html_e( 'Field Name', 'autopoly-ai-translation-for-polylang-pro' ); ?></th>
<th><?php esc_html_e( 'Type', 'autopoly-ai-translation-for-polylang-pro' ); ?></th>
<th><?php esc_html_e( 'Status', 'autopoly-ai-translation-for-polylang-pro' ); ?></th>
<th align="center"><?php esc_html_e( 'Translate', 'autopoly-ai-translation-for-polylang-pro' ); ?></th>
</tr>
</thead>
<tbody>
<?php
$this->get_all_meta_fields_table();
?>
</tbody>
</table>
</div>
</div>
</div>
<?php
}
public function get_all_meta_fields_table() {
$meta_fields=ATFPP_Helper::get_instance()->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 '<tr>';
echo '<td>' . $s_no++ . '</td>';
echo '<td>' . $meta_field . '</td>';
echo '<td>' . $value_type . '</td>';
echo '<td>' . $status . '</td>';
echo '<td align="center"><input type="checkbox" name="atfp_fields_status" value="' . $meta_field . '" ' . $checked . '></td>';
echo '</tr>';
}
}
}
}
}
ATFP_Custom_Fields::get_instance();

View File

@@ -0,0 +1,59 @@
<div class="atfpp-dashboard-left-section">
<!-- Welcome Section -->
<div class="atfpp-dashboard-welcome">
<div class="atfpp-dashboard-welcome-video">
<a href="https://docs.coolplugins.net/doc/ai-translation-polylang-video-tutorials/?utm_source=atfp_plugin&utm_medium=inside&utm_campaign=docs&utm_content=dashboard_pro" target="_blank" class="atfpp-dashboard-video-link">
<img decoding="async" src="<?php echo esc_url(ATFPP_URL . 'admin/atfpp-dashboard/images/youtube-icon.svg'); ?>" class="play-icon" alt="play-icon">
<picture>
<source srcset="<?php echo esc_url(ATFPP_URL . 'admin/atfpp-dashboard/images/polylang-addon-video.png'); ?>" type="image/webp">
<img src="<?php echo esc_url(ATFPP_URL . 'admin/atfpp-dashboard/images/polylang-addon-video.png'); ?>" class="loco-video" alt="loco translate addon preview">
</picture>
</a>
</div>
</div>
<!-- Translation Providers -->
<div class="atfpp-dashboard-translation-providers">
<h3><?php echo esc_html__('Translation Providers', $text_domain); ?></h3>
<div class="atfpp-dashboard-providers-grid">
<?php
$providers = [
["Gemini AI", "geminiai-logo.png", "Pro", ["Unlimited Translations", "Fast Translations via AI", "Gemini API Key Required"], esc_url('https://docs.coolplugins.net/doc/translate-via-gemini-open-ai-openrouter/?utm_source=atfp_plugin&utm_medium=inside&utm_campaign=docs&utm_content=dashboard_gemini_pro'), esc_url('admin.php?page=polylang-atfpp-dashboard&tab=settings')],
["OpenAI", "openai-translate-logo.png", "Pro", ["Unlimited Translations", "Fast Translations via AI", "OpenAI API Key Required"], esc_url('https://docs.coolplugins.net/doc/translate-via-gemini-open-ai-openrouter/?utm_source=atfp_plugin&utm_medium=inside&utm_campaign=docs&utm_content=dashboard_openai_pro'), esc_url('admin.php?page=polylang-atfpp-dashboard&tab=settings')],
// ["Openrouter AI", "openrouter-translate-logo.png", "Pro", ["Unlimited Translations", "Fast Translations via AI", "Openrouter API Key Required"], esc_url('#'), esc_url('admin.php?page=polylang-atfpp-dashboard&tab=settings')],
["Google Translate", "google-translate-logo.png", "Pro", ["Unlimited Free Translations", "Fast & No API Key Required"], esc_url('https://docs.coolplugins.net/doc/google-translate-for-polylang/?utm_source=atfp_plugin&utm_medium=inside&utm_campaign=docs&utm_content=dashboard_google_pro')],
["Chrome Built-in AI", "chrome-built-in-ai-logo.png", "Free", ["Fast AI Translations in Browser", "Unlimited Free Translations", "Use Translation Modals"], esc_url('https://docs.coolplugins.net/docs/ai-translation-for-polylang/how-to-automatically-translate-your-website-content-via-chrome-ai/?utm_source=atfp_plugin&utm_medium=inside&utm_campaign=docs&utm_content=dashboard_chrome_pro')],
["Yandex Translate", "yandex-translate-logo.png", "Free", ["Unlimited Free Translations", "No API & No Extra Cost"], esc_url('https://docs.coolplugins.net/docs/ai-translation-for-polylang/how-to-automatically-translate-your-website-content-via-yandex/?utm_source=atfp_plugin&utm_medium=inside&utm_campaign=docs&utm_content=dashboard_yandex_pro')],
];
foreach ($providers as $index => $provider) {
?>
<div class="atfpp-dashboard-provider-card">
<div class="atfpp-dashboard-provider-header">
<a href="<?php echo esc_url($provider[4]); ?>" target="_blank">
<img src="<?php echo esc_url(ATFPP_URL . 'assets/images/' . $provider[1]); ?>"
alt="<?php echo esc_html($provider[0]); ?>">
</a>
</div>
<h4><?php echo esc_html($provider[0]); ?></h4>
<ul>
<?php foreach ($provider[3] as $feature) { ?>
<li>✅ <?php echo esc_html($feature); ?></li>
<?php } ?>
</ul>
<div class="atfpp-dashboard-provider-buttons">
<a href="<?php echo esc_url($provider[4]); ?>" class="atfpp-dashboard-btn" target="_blank">Docs</a>
<?php if (isset($provider[5])) { ?>
<a href="<?php echo esc_url($provider[5]); ?>" class="atfpp-dashboard-btn">Settings</a>
<?php } ?>
</div>
</div>
<?php
}
?>
</div>
</div>
</div>

View File

@@ -0,0 +1,29 @@
<div class="atfpp-dashboard-info">
<div class="atfpp-dashboard-info-links">
<p>
<?php echo esc_html__('Made with ❤️ by', $text_domain); ?>
<span class="logo">
<a href="https://coolplugins.net/?utm_source=atfp_plugin&utm_medium=inside&utm_campaign=author_page&utm_content=dashboard_footer_pro" target="_blank">
<img src="<?php echo esc_url(ATFPP_URL . 'admin/atfpp-dashboard/images/cool-plugins-logo-black.svg'); ?>" alt="<?php esc_attr_e('Cool Plugins Logo', $text_domain); ?>">
</a>
</span>
</p>
<a href="https://coolplugins.net/support/?utm_source=atfp_plugin&utm_medium=inside&utm_campaign=support&utm_content=dashboard_footer_pro" target="_blank"><?php echo esc_html__('Support', $text_domain); ?></a> |
<a href="https://docs.coolplugins.net/docs/ai-translation-for-polylang/?utm_source=atfp_plugin&utm_medium=inside&utm_campaign=docs&utm_content=dashboard_footer_pro" target="_blank"><?php echo esc_html__('Docs', $text_domain); ?></a>
<div class="atfpp-dashboard-social-icons">
<?php
$social_links = [
['https://www.facebook.com/coolplugins/', 'facebook.svg', esc_html__('Facebook', $text_domain)],
['https://linkedin.com/company/coolplugins', 'linkedin.svg', esc_html__('Linkedin', $text_domain)],
['https://x.com/cool_plugins', 'twitter.svg', esc_html__('Twitter', $text_domain)],
['https://www.youtube.com/@cool_plugins', 'youtube.svg', esc_html__('YouTube Channel', $text_domain)]
];
foreach ($social_links as $link) {
echo '<a href="' . esc_url($link[0]) . '" target="_blank">
<img src="' . esc_url(ATFPP_URL . 'admin/atfpp-dashboard/images/' . $link[1]) . '" alt="' . esc_attr($link[2]) . '">
</a>';
}
?>
</div>
</div>
</div>

View File

@@ -0,0 +1,505 @@
<?php
$glossary_data = get_option('atfpp_glossary_data', []);
if (!is_array($glossary_data)) {
$glossary_data = [];
}
// --- Build $languages array ONCE at the top and reuse everywhere ---
$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'],
'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] : '';
?>
<style>
<?php foreach ($languages as $lang):
$code = esc_attr($lang['code']);
?>
.atfpp-glossary-table.atfpp-hide-lang-<?php echo $code; ?> th[data-lang="<?php echo $code; ?>"],
.atfpp-glossary-table.atfpp-hide-lang-<?php echo $code; ?> td[data-lang="<?php echo $code; ?>"] {
display: none !important;
}
<?php endforeach; ?>
</style>
<div class="atfpp-glossary">
<div class="atfpp-glossary-container">
<?php wp_nonce_field('atfpp_glossary_nonce', 'atfpp_glossary_nonce'); ?>
<header class="atfpp-header">
<h1><?php esc_html_e('Glossary', $text_domain); ?></h1>
<p><?php esc_html_e('Define how you want to translate or not translate important words and phrases.', $text_domain); ?></p>
<ul>
<li><?php esc_html_e('Specific translations you want to use;', $text_domain); ?></li>
<li><?php esc_html_e('Terms you want to exclude from being translated;', $text_domain); ?></li>
<li><?php esc_html_e('Additional context for each term.', $text_domain); ?></li>
</ul>
<a href="#"><?php esc_html_e('Learn more about adding and managing glossary terms.', $text_domain); ?></a>
</header>
<div class="atfpp-controls">
<input type="text" class="atfpp-search" placeholder="<?php esc_attr_e('Search', $text_domain); ?>" />
<select class="atfpp-glossary-type" name="glossary_type">
<option value=""><?php esc_html_e('Glossary Type', $text_domain); ?></option>
<option value="name"><?php esc_html_e('Name', $text_domain); ?></option>
<option value="general"><?php esc_html_e('General', $text_domain); ?></option>
</select>
<button class="atfpp-add-btn button button-primary">
<?php esc_html_e('Add glossary entry', $text_domain); ?>
</button>
<button class="atfpp-import-btn button button-primary">
<?php esc_html_e('Import glossary', $text_domain); ?>
</button>
<button class="atfpp-export-btn button button-primary">
<?php esc_html_e('Export glossary', $text_domain); ?>
</button>
<!-- Modal -->
<div id="atfpp-glossary-modal-add" class="atfpp-glossary-modal atfpp-hidden">
<div class="atfpp-glossary-modal-content-wrapper">
<button type="button" class="atfpp-modal-close-btn" aria-label="Close">&times;</button>
<div class="atfpp-glossary-modal-content">
<h2><?php esc_html_e('Add New Glossary Term', $text_domain); ?></h2>
<form id="atfpp-add-glossary-form">
<label for="atfpp-add-term"><?php esc_html_e('Term', $text_domain); ?></label>
<textarea id="atfpp-add-term" name="term" class="atfpp-add-term" required placeholder="<?php esc_attr_e('Enter the term to be translated', $text_domain); ?>"></textarea>
<div class="atfpp-translation-error"></div>
<label for="atfpp-add-desc"><?php esc_html_e('Description', $text_domain); ?></label>
<textarea id="atfpp-add-desc" name="description" class="atfpp-add-desc" rows="3" placeholder="<?php esc_attr_e('Add a description to provide context for translators', $text_domain); ?>"></textarea>
<div class="atfpp-translation-error"></div>
<label for="atfpp-add-source-lang"><?php esc_html_e('Original Language', $text_domain); ?></label>
<select id="atfpp-add-source-lang" name="source_lang" class="atfpp-add-source-lang" required>
<option value=""><?php esc_html_e('Select language', $text_domain); ?></option>
<?php foreach ($languages as $lang): ?>
<option value="<?php echo esc_attr($lang['code']); ?>">
<?php echo esc_html($lang['alt']); ?>
</option>
<?php endforeach; ?>
</select>
<label for="atfpp-add-type"><?php esc_html_e('Type', $text_domain); ?></label>
<select id="atfpp-add-type" name="type" class="atfpp-add-type" required>
<option value="general"><?php esc_html_e('General', $text_domain); ?></option>
<option value="name"><?php esc_html_e('Name', $text_domain); ?></option>
</select>
<div class="atfpp-add-translations atfpp-translations-grid">
<?php foreach ($languages as $lang): ?>
<div class="atfpp-translation-field atfpp-translation-field-<?php echo esc_attr($lang['code']); ?>">
<label>
<div class="atfpp-translation-label-row">
<?php if (!empty($lang['img'])): ?>
<img src="<?php echo esc_attr($lang['img']); ?>" alt="<?php echo esc_attr($lang['alt']); ?>" class="atfpp-lang-flag">
<?php endif; ?>
<span class="atfpp-lang-name"><?php echo esc_html($lang['alt']); ?></span>
<span class="atfpp-lang-translation-label"><?php esc_html_e('Translation', $text_domain); ?></span>
</div>
<textarea name="translation_<?php echo esc_attr($lang['code']); ?>" class="atfpp-add-translation" rows="2" placeholder="<?php esc_attr_e('Custom Translation', $text_domain); ?>"></textarea>
<div class="atfpp-translation-error"><?php esc_html_e('Too long, must be less than 240 characters', $text_domain); ?></div>
</label>
</div>
<?php endforeach; ?>
</div>
<div class="atfpp-glossary-modal-actions" style="margin-top: 18px;">
<span class="atfpp-glossary-modal-actions-left" style="cursor:pointer;"><?php esc_html_e('Cancel', $text_domain); ?></span>
<button type="submit" id="add-glossary-term-btn" class="button button-primary"><?php esc_html_e('Add Term', $text_domain); ?></button>
</div>
</form>
<div id="add-glossary-success" class="atfpp-import-success atfpp-hidden">
<div class="import-success-icon">
<img src="<?php echo ATFPP_URL . 'admin/atfpp-dashboard/images/success.svg'; ?>" alt="Success Icon" />
</div>
<div class="atfpp-import-success-message">
<?php esc_html_e('Glossary term added successfully!', $text_domain); ?>
</div>
<button id="atfpp-glossary-success-close" class="atfpp-close-button" type="button"><?php esc_html_e('Close', $text_domain); ?></button>
</div>
</div>
</div>
</div>
<!-- End Modal -->
<div id="atfpp-glossary-modal-import" class="atfpp-glossary-modal atfpp-hidden">
<div class="atfpp-glossary-modal-content-wrapper">
<button type="button" class="atfpp-modal-close-btn" aria-label="Close">&times;</button>
<div class="atfpp-glossary-modal-content">
<div class="atfpp-import-glossary" id="atfpp-import-glossary-ui">
<h2 class="atfpp-title"><?php esc_html_e( 'Import glossary', $text_domain ); ?></h2>
<label class="atfpp-upload-box" id="upload-label">
<input type="file" accept=".csv" id="atfpp-csv-upload" hidden>
<div class="atfpp-upload-area">
<img src="<?php echo ATFPP_URL . 'admin/atfpp-dashboard/images/csv.svg'; ?>" alt="CSV Icon" />
<span id="file-name-display"><?php esc_html_e( 'Select a CSV file to upload', $text_domain ); ?></span>
</div>
</label>
<a
href="<?php echo esc_url( ATFPP_URL . 'admin/atfpp-dashboard/sample-glossary.csv' ); ?>"
class="atfpp-download-link"
download="sample-glossary.csv">
<?php esc_html_e( 'Download sample glossary CSV file', $text_domain ); ?>
</a>
</div>
<!-- Success UI (hidden by default) -->
<div id="atfpp-import-success-ui" class="atfpp-hidden">
<div id="atfpp-import-success" class="atfpp-import-success">
<div class="import-success-icon">
<img src="<?php echo ATFPP_URL . 'admin/atfpp-dashboard/images/success.svg'; ?>" alt="Success Icon" />
</div>
<div class="import-success-file">
<span id="importing-file-label"><?php esc_html_e('Importing:', $text_domain); ?></span>
<span id="importing-file-name"></span>
</div>
<div class="atfpp-import-success-message">
<?php esc_html_e('Glossary terms imported successfully', $text_domain); ?>
</div>
<button class="atfpp-import-close-btn atfpp-close-button" type="button">Close</button>
</div>
</div>
</div>
</div>
</div>
</div>
<nav class="atfpp-alphabet" aria-label="<?php esc_attr_e('Glossary Alphabet Navigation', $text_domain); ?>">
<?php
$alphabet = array_merge(['123'], range('A', 'Z'), ['#&à']);
// Build a set of enabled letters based on $glossary_data
$enabled_letters = [];
foreach ($glossary_data as $entry) {
if (!empty($entry['original_term'])) {
$first = mb_substr(trim($entry['original_term']), 0, 1, 'UTF-8');
if (is_numeric($first)) {
$enabled_letters['123'] = true;
} elseif (preg_match('/[A-Za-z]/u', $first)) {
$enabled_letters[strtoupper($first)] = true;
} else {
$enabled_letters['#&à'] = true;
}
}
}
$first_active_set = false;
foreach ($alphabet as $char) {
$enabled = isset($enabled_letters[$char]) ? '' : 'disabled';
$active = '';
if ($enabled === '' && !$first_active_set) {
$first_active_set = true;
}
printf(
'<button class="atfpp-alphabet-btn%s" %s data-letter="%s">%s</button>',
$active,
$enabled,
esc_attr($char),
esc_html($char)
);
}
?>
</nav>
<!-- Language Filter Buttons -->
<?php
if (count($language_codes_with_entries) > 1): ?>
<div class="atfpp-language-filters">
<?php foreach ($language_codes_with_entries as $i => $code): ?>
<?php
// Skip if language code not in map
if (!isset($language_map[$code])) {
continue;
}
$lang = $language_map[$code];
?>
<button class="atfpp-lang-filter-btn<?php echo $i === 0 ? ' active' : ''; ?>" data-lang="<?php echo esc_attr($code); ?>">
<?php if (!empty($lang['img'])): ?>
<img src="<?php echo esc_url($lang['img']); ?>" alt="<?php echo esc_attr($lang['alt']); ?>" />
<?php endif; ?>
<?php echo esc_html($lang['alt']) . ' Terms'; ?>
</button>
<?php endforeach; ?>
</div>
<?php endif; ?>
<div class="atfpp-glossary-table-wrapper">
<?php if (!empty($grouped_entries)): ?>
<table class="atfpp-glossary-table" data-default-lang="<?php echo esc_attr($default_selected_lang); ?>">
<thead>
<tr>
<th colspan="2"></th>
<?php foreach ($languages as $lang): ?>
<?php if ($single_language_mode && $lang['code'] === $single_language_code) continue; ?>
<th colspan="2" title="<?php echo esc_attr($lang['alt']); ?>"
class="atfpp-lang-header atfpp-lang-col-<?php echo esc_attr($lang['code']); ?>"
data-lang="<?php echo esc_attr($lang['code']); ?>">
<?php
echo !empty($lang['flag'])
? $lang['flag']
: '<img src="' . esc_attr($lang['img']) . '" alt="' . esc_attr($lang['alt']) . '" />';
?>
</th>
<?php endforeach; ?>
<th class="atfpp-actions-cell">
<div class="atfpp-action-buttons-header">
<button class="atfpp-actions-header-btn" id="atfpp-actions-header-btn-left" title="Scroll Left">
<img src="<?php echo ATFPP_URL . 'admin/atfpp-dashboard/images/arrow-left.svg'; ?>" />
</button>
<button class="atfpp-actions-header-btn" id="atfpp-actions-header-btn-right" title="Scroll Right">
<img src="<?php echo ATFPP_URL . 'admin/atfpp-dashboard/images/arrow-right.svg'; ?>" />
</button>
</div>
</th>
</tr>
<tr>
<th>Glossary Entry</th>
<th>Type</th>
<?php foreach ($languages as $lang): ?>
<?php if ($single_language_mode && $lang['code'] === $single_language_code) continue; ?>
<th colspan="2" data-lang="<?php echo esc_attr($lang['code']); ?>">
<?php echo esc_html($lang['alt']); ?>
</th>
<?php endforeach; ?>
<th colspan="2">Actions</th>
</tr>
</thead>
<tbody>
<?php
// UTF-8 safe truncate helper
if ( ! function_exists('atfpp_truncate')) {
function atfpp_truncate( $str, $limit = 10 ) {
$str = (string) $str;
if (mb_strlen($str, 'UTF-8') > $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) : '#&à');
?>
<tr data-type="<?php echo esc_attr($data['type']); ?>"
data-original-language="<?php echo esc_attr($original_language_code); ?>"
data-term="<?php echo esc_attr($term); ?>"
data-letter="<?php echo esc_attr($row_letter); ?>">
<td>
<div class="atfpp-entry-title">
<?php echo esc_html($term); ?>
</div>
<div class="atfpp-entry-desc">
<?php echo esc_html($data['desc']); ?>
</div>
</td>
<td>
<span class="atfpp-type-badge <?php echo esc_attr($data['type']); ?>">
<?php echo esc_html(ucfirst($data['type'])); ?>
</span>
</td>
<?php
foreach ($languages as $lang):
if ($single_language_mode && $lang['code'] === $single_language_code) continue;
$translation = '';
$is_source = ($lang['code'] === $original_language_code);
?>
<td colspan="2" class="atfpp-lang-col-<?php echo esc_attr($lang['code']); ?>"
data-lang="<?php echo esc_attr($lang['code']); ?>"
data-is-source="<?php echo $is_source ? 'true' : 'false'; ?>">
<?php if ($is_source): ?>
<span class="atfpp-source-term">
<?php echo esc_html($term); ?>
</span>
<?php else: ?>
<?php
// Get translation from the new data structure
$translation = isset($data['translations'][$lang['code']]) ? $data['translations'][$lang['code']] : '';
?>
<?php if (!empty($translation) && trim($translation) !== ''): ?>
<?php $truncated = atfpp_truncate($translation, 7); ?>
<span class="atfpp-translated-term"
title="<?php echo esc_attr($translation); ?>"
data-full-text="<?php echo esc_attr($translation); ?>">
<?php echo esc_html($truncated); ?>
</span>
<?php else: ?>
<span class="atfpp-no-translation">
<button type="button" class="atfpp-edit-btn-svg"
data-term="<?php echo esc_attr(sanitize_text_field($term)); ?>"
data-source-lang="<?php echo esc_attr(sanitize_key($original_language_code)); ?>">
<img src="<?php echo ATFPP_URL . 'admin/atfpp-dashboard/images/file.svg'; ?>"
alt="<?php esc_attr_e('No translation', $text_domain); ?>" />
</button>
</span>
<?php endif; ?>
<?php endif; ?>
</td>
<?php endforeach; ?>
<td class="atfpp-actions-cell">
<div class="atfpp-action-buttons">
<button type="button" class="atfpp-edit-btn"
data-term="<?php echo esc_attr(sanitize_text_field($term)); ?>"
data-source-lang="<?php echo esc_attr(sanitize_key($original_language_code)); ?>">
<?php esc_html_e('Edit', $text_domain); ?>
</button>
<button type="button" class="atfpp-delete-btn"
data-term="<?php echo esc_attr(sanitize_text_field($term)); ?>"
data-source-lang="<?php echo esc_attr(sanitize_key($original_language_code)); ?>">
<?php esc_html_e('Delete', $text_domain); ?>
</button>
</div>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php else: ?>
<div id="atfpp-no-results">No glossary entries found.</div>
<?php endif; ?>
</div>
<!-- Move the template outside the table wrapper so it is always present -->
<script type="text/template" id="atfpp-glossary-edit-row-template">
<tr class="atfpp-glossary-edit-row">
<td>
<textarea class="atfpp-edit-term" rows="3" placeholder="<?php esc_attr_e('String Translation', $text_domain); ?>"><%= term %></textarea>
<textarea class="atfpp-edit-desc" rows="4" placeholder="<?php esc_attr_e('Example: The name of the add-on that allows translating strings', $text_domain); ?>"><%= desc %></textarea>
</td>
<td>
<select class="atfpp-edit-type">
<option value="general" <%= type === 'general' ? 'selected' : '' %>><?php esc_html_e('General', $text_domain); ?></option>
<option value="name" <%= type === 'name' ? 'selected' : '' %>><?php esc_html_e('Name', $text_domain); ?></option>
</select>
</td>
<% for (var i = 0; i < languages.length; i++) {
if (languages[i].code === source_lang) continue;
%>
<td colspan="2">
<textarea class="atfpp-edit-translation" data-lang="<%= languages[i].code %>" placeholder="<?php esc_attr_e('Custom Translation', $text_domain); ?>" rows="9"><%= translations[languages[i].code] || '' %></textarea>
<div class="atfpp-translation-error"><?php esc_html_e('Too long, must be less than 220 characters', $text_domain); ?></div>
</td>
<% } %>
<td colspan="2" class="atfpp-actions-cell">
<div class="atfpp-action-buttons">
<button type="button" class="atfpp-save-edit-btn button button-primary">
<?php esc_html_e('Save', $text_domain); ?>
</button>
<button type="button" class="atfpp-cancel-edit-btn">
<?php esc_html_e('Cancel', $text_domain); ?>
</button>
</div>
</td>
</tr>
</script>
</div>
</div>

View File

@@ -0,0 +1,287 @@
<?php
/**
* Renders the license activation page
*/
function atfpp_render_license_page() {
$text_domain = 'autopoly-ai-translation-for-polylang-pro';
$purchase_email = get_option('AIAutomaticTranslationsForPolylang_lic_email', get_bloginfo('admin_email'));
// Escape early for security
$admin_url = esc_url(admin_url('admin-post.php'));
$purchase_email_escaped = esc_attr($purchase_email);
?>
<div class="atfpp-dashboard-license">
<div class="atfpp-dashboard-license-container">
<div class="header">
<h1>🔑 <?php echo esc_html__('Activate License', $text_domain); ?></h1>
</div>
<div class="atfpp-dashboard-license-form">
<form method="post" action="<?php echo $admin_url; ?>">
<input type="hidden" name="action" value="AIAutomaticTranslationsForPolylang_el_activate_license"/>
<?php wp_nonce_field('el-atfpp-license'); ?>
<div class="atfpp-dashboard-license-field">
<label for="license_code"><?php esc_html_e('License Key', $text_domain); ?></label>
<input type="text" name="license_code" id="license_code" required
placeholder="<?php esc_attr_e('xxxxxxxx-xxxxxxxx-xxxxxxxx-xxxxxxxx', $text_domain); ?>">
</div>
<div class="atfpp-dashboard-license-field">
<label for="email"><?php esc_html_e('Email Address', $text_domain); ?></label>
<input type="email" name="email" id="email" required
value="<?php echo $purchase_email_escaped; ?>">
<small class="atfpp-dashboard-activation-note"><?php esc_html_e("Plugin updates news will be sent to this email. Don't worry, we hate spam.", $text_domain); ?></small>
</div>
<button type="submit" class="button button-primary">
<?php echo esc_html__('Activate License', $text_domain); ?>
</button>
</form>
<p class="activation-note">
<?php echo esc_html__('Activate to receive automatic plugin updates and support.', $text_domain); ?>
</p>
<?php atfpp_render_license_help_buttons($text_domain); ?>
</div>
</div>
</div>
<?php
}
/**
* Renders the license information page for Pro users
*
* @param object|null $license_info License information object
*/
function atfpp_render_license_page_pro($license_info = null) {
$text_domain = 'autopoly-ai-translation-for-polylang-pro';
// Early return if invalid license info
if (!$license_info) {
$license_info = AI_Automatic_Translations_For_Polylang_Base::GetRegisterInfo();
}
if (!is_object($license_info) || !isset($license_info->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'));
?>
<div class="atfpp-dashboard-license">
<div class="atfpp-dashboard-license-pro-container">
<div class="atfpp-dashboard-license-header-container" style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
<h1>🔒 <?php esc_html_e('Your License Info', $text_domain); ?></h1>
<?php if (atfpp_needs_refresh($license_info)): ?>
<button type="button" class="atfpp-refresh-btn" id="atfpp-refresh-license-btn">
<?php esc_html_e('🔄Check license status', $text_domain); ?>
</button>
<?php endif; ?>
</div>
<ul>
<li><strong><?php esc_html_e('Status:', $text_domain); ?></strong>
<span class="validity">
<?php if ($license_info->is_valid): ?>
<?php if (atfpp_is_license_expired($license_info)): ?>
<strong>❌ <?php esc_html_e('License Expired', $text_domain); ?></strong>
<?php elseif (atfpp_is_support_expired($license_info)): ?>
<strong>❌ <?php esc_html_e('Support Expired', $text_domain); ?></strong>
<?php else: ?>
<strong class="valid">✅ <?php esc_html_e('Valid', $text_domain); ?></strong>
<?php endif; ?>
<?php else: ?>
<strong>❌ <?php esc_html_e('Invalid', $text_domain); ?></strong>
<?php endif; ?>
</span>
</li>
<li><strong><?php esc_html_e('License Type:', $text_domain); ?></strong> <span class="license-type"><?php echo esc_html($license_info->license_title); ?></span></li>
<li><strong><?php esc_html_e('Plugin Updates & Support Validity:', $text_domain); ?></strong> <span class="validity">
<?php
$current_time = time();
// Handle "No expiry" case for expire_date
$expire_date_expired = false;
if (strtolower($license_info->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));
}
}
?>
</span>
</li>
<li><strong><?php echo esc_html__('Your License Key:', $text_domain); ?></strong> <span class="license-key"><?php echo esc_html($masked_key); ?></span></li>
</ul>
<div class="atfpp-dashboard-license-pro-container-deactivate-btn">
<p><?php esc_html_e('Want to deactivate the license for any reason?', $text_domain); ?></p>
<form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
<input type="hidden" name="action" value="AIAutomaticTranslationsForPolylang_el_deactivate_license" />
<?php wp_nonce_field('el-atfpp-license'); ?>
<button type="submit" class="deactivate-btn">
<?php echo esc_html__('Deactivate License', $text_domain); ?>
</button>
</form>
</div>
<?php if (atfpp_is_license_expired($license_info)): ?>
<div class="notice notice-error" style="margin-top: 10px; color: #d63638;">
<?php atfpp_render_expiry_message($license_info, 'license'); ?>
</div>
<?php elseif (atfpp_is_support_expired($license_info)): ?>
<div class="notice notice-error" style="margin-top: 10px; color: #d63638;">
<?php atfpp_render_expiry_message($license_info, 'support'); ?>
</div>
<?php endif; ?>
<?php atfpp_render_license_help_buttons($text_domain); ?>
</div>
</div>
<?php
}
/**
* Renders the license buttons section
* @param string $text_domain The text domain for translations
*/
function atfpp_render_license_help_buttons($text_domain) {
?>
<div class="atfpp-dashboard-license-pro-container-buttons">
<p><?php esc_html_e('Want to know more about the license key?', $text_domain); ?></p>
<div class="btns">
<a href="https://my.coolplugins.net/account/" target="_blank" class="atfpp-dashboard-btn">
<?php echo esc_html__('Check Account', $text_domain); ?>
</a>
<a href="https://coolplugins.net/support/?utm_source=atfp_plugin&utm_medium=inside&utm_campaign=support&utm_content=dashboard_license" target="_blank" class="atfpp-dashboard-btn">
<?php echo esc_html__('Contact Support', $text_domain); ?>
</a>
</div>
</div>
<?php
}
function atfpp_is_license_expired($license_info) {
return $license_info->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('<a href="%s" target="_blank" rel="noopener noreferrer">%s</a>', 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'
? ''
: ' <a href="'.esc_url('https://my.coolplugins.net/account/subscriptions/').'" target="_blank" rel="noopener noreferrer">'.esc_html__('Renew now', 'atfpp').'</a>';
$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;
}

View File

@@ -0,0 +1,141 @@
<!-- Right Sidebar -->
<div class="atfpp-dashboard-sidebar">
<div class="atfpp-dashboard-status">
<h3><?php echo esc_html__('Auto Translation Status', $text_domain); ?></h3>
<div class="atfpp-dashboard-sts-top">
<?php
$service_providers = array();
$all_data = get_option('cpt_dashboard_data', array());
$avilable_service_providers = array('google'=>'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);
?>
<span><?php echo esc_html(atfp_format_number($totals['character_count'], $text_domain)); ?></span>
<span><?php echo esc_html__('Total Characters Translated!', $text_domain); ?></span>
</div>
<ul class="atfpp-dashboard-sts-btm">
<li><span><?php echo esc_html__('Total Strings', $text_domain); ?></span> <span><?php echo esc_html(atfp_format_number($totals['string_count'], $text_domain)); ?></span></li>
<li><span><?php echo esc_html__('Total Pages / Posts', $text_domain); ?></span> <span><?php echo esc_html($totals['translation_count']); ?></span></li>
<li><span><?php echo esc_html__('Time Taken', $text_domain); ?></span> <span><?php echo esc_html($time_taken_str); ?></span></li>
<?php if(count($service_providers) > 0): ?>
<li class="atfpp-dashboard-sts-btm-service-providers"><span><?php echo esc_html__('Service Providers', $text_domain); ?></span> <div class="atfpp-dashboard-sts-btm-service-providers-list"><?php foreach($service_providers as $service_provider): ?>
<span><?php echo esc_html($avilable_service_providers[$service_provider]); ?></span>
<?php endforeach; ?></div></li>
<?php endif; ?>
</ul>
</div>
<div class="atfpp-dashboard-translate-full">
<h3><?php echo esc_html__('Automatically Translate Plugins & Themes', $text_domain); ?></h3>
<div class="atfpp-dashboard-addon first">
<div class="atfpp-dashboard-addon-l">
<strong><?php echo esc_html(atfp_get_plugin_display_name('automatic-translator-addon-for-loco-translate', $text_domain)); ?></strong>
<span class="addon-desc"><?php echo esc_html__('Loco addon to translate plugins and themes.', $text_domain); ?></span>
<?php if (atfp_is_plugin_installed('automatic-translator-addon-for-loco-translate')): ?>
<span class="installed"><?php echo esc_html__('Installed', $text_domain); ?></span>
<?php else: ?>
<a href="<?php echo esc_url(admin_url('plugin-install.php?s=LocoAI++Auto+Translate+for+Loco+Translate+by+CoolPlugins&tab=search&type=term') ); ?>" class="atfpp-dashboard-btn" target="_blank"><?php _e('Install', $text_domain); ?></a>
<?php endif; ?>
</div>
<div class="atfpp-dashboard-addon-r">
<img src="<?php echo esc_url(ATFPP_URL . 'admin/atfpp-dashboard/images/atlt-logo.png'); ?>" alt="<?php echo esc_html__('TranslatePress Addon', $text_domain); ?>">
</div>
</div>
</div>
<div class="atfpp-dashboard-rate-us">
<h3><?php echo esc_html__('Rate Us ⭐⭐⭐⭐⭐', $text_domain); ?></h3>
<p><?php echo esc_html__('We\'d love your feedback! Hope this addon made auto-translations easier for you.', $text_domain); ?></p>
<a href="https://wordpress.org/support/plugin/automatic-translations-for-polylang/reviews/#new-post" class="review-link" target="_blank"><?php echo esc_html__('Submit a Review →', $text_domain); ?></a>
</div>
</div>
<?php
function atfp_format_time_taken($time_taken, $text_domain) {
if ($time_taken === 0) return esc_html__('0', $text_domain);
if ($time_taken < 60) return sprintf(esc_html__('%d sec', $text_domain), $time_taken);
if ($time_taken < 3600) {
$min = floor($time_taken / 60);
$sec = $time_taken % 60;
return sprintf(esc_html__('%d min %d sec', $text_domain), $min, $sec);
}
$hours = floor($time_taken / 3600);
$min = floor(($time_taken % 3600) / 60);
return sprintf(esc_html__('%d hours %d min', $text_domain), $hours, $min);
}
function atfp_is_plugin_installed($plugin_slug) {
$plugins = get_plugins();
// Check if the plugin is installed
if ($plugin_slug === 'automatic-translator-addon-for-loco-translate') {
return isset($plugins['automatic-translator-addon-for-loco-translate/automatic-translator-addon-for-loco-translate.php']) || isset($plugins['loco-automatic-translate-addon-pro/loco-automatic-translate-addon-pro.php']);
}
return false; // Return false if no match found
}
function atfp_get_plugin_display_name($plugin_slug, $text_domain) {
$plugins = get_plugins();
// Define free and pro plugin paths
$plugin_paths = [
'automatic-translator-addon-for-loco-translate' => [
'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;
}

View File

@@ -0,0 +1,213 @@
<?php
/**
* Do not access the page directly
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
if ( ! class_exists( 'ATFP_Supported_Blocks' ) ) {
/**
* Class ATFP_Supported_Blocks
*
* This class handles the supported blocks for the AutoPoly - AI Translation For Polylang plugin.
*
* @package ATFPP
*/
class ATFP_Supported_Blocks {
/**
* Singleton instance.
*
* @var ATFP_Supported_Blocks
*/
private static $instance = null;
/**
* ATFPP plugin category.
*
* @var array
*/
private $atfpp_plugin_category = array();
/**
* Get the singleton instance of the class.
*
* @return ATFP_Supported_Blocks
*/
public static function get_instance() {
if ( null === self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Constructor for the ATFP_Supported_Blocks class.
*/
private function __construct() {
// wp:phpcs:ignore Wordpress.security Nonce verification is not required here
$tab=isset($_GET['tab']) ? sanitize_text_field(wp_unslash($_GET['tab'])) : '';
$page=isset($_GET['page']) ? sanitize_text_field(wp_unslash($_GET['page'])) : '';
if('support-blocks' === $tab && 'polylang-atfpp-dashboard' === $page){
$this->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() {
?>
<div class="atfp-custom-data-table-wrapper">
<h3><?php echo __('Supported Blocks Translation Settings', 'autopoly-ai-translation-for-polylang-pro'); ?>
<br>
<p><?php echo sprintf(esc_html__('Manage Gutenberg blocks to make them translation-ready with %s.', 'autopoly-ai-translation-for-polylang-pro'), 'AutoPoly'); ?></p>
</h3>
<div class="atfp-custom-data-table-filters">
<div class="atfp-filter-tab" data-column="1" data-default="all">
<label for="atfp-blocks-category"><?php esc_html_e( 'Block Type Category:', 'autopoly-ai-translation-for-polylang-pro' ); ?></label>
<select id="atfp-blocks-category" name="atfp_blocks_category">
<option value="all"><?php esc_html_e( 'All', 'autopoly-ai-translation-for-polylang-pro' ); ?></option>
<option value="core">Core</option>
<?php $this->atfpp_get_blocks_category(); ?>
</select>
</div>
<div class="atfp-filter-tab" data-column="3" data-default="all">
<label for="atfp-blocks-filter"><?php esc_html_e( 'Show Blocks:', 'autopoly-ai-translation-for-polylang-pro' ); ?></label>
<select id="atfp-blocks-filter" name="atfp_blocks_filter">
<option value="all"><?php esc_html_e( 'All', 'autopoly-ai-translation-for-polylang-pro' ); ?></option>
<option value="supported"><?php esc_html_e( 'Supported Blocks', 'autopoly-ai-translation-for-polylang-pro' ); ?></option>
<option value="unsupported"><?php esc_html_e( 'Unsupported Blocks', 'autopoly-ai-translation-for-polylang-pro' ); ?></option>
</select>
</div>
</div>
<div class="atfp-custom-table-section">
<div class="atfp-custom-table-lists">
<table class="atfp-custom-data-table-table" id="atfp-custom-datatable">
<thead>
<tr>
<th><?php esc_html_e( 'Sr.No', 'autopoly-ai-translation-for-polylang-pro' ); ?></th>
<th><?php esc_html_e( 'Block Name', 'autopoly-ai-translation-for-polylang-pro' ); ?></th>
<th><?php esc_html_e( 'Block Title', 'autopoly-ai-translation-for-polylang-pro' ); ?></th>
<th><?php esc_html_e( 'Status', 'autopoly-ai-translation-for-polylang-pro' ); ?></th>
<th><?php esc_html_e( 'Modify', 'autopoly-ai-translation-for-polylang-pro' ); ?></th>
</tr>
</thead>
<tbody>
<?php
$this->atfpp_get_supported_blocks_table()
?>
</tbody>
</table>
</div>
</div>
</div>
<?php
}
/**
* Get the blocks category.
*/
public function atfpp_get_blocks_category() {
$blocks_data = WP_Block_Type_Registry::get_instance()->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 '<option value="' . esc_attr( $plugin_name ) . '">' . esc_html( $filter_plugin_name ) . '</option>';
}
}
}
/**
* 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 = '<a href="' . esc_url( admin_url( 'post.php?post=' . esc_attr( $atfp_post_id ) . '&action=edit&atfp_new_block=' ) . esc_attr( $block_name ) ) . '">' . $modify_text . '</a>'; // Modify link
$modify_link = '<a href="' . esc_url( admin_url( 'post.php?post=' . esc_attr( $atfp_post_id ) . '&action=edit&atfp_new_block=' ) . esc_attr( $block_name ) ) . '">' . $modify_text . '</a>'; // Modify link
echo '<tr data-block-name="' . esc_attr( strtolower( $block_name ) ) . '" data-block-status="' . esc_attr( strtolower( $status ) ) . '" >';
echo '<td>' . esc_html($s_no++) . '</td>';
echo '<td>' . esc_html($block_name) . '</td>';
echo '<td>' . esc_html($block_title) . '</td>';
echo '<td>' . esc_html($status) . '</td>';
echo '<td>' . wp_kses($modify_link, array('a' => array('href' => array(), 'target' => array(), 'rel' => array()))) . '</td>';
echo '</tr>';
}
}
}
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();
}

View File

@@ -0,0 +1,575 @@
<?php
if (!defined('ABSPATH')) {
exit; // Exit if accessed directly
}
if (!class_exists('Atfpp_Glossary')) {
class Atfpp_Glossary {
/**
* Init
* @var object
*/
private static $init;
/**
* Instance
* @return object
*/
public static function instance() {
if (!isset(self::$init)) {
self::$init = new self();
}
return self::$init;
}
/**
* Constructor
*/
public function __construct() {
// Add AJAX hooks here
add_action('wp_ajax_atfpp_import_glossary', array($this, 'atfpp_import_glossary_ajax'));
add_action('wp_ajax_atfpp_update_glossary', array($this, 'atfpp_update_glossary_ajax'));
add_action('wp_ajax_atfpp_delete_glossary', array($this, 'atfpp_delete_glossary_ajax'));
add_action('wp_ajax_atfpp_add_glossary', array($this, 'atfpp_add_glossary_ajax'));
add_action('wp_ajax_atfpp_export_glossary', array($this, 'atfpp_export_glossary_ajax'));
add_action('wp_ajax_atfpp_get_glossary', array($this, 'atfpp_get_glossary_ajax'));
add_action('wp_ajax_nopriv_atfpp_get_glossary', array($this, 'atfpp_get_glossary_ajax'));
}
/**
* Store glossary entry
*/
public static function store_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'] ?? '');
// Support single or multiple translations
$target_langs = (array) ($glossary_data['target_lang'] ?? []);
$translated_terms = (array) ($glossary_data['translated_term'] ?? []);
$all_glossaries = get_option('atfpp_glossary_data', array());
$found = false;
foreach ($all_glossaries as &$entry) {
if (
($entry['original_term'] ?? '') === $glossary_term &&
($entry['original_language_code'] ?? '') === $source_language_code
) {
// Entry exists, add new translations if not duplicates
if (!isset($entry['translations']) || !is_array($entry['translations'])) {
$entry['translations'] = array();
}
$existing_langs = array_column($entry['translations'], 'target_language_code');
foreach ($target_langs as $idx => $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'] : '',
];
}
}
?>

View File

@@ -0,0 +1,225 @@
<?php
if ( ! defined( 'ABSPATH' )) exit;
class CPFM_Feedback_Notice {
private static $registered_notices = [];
public function __construct() {
add_action('admin_init', [ $this, 'cpfm_listen_for_external_notice_registration' ]);
add_action('admin_enqueue_scripts', [ $this, 'cpfm_enqueue_assets' ]);
add_action('wp_ajax_cpfm_handle_opt_in', [ $this, 'cpfm_handle_opt_in_choice' ]);
add_action('admin_footer', [ $this, 'cpfm_render_notice_panel' ]);
}
public static function cpfm_register_notice($key, $args) {
if (!current_user_can('manage_options')) {
return;
}
if (!isset(self::$registered_notices[$key])) {
self::$registered_notices[$key] = wp_parse_args($args, [
'title' => '',
'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 .= '<div id="cpfNoticePanel" class="notice-panel"' . ($auto_show ? ' data-auto-show="true"' : '') . '>';
$output .= '<div class="notice-panel-header">' . esc_html__('Help Improve Plugins', 'ccpw') . ' <span class="dashicons dashicons-no" id="cpfm_remove_notice"></span></div>';
$output .= '<div class="notice-panel-content">';
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 .= '<div class="notice-item unread" data-notice-id="' . esc_attr($key) . '">';
$output .= '<strong>' . esc_html($notice['title']) . '</strong>';
$output .= '<div class="notice-message-with-toggle">';
$output .= '<p>' . esc_html($notice['message']) . '<a href="#" class="cpf-toggle-extra">' . esc_html__(' More info', 'ccpw') . '</a></p>';
$output .= '</div>';
$output .= '<div class="cpf-extra-info">';
$output .= '<p>' . esc_html__('Opt in to receive email updates about security improvements, new features, helpful tutorials, and occasional special offers. We\'ll collect:', 'ccpw') . '</p>';
$output .= '<ul>';
$output .= '<li>' . esc_html__('Your website home URL and WordPress admin email.', 'ccpw') . '</li>';
$output .= '<li>' . 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') . '</li>';
$output .= '</ul>';
$output .= '</div>';
$output .= '<div class="notice-actions">';
$output .= '<button class="button button-primary opt-in-yes" data-category="' . esc_attr($key) . '" id="yes-share-data" value="yes">' . esc_html__("Yes, I Agree", 'ccpw') . '</button>';
$output .= '<button class="button opt-in-no" data-category="' . esc_attr($key) . '" id="no-share-data" value="no">' . esc_html__('No, Thanks', 'ccpw') . '</button>';
$output .= '</div>';
$output .= '</div>';
}
$output .= '</div>';
$output .= '</div>';
if ($unread_count > 0) {
echo $output;
}
}
}
new CPFM_Feedback_Notice();

View File

@@ -0,0 +1,119 @@
<?php
if (!defined('ABSPATH')) {
exit();
}
if (!class_exists('ATFPP_cronjob')) {
class ATFPP_cronjob
{
public function __construct()
{
}
public function atfpp_cron_init_hooks()
{
//initialize Cron Jobs
add_filter('cron_schedules', array($this, 'atfpp_cron_schedules'));
add_action('atfpp_extra_data_update', array($this, 'atfpp_cron_extra_data_autoupdater'));
}
/*
|--------------------------------------------------------------------------
| cron custom schedules
|--------------------------------------------------------------------------
*/
public function atfpp_cron_schedules($schedules)
{
if (!isset($schedules['every_30_days'])) {
$schedules['every_30_days'] = array(
'interval' => 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');
}
}
}
}

View File

@@ -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}

View File

@@ -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() }) })

View File

@@ -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;
}

View File

@@ -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();
});
});
});

View File

@@ -0,0 +1,316 @@
<?php
if(!defined('ABSPATH')){
exit;
}
/**
* Dashboard
*
* example:
*
* Dashbord initialize
* if(!class_exists('Atfpp_Dashboard')){
* $dashboard=Atfpp_Dashboard::instance();
* }
*
* Store options
* if(class_exists('Atfpp_Dashboard')){
* Atfpp_Dashboard::store_options(
* 'prefix', // Required plugin prefix
* 'unique_key',// Optional unique key is used to update the data based on post/page id or plugin/themes name
* 'update', // Optional preview string count or character count update or replace
* array(
* 'post/page or theme/plugin name' => '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 <b>%1$s</b>! You have translated <b>%2$s</b> characters so far using our plugin!<br>Please give us a quick rating, it works as a boost for us to keep working on more <a style="text-decoration: none;" href="%3$s" target="_blank" rel="noopener noreferrer"><b>Cool Plugins</b></a>!', '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= '<div class="notice notice-info is-dismissible cpt-review-notice">';
$html .= '<div class="cpt-review-notice-content"><p>'.$message.'</p><div class="atfpp-review-notice-dismiss" data-prefix="'.esc_attr($prefix).'" data-nonce="'.esc_attr(wp_create_nonce('atfpp_hide_review_notice')).'"><a href="'.esc_url($url).'" target="_blank" class="button button-primary">Rate Now! ★★★★★</a><button class="button cpt-already-reviewed">'.esc_html__('Already Reviewed', 'cp-notice').'</button><button class="button cpt-not-interested">'.esc_html__('Not Interested', 'cp-notice').'</button></div></div></div>';
echo wp_kses($html, $allowed);
});
add_action('atfpp_display_admin_notices', function() use ($message, $prefix, $url, $allowed){
$html= '<div class="notice notice-info is-dismissible cpt-review-notice">';
$html .= '<div class="cpt-review-notice-content"><p>'.$message.'</p><div class="atfpp-review-notice-dismiss" data-prefix="'.$prefix.'" data-nonce="'.wp_create_nonce('atfpp_hide_review_notice').'"><a href="'. $url .'" target="_blank" class="button button-primary">Rate Now! ★★★★★</a><button class="button cpt-not-interested">'.__('Not Interested', 'cp-notice').'</button><button class="button cpt-already-reviewed">'.__('Already Reviewed', 'cp-notice').'</button></div></div></div>';
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 );
}
}
}
}

View File

@@ -0,0 +1 @@
<?php return array('dependencies' => array('react', 'react-dom', 'wp-blocks', 'wp-data', 'wp-element', 'wp-i18n'), 'version' => '1f7ec3dfbd17a30f0ca3');

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
<?php return array('dependencies' => array('react', 'wp-block-editor', 'wp-components', 'wp-element', 'wp-rich-text'), 'version' => 'a63731a1391f5c864e74');

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
<?php return array('dependencies' => array('react', 'react-dom', 'wp-data', 'wp-element', 'wp-i18n'), 'version' => '856cfb9b2784e105fa5e');

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
<?php return array('dependencies' => array('react', 'react-dom', 'wp-i18n'), 'version' => '3aba280d86d09cf46a55');

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,413 @@
.atfp-custom-data-table-wrapper {
width: 100%;
margin-top: 25px;
background: white;
padding: 24px;
border-radius: 8px;
border: 1px solid var(--border-color);
/* Keyframes: subtle pop + fade */
/* Accessibility: honor reduced motion */
}
.atfp-custom-data-table-wrapper .saving-text {
margin-left: 8px;
font-size: 13px;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
}
.atfp-custom-data-table-wrapper .saving-text .dot {
width: 4px;
height: 4px;
border-radius: 999px;
background: #ffffff;
margin-left: 1px;
margin-top: 6px;
opacity: 0.25;
-webkit-animation: savingBlink 1.05s infinite ease-in-out;
animation: savingBlink 1.05s infinite ease-in-out;
-webkit-animation-delay: calc(var(--i) * 0.18s - 1.05s);
animation-delay: calc(var(--i) * 0.18s - 1.05s);
will-change: opacity, transform;
}
.atfp-custom-data-table-wrapper h3 {
display: -webkit-inline-box;
display: -ms-inline-flexbox;
display: inline-flex;
margin: 5px 0 20px;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-ms-flex-direction: column;
flex-direction: column;
}
.atfp-custom-data-table-wrapper h3 p {
margin-bottom: 0;
}
.atfp-custom-data-table-wrapper.atfp-custom-fields .atfp-save-custom-fields {
float: right;
height: 30px;
display: -webkit-inline-box;
display: -ms-inline-flexbox;
display: inline-flex;
-ms-flex-item-align: center;
align-self: center;
}
.atfp-custom-data-table-wrapper.atfp-custom-fields .atfp-save-custom-fields:focus {
-webkit-box-shadow: none;
box-shadow: none;
}
.atfp-custom-data-table-wrapper .atfp-custom-data-table-filters {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-column-gap: 1rem;
column-gap: 1rem;
}
.atfp-custom-data-table-wrapper .atfp-custom-data-table-filters .atfp-search-tab {
margin-left: auto;
}
.atfp-custom-data-table-wrapper .atfp-custom-data-table-filters .atfp-search-tab #atfp-blocks-search {
min-width: 220px;
}
.atfp-custom-data-table-wrapper .atfp-custom-data-table-filters .atfp-filter-tab,
.atfp-custom-data-table-wrapper .atfp-custom-data-table-filters .atfp-search-tab {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-column-gap: 1rem;
column-gap: 1rem;
}
.atfp-custom-data-table-wrapper .atfp-custom-data-table-filters .atfp-filter-tab label,
.atfp-custom-data-table-wrapper .atfp-custom-data-table-filters .atfp-search-tab label {
margin: 0 0.5rem 0 0;
font-size: 15px;
font-weight: 600;
text-transform: capitalize;
}
.atfp-custom-data-table-wrapper .atfp-custom-table-lists {
margin: 20px 0px;
font-family: Arial, sans-serif;
}
.atfp-custom-data-table-wrapper .atfp-custom-table-lists .atfp-custom-data-table-table {
width: 100%;
border-collapse: collapse;
margin-top: 10px;
border: 1px solid #ddd;
-webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
}
.atfp-custom-data-table-wrapper .atfp-custom-table-lists .atfp-custom-data-table-table thead {
background-color: #f2f2f2;
-webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
}
.atfp-custom-data-table-wrapper .atfp-custom-table-lists .atfp-custom-data-table-table thead th {
padding: 10px;
text-align: left;
border-bottom: 2px solid #ddd;
}
.atfp-custom-data-table-wrapper .atfp-custom-table-lists .atfp-custom-data-table-table thead th:last-child {
text-align: center;
}
.atfp-custom-data-table-wrapper .atfp-custom-table-lists .atfp-custom-data-table-table tbody tr .atfp-search-highlight {
background-color: orange;
color: black;
}
.atfp-custom-data-table-wrapper .atfp-custom-table-lists .atfp-custom-data-table-table tbody tr:nth-child(even) {
background-color: #f9f9f9;
}
.atfp-custom-data-table-wrapper .atfp-custom-table-lists .atfp-custom-data-table-table tbody tr td {
padding: 10px;
border-bottom: 1px solid #ddd;
}
.atfp-custom-data-table-wrapper .atfp-custom-table-lists .atfp-custom-data-table-table tbody tr td:last-child {
text-align: center;
}
.atfp-custom-data-table-wrapper .atfp-custom-table-lists .atfp-custom-data-table-table tbody tr td.dt-empty {
text-align: center;
font-size: 1.1rem;
}
.atfp-custom-data-table-wrapper .atfp-custom-table-lists .atfp-custom-data-table-table tbody tr td a {
color: #0073aa;
text-decoration: none;
}
.atfp-custom-data-table-wrapper .atfp-custom-table-lists .atfp-custom-data-table-table tbody tr td a:hover {
text-decoration: underline;
}
.atfp-custom-data-table-wrapper .atfp-pagination {
margin: 20px 0px;
font-size: 14px;
text-align: end;
}
.atfp-custom-data-table-wrapper .atfp-pagination .page-numbers {
display: inline-block;
padding: 0.5em 1em;
margin: 0 0.2em;
border: 1px solid #ddd;
border-radius: 3px;
color: #0073aa;
text-decoration: none;
background-color: #fff;
-webkit-transition: background-color 0.3s;
transition: background-color 0.3s;
}
.atfp-custom-data-table-wrapper .atfp-pagination .page-numbers.current {
background-color: #0073aa;
color: #fff;
border: 1px solid #0073aa;
}
.atfp-custom-data-table-wrapper .atfp-pagination .page-numbers:hover:not(.current) {
background-color: #f0f0f0;
}
.atfp-custom-data-table-wrapper .atfp-pagination .page-numbers.dots {
padding: 0;
color: #555;
}
.atfp-custom-data-table-wrapper .atfp-pagination .page-numbers.next {
margin-left: auto;
font-weight: bold;
}
@-webkit-keyframes savingBlink {
0%,
20% {
opacity: 0.25;
-webkit-transform: translateY(0);
transform: translateY(0);
}
40% {
opacity: 1;
-webkit-transform: translateY(-1px);
transform: translateY(-1px);
}
60% {
opacity: 0.9;
-webkit-transform: translateY(0);
transform: translateY(0);
}
100% {
opacity: 0.25;
-webkit-transform: translateY(0);
transform: translateY(0);
}
}
@keyframes savingBlink {
0%,
20% {
opacity: 0.25;
-webkit-transform: translateY(0);
transform: translateY(0);
}
40% {
opacity: 1;
-webkit-transform: translateY(-1px);
transform: translateY(-1px);
}
60% {
opacity: 0.9;
-webkit-transform: translateY(0);
transform: translateY(0);
}
100% {
opacity: 0.25;
-webkit-transform: translateY(0);
transform: translateY(0);
}
}
@media (prefers-reduced-motion: reduce) {
.atfp-custom-data-table-wrapper .saving-dot {
-webkit-animation: none;
animation: none;
opacity: 1;
}
}
#atfp-custom-datatable_wrapper {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
gap: 1rem;
-webkit-box-pack: justify;
-ms-flex-pack: justify;
justify-content: space-between;
}
#atfp-custom-datatable_wrapper .dt-layout-row.dt-layout-table {
width: 100%;
}
#atfp-custom-datatable_wrapper .dt-layout-row:last-child {
margin-left: auto;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: end;
-ms-flex-pack: end;
justify-content: end;
-webkit-column-gap: 1rem;
column-gap: 1rem;
padding: 0;
}
#atfp-custom-datatable_wrapper .dt-layout-row:last-child .dt-layout-start {
font-size: 15px;
}
#atfp-custom-datatable_wrapper .dt-layout-row:last-child .dt-paging {
margin: 1rem 0;
}
#atfp-custom-datatable_wrapper .dt-layout-row:last-child .dt-paging .dt-paging-button {
padding: 0.5em 1em;
margin: 0 0.2em;
border: 1px solid #ddd;
border-radius: 3px;
color: #0073aa;
text-decoration: none;
background-color: #fff;
-webkit-transition: background-color 0.3s;
transition: background-color 0.3s;
cursor: pointer;
}
#atfp-custom-datatable_wrapper .dt-layout-row:last-child .dt-paging .dt-paging-button.current {
background-color: #0073aa;
color: #fff;
border: 1px solid #0073aa;
}
#atfp-custom-datatable_wrapper .dt-layout-row:last-child .dt-paging .dt-paging-button:hover:not(.current) {
background-color: #f0f0f0;
}
#atfp-custom-datatable_wrapper .dt-layout-row:last-child .dt-paging .dt-paging-button.disabled {
color: #aaa;
cursor: not-allowed;
}
#atfp-custom-datatable_wrapper .dt-layout-row:last-child .dt-paging .dt-paging-button.dots {
padding: 0;
color: #555;
}
#atfp-custom-datatable_wrapper .dt-layout-row:last-child .dt-paging .dt-paging-button.next {
margin-left: auto;
font-weight: bold;
}
#atfp-custom-datatable_wrapper .dt-layout-row:last-child .dt-paging .ellipsis {
padding: 0 1em;
color: #555;
}
#atfp-custom-datatable_wrapper .dt-layout-row:nth-child(2) {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: end;
-ms-flex-pack: end;
justify-content: end;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
gap: 1rem;
}
#atfp-custom-datatable_wrapper .dt-layout-row:nth-child(2) .dt-length {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-orient: horizontal;
-webkit-box-direction: reverse;
-ms-flex-direction: row-reverse;
flex-direction: row-reverse;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
}
#atfp-custom-datatable_wrapper .dt-layout-row:nth-child(2) label {
margin: 0 0.5rem 0 0;
font-size: 15px;
font-weight: 600;
text-transform: capitalize;
}
#atfp-copy-btn {
z-index: 999999;
background-color: #333;
color: white;
border: none;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
-webkit-transition: all 0.3s;
transition: all 0.3s;
position: fixed;
top: 50%;
-webkit-transform: translateY(-50%) scale(0.9);
transform: translateY(-50%) scale(0.9);
left: 0px;
}
#atfp-copy-btn:hover {
font-weight: 600;
-webkit-transform: translateY(-50%) scale(1);
transform: translateY(-50%) scale(1);
-webkit-box-shadow: 0 0 10px #949494, 0 0 30px #898787;
box-shadow: 0 0 10px #949494, 0 0 30px #898787;
}
#atfp-copy-text {
display: fixed;
bottom: -50%;
left: -50%;
}
/*# sourceMappingURL=atfp-custom-data-table.css.map */

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,87 @@
.atfp-elementor-translate-confirm-box{
display: none;
position: fixed;
z-index: 99999;
top: 0;
width: 100%;
height: 100vh;
overflow: hidden;
background-color: rgba(0,0,0,.75);
justify-content: center;
align-items: center;
font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;
}
.atfp-elementor-translate-confirm-box .modal-content{
padding: 16px;
text-align: center;
position: relative;
background-color: #fefefe;
width: clamp(200px,400px,80%);
height: auto;
overflow: hidden;
border-radius: 5px;
box-shadow: 0 4px 8px 0 rgba(0,0,0,.2),0 6px 20px 0 rgba(0,0,0,.19);
-webkit-animation-name: animatetopsetting;
-webkit-animation-duration: .6s;
-webkit-animation-fill-mode: forwards;
animation-name: animatetopsetting;
animation-duration: .6s;
animation-fill-mode: forwards;
padding-block: 16px;
color: #000;
border-radius: 8px;
}
.atfp-elementor-translate-confirm-box .modal-content p{
font-size: 16px;
}
.atfp-elementor-translate-confirm-box .modal-content button[data-value]{
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 13px;
line-height: 1.3;
min-height: 30px;
margin: 0 0.3rem;
cursor: pointer;
border-width: 1px;
border-style: solid;
-webkit-appearance: none;
border-radius: 3px;
box-sizing: border-box;
font-family: inherit;
font-weight: inherit;
vertical-align: top;
text-decoration: none;
text-shadow: none;
text-align: center;
}
.atfp-elementor-translate-confirm-box .modal-content button[data-value="yes"]{
padding: 2px 11px;
white-space: nowrap;
background: #3db63d;
border-color: #33a133;
color: #fff;
}
.atfp-elementor-translate-confirm-box .modal-content button[data-value="no"]{
background: #ee4237;
border-color: #dd362b;
color: #fff;
padding: 2px 11px;
display: inline-flex;
align-items: center;
}
.atfp-elementor-translate-confirm-box .modal-content button[data-value="yes"]:hover{
background: #37a737;
border-color: #238b23;
}
.atfp-elementor-translate-confirm-box .modal-content button[data-value="no"]:hover{
background: #dd382e;
border-color: #c72a20;
}

View File

@@ -0,0 +1,58 @@
button.elementor-button.atfp-translate-button:enabled {
border-radius: 0px;
color: rgb(12, 13, 14);
background-color: #F0ABFC
}
button.elementor-button.atfp-translate-button:enabled:hover {
background-color: rgb(235, 142, 251);
}
button.elementor-button.atfp-translate-button:disabled {
cursor: default;
border-radius: 0px;
color: rgb(12, 13, 14);
}
/* Footer Close Button */
button.atfp-setting-close {
margin-top: .9rem;
font-size: 1rem;
padding: .1rem .4rem;
cursor: pointer;
opacity: .7;
}
button.atfp-setting-close:hover {
opacity: 1;
}
/* ******Popup***** */
.save_it.button-primary[disabled] {
color: #a7aaad !important;
background: #f6f7f7 !important;
border-color: #dcdcde !important;
box-shadow: none !important;
text-shadow: none !important;
cursor: default;
padding: 8px 12px;
border: none;
border-radius: 2px;
}
.save_it.button-primary {
background: #2271b1;
border-color: #2271b1;
color: #fff;
text-decoration: none;
text-shadow: none;
padding: 8px 12px;
border: none;
border-radius: 2px;
cursor: pointer;
}
#atfp_strings_model .save_btn_cont button:not([disabled])::before {
line-height: 14px !important;
}

View File

@@ -0,0 +1 @@
button.elementor-button.atfp-translate-button:enabled{border-radius:0;color:#0c0d0e;background-color:#f0abfc}button.elementor-button.atfp-translate-button:enabled:hover{background-color:#eb8efb}button.elementor-button.atfp-translate-button:disabled{cursor:default;border-radius:0;color:#0c0d0e}button.atfp-setting-close{margin-top:.9rem;font-size:1rem;padding:.1rem .4rem;cursor:pointer;opacity:.7}button.atfp-setting-close:hover{opacity:1}.save_it.button-primary[disabled]{color:#a7aaad!important;background:#f6f7f7!important;border-color:#dcdcde!important;box-shadow:none!important;text-shadow:none!important;cursor:default;padding:8px 12px;border:none;border-radius:2px}.save_it.button-primary{background:#2271b1;border-color:#2271b1;color:#fff;text-decoration:none;text-shadow:none;padding:8px 12px;border:none;border-radius:2px;cursor:pointer}#atfp_strings_model .save_btn_cont button:not([disabled])::before{line-height:14px!important}

View File

@@ -0,0 +1,94 @@
/* lock scroll when overlay is open */
body.atfp-overlay-open { overflow: hidden; }
/* overlay shell */
.atfp-overlay {
position: fixed; inset: 0; z-index: 100000;
display: grid; place-items: center;
animation: fade-in .15s ease-out forwards; opacity: 0;
}
.atfp-backdrop { position: absolute; inset: 0; background: rgba(0,0,0,.45); }
/* card */
.atfp-box {
position: relative; z-index: 1;
width: min(92vw, 480px);
background: #fff; color: #1d2327;
border-radius: 12px; padding: 16px 16px 12px;
box-shadow: 0 10px 30px rgba(0,0,0,.25);
border: 1px solid #e6e8eb;
animation: rise .18s ease-out forwards; transform: translateY(6px);
font: 14px/1.45 system-ui, -apple-system, Segoe UI, Roboto, "Helvetica Neue", Arial, "Noto Sans", "Apple Color Emoji","Segoe UI Emoji";
}
/* dark mode */
@media (prefers-color-scheme: dark) {
.atfp-box { background: #101418; color: #e8edf5; border-color: #1c232d; box-shadow: 0 10px 30px rgba(0,0,0,.6); }
}
/* row */
.atfp-row { display: grid; grid-template-columns: 28px 1fr; gap: 12px; align-items: center; }
/* spinner & icons */
.atfp-spinner,
.atfp-icon { width: 24px; height: 24px; display: none; }
.atfp-spinner {
box-sizing: border-box;
border: 3px solid rgba(0,0,0,.1);
border-top-color: #3db63d;
border-radius: 50%;
animation: spin .9s linear infinite;
}
@media (prefers-color-scheme: dark) {
.atfp-spinner { border-color: rgba(255,255,255,.2); border-top-color: #56ce56; }
}
.atfp-icon { text-align: center; line-height: 24px; font-weight: 700; }
.atfp-icon--ok { color: #19a35b; }
.atfp-icon--err { color: #d23a3a; }
/* text */
.atfp-title { display: none; font-weight: 600; }
.atfp-desc { display: none; color: #55606e; margin-top: 2px; }
@media (prefers-color-scheme: dark) { .atfp-desc { color: #a8b4c6; } }
/* progress bar */
.atfp-bar { height: 6px; background: rgba(0,0,0,.08); border-radius: 999px; overflow: hidden; margin-top: 10px; }
.atfp-bar > span { display: block; height: 100%; width: 40%;
background: linear-gradient(90deg, #3db63d8e, #3db63d);
animation: slide 1.5s ease-in-out infinite; border-radius: 999px;
}
@media (prefers-color-scheme: dark) { .atfp-bar { background: rgba(255,255,255,.12); } }
/* state toggles */
.atfp-box[data-state="loading"] .atfp-spinner { display: block; }
.atfp-box[data-state="loading"] .atfp-title[data-label="loading"],
.atfp-box[data-state="loading"] .atfp-desc[data-label="loading"] { display: block; }
.atfp-box[data-state="success"] .atfp-icon--ok { display: block; }
.atfp-box[data-state="success"] .atfp-title[data-label="success"],
.atfp-box[data-state="success"] .atfp-desc[data-label="success"] { display: block; }
.atfp-box[data-state="success"] .atfp-bar { display: none; }
.atfp-box[data-state="error"] .atfp-icon--err { display: block; }
.atfp-box[data-state="error"] .atfp-title[data-label="error"],
.atfp-box[data-state="error"] .atfp-desc[data-label="error"] { display: block; }
.atfp-box[data-state="error"] .atfp-bar > span {
background: linear-gradient(90deg, rgba(210,58,58,.2), rgba(210,58,58,.6));
}
/* animations */
@keyframes fade-in { to { opacity: 1; } }
@keyframes rise { to { transform: translateY(0); } }
@keyframes spin { to { transform: rotate(360deg); } }
@keyframes slide {
0% { transform: translateX(-60%); }
50% { transform: translateX(6%); }
100% { transform: translateX(110%); }
}
/* reduced motion */
@media (prefers-reduced-motion: reduce) {
.atfp-overlay, .atfp-box, .atfp-spinner, .atfp-bar > span { animation: none !important; }
}

View File

@@ -0,0 +1 @@
body.atfp-overlay-open{overflow:hidden}.atfp-overlay{position:fixed;inset:0;z-index:100000;display:grid;place-items:center;animation:fade-in .15s ease-out forwards;opacity:0}.atfp-backdrop{position:absolute;inset:0;background:rgba(0,0,0,.45)}.atfp-box{position:relative;z-index:1;width:min(92vw,480px);background:#fff;color:#1d2327;border-radius:12px;padding:16px 16px 12px;box-shadow:0 10px 30px rgba(0,0,0,.25);border:1px solid #e6e8eb;animation:rise .18s ease-out forwards;transform:translateY(6px);font:14px/1.45 system-ui,-apple-system,Segoe UI,Roboto,"Helvetica Neue",Arial,"Noto Sans","Apple Color Emoji","Segoe UI Emoji"}@media (prefers-color-scheme:dark){.atfp-box{background:#101418;color:#e8edf5;border-color:#1c232d;box-shadow:0 10px 30px rgba(0,0,0,.6)}}.atfp-row{display:grid;grid-template-columns:28px 1fr;gap:12px;align-items:center}.atfp-icon,.atfp-spinner{width:24px;height:24px;display:none}.atfp-spinner{box-sizing:border-box;border:3px solid rgba(0,0,0,.1);border-top-color:#3db63d;border-radius:50%;animation:spin .9s linear infinite}@media (prefers-color-scheme:dark){.atfp-spinner{border-color:rgba(255,255,255,.2);border-top-color:#56ce56}}.atfp-icon{text-align:center;line-height:24px;font-weight:700}.atfp-icon--ok{color:#19a35b}.atfp-icon--err{color:#d23a3a}.atfp-title{display:none;font-weight:600}.atfp-desc{display:none;color:#55606e;margin-top:2px}@media (prefers-color-scheme:dark){.atfp-desc{color:#a8b4c6}}.atfp-bar{height:6px;background:rgba(0,0,0,.08);border-radius:999px;overflow:hidden;margin-top:10px}.atfp-bar>span{display:block;height:100%;width:40%;background:linear-gradient(90deg,#3db63d8e,#3db63d);animation:slide 1.5s ease-in-out infinite;border-radius:999px}@media (prefers-color-scheme:dark){.atfp-bar{background:rgba(255,255,255,.12)}}.atfp-box[data-state=loading] .atfp-spinner{display:block}.atfp-box[data-state=loading] .atfp-desc[data-label=loading],.atfp-box[data-state=loading] .atfp-title[data-label=loading]{display:block}.atfp-box[data-state=success] .atfp-icon--ok{display:block}.atfp-box[data-state=success] .atfp-desc[data-label=success],.atfp-box[data-state=success] .atfp-title[data-label=success]{display:block}.atfp-box[data-state=success] .atfp-bar{display:none}.atfp-box[data-state=error] .atfp-icon--err{display:block}.atfp-box[data-state=error] .atfp-desc[data-label=error],.atfp-box[data-state=error] .atfp-title[data-label=error]{display:block}.atfp-box[data-state=error] .atfp-bar>span{background:linear-gradient(90deg,rgba(210,58,58,.2),rgba(210,58,58,.6))}@keyframes fade-in{to{opacity:1}}@keyframes rise{to{transform:translateY(0)}}@keyframes spin{to{transform:rotate(360deg)}}@keyframes slide{0%{transform:translateX(-60%)}50%{transform:translateX(6%)}100%{transform:translateX(110%)}}@media (prefers-reduced-motion:reduce){.atfp-bar>span,.atfp-box,.atfp-overlay,.atfp-spinner{animation:none!important}}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
<?php return array('dependencies' => array('react', 'react-dom', 'wp-i18n'), 'version' => '194ccb285b472bc652c6');

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="currentColor" d="M12 17q.425 0 .713-.288T13 16t-.288-.712T12 15t-.712.288T11 16t.288.713T12 17m0-4q.425 0 .713-.288T13 12V8q0-.425-.288-.712T12 7t-.712.288T11 8v4q0 .425.288.713T12 13m0 9q-2.075 0-3.9-.788t-3.175-2.137T2.788 15.9T2 12t.788-3.9t2.137-3.175T8.1 2.788T12 2t3.9.788t3.175 2.137T21.213 8.1T22 12t-.788 3.9t-2.137 3.175t-3.175 2.138T12 22m0-2q3.35 0 5.675-2.325T20 12t-2.325-5.675T12 4T6.325 6.325T4 12t2.325 5.675T12 20m0-8"/></svg>

After

Width:  |  Height:  |  Size: 539 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 314 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@@ -0,0 +1 @@
<?php return array('dependencies' => array('react', 'wp-components', 'wp-element', 'wp-i18n'), 'version' => '1b54f74346c67e65fd98');

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,312 @@
'use strict';
(() => {
const { createBlock } = wp.blocks;
const { dispatch, select } = wp.data;
class atfpCreateNewBlock {
constructor() {
this.updateBlockStore = {};
this.loaderRemove = null;
this.loader = null;
this.replaceAttributes = null;
this.updateBlockId = new Array();
}
copyTranslateText = () => {
// Get the current selection object
const selection = window.getSelection();
// Create a new range object
const range = document.createRange();
// Select the contents of the copy text element
range.selectNodeContents(document.getElementById('atfp-copy-text'));
// Remove any existing selections
selection.removeAllRanges();
// Add the new range to the selection
selection.addRange(range);
// Execute the copy command
document.execCommand('copy');
// Clear the selection
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 <b>"Copy Text"</b> to use. Then, paste it into the section of your block you want automatically translated',
{
isDismissible : false,
id: 'atfp-notice-id',
actions: [
{
label: 'Watch Video.',
url: `${atfpAddBlockVars.atfp_demo_page_url}#custom-block-translate`,
},
],
__unstableHTML: true
}).then(()=>{
const targetAnchor=document.querySelector(`a[href^="${atfpAddBlockVars.atfp_demo_page_url}#custom-block-translate"]`);
if(targetAnchor){
targetAnchor.addEventListener('click', (e)=>{
e.preventDefault();
window.open(targetAnchor.href, '_blank');
})
}
});
}
copyBtnInitialize = () => { // Initialize the copy button
const copyBtn = document.createElement('div'); // Create a new div element for the copy button
copyBtn.id = 'atfp-copy-btn'; // Set the ID of the copy button
copyBtn.innerHTML = 'Copy Text'; // Set the inner HTML of the copy button
copyBtn.addEventListener('click', this.copyTranslateText); // Add click event listener to copy text
copyBtn.ariaLabel = 'Copy Text'; // Set the aria-label for accessibility
copyBtn.title = 'Click to copy the text "Make This Content Available for Translation"'; // Tooltip message
const copyText = document.createElement('div'); // Create a new div element for the copy text
copyText.id = 'atfp-copy-text'; // Set the ID of the copy text div
copyText.innerHTML = 'Make This Content Available for Translation'; // Set the inner HTML of the copy text div
document.body.appendChild(copyBtn); // Append the copy button to the document body
document.body.appendChild(copyText); // Append the copy text to the document body
}
addBlockInitialize = (newBlock) => {
this.newBlock = newBlock;
this.creteNewBlock();
this.skeletonLoader();
}
removeLoader = () => {
clearTimeout(this.loaderRemove);
this.loaderRemove = setTimeout(() => {
if (this.loader) {
this.loader.remove();
}
}, 2000);
}
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}`);
if (blockWrp) {
blockWrp.appendChild(this.loader);
}
}, 100);
setTimeout(() => {
this.updateBlockContent(Block);
}, 400);
}
updateBlockContent = (Block) => {
const newBlock = document.getElementById(`block-${Block.clientId}`);
if (newBlock) {
this.updateContent(newBlock);
}
return;
}
updateContent = async (ele) => {
const element = ele;
let i = 1;
this.removeLoader();
if (element) {
if (element.contentEditable == 'true' && element.children.length < 2) {
element.innerHTML = 'Make This Content Available for Translation';
} else {
const innerElements = element.getElementsByTagName('*');
for (let innerElement of innerElements) {
if (i === innerElements.length) {
this.removeLoader();
setTimeout(() => {
this.updateBlockFromStore();
}, 500);
}
i++;
if (["SCRIPT", "STYLE", "META", "LINK", "TITLE", "NOSCRIPT", "STYLE", "SCRIPT", "NOSCRIPT", "STYLE", "SCRIPT", "NOSCRIPT", "STYLE", "SCRIPT", "NOSCRIPT"].includes(innerElement.tagName)) {
continue;
}
if (innerElement.childNodes.length > 0) {
innerElement.childNodes.forEach((child) => {
if (child.nodeType === Node.TEXT_NODE) {
this.updateBlockAttr(innerElement, child);
}
});
}
}
}
}
}
updateBlockAttr = (innerElement, child) => {
let blockId = false;
if (innerElement.classList.contains('wp-block')) {
blockId = innerElement.dataset.block;
} else {
const parentBlock = innerElement.closest('.wp-block');
if (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) => {
index++;
if (typeof attributes[key] === 'string' && attributes[key].trim() !== '' && (attributes[key].trim() === child.textContent.trim() || attributes[key] === child.textContent.trim())) {
const originalValue = attributes[key];
const newValue = 'Make This Content Available for Translation ' + index;
this.updateBlockStore[blockId].updateBlockData[newValue.replace(/\s+/g, '-')] = originalValue;
attributes[key] = newValue;
}
};
if (typeof attributes === 'object' && attributes !== null) {
for (const key of Object.keys(attributes)) {
await updateAttributes(key);
if (typeof attributes[key] === 'object' && attributes[key] !== null) {
await updateNestedAttributes(attributes[key], child); // Recursively update nested objects
}
}
}
};
const 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;
const checkValidAttributes = (value = false, blockId) => {
const blockElement = document.querySelector(`#block-${blockId}`);
const regex = new RegExp(value, 'g'); // Create regex from the value parameter with global flag
const matchFound = regex.test(blockElement.innerText); // Check if the regex matches
return matchFound; // Return the result of the regex test directly
};
const blockAttributes = blockStoreAttributes[blockId].attributes;
const upateNestedAttributes = async (attributes) => {
const updateAttributes = async (key) => {
if (typeof attributes[key] === 'string' && attributes[key].includes('Make This Content Available for Translation')) {
try {
const keyWithDashes = attributes[key].replace(/\s+/g, '-');
const originalValue = this.updateBlockStore[blockId].updateBlockData[keyWithDashes];
const status = checkValidAttributes(attributes[key], blockId);
if (!status) {
attributes[key] = originalValue;
} else {
attributes[key] = 'Make This Content Available for Translation';
}
} catch (e) {
console.log(`${attributes[key]} is not valid JSON.`);
}
}
};
if (typeof attributes === 'object' && attributes !== null) {
for (const key of Object.keys(attributes)) {
await updateAttributes(key);
if (typeof attributes[key] === 'object' && attributes[key] !== null) {
await upateNestedAttributes(attributes[key]); // Recursively update nested objects
}
}
}
}
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));
if (firstBlockId) {
this.replaceBlockContent(firstBlockId);
}
}
});
}, 500);
}
skeletonLoader = () => {
const loader = document.createElement('div');
const loaderContainer = () => {
const container = '<style>.atfp-loader-wrapper{position:absolute;width:100%;height:100%;top:0;left:0;z-index:99999;}.atfp-loader-container{width:100%;height:100%;}.atfp-loader-skeleton{--skbg:hsl(227deg, 13%, 50%, 0.2);display:grid;gap:20px;width:100%;height:100%;background:#ffffff;padding:15px;border-radius:8px;box-shadow:0 4px 12px rgba(0, 0, 0, 0.1);transition:transform 0.3s ease;transform:scale(1.02);}.atfp-loader-shimmer{display:flex;aspect-ratio:2/1;width:100%;height:100%;background:var(--skbg);border-radius:4px;overflow:hidden;position:relative;}.atfp-loader-shimmer::before{content:"";position:absolute;width:100%;height:100%;background-image:linear-gradient(-90deg,transparent 8%,rgba(255,255,255,0.28) 18%,transparent 33%);background-size:200%;animation:shimerAnimate 1.5s ease-in-out infinite;}@keyframes shimerAnimate{0%{background-position:100% 0;}100%{background-position:-100% 0;}}</style>'
return '<div class="atfp-loader-container">' + container + '<div class="atfp-loader-skeleton"><span class="atfp-loader-shimmer"></span></div></div>';
}
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);
}
});
})();

File diff suppressed because one or more lines are too long

View File

@@ -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();
});
}
}
});

View File

@@ -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 doesnt 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('<span class="saving-text">Saving<span class="dot" style="--i:0"></span><span class="dot" style="--i:1"></span><span class="dot" style="--i:2"></span></span>', 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('<div id="atfp-custom-fields-message-notice"><p>' + message + '</p></div>');
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();
});

File diff suppressed because one or more lines are too long

View File

@@ -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(`<div class="atfp-elementor-translate-confirm-box modal-container" style="display:flex">
<div class="modal-content">
<p>
${sprintf(
__("The original page in %s was built with Elementor. Its content has already been copied into this new %s version. You can now translate it with Elementor to keep the same design, or edit it with the Gutenberg editor.", "autopoly-ai-translation-for-polylang-pro"),
sourceLangName,
targetLangName
)}
</p>
<div>
<button data-value="yes">
${__("Translate with Elementor", "autopoly-ai-translation-for-polylang-pro")}
</button>
<button data-value="no">
${__("Edit with Gutenberg", "autopoly-ai-translation-for-polylang-pro")}
</button>
</div>
</div>
</div>
`);
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();
});

View File

@@ -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 <body>
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 `
<div class="atfp-overlay" role="status" aria-live="polite">
<div class="atfp-backdrop"></div>
<div class="atfp-box" data-state="loading">
<div class="atfp-row">
<span class="atfp-spinner" aria-hidden="true"></span>
<span class="atfp-icon atfp-icon--ok" aria-hidden="true">✓</span>
<span class="atfp-icon atfp-icon--err" aria-hidden="true">!</span>
<div class="atfp-text">
<div class="atfp-title" data-label="loading">Saving block content</div>
<div class="atfp-title" data-label="success">Supported block content has been updated</div>
<div class="atfp-title" data-label="error">Update failed</div>
<div class="atfp-desc" data-label="loading">
Please dont close or refresh this window until the update is complete.
</div>
<div class="atfp-desc" data-label="success">
Supported block content has been updated. You may continue.
</div>
<div class="atfp-desc" data-label="error">
Something went wrong. You can retry without closing this window.
</div>
</div>
</div>
<div class="atfp-bar"><span></span></div>
</div>
</div>
`;
}
}
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();
}
})
}

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More