From e9c8baf4a260856069997c23bb24fcc87191a5ac Mon Sep 17 00:00:00 2001 From: Julien MA Jacob Date: Sat, 5 Oct 2024 15:37:11 -0200 Subject: [PATCH 01/37] Slug translation: First prototype --- inc/admin/assets.php | 33 +++ inc/admin/option-page.php | 9 + inc/admin/slug-cpt.php | 79 ++++++ inc/admin/slug-meta.php | 490 +++++++++++++++++++++++++++++++++++ inc/buffering.php | 3 +- inc/slug.php | 530 ++++++++++++++++++++++++++++++++++++++ inc/url.php | 73 +++++- loader.php | 3 + src/css/admin/slug.scss | 159 ++++++++++++ wplingua.php | 42 +++ 10 files changed, 1413 insertions(+), 8 deletions(-) create mode 100644 inc/admin/slug-cpt.php create mode 100644 inc/admin/slug-meta.php create mode 100644 inc/slug.php create mode 100644 src/css/admin/slug.scss diff --git a/inc/admin/assets.php b/inc/admin/assets.php index 315848f..9dba373 100644 --- a/inc/admin/assets.php +++ b/inc/admin/assets.php @@ -349,6 +349,39 @@ function wplng_translation_assets() { } +/** + * Register wpLingua assets on slugs edit pages + * + * @return void + */ +function wplng_slug_assets() { + + global $post_type; + + if ( 'wplng_slug' === $post_type ) { + + /** + * Enqueue jQuery + */ + + wp_enqueue_script( 'jquery' ); + + /** + * Enqueue wpLingua CSS styles + */ + + wp_enqueue_style( + 'wplingua-slug', + plugins_url() . '/wplingua/assets/css/admin/slug.css', + array(), + WPLNG_PLUGIN_VERSION + ); + + } + +} + + /** * Print wpLingua head script (JSON with all languages informations) * diff --git a/inc/admin/option-page.php b/inc/admin/option-page.php index 16ec0ee..adf1148 100644 --- a/inc/admin/option-page.php +++ b/inc/admin/option-page.php @@ -78,6 +78,15 @@ function wplng_create_menu() { 'wplng_option_page_exclusions' ); + add_submenu_page( + 'wplingua-settings', + __( 'wplingua: Website slugs', 'wplingua' ), + __( 'Website slugs', 'wplingua' ), + 'administrator', + 'edit.php?post_type=wplng_slug', + false + ); + add_submenu_page( 'wplingua-settings', __( 'wplingua: Translations', 'wplingua' ), diff --git a/inc/admin/slug-cpt.php b/inc/admin/slug-cpt.php new file mode 100644 index 0000000..baa3eb8 --- /dev/null +++ b/inc/admin/slug-cpt.php @@ -0,0 +1,79 @@ + array( + 'name' => __( 'Website slugs', 'wplingua' ), + 'singular_name' => __( 'Website slug', 'wplingua' ), + 'all_items' => __( 'All slugs', 'wplingua' ), + 'edit_item' => __( 'Edit slug', 'wplingua' ), + 'menu_name' => __( 'Website slugs', 'wplingua' ), + ), + 'public' => false, + 'publicly_queryable' => false, + 'show_ui' => true, + 'exclude_from_search' => true, + 'show_in_nav_menus' => false, + 'show_in_menu' => false, + 'has_archive' => false, + 'rewrite' => false, + 'menu_icon' => 'dashicons-translation', + 'supports' => array( + 'title', + ), + 'capability_type' => 'post', + 'capabilities' => array( + 'create_posts' => false, + ), + 'map_meta_cap' => true, + ) + ); +} + + +/** + * Remove quick edit on wpLingua slugs list + * + * @param array $actions + * @param object $post + * @return array + */ +function wplng_slug_remove_quick_edit( $actions, $post ) { + + if ( $post->post_type != 'wplng_slug' ) { + return $actions; + } + + unset( $actions['view'] ); + unset( $actions['inline hide-if-no-js'] ); + + return $actions; +} + + +/** + * Display 100 translations by default in admin area + * + * @param mixed $result + * @return mixed + */ +function wplng_slug_per_page( $result ) { + if ( false === $result ) { + $result = '100'; + } + + return $result; +} diff --git a/inc/admin/slug-meta.php b/inc/admin/slug-meta.php new file mode 100644 index 0000000..7f0ee75 --- /dev/null +++ b/inc/admin/slug-meta.php @@ -0,0 +1,490 @@ +'; + echo wplng_slug_editor_get_html( $post ); + echo ''; + +} + + +/** + * Return HTML of slugs editor in modal + * + * @param WP_Post $post + * @return void + */ +function wplng_slug_editor_get_html( $post ) { + + //used later for security + $html = wp_nonce_field( + basename( __FILE__ ), + 'wplng_slug_meta_box_nonce', + true, + false + ); + + $meta = get_post_meta( $post->ID ); + + // Display original text + if ( ! empty( $meta['wplng_slug_original'][0] ) + && is_string( $meta['wplng_slug_original'][0] ) + && ! empty( $meta['wplng_slug_original_language_id'][0] ) + && wplng_is_valid_language_id( $meta['wplng_slug_original_language_id'][0] ) + ) { + + $language_id = $meta['wplng_slug_original_language_id'][0]; + $language = wplng_get_language_by_id( $language_id ); + $alt = __( 'Flag for language: ', 'wplingua' ) . $language['name']; + + $html .= '
'; + $html .= '
'; + $html .= ' $language_target, + 'translation' => '[WPLNG_EMPTY]', + 'status' => 'ungenerated', + ); + } + } + + foreach ( $translations as $translation ) { + + $language_id = $translation['language_id']; + $language = wplng_get_language_by_id( $language_id ); + $slug_input = $translation['translation']; + $name = 'wplng_slug_' . $language_id; + $container_id = 'wplng-slug-' . $language_id; + $generate_link = __( 'Regenerate translation', 'wplingua' ); + $alt = __( 'Flag for language: ', 'wplingua' ) . $language['name']; + $class = 'wplng-edit-language'; + $reviewed_title = __( 'Mark slug translation as review', 'wplingua' ); + $is_reviewed = false; + + if ( '[WPLNG_EMPTY]' === $slug_input ) { + $slug_input = sanitize_title( $meta['wplng_slug_original'][0] ); + } else { + $slug_input = sanitize_title( $slug_input ); + } + + switch ( $translation['status'] ) { + case 'ungenerated': + $generate_link = __( 'Generate translation', 'wplingua' ); + $class .= ' wplng-status-ungenerated'; + break; + + case 'generated': + $class .= ' wplng-status-generated'; + break; + + default: + if ( is_int( $translation['status'] ) ) { + + $class .= ' wplng-status-reviewed'; + $is_reviewed = true; + + // Get and check date format + $date_format = get_option( 'date_format' ); + if ( ! is_string( $date_format ) || empty( $date_format ) ) { + $date_format = 'F j, Y'; + } + + // Get and check time format + $time_format = get_option( 'time_format' ); + if ( ! is_string( $time_format ) || empty( $time_format ) ) { + $time_format = 'g:i a'; + } + + $reviewed_title = __( 'Reviewed on ', 'wplingua' ); + $reviewed_title .= esc_html( + gmdate( + $date_format, + $translation['status'] + ) + ); + $reviewed_title .= ', ' . esc_html( + gmdate( + $time_format, + $translation['status'] + ) + ); + } + break; + } + + $html .= ''; + $html .= ' $language_target, + 'translation' => '[WPLNG_EMPTY]', + 'status' => 'ungenerated', + ); + } + } + + foreach ( $translations as $key => $translation ) { + + if ( empty( $translation['language_id'] ) + || ! wplng_is_valid_language_id( $translation['language_id'] ) + ) { + continue; + } + + $name = 'wplng_slug_' . $translation['language_id']; + $reviewed = 'wplng_mark_as_reviewed_' . $translation['language_id']; + + if ( ! isset( $_REQUEST[ $name ] ) ) { + continue; + } + + // $temp = stripslashes( wplng_text_esc( $_REQUEST[ $name ] ) ); + $temp = sanitize_title( $_REQUEST[ $name ] ); + + if ( empty( $temp ) || $slug_original === $temp ) { + $temp = '[WPLNG_EMPTY]'; + $translations[ $key ]['status'] = 'ungenerated'; + } else { + + if ( $temp !== $translation['translation'] ) { + $temp = str_replace( '\\', '', $temp ); + } + + if ( ! empty( $_POST[ $reviewed ] ) ) { + if ( 'false' !== $_POST[ $reviewed ] ) { + $translations[ $key ]['status'] = time(); + } else { + $translations[ $key ]['status'] = 'generated'; + } + } else { + $translations[ $key ]['status'] = 'generated'; + } + } + + $translations[ $key ]['translation'] = esc_html( $temp ); + } + + // TODO : Clear cache ? + + return update_post_meta( + $post_id, + 'wplng_slug_translations', + wp_json_encode( + $translations, + JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES + ) + ) === true; + +} + + +/** + * wpLingua AJAX function to get slugs on CPT edit page + * + * @return void + */ +function wplng_ajax_generate_slug() { + + // TODO : Disabled function + wp_send_json_error( __( 'Disabled function', 'wplingua' ) ); + + /** + * Check and sanitize data + */ + + // Check data + + if ( empty( $_POST['language_source'] ) + || ! is_string( $_POST['language_source'] ) + || empty( $_POST['language_target'] ) + || ! is_string( $_POST['language_target'] ) + || ! isset( $_POST['text'] ) + || ! is_string( $_POST['text'] ) + ) { + wp_send_json_error( __( 'Invalid parameters', 'wplingua' ) ); + return; + } + + // Check and sanitize sources and target languages + + $language_source = sanitize_key( $_POST['language_source'] ); + $language_target = sanitize_key( $_POST['language_target'] ); + + if ( ! wplng_is_valid_language_id( $language_source ) + || ! wplng_is_valid_language_id( $language_target ) + || $language_source === $language_target + ) { + wp_send_json_error( __( 'Invalid languages parameters', 'wplingua' ) ); + return; + } + + // Check and sanitize text to translate + // (And convert img emoji to emoji) + + $text = wp_kses( + $_POST['text'], + array( + 'img' => array( + 'alt' => array(), + ), + ) + ); + + $text = preg_replace( + '/\\"(.*)\\"/U', + '$1', + $text + ); + + $text = wplng_text_esc( $text ); + + if ( ! wplng_text_is_translatable( $text ) ) { + wp_send_json_success( $text ); + return; + } + + /** + * Call API and get translation + */ + + $response = wplng_api_call_translate( + array( $text ), + $language_source, + $language_target + ); + + if ( ! isset( $response[0] ) ) { + wp_send_json_error( __( 'Invalid API response', 'wplingua' ) ); + return; + } + + wp_send_json_success( $response[0] ); +} diff --git a/inc/buffering.php b/inc/buffering.php index fd5079c..334821d 100644 --- a/inc/buffering.php +++ b/inc/buffering.php @@ -29,8 +29,7 @@ function wplng_ob_start() { return; } - $origin_path = '/' . substr( $current_path, 4, strlen( $current_path ) - 1 ); - $origin_path = sanitize_url( $origin_path ); + $origin_path = wplng_get_url_original($current_path); if ( ! wplng_url_is_translatable( $origin_path ) ) { wp_safe_redirect( $origin_path ); diff --git a/inc/slug.php b/inc/slug.php new file mode 100644 index 0000000..65f00f0 --- /dev/null +++ b/inc/slug.php @@ -0,0 +1,530 @@ + $slug_translations ) { + + if ( $slug !== $slug_translations['source'] + || ! isset( $slug_translations['translations'][ $language_id ] ) + ) { + continue; + } + + $slug = $slug_translations['translations'][ $language_id ]['translation']; + + $slug_translation_exist = true; + break; + } + + if ( false === $slug_translation_exist ) { + /** + * Slug translation is not in slug cache + * Check if exist in DB + * If not exist, create it + */ + + if ( false === wplng_get_slug_saved_from_original( $slug ) ) { + wplng_create_slug( $slug ); + } + } + + return $slug; + +} + + +function wplng_slug_translate_path( $path, $language_id ) { + + /** + * Remove language ID + */ + + $target = wplng_get_languages_target_ids(); + + foreach ( $target as $target_id ) { + if ( wplng_str_starts_with( $path, '/' . $target_id . '/' ) ) { + $path = str_replace( '/' . $target_id . '/', '/', $path ); + } elseif ( wplng_str_starts_with( $path, $target_id . '/' ) ) { + $path = str_replace( $target_id . '/', '/', $path ); + } + } + + /** + * Return parth if no contains slug + */ + + if ( '/' === $path || '' === $path ) { + return $path; + } + + /** + * Transform the multi part slug to an slug array + */ + + $slugs = explode( '/', $path ); + $slugs = array_filter( $slugs ); + + /** + * Get the slug translation list and translate $path + */ + + $slugs_translations = wplng_get_slugs(); + + $path_translated = '/'; + + foreach ( $slugs as $key => $slug ) { + if ( wplng_text_is_translatable( $slug ) ) { + $path_translated .= wplng_slug_translate( + $slug, + $language_id, + $slugs_translations + ) . '/'; + } else { + $path_translated .= $slug . '/'; + } + } + + /** + * Set the first and last '/' to $path_translated like $path + */ + + if ( ! wplng_str_starts_with( $path, '/' ) ) { + $path_translated = substr( $path_translated, 1 ); + } + + if ( ! wplng_str_ends_with( $path, '/' ) ) { + $path_translated = substr( $path_translated, 0, -1 ); + } + + return $path_translated; + +} + + + + +function wplng_create_slug( $slug ) { + + $slug = sanitize_title( $slug ); + + if ( '' === $slug ) { + return false; + } + + /** + * Make the title + */ + + $tite_max_length = 100; + $title = mb_substr( $slug, 0, $tite_max_length ); + $title = sanitize_title( $slug ); + if ( strlen( $slug ) > $tite_max_length ) { + $title = '/' . $title . __( '...', 'wplingua' ); + } else { + $title = '/' . $title . '/'; + } + + /** + * Create the post and get this ID + */ + + $new_post_id = wp_insert_post( + array( + 'post_title' => esc_html( $title ), + 'post_type' => 'wplng_slug', + 'post_status' => 'publish', + 'post_name' => sanitize_title( + 'wplingua-slug-' . md5( $title ) . '-' . $title + ), + ) + ); + + if ( is_wp_error( $new_post_id ) ) { + return false; + } + + /** + * Make $translation_meta + */ + + $languages_target = wplng_get_languages_target_ids(); + $slug_meta = array(); + + foreach ( $languages_target as $target_language ) { + $slug_meta[] = array( + 'language_id' => $target_language, + 'translation' => $slug, + 'status' => 'generated', + ); + } + + add_post_meta( + $new_post_id, + 'wplng_slug_original', + $slug + ); + + add_post_meta( + $new_post_id, + 'wplng_slug_md5', + md5( $slug ) + ); + + add_post_meta( + $new_post_id, + 'wplng_slug_original_language_id', + wplng_get_language_website_id() + ); + + add_post_meta( + $new_post_id, + 'wplng_slug_translations', + wp_json_encode( + $slug_meta, + JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES + ) + ); + + wplng_clear_slugs_cache(); + + return $new_post_id; +} + + +/** + * Get all saved slugs from a wp_query + * + * @return array + */ +function wplng_get_slugs_from_query() { + + $slugs = array(); + $args = array( + 'post_type' => 'wplng_slug', + 'posts_per_page' => -1, + 'no_found_rows' => true, + 'update_post_term_cache' => false, + 'update_post_meta_cache' => false, + 'cache_results' => false, + ); + + $the_query = new WP_Query( $args ); + + while ( $the_query->have_posts() ) { + + $the_query->the_post(); + + $meta = get_post_meta( get_the_ID() ); + + /** + * Check and clear source slug + */ + + if ( ! isset( $meta['wplng_slug_original'][0] ) + || ! is_string( $meta['wplng_slug_original'][0] ) + ) { + continue; + } + + $source = esc_attr( $meta['wplng_slug_original'][0] ); + + /** + * Check and clear slug translations, setup review array + */ + + if ( empty( $meta['wplng_slug_translations'][0] ) ) { + continue; + } + + $translations_meta = json_decode( + $meta['wplng_slug_translations'][0], + true + ); + + $translations = array(); + $review = array(); + + foreach ( $translations_meta as $translation_meta ) { + + /** + * Language ID + */ + + if ( empty( $translation_meta['language_id'] ) + || ! wplng_is_valid_language_id( $translation_meta['language_id'] ) + ) { + continue; + } + + $language_id = sanitize_key( $translation_meta['language_id'] ); + + /** + * Status and Review + */ + + $status = 'ungenerated'; + + if ( isset( $translation_meta['status'] ) ) { + if ( 'generated' === $translation_meta['status'] ) { + $status = 'generated'; + } elseif ( is_int( $translation_meta['status'] ) ) { + $status = $translation_meta['status']; + $review[] = $language_id; + } + } + + /** + * Slug translation + */ + + if ( empty( $translation_meta['translation'] ) + || ! is_string( $translation_meta['translation'] ) + || $translation_meta['translation'] === '[WPLNG_EMPTY]' + || $translation_meta['translation'] === $source + ) { + continue; + } + + $translation = sanitize_title( $translation_meta['translation'] ); + + /** + * Add slug translated to slug translations + */ + + $translations[ $language_id ] = array( + 'language_id' => $language_id, + 'translation' => $translation, + 'status' => $status, + ); + } + + $slugs[] = array( + 'source' => $source, + 'post_id' => get_the_ID(), + 'review' => $review, + 'translations' => $translations, + ); + + } + + wp_reset_postdata(); + + set_transient( + 'wplng_cached_slugs', + $slugs + ); + + return $slugs; +} + + +// TODO : Retourner les traduction de slug par langue ? +// TODO : Checker les data +// TODO : Retourner directement si ça vient de wplng_get_slugs_from_query() ? +function wplng_get_slugs() { + + $slugs_from_data = get_transient( 'wplng_cached_slugs' ); + + if ( ! is_array( $slugs_from_data ) ) { + $slugs_from_data = wplng_get_slugs_from_query(); + } + + $slugs = array(); + + foreach ( $slugs_from_data as $slug ) { + + if ( empty( $slug['source'] ) + || ! is_string( $slug['source'] ) + || empty( $slug['post_id'] ) + // || ! is_array( $slug['translations'] ) + // || empty( $slug['translations'] ) + // || ! is_string( $slug['translations'][ $target_language_id ] ) + || ! isset( $slug['review'] ) + || ! is_array( $slug['review'] ) + ) { + continue; + } + + $slugs[] = $slug; + + } + + return $slugs; +} + + +/** + * Get slug data from original slug + * + * @param string $original + * @return array Translation data + */ +function wplng_get_slug_saved_from_original( $original ) { + + $translation = false; + + $args = array( + 'post_type' => 'wplng_slug', + 'posts_per_page' => -1, + 'meta_query' => array( + array( + 'key' => 'wplng_slug_md5', + 'value' => md5( $original ), + 'compare' => '=', + ), + ), + ); + + $the_query = new WP_Query( $args ); + + // The Loop + while ( $the_query->have_posts() ) { + + $the_query->the_post(); + + $meta = get_post_meta( get_the_ID() ); + + if ( ! isset( $meta['wplng_slug_original'][0] ) + || ! is_string( $meta['wplng_slug_original'][0] ) + ) { + continue; + } + + if ( $meta['wplng_slug_original'][0] === $original ) { + $translation = get_post(); + break; + } + } + + wp_reset_postdata(); + + return $translation; +} + + +/** + * Clear cached slugs + * + * @return void + */ +function wplng_clear_slugs_cache() { + delete_transient( 'wplng_cached_slugs' ); +} + + +/** + * Clear cached translations if $post_id parametter is a slug + * + * @return void + */ +function wplng_clear_slugs_cache_trash_untrash( $post_id ) { + + if ( 'wplng_slug' !== get_post_type( $post_id ) ) { + return; + } + + wplng_clear_slugs_cache(); +} diff --git a/inc/url.php b/inc/url.php index 0c1c8b3..6e055ac 100644 --- a/inc/url.php +++ b/inc/url.php @@ -54,6 +54,10 @@ function wplng_url_translate( $url, $language_target_id = '' ) { && preg_match( '#^(http:\/\/|https:\/\/)?' . $preg_domain . '(.*)$#', $url ) ) { + /** + * It's an URL starting y a domain name + */ + // Check if URL is already translated foreach ( $languages_target as $language_target ) { if ( wplng_str_contains( $url, '/' . $language_target['id'] . '/' ) ) { @@ -61,20 +65,35 @@ function wplng_url_translate( $url, $language_target_id = '' ) { } } + $parsed_url = wp_parse_url( $url ); + + if ( ! empty( $parsed_url['path'] ) ) { + $url = str_replace( + $parsed_url['path'], + wplng_slug_translate_path( + $parsed_url['path'], + $language_target_id + ), + $url + ); + } + $url = preg_replace( '#^(http:\/\/|https:\/\/)?' . $preg_domain . '(.*)$#', '${1}' . $parsed_url_home['host'] . '/' . $language_target_id . '${2}', $url ); - $parsed_url = wp_parse_url( $url ); - if ( empty( $parsed_url['fragment'] ) ) { // Add slash at the end if is not an anchor link $url = trailingslashit( $url ); } } elseif ( preg_match( '#^[^\/]+\/[^\/].*$|^\/[^\/].*$#', $url ) ) { + /** + * It's a path + */ + // Check if URL is already translated foreach ( $languages_target as $language_target ) { if ( substr( $url, 0, 4 ) == '/' . $language_target['id'] . '/' @@ -84,7 +103,12 @@ function wplng_url_translate( $url, $language_target_id = '' ) { } } - if ( '/' === substr( $url, 0, 1 ) ) { + $url = wplng_slug_translate_path( + $url, + $language_target_id + ); + + if ( wplng_str_starts_with( $url, '/' ) ) { $url = '/' . $language_target_id . $url; } else { $url = $language_target_id . '/' . $url; @@ -124,8 +148,17 @@ function wplng_url_is_translatable( $url = '' ) { $is_translatable = false; } + // Check if is wp-login.php + if ( $is_translatable + && wplng_str_contains( $url, 'wp-login.php' ) + ) { + $is_translatable = false; + } + // Check if is a /feed/ - if ( wplng_str_ends_with( $url, '/feed/' ) ) { + if ( $is_translatable + && wplng_str_ends_with( $url, '/feed/' ) + ) { $is_translatable = false; } @@ -257,10 +290,38 @@ function wplng_get_url_original( $url = '' ) { $target = wplng_get_languages_target_ids(); foreach ( $target as $target_id ) { - $url = str_replace( '/' . $target_id . '/', '/', $url ); + + if ( ! wplng_str_contains( $url, '/' . $target_id . '/' ) ) { + continue; + } + + $url = str_replace( + '/' . $target_id . '/', + '/', + $url + ); + + $parsed_url = wp_parse_url( $url ); + + if ( isset( $parsed_url['path'] ) + && $parsed_url['path'] !== '' + && $parsed_url['path'] !== '/' + ) { + $url = str_replace( + $parsed_url['path'], + wplng_slug_original_path( + $parsed_url['path'], + $target_id + ), + $url + ); + } + + break; } - $url = esc_url_raw( $url ); + // TODO : Maintain ? + // $url = esc_url_raw( $url ); $url = apply_filters( 'wplng_url_original', diff --git a/loader.php b/loader.php index c742dfd..6ee859e 100644 --- a/loader.php +++ b/loader.php @@ -22,6 +22,7 @@ require_once WPLNG_PLUGIN_PATH . '/inc/languages.php'; require_once WPLNG_PLUGIN_PATH . '/inc/search.php'; require_once WPLNG_PLUGIN_PATH . '/inc/shortcode.php'; +require_once WPLNG_PLUGIN_PATH . '/inc/slug.php'; require_once WPLNG_PLUGIN_PATH . '/inc/switcher-nav-menu.php'; require_once WPLNG_PLUGIN_PATH . '/inc/switcher.php'; require_once WPLNG_PLUGIN_PATH . '/inc/translation.php'; @@ -38,6 +39,8 @@ require_once WPLNG_PLUGIN_PATH . '/inc/admin/option-page-settings.php'; require_once WPLNG_PLUGIN_PATH . '/inc/admin/option-page-switcher.php'; require_once WPLNG_PLUGIN_PATH . '/inc/admin/option-page.php'; +require_once WPLNG_PLUGIN_PATH . '/inc/admin/slug-cpt.php'; +require_once WPLNG_PLUGIN_PATH . '/inc/admin/slug-meta.php'; require_once WPLNG_PLUGIN_PATH . '/inc/admin/translation-cpt.php'; require_once WPLNG_PLUGIN_PATH . '/inc/admin/translation-edit-modal.php'; require_once WPLNG_PLUGIN_PATH . '/inc/admin/translation-meta.php'; diff --git a/src/css/admin/slug.scss b/src/css/admin/slug.scss new file mode 100644 index 0000000..9dd487a --- /dev/null +++ b/src/css/admin/slug.scss @@ -0,0 +1,159 @@ +@import "../header"; +@import "../dashicons-spin"; + +#post-body-content { + display: none; +} + +#wplng-source-title { + font-weight: 600; + margin-bottom: 15px; +} + +.wplng-flag { + max-width: 1.5em; + max-height: 1.5em; + height: auto; + margin-right: .4em; + vertical-align: text-top; +} + +#wplng-source { + font-size: 14px; + line-height: 1.4em; +} + +#wplng-original-language { + margin: 20px 0 0; +} + +#wplng-slug-editor { + + color: #3c434a; + + .wplng-edit-language { + + border: 1px solid #c3c4c7; + padding: 10px; + margin-top: 25px; + background-color: #f6f7f7; + box-shadow: rgba(0, 0, 0, 0.04) 0px 1px 1px 0px; + font-size: 14px; + + &.wplng-status-reviewed { + border-left: 4px #00a32a solid; + border-top-left-radius: 0; + border-bottom-left-radius: 0; + } + + &.wplng-status-generated { + border-left: 4px #72aee6 solid; + border-top-left-radius: 0; + border-bottom-left-radius: 0; + } + + &.wplng-status-ungenerated { + border-left: 4px #c3c4c7 solid; + border-top-left-radius: 0; + border-bottom-left-radius: 0; + } + + label.wplng-target-title { + font-weight: 600; + margin-bottom: 8px; + display: block; + } + + input[type=text] { + box-sizing: border-box; + overflow: hidden; + width: 100%; + resize: none; + box-shadow: 0 0 0 transparent; + border-radius: 4px; + border: 1px solid #8c8f94; + background-color: #fff; + padding: 2px 6px; + line-height: 1.5em; + font-size: inherit; + min-height: 30px; + } + + .wplng-slug-footer { + + color: #3c434a; + display: flex; + justify-content: space-between; + margin-top: 4px; + + a { + color: #135e96; + text-decoration: underline; + + &:focus { + box-shadow: none; + } + } + + + .wplng-mark-as-reviewed { + + display: inline-block; + border: none; + padding: 0; + + label { + color: #2271b1; + display: contents; + text-decoration: underline; + display: inline; + vertical-align: baseline; + font-weight: normal; + } + + input[type="checkbox"] { + border: 1px solid #8c8f94; + border-radius: 4px; + background: #fff; + color: #50575e; + clear: none; + cursor: pointer; + display: inline-block; + line-height: 0; + height: 16px; + margin: -2px 6px 0 0; + margin-bottom: 0px; + outline: 0; + padding: 0 !important; + text-align: center; + vertical-align: bottom; + width: 16px; + min-width: 16px; + box-shadow: inset 0 1px 2px rgba(0,0,0,.1); + transition: .05s border-color ease-in-out; + -webkit-appearance: checkbox; + } + } + + } + + } +} + + +@media only screen and (max-width: 850px) { + #wplng-slug-editor { + .wplng-edit-language { + .wplng-slug-footer { + + display: block; + margin-top: 0; + + .wplng-slug-footer-left, + .wplng-slug-footer-right { + padding-top: 8px; + } + } + } + } +} diff --git a/wplingua.php b/wplingua.php index 4887c31..c653ff0 100644 --- a/wplingua.php +++ b/wplingua.php @@ -192,6 +192,48 @@ function wplng_start() { add_action( 'manage_wplng_translation_posts_custom_column', 'wplng_translation_status_item', 10, 2 ); add_action( 'admin_head-edit.php', 'wplng_translation_status_style', 10, 2 ); + /** + * wplng_translation : CPT, taxo, meta + */ + + // Register wplng_translation CPT + add_action( 'init', 'wplng_register_post_type_slug' ); + + // Add metabox for wplng_slug + add_action( 'add_meta_boxes_wplng_slug', 'wplng_slug_add_meta_box' ); + + // Save metabox on posts saving + add_action( 'save_post_wplng_slug', 'wplng_slug_save_meta_boxes_data', 10, 2 ); + + // Clear slugs cache on trash / untrash + add_action( 'trashed_post', 'wplng_clear_slugs_cache_trash_untrash' ); + add_action( 'untrash_post', 'wplng_clear_slugs_cache_trash_untrash' ); + add_action( 'delete_post', 'wplng_clear_slugs_cache_trash_untrash' ); + + // Enqueue Script for wplng_translation admin + add_action( 'admin_print_scripts-post-new.php', 'wplng_slug_assets' ); + add_action( 'admin_print_scripts-post.php', 'wplng_slug_assets' ); + + // Remove Quick edit from slugs list + add_filter( 'post_row_actions', 'wplng_slug_remove_quick_edit', 10, 2 ); + + // // Ajax function for regenerate translation on edit page + // add_action( 'wp_ajax_wplng_ajax_translation', 'wplng_ajax_generate_translation' ); + + // Display 100 translation in admin area by default + add_filter( 'get_user_option_edit_wplng_slug_per_page', 'wplng_slug_per_page' ); + + // // Filter translations by status + // add_action( 'restrict_manage_posts', 'wplng_restrict_manage_posts_translation_status' ); + // add_filter( 'parse_query', 'wplng_posts_filter_translation_status' ); + + // // Translation status on website translations list + // add_filter( 'post_class', 'wplng_post_class_translation_status', 10, 3 ); + // add_filter( 'post_row_actions', 'wplng_post_row_actions_status', 10, 2 ); + // add_filter( 'manage_wplng_translation_posts_columns', 'wplng_translation_status_columns' ); + // add_action( 'manage_wplng_translation_posts_custom_column', 'wplng_translation_status_item', 10, 2 ); + // add_action( 'admin_head-edit.php', 'wplng_translation_status_style', 10, 2 ); + /** * Front */ From d8115facb2d986d7112fc8a4bd8173fa140225fd Mon Sep 17 00:00:00 2001 From: Julien MA Jacob Date: Sat, 5 Oct 2024 16:25:37 -0200 Subject: [PATCH 02/37] Rewrite wplng_get_url_current_for_language() --- inc/url.php | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/inc/url.php b/inc/url.php index 6e055ac..c7413fa 100644 --- a/inc/url.php +++ b/inc/url.php @@ -350,15 +350,8 @@ function wplng_get_url_current() { * @return string */ function wplng_get_url_current_for_language( $language_id ) { - - global $wplng_request_uri; - $path = wplng_get_url_original( $wplng_request_uri ); - - if ( wplng_url_is_translatable( $path ) - && ( wplng_get_language_website_id() !== $language_id ) - ) { - $path = '/' . $language_id . $path; - } - - return home_url( $path ); + return wplng_url_translate( + wplng_get_url_original(), + $language_id + ); } From 5cf0a639520a44ade31b1f43df1567e1714a44a2 Mon Sep 17 00:00:00 2001 From: Julien MA Jacob Date: Sat, 5 Oct 2024 17:35:14 -0200 Subject: [PATCH 03/37] Manage slug translation with anchor and parameters --- inc/slug.php | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/inc/slug.php b/inc/slug.php index 65f00f0..60b0ffb 100644 --- a/inc/slug.php +++ b/inc/slug.php @@ -152,6 +152,17 @@ function wplng_slug_translate_path( $path, $language_id ) { } } + /** + * Manage anchor ang $_GET parameters + */ + + $path = str_replace( + array('#', '?'), + array('/#', '/?'), + $path + ); + + /** * Return parth if no contains slug */ @@ -175,8 +186,11 @@ function wplng_slug_translate_path( $path, $language_id ) { $path_translated = '/'; - foreach ( $slugs as $key => $slug ) { - if ( wplng_text_is_translatable( $slug ) ) { + foreach ( $slugs as $slug ) { + if ( wplng_text_is_translatable( $slug ) + && ! wplng_str_starts_with($slug, '#') + && ! wplng_str_starts_with($slug, '?') + ) { $path_translated .= wplng_slug_translate( $slug, $language_id, From b9cd298877f89c0a9ed98fed8b776d671caadb34 Mon Sep 17 00:00:00 2001 From: Julien MA Jacob Date: Sat, 5 Oct 2024 17:40:00 -0200 Subject: [PATCH 04/37] Format code --- inc/admin/slug-meta.php | 6 +++--- inc/buffering.php | 2 +- inc/slug.php | 11 +++++------ 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/inc/admin/slug-meta.php b/inc/admin/slug-meta.php index 7f0ee75..37d152c 100644 --- a/inc/admin/slug-meta.php +++ b/inc/admin/slug-meta.php @@ -306,13 +306,13 @@ function wplng_slug_save_meta_boxes_data( $post_id ) { $meta = get_post_meta( $post_id ); - if ( empty( $meta['wplng_slug_translations'][0] ) - || empty( $meta['wplng_slug_original'][0] ) + if ( empty( $meta['wplng_slug_translations'][0] ) + || empty( $meta['wplng_slug_original'][0] ) ) { return false; } - $slug_original = sanitize_title($meta['wplng_slug_original'][0]); + $slug_original = sanitize_title( $meta['wplng_slug_original'][0] ); $languages_target = wplng_get_languages_target_ids(); $translations = json_decode( $meta['wplng_slug_translations'][0], diff --git a/inc/buffering.php b/inc/buffering.php index 334821d..8380c8d 100644 --- a/inc/buffering.php +++ b/inc/buffering.php @@ -29,7 +29,7 @@ function wplng_ob_start() { return; } - $origin_path = wplng_get_url_original($current_path); + $origin_path = wplng_get_url_original( $current_path ); if ( ! wplng_url_is_translatable( $origin_path ) ) { wp_safe_redirect( $origin_path ); diff --git a/inc/slug.php b/inc/slug.php index 60b0ffb..0e4bc33 100644 --- a/inc/slug.php +++ b/inc/slug.php @@ -157,11 +157,10 @@ function wplng_slug_translate_path( $path, $language_id ) { */ $path = str_replace( - array('#', '?'), - array('/#', '/?'), + array( '#', '?' ), + array( '/#', '/?' ), $path ); - /** * Return parth if no contains slug @@ -187,9 +186,9 @@ function wplng_slug_translate_path( $path, $language_id ) { $path_translated = '/'; foreach ( $slugs as $slug ) { - if ( wplng_text_is_translatable( $slug ) - && ! wplng_str_starts_with($slug, '#') - && ! wplng_str_starts_with($slug, '?') + if ( wplng_text_is_translatable( $slug ) + && ! wplng_str_starts_with( $slug, '#' ) + && ! wplng_str_starts_with( $slug, '?' ) ) { $path_translated .= wplng_slug_translate( $slug, From 167ab7b5bbb3af517c0b65d46451445cbe86759b Mon Sep 17 00:00:00 2001 From: Julien MA Jacob Date: Sat, 5 Oct 2024 17:52:35 -0200 Subject: [PATCH 05/37] Redirect old slug to translated if needed --- inc/buffering.php | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/inc/buffering.php b/inc/buffering.php index 8380c8d..2668b7f 100644 --- a/inc/buffering.php +++ b/inc/buffering.php @@ -18,7 +18,10 @@ function wplng_ob_start() { return; } - if ( wplng_get_language_website_id() === wplng_get_language_current_id() ) { + $language_website_id = wplng_get_language_website_id(); + $language_current_id = wplng_get_language_current_id(); + + if ( $language_website_id === $language_current_id ) { return; } @@ -36,6 +39,16 @@ function wplng_ob_start() { exit; } + $origin_path_translated = wplng_url_translate( + $origin_path, + $language_current_id + ); + + if ( $current_path !== $origin_path_translated ) { + wp_safe_redirect( $origin_path_translated ); + exit; + } + $_SERVER['REQUEST_URI'] = $origin_path; ob_start( 'wplng_ob_callback_page' ); From 80e078407a167d3c9c14e8f88ed6f19549d2f2a4 Mon Sep 17 00:00:00 2001 From: Julien MA Jacob Date: Sat, 5 Oct 2024 18:00:31 -0200 Subject: [PATCH 06/37] Fix old link redirection for front page --- inc/buffering.php | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/inc/buffering.php b/inc/buffering.php index 2668b7f..3b75eae 100644 --- a/inc/buffering.php +++ b/inc/buffering.php @@ -39,14 +39,16 @@ function wplng_ob_start() { exit; } - $origin_path_translated = wplng_url_translate( - $origin_path, - $language_current_id - ); - - if ( $current_path !== $origin_path_translated ) { - wp_safe_redirect( $origin_path_translated ); - exit; + if ('/' !== $origin_path && '' !== $origin_path) { + $origin_path_translated = wplng_url_translate( + $origin_path, + $language_current_id + ); + + if ( $current_path !== $origin_path_translated ) { + wp_safe_redirect( $origin_path_translated ); + exit; + } } $_SERVER['REQUEST_URI'] = $origin_path; From 40cb1f6704082d4f217f729ebb6fd6f84fb0b1c3 Mon Sep 17 00:00:00 2001 From: Julien MA Jacob Date: Sun, 6 Oct 2024 01:37:01 -0200 Subject: [PATCH 07/37] Add Edit slug translation link in admin bar menu --- inc/admin/admin-bar.php | 70 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 65 insertions(+), 5 deletions(-) diff --git a/inc/admin/admin-bar.php b/inc/admin/admin-bar.php index e00c384..41d6067 100644 --- a/inc/admin/admin-bar.php +++ b/inc/admin/admin-bar.php @@ -45,8 +45,8 @@ function wplng_admin_bar_menu() { return; } - $url = wplng_get_url_current(); - $url_original = remove_query_arg( + $url = wplng_get_url_current(); + $url_clean = remove_query_arg( array( 'wplng-mode', 'wplng-load', 'wplng-nocache' ), $url ); @@ -63,7 +63,7 @@ function wplng_admin_bar_menu() { 'id' => 'wplingua-return', 'parent' => 'wplingua-menu', 'title' => __( 'Return on page', 'wplingua' ), - 'href' => $url_original, + 'href' => $url_clean, ) ); } @@ -111,7 +111,7 @@ function wplng_admin_bar_menu() { 'wplng-mode', 'editor', wplng_url_translate( - wplng_get_url_original( $url_original ), + wplng_get_url_original( $url_clean ), $language['id'] ) ), @@ -127,7 +127,7 @@ function wplng_admin_bar_menu() { 'wplng-mode', 'list', wplng_url_translate( - wplng_get_url_original( $url_original ), + wplng_get_url_original( $url_clean ), $language['id'] ) ), @@ -137,4 +137,64 @@ function wplng_admin_bar_menu() { } // End foreach ( $languages_target as $language ) } // End if ( ! empty( $languages_target ) ) + /** + * Edit slug translation link + */ + + $url_original = wplng_get_url_original( $url_clean ); + $url_original_parsed = parse_url( $url_original ); + + if ( ! isset( $url_original_parsed['path'] ) ) { + return; + } + + $path = $url_original_parsed['path']; + + // Return path if no contains slug + + if ( '/' === $path || '' === $path ) { + return; + } + + // Transform the multi part slug to an slug array + + $slugs = explode( '/', $path ); + $slugs = array_filter( $slugs ); + + $last_slug = ''; + + foreach ( $slugs as $slug ) { + if ( '/' === $slug || '' === $slug ) { + continue; + } + $last_slug = $slug; + } + + if ( '/' === $last_slug || '' === $last_slug ) { + return; + } + + $slug_id = wplng_get_slug_saved_from_original( $last_slug ); + + if ( false === $slug_id ) { + $slug_id = wplng_create_slug( $slug ); + } + + $edit_slug_link = get_edit_post_link( $slug_id ); + + if ( ! is_string( $edit_slug_link ) ) { + return; + } + + $wp_admin_bar->add_menu( + array( + 'id' => 'wplingua-slug', + 'parent' => 'wplingua-menu', + 'title' => __( 'Edit the page slug', 'wplingua' ), + 'href' => $edit_slug_link, + 'meta' => array( + 'target' => '_blank', + ), + ) + ); } From 3064392a41cf4bc4d02fe1a7d080e44809d9475f Mon Sep 17 00:00:00 2001 From: Julien MA Jacob Date: Mon, 7 Oct 2024 17:11:28 -0200 Subject: [PATCH 08/37] Slugs: Add JS and AJAX translation --- inc/admin/assets.php | 33 ++++++ inc/admin/slug-meta.php | 29 +++-- src/js/admin/slug.js | 242 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 296 insertions(+), 8 deletions(-) create mode 100644 src/js/admin/slug.js diff --git a/inc/admin/assets.php b/inc/admin/assets.php index 9dba373..ac15ae0 100644 --- a/inc/admin/assets.php +++ b/inc/admin/assets.php @@ -366,6 +366,39 @@ function wplng_slug_assets() { wp_enqueue_script( 'jquery' ); + /** + * Enqueue wpLingua JS scripts + */ + + wp_enqueue_script( + 'wplingua-slug', + plugins_url() . '/wplingua/assets/js/admin/slug.js', + array( 'jquery' ), + WPLNG_PLUGIN_VERSION + ); + + /** + * Localize script + */ + + wp_localize_script( + 'wplingua-slug', + 'wplngLocalize', + array( + 'ajaxUrl' => admin_url( 'admin-ajax.php' ), + 'currentLanguage' => false, + 'message' => array( + 'exitPage' => esc_html__( + 'You are about to leave the page without saving your changes. They will be lost if you continue. Would you like to leave the page anyway?', + 'wplingua' + ), + 'exitEditorModal' => '', + 'buttonSave' => '', + 'buttonSaveInProgress' => '', + ), + ) + ); + /** * Enqueue wpLingua CSS styles */ diff --git a/inc/admin/slug-meta.php b/inc/admin/slug-meta.php index 37d152c..ca9d37d 100644 --- a/inc/admin/slug-meta.php +++ b/inc/admin/slug-meta.php @@ -413,9 +413,6 @@ function wplng_slug_save_meta_boxes_data( $post_id ) { */ function wplng_ajax_generate_slug() { - // TODO : Disabled function - wp_send_json_error( __( 'Disabled function', 'wplingua' ) ); - /** * Check and sanitize data */ @@ -447,7 +444,17 @@ function wplng_ajax_generate_slug() { } // Check and sanitize text to translate - // (And convert img emoji to emoji) + + $text = $_POST['text']; + $text = wplng_text_esc( $text ); + + $text = str_replace( + array( '/', '-', '_' ), + array( '', ' ', ' ' ), + $text + ); + + // Remove img emoji $text = wp_kses( $_POST['text'], @@ -460,14 +467,18 @@ function wplng_ajax_generate_slug() { $text = preg_replace( '/\\"(.*)\\"/U', - '$1', + '', $text ); - $text = wplng_text_esc( $text ); + // Check if slug is stranlatable if ( ! wplng_text_is_translatable( $text ) ) { - wp_send_json_success( $text ); + wp_send_json_success( + sanitize_title( + '/' . $text . '/' + ) + ); return; } @@ -486,5 +497,7 @@ function wplng_ajax_generate_slug() { return; } - wp_send_json_success( $response[0] ); + $response = '/' . sanitize_title( $response[0] ) . '/'; + + wp_send_json_success( $response ); } diff --git a/src/js/admin/slug.js b/src/js/admin/slug.js new file mode 100644 index 0000000..1780747 --- /dev/null +++ b/src/js/admin/slug.js @@ -0,0 +1,242 @@ +jQuery(document).ready(function ($) { + + let wplngEditor = $("#wplng-slug-editor"); + + /** + * Decode HTML + */ + function wplngDecodeHtml(string) { + let returnText = string; + returnText = returnText.replace(/ /gi, " "); + returnText = returnText.replace(/&/gi, "&"); + returnText = returnText.replace(/"/gi, '"'); + returnText = returnText.replace(/</gi, "<"); + returnText = returnText.replace(/>/gi, ">"); + return returnText; + } + + /** + * CLose editor modal + */ + function wplngCloseEditorModal() { + $("#wplng-modal-edit-container").hide(); + $("#wplng-modal-edit-save").prop("disabled", false); + $("#wplng-modal-container").show(); + wplngInputSignature.onload = ''; + wplngInputSignature.now = ''; + } + + /** + * Prepare all events and default value on slug editor + */ + + function wplngUpdateEditorEvents() { + + wplngIsUpdatePost = false; + wplngInputSignature = { + onload: wplngGetInputSignature(), + now: wplngGetInputSignature() + }; + + + /** + * Review + */ + + wplngEditor.find('.wplng-mark-as-reviewed input[type="checkbox"]').change(function () { + + var parentSelector = wplngEditor.find("#wplng-slug-" + $(this).attr("wplng-lang")); + + parentSelector.removeClass("wplng-status-generated"); + parentSelector.removeClass("wplng-status-reviewed"); + parentSelector.removeClass("wplng-status-ungenerated"); + + if (this.checked) { + parentSelector.addClass("wplng-status-reviewed"); + } else { + parentSelector.addClass("wplng-status-generated"); + } + }); + + wplngEditor.find('.wplng-edit-language .wplng-slug-input').each(function () { + var reviewSelector = wplngEditor.find("#wplng_mark_as_reviewed_" + $(this).attr("lang")); + reviewSelector.prop("disabled", $(this).val().trim() == ""); + }); + + wplngEditor.find('.wplng-edit-language .wplng-slug-input').on("keyup paste", function () { + + var parentSelector = wplngEditor.find("#wplng-slug-" + $(this).attr("lang")); + var reviewSelector = wplngEditor.find("#wplng_mark_as_reviewed_" + $(this).attr("lang")); + + parentSelector.removeClass("wplng-status-generated"); + parentSelector.removeClass("wplng-status-reviewed"); + parentSelector.removeClass("wplng-status-ungenerated"); + + if ($(this).val().trim() == "") { + parentSelector.addClass("wplng-status-ungenerated"); + reviewSelector.prop("checked", false); + reviewSelector.prop("disabled", true); + } else { + parentSelector.addClass("wplng-status-reviewed"); + reviewSelector.prop("checked", true); + reviewSelector.prop("disabled", false); + } + + }); + + /** + * Ajax slug + */ + + wplngEditor.find(".wplng-generate-spin").hide(); + + wplngEditor.find(".wplng-generate").on("click", function () { + + if ("disabled" == $(this).attr("disabled")) { + return; + } + + var source = wplngEditor.find("#wplng-original-language").attr("wplng-lang"); + var target = wplngEditor.find(this).attr("wplng-lang"); + var text = wplngEditor.find("#wplng-source").html(); + + if (undefined == source || undefined == target || undefined == text) { + return; + } + + var container = "#wplng-slug-" + target; + + wplngEditor.find(container + " .wplng-generate").attr("disabled", true); + wplngEditor.find(container + " .wplng-generate-spin").show(); + + $.ajax({ + url: wplngLocalize.ajaxUrl, + method: 'POST', + data: { + action: 'wplng_ajax_slug', + text: text, + language_source: source, + language_target: target + }, + success: function (data) { + if (data.success) { + var textarea = "#wplng_slug_" + target; + $(textarea).val(data.data); + + if (data.data != "") { + var parentSelector = wplngEditor.find("#wplng-slug-" + target); + var reviewSelector = wplngEditor.find("#wplng_mark_as_reviewed_" + target); + + parentSelector.removeClass("wplng-status-generated"); + parentSelector.removeClass("wplng-status-reviewed"); + parentSelector.removeClass("wplng-status-ungenerated"); + + parentSelector.addClass("wplng-status-reviewed"); + reviewSelector.prop("checked", true); + reviewSelector.prop("disabled", false); + } + + wplngCheckInputSignature(); + + $(container + " .wplng-generate-spin").hide(); + + setTimeout(function () { + wplngEditor.find(container + " .wplng-generate").attr("disabled", false); + }, 8000); + + } else { + console.log("wpLingua - Error:"); + console.log(data); + wplngEditor.find(container + " .wplng-generate-spin") + .removeClass("dashicons-update") + .removeClass("wplng-spin") + .addClass("dashicons-no"); + } + }, + error: function (data) { + console.log("wpLingua - Error:"); + console.log(data); + wplngEditor.find(container + " .wplng-generate-spin") + .removeClass("dashicons-update") + .removeClass("wplng-spin") + .addClass("dashicons-no"); + } + }); + + }); + + /** + * Alert if page is leave without saving + * Disable / Enable save button + */ + + wplngEditor + .find(".wplng-edit-language textarea, .wplng-edit-language input") + .on("change input propertychange", wplngCheckInputSignature); + + $('#submitpost [type=submit], #wplng-modal-edit-save').click(function () { + wplngIsUpdatePost = true; + }); + + $(window).on('beforeunload', function () { + if (!wplngIsUpdatePost + && wplngInputSignature.onload != wplngInputSignature.now + ) { + return confirm(wplngLocalize.message.exitPage); + } + }); + + } + + /** + * Show all languages + */ + + $("#wplng-modal-edit-show-all").on("click", function () { + $(this).hide(); + wplngEditor.find(".wplng-edit-language").show(); + }); + + + /** + * Alert if page is leave without saving + */ + + let wplngIsUpdatePost = false; + let wplngInputSignature = { + onload: wplngGetInputSignature(), + now: wplngGetInputSignature() + }; + + function wplngGetInputSignature() { + + let signature = ""; + + wplngEditor.find(".wplng-edit-language textarea").each(function () { + signature += $(this).val(); + }); + + wplngEditor.find(".wplng-edit-language input[type=checkbox]").each(function () { + signature += $(this).prop("checked"); + }); + + return signature; + } + + function wplngCheckInputSignature() { + + wplngInputSignature.now = wplngGetInputSignature(); + + if (wplngInputSignature.onload == wplngInputSignature.now) { + $("#wplng-modal-edit-save").prop("disabled", true); + } else { + $("#wplng-modal-edit-save").prop("disabled", false); + } + } + + + wplngUpdateEditorEvents(); + + + +}); // End jQuery loaded event \ No newline at end of file From e49219d9ec5bf04d2444dae0e1549bd0c10e30a2 Mon Sep 17 00:00:00 2001 From: Julien MA Jacob Date: Tue, 8 Oct 2024 19:10:33 -0200 Subject: [PATCH 09/37] Fix AJAX slug translation --- inc/admin/slug-meta.php | 5 ++++- wplingua.php | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/inc/admin/slug-meta.php b/inc/admin/slug-meta.php index ca9d37d..1a7410d 100644 --- a/inc/admin/slug-meta.php +++ b/inc/admin/slug-meta.php @@ -457,7 +457,7 @@ function wplng_ajax_generate_slug() { // Remove img emoji $text = wp_kses( - $_POST['text'], + $text, array( 'img' => array( 'alt' => array(), @@ -482,6 +482,9 @@ function wplng_ajax_generate_slug() { return; } + // wp_send_json_error( $text ); + // return; + /** * Call API and get translation */ diff --git a/wplingua.php b/wplingua.php index 22d9f33..425f55c 100644 --- a/wplingua.php +++ b/wplingua.php @@ -217,8 +217,8 @@ function wplng_start() { // Remove Quick edit from slugs list add_filter( 'post_row_actions', 'wplng_slug_remove_quick_edit', 10, 2 ); - // // Ajax function for regenerate translation on edit page - // add_action( 'wp_ajax_wplng_ajax_translation', 'wplng_ajax_generate_translation' ); + // Ajax function for regenerate slug on edit page + add_action( 'wp_ajax_wplng_ajax_slug', 'wplng_ajax_generate_slug' ); // Display 100 translation in admin area by default add_filter( 'get_user_option_edit_wplng_slug_per_page', 'wplng_slug_per_page' ); From 419847b8056ef7fa82fd8bd7793ee9440d1f9188 Mon Sep 17 00:00:00 2001 From: Julien MA Jacob Date: Tue, 8 Oct 2024 19:44:23 -0200 Subject: [PATCH 10/37] Add slug filter by status --- inc/admin/slug-cpt.php | 151 +++++++++++++++++++++++++++++++++++++++++ wplingua.php | 6 +- 2 files changed, 154 insertions(+), 3 deletions(-) diff --git a/inc/admin/slug-cpt.php b/inc/admin/slug-cpt.php index baa3eb8..97a39b3 100644 --- a/inc/admin/slug-cpt.php +++ b/inc/admin/slug-cpt.php @@ -77,3 +77,154 @@ function wplng_slug_per_page( $result ) { return $result; } + + + +/** + * Filter slugs by status: Display option on CPT list + * + * @return void + */ +function wplng_restrict_manage_posts_slug_status() { + + if ( empty( $_GET['post_type'] ) + || 'wplng_slug' !== $_GET['post_type'] + ) { + return; + } + + $languages_target = wplng_get_languages_target_ids(); + $options = array(); + $slug_status = ''; + + if ( ! empty( $_GET['slug_status'] ) ) { + $slug_status = sanitize_title( $_GET['slug_status'] ); + } + + if ( count( $languages_target ) === 1 ) { + $options = array( + '' => __( 'All slug status', 'wplingua' ), + 'reviewed' => __( 'Reviewed', 'wplingua' ), + 'unreviewed' => __( 'Unreviewed', 'wplingua' ), + ); + } else { + $options = array( + '' => __( 'All slug status', 'wplingua' ), + 'full-reviewed' => __( 'Full reviewed', 'wplingua' ), + 'partially-reviewed' => __( 'Partially reviewed', 'wplingua' ), + 'reviewed' => __( 'Reviewed', 'wplingua' ), + 'unreviewed' => __( 'Full unreviewed', 'wplingua' ), + ); + } + + $html = '