*/ class Falang_Admin extends Falang_Rewrite { protected $active_tab; /** * @from 1.0 * * @var boolean */ var $disable_translate_home_url = false; /** * Initialize the class and set its properties. * * @since 1.0.0 * * @param string $plugin_name The name of this plugin. * @param string $version The version of this plugin. */ public function __construct( $plugin_name, $version ) { parent::__construct( $plugin_name, $version ); $this->plugin_name = $plugin_name; $this->version = $version; add_action( 'plugins_loaded', array( $this, 'load' ) ); if ( isset( $_GET['page'] ) ) { $this->active_tab = 'falang' === $_GET['page'] ? 'falang' : substr( sanitize_key($_GET['page']), 7 ); } } public function load() { parent::load(); // register post types without UI not use for all (ex: menu) // add_action('init', array($this, 'register_extra_post'), 20); //post settings option //TODO put this in Ajax Handler add_action( 'wp_ajax_falang_settings_post_options', array( &$this, 'ajax_settings_post_options' ) ); add_action( 'wp_ajax_falang_settings_taxonomy_options', array( &$this, 'ajax_settings_taxonmy_options' ) ); // update post settings optons add_action( 'wp_ajax_update_settings_post_options', array( &$this, 'ajax_update_settings_post_options' ) ); add_action( 'wp_ajax_update_settings_taxonomy_options', array(&$this,'ajax_update_settings_taxonomy_options') ); //term translation popup add_action( 'wp_ajax_falang_term_translation', array( &$this, 'ajax_falang_term_translation' ) ); //ajax delete term translation add_action( 'wp_ajax_falang_term_delete_translation', array( &$this, 'ajax_falang_term_delete_translation' ) ); //string translation popup add_action( 'wp_ajax_falang_string_translation', array( &$this, 'ajax_falang_string_translation' ) ); //option translation popup add_action( 'wp_ajax_falang_option_translation', array( &$this, 'ajax_falang_option_translation' ) ); //update term/taxonomy translation add_action( 'wp_ajax_falang_term_update_translation', array( &$this, 'ajax_update_term_translation' ) ); //update string translation add_action( 'wp_ajax_falang_string_update_translation', array( &$this, 'ajax_update_string_translation' ) ); //update option translation add_action( 'wp_ajax_falang_option_update_translation', array( &$this, 'ajax_update_option_translation' ) ); //ajax delete post add_action( 'wp_ajax_falang_post_delete_translation', array( &$this, 'ajax_falang_post_delete_translation' ) ); //ajax delete menu add_action( 'wp_ajax_falang_menu_delete_translation', array( &$this, 'ajax_falang_menu_delete_translation' ) ); // register ajax hooks for getting/setting add_action( 'wp_ajax_falang_export_options', array($this, 'ajax_export_options') ); add_action( 'wp_ajax_falang_option_translations', array($this, 'ajax_get_option_translations') ); add_action( 'wp_ajax_falang_set_option_translation', array($this, 'ajax_set_option_translation') ); // set posts, pages and taxonomy translatable by default add_filter( 'falang_default-post', array( $this, 'set_post_type_translatable' ) ); add_filter( 'falang_default-page', array( $this, 'set_post_type_translatable' ) ); add_filter( 'falang_taxonomy_default-category', array( $this, 'set_post_type_translatable' ) ); // Flush rewrite rules if needed add_action('wp_loaded', array($this, 'flush_rewrite_rules'), 12); add_action('generate_rewrite_rules', array($this, 'generate_rewrite_rules')); // set nav menu item translation defaults add_filter( 'falang_default-nav_menu_item', array( $this, 'nav_menu_item_default_options' ) ); add_filter( 'falang_type_metakeys', array( $this, 'nav_menu_item_metakeys' ), 10, 2 ); } /** * Register the stylesheets for the admin area. * * @since 1.0.0 */ public function enqueue_styles() { /** * This function is provided for demonstration purposes only. * * An instance of this class should be passed to the run() function * defined in Falang_Loader as all of the hooks are defined * in that particular class. * * The Falang_Loader will then create the relationship * between the defined hooks and the functions defined in this * class. */ wp_enqueue_style( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'css/falang-admin.css', array(), $this->version, 'all' ); wp_enqueue_style( 'select2', plugin_dir_url( __FILE__ ) . 'css/select2.min.css', array(), '4.0.11', 'all' ); } /** * Register the JavaScript for the admin area. * * @since 1.0.0 */ public function enqueue_scripts() { /** * This function is provided for demonstration purposes only. * * An instance of this class should be passed to the run() function * defined in Falang_Loader as all of the hooks are defined * in that particular class. * * The Falang_Loader will then create the relationship * between the defined hooks and the functions defined in this * class. */ wp_enqueue_script( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'js/falang-admin.js', array( 'jquery' ), $this->version, false ); // enqueue tipTip.js wp_register_script( 'jquery-tiptip', plugin_dir_url( __FILE__ ) . '/js/jquery-tiptip/jquery.tipTip.min.js', array( 'jquery' ), $this->version, true ); wp_enqueue_script( 'jquery-tiptip' ); // enqueue select2.js wp_register_script( 'jquery-select2', plugin_dir_url( __FILE__ ) . 'js/select2.min.js', array( 'jquery' ), '4.0.11', true ); wp_enqueue_script( 'jquery-select2' ); // register JS for option translation page } public function add_admin_menu() { $falang_model = new \Falang\Model\Falang_Model(); $falang_post = new \Falang\Core\Post(); //TODO check if we add a menu link to all translateble post_type $post_types = get_post_types( array( 'show_ui' => true ) ); foreach ( $post_types as $post_type ) { if ( $falang_post->is_post_type_translatable( $post_type ) ) { } } // Prepare the list of tabs $tabs = array(); // Only if at least one language has been created if ( $falang_model->get_languages_list() ) { $tabs['translation'] = __( 'Translate Posts', 'Falang' ); $tabs['terms'] = __( 'Translate Terms/Taxo', 'Falang' ); $tabs['menus'] = __( 'Translate Menus', 'Falang' ); $tabs['strings'] = __( 'Translate Strings', 'Falang' ); $tabs['options'] = __( 'Translate Options', 'Falang' ); } // $tabs['options'] = __( 'Translate Options', 'Falang' ); $tabs['language'] = __( 'Languages', 'Falang' ); $tabs['settings'] = __( 'Settings', 'Falang' ); $tabs['help'] = __( 'Get Help', 'Falang' ); //$tabs = apply_filters( 'falang_settings_tabs', $tabs ); //add falang top level menu and submbenu foreach ( $tabs as $tab => $title ) { $page = 'falang' === $tab ? 'falang' : "falang-$tab"; if ( empty( $parent ) ) { $parent = $page; add_menu_page( $title, __( 'Falang', 'Falang' ), 'manage_options', $page, null, FALANG_ADMIN_URL.'/img/icon-20.png', '90' ); } add_submenu_page( $parent, $title, $title, 'manage_options', $page, array( $this, 'languages_page' ) ); } } public function languages_page() { // Handle user input $action = isset( $_REQUEST['action'] ) ? $_REQUEST['action'] : ''; //prepare page for display switch ( $this->active_tab ) { case 'language': if ( 'add_new_language' === $action ) { return $this->display_edit_language_page(); } if ( 'edit' === $action && ! empty( $_GET['id'] ) ) { return $this->display_edit_language_page( intval($_GET['id']) ); } else { //default action for edit_language_page $this->handle_language_actions( $action ); } // Prepare the list table of languages $this->model->reset_languages_list();//TODO remove this ugly code but it's necessary due to falang_rewrite and the registered_taxonomy action. $language_list_table = new \Falang\Table\Languages(); $language_list_table->prepare_items( $this->model->get_languages_list() ); break; case 'translation': $post_list_table = new \Falang\Table\Post( $this->model ); //TODO probably put a configuration in settings to have specifc menu and remove it here //if post_type filter is used exclude all post_type without the filter TODO put this in other place if ( isset($_REQUEST['pt-filter']) && $_REQUEST['pt-filter'] ) { $translable_post_list = get_posts( array( 'post_type' => $_REQUEST['pt-filter'], 'post_status' => 'any', 'orderby' => 'post_title', 'order' => 'ASC', 'nopaging' => true ) ); } else { $translable_post_list = get_posts( array( // 'post_type' => $this->model->get_translated_post_types( true, $exluded_post_type ), 'post_type' => $this->model->get_transtable_post_types(true), 'post_status' => 'any', 'orderby' => 'post_title', 'order' => 'ASC', 'nopaging' => true ) ); } $post_list_table->prepare_items( $translable_post_list ); if ( 'edit' === $action ) { return $this->display_edit_post_page(); } else { //default action for edit_language_page $this->handle_translation_actions( $action ); } break; case 'terms': $term_list_table = new \Falang\Table\Term(); //TODO put this like translation in another place. if ( isset($_REQUEST['tt-filter']) && $_REQUEST['tt-filter'] ) { $args = array( 'name' => $_REQUEST['tt-filter'] ); $translable_term_list = $this->get_terms_to_translate($args); } else { $translable_term_list = $this->get_terms_to_translate(); } $term_list_table->prepare_items( $translable_term_list ); //need to laoad Translator for thibox don't work inside if ( $this->model->get_option( 'enable_service' ) ) { //no target language \Falang\Factory\TranslatorFactory::getTranslator( $this->model->get_default_locale() ); } if ( '' === $action ) { //return $this->display_edit_terms_page(); } else { //default action for edit_language_page $this->handle_terms_actions( $action ); } break; case 'menus': $menu_list_table = new \Falang\Table\Menu(); $translable_menu_list = get_posts( array( 'post_type' => 'nav_menu_item', 'post_status' => 'any', 'orderby' => 'post_title', 'order' => 'ASC', 'nopaging' => true ) ); $menu_list_table->prepare_items( $translable_menu_list ); if ( 'edit' === $action ) { return $this->display_edit_menu_page(); } else { //default action for edit_language_page $this->handle_menus_actions( $action ); } break; case 'settings' : if ( '' === $action ) { return $this->display_settings_page(); } else { //default action for edit_language_page $this->handle_settings_actions( $action ); } break; case 'strings' : $strings_list_table = new \Falang\Table\FStrings( $this->model ); $strings_list_table->prepare_items(); //need to laoad Translator for thibox don't work inside if ( $this->model->get_option( 'enable_service' ) ) { //no target language \Falang\Factory\TranslatorFactory::getTranslator( $this->model->get_default_locale() ); } if ( 'edit' === $action ) { return $this->display_strings_page(); } else { //default action for edit_language_page $this->handle_strings_actions( $action ); } break; case 'options' : $options_list_table = new \Falang\Table\FOptions( $this->model ); $translable_options_list = $this->get_options_to_translate(); $options_list_table->prepare_items( $translable_options_list ); //need to laoad Translator for thibox don't work inside if ( $this->model->get_option( 'enable_service' ) ) { //no target language \Falang\Factory\TranslatorFactory::getTranslator( $this->model->get_default_locale() ); } if ( 'edit' === $action ) { return $this->display_options_page(); } else { //default action for edit_language_page $this->handle_options_actions( $action ); } break; } //display page include_once 'views/falang-admin-display.php'; //$this->display('falang-admin-display'); } /** * Manages the user input for the languages pages * * @param string $action */ public function handle_language_actions( $action ) { $falangModel = new \Falang\Model\Falang_Model(); switch ( $action ) { case 'add': //check_admin_referer( 'add', '_wpnonce_add' ); $falangModel->add_language( $_POST ); $this->display( 'views/falang-admin-display.php' ); break; case 'delete': //TODO the delete/default use the id of the language and not the locale - change this //check_admin_referer( 'delete' ); if ( ! empty( $_GET['language'] ) ) { $falangModel->delete_language( intval ($_GET['language']) ); } //self::redirect(); // To refresh the page break; case 'update': $falangModel->update_language( $_POST ); break; case 'default': //TODO the delete/default use the id of the language and not the locale - change this check_admin_referer( 'default' ); $language = $falangModel->get_language( intval( $_GET['language']) ); if ( $language instanceof \Falang\Core\Language ) { $falangModel->update_default_lang( $language->locale ); } //self::redirect(); // To refresh the page break; break; case 'publish': break; case 'unpublish': break; default: break; } } /** * Manages the user input for the translations pages * * @param string $action */ public function handle_translation_actions( $action ) { //if ( ! current_user_can('manage_post_translation') ) return; $request_action = $this->request_action(); // save accounts if ( $request_action == 'falang_save_post' ) { //check_admin_referer( 'falang_save_pody' ); $this->save_translation_post(); } } /** * Manages the user input for the terms pages * * @param string $action */ public function handle_terms_actions( $action ) { //if ( ! current_user_can('manage_terms_translation') ) return; $request_action = $this->request_action(); // save accounts // if ( $request_action == 'falang_save_terms' ) { //check_admin_referer( 'falang_save_pody' ); //$this->save_translation_terms(); // } // if ($request_action == 'delete' ) { // $this->delete_translation_terms(); // } } /** * Manages the user input for the translations pages * * @param string $action */ public function handle_menus_actions( $action ) { //if ( ! current_user_can('manage_post_translation') ) return; $request_action = $this->request_action(); // save accounts if ( $request_action == 'falang_save_menu' ) { //check_admin_referer( 'falang_save_pody' ); $this->save_translation_menu(); } } public function handle_strings_actions( $action ) { $request_action = $this->request_action(); // save string if ( $request_action == 'falang_save_string' ) { //check_admin_referer( 'falang_save_pody' ); //$this->save_translation_string(); } } public function handle_options_actions( $action ) { $request_action = $this->request_action(); // save string if ( $request_action == 'falang_save_options' ) { //check_admin_referer( 'falang_save_pody' ); //$this->save_translation_string(); } } public function ajax_falang_post_delete_translation() { $response = new stdClass(); $response->success = true; $response->success = $this->post_delete_translation(); if ( $response->success ) { $response->message = esc_html__( 'Post translation deleted', 'falang' ); } else { $response->message = esc_html__( 'Post translation not deleted', 'falang' ); } $this->return_json( $response ); exit(); } public function post_delete_translation() { $post_id = empty( $_GET['post_id'] ) ? '' : intval($_GET['post_id']); $language_locale = empty( $_GET['language'] ) ? '' : sanitize_html_class($_GET['language']); if ( wp_verify_nonce( $_GET['_wpnonce'], 'delete-post-translation' ) && ! empty( $post_id ) && ! empty( $language_locale ) ) { $post_type = get_post_type( $post_id ); //get translatable field $falang_post = new \Falang\Core\Post(); //TODO get the list of all field not only the enabled $translated_post_metakeys = $falang_post->get_post_type_option( $post_type, 'fields', $falang_post->fields );//TODO remove the $this->fields must be in option //add published to the list of $translated_post_metakeys fields $translated_post_metakeys[] = 'published'; foreach ( $translated_post_metakeys as $field ) { delete_post_meta( $post_id, Falang_Core::get_prefix( $language_locale ) . $field ); } return true; } else { return false; } } /** * Manages the user input for the translations pages * * @param string $action */ public function handle_settings_actions( $action ) { //if ( ! current_user_can('manage_post_translation') ) return; $request_action = $this->request_action(); // save accounts if ( $request_action == 'falang_save_settings' ) { //check_admin_referer( 'falang_save_pody' ); $this->save_settings(); } } public function save_translation_post() { $language = $this->get_value_from_post( 'target_language', '' ); $post_id = $this->get_value_from_post( 'post_id', '' ); $post = get_post( $post_id ); $falang_post = new \Falang\Core\Post( $post_id ); $fields = $falang_post->get_post_type_option( $post->post_type, 'fields', $falang_post->fields );//TODO Fallback pas normal ici on doit utiliser les optiosn $post_meta_key = $falang_post->get_post_type_metakeys($post->post_type); foreach ( $fields as $field ) { $field_value = $this->get_value_from_post( $field, '' ); $meta_key = Falang_Core::get_prefix( $language ) . $field; update_post_meta( $post_id, $meta_key, $field_value ); } foreach ( $post_meta_key as $post_meta ) { $field_value = $this->get_value_from_post( $post_meta, '' ); $meta_key = Falang_Core::get_prefix( $language ) . $post_meta; update_post_meta( $post_id, $meta_key, $field_value ); } //save published status //TODO only if different $published = $this->get_value_from_post( 'published', '' ) == '1' ? '1' : '0'; update_post_meta( $post_id, Falang_Core::get_prefix( $language ) . 'published', $published ); return; } public function save_translation_menu() { //save the title of the post $this->save_translation_post(); //save the extra_cpt $this->save_extra_custom_post(); } public function ajax_falang_menu_delete_translation() { $response = new stdClass(); $response->success = $this->menu_delete_translation(); if ( $response->success ) { $response->message = esc_html__( 'Menu translation deleted', 'falang' ); } else { $response->message = esc_html__( 'Menu translation not deleted', 'falang' ); } $this->return_json( $response ); exit(); } public function menu_delete_translation() { $post_id = empty( $_GET['post_id'] ) ? '' : intval($_GET['post_id']); $language_locale = empty( $_GET['language'] ) ? '' : sanitize_html_class($_GET['language']); if ( wp_verify_nonce( $_GET['_wpnonce'], 'delete-menu-translation' ) && ! empty( $post_id ) && ! empty( $language_locale ) ) { //delete post information $post_type = get_post_type( $post_id ); //get translatable field $falang_post = new \Falang\Core\Post(); //TODO get the list of all field not only the enabled $translated_post_metakeys = $falang_post->get_post_type_option( $post_type, 'fields', $falang_post->fields );//TODO remove the $this->fields must be in option //add published to the list of $translated_post_metakeys fields $translated_post_metakeys[] = 'published'; $translated_post_metakeys[] = 'falang_hide'; $translated_post_metakeys[] = '_menu_item_url'; foreach ( $translated_post_metakeys as $field ) { delete_post_meta( $post_id, Falang_Core::get_prefix( $language_locale ) . $field ); } return true; } else { return false; } } /** * Ajax route to fetch options * * @from 1.0 */ public function ajax_export_options() { global $wpdb; $options = $wpdb->get_results( "SELECT option_name, option_value FROM $wpdb->options ORDER BY option_name" ); $unserialized_options = array(); foreach ($options as $option) { $unserialized_options[$option->option_name] = $this->unserialize_deep($option->option_value); } echo json_encode($unserialized_options); die(); } /** * Unserialize options. Callback for array_reduce * * @from 2.3 */ public function unserialize_deep($value) { if ($value) { $value = maybe_unserialize($value); if (is_array($value)) { foreach ($value as $key => $child) { $value[$key] = $this->unserialize_deep($child); } } } return $value; } /** * Get option translations for ajax * * @from 1.0 */ public function ajax_get_option_translations() { echo json_encode($this->get_option_translations()); wp_die(); } /** * get all options translations * * @from 1.0 * * @return array */ public function get_option_translations() { $translations = $this->model->get_option('translations', array()); if (isset($translations['option'])) { return $translations['option']; } return array(); } /** * Set option translation for ajax * * @from 1.5 */ public function ajax_set_option_translation() { if (isset($_POST['falang_option_translation'])) { $option_tree = $this->map_deep($_POST['falang_option_translation'], array($this, 'format_option')); $this->update_option_translations($option_tree); } wp_die(); } /** * Save translatable custom post type without admin UI * * @hook for "save_post_{$post->post_type}" * @from 1.0 */ public function save_extra_custom_post() { $post_id = $this->get_value_from_post( 'post_id', '' ); $post = get_post( $post_id ); $language = $this->get_value_from_post( 'target_language', '' ); $translate_prefix = Falang_Core::get_prefix( $language ); $falang_post = new \Falang\Core\Post(); if ( ( ! defined( 'DOING_AUTOSAVE' ) || ! DOING_AUTOSAVE ) && current_user_can( 'edit_post', $post_id ) ) { if ( isset( $_POST['falang_extra_cpt_nonce'], $_POST['falang_extra_cpt'] ) && wp_verify_nonce( $_POST['falang_extra_cpt_nonce'], 'falang' ) ) { foreach ( $_POST['falang_extra_cpt'] as $key => $value ) { //save only supported option if ( in_array( $key, $falang_post->get_post_type_metakeys( $post->post_type ) ) ) { update_post_meta( $post_id, $translate_prefix . $key, $value ); } } if ( isset( $_POST['_menu_item_classes'] ) ) { update_post_meta( $post_id, $translate_prefix . '_menu_item_classes', explode( ' ', $_POST['_menu_item_classes'] ) ); } } } } /** * Save post-type/taxonomy option page * * @from 2.0 */ public function save_settings() { if ( current_user_can( 'manage_options' ) ) { if ( isset( $_POST['falang_settings_option'] ) && wp_verify_nonce( $_POST['falang_settings_option'], 'falang_action' ) ) { $options = get_option( $this->model->option_name ); $falang_post = new \Falang\Core\Post(); $post_type_options = $falang_post->get_post_types_options(); $post_type_objs = get_post_types( array(), 'objects' ); foreach ( $post_type_objs as $post_type_obj ) { if ( isset( $_POST['post_type'] ) && in_array( $post_type_obj->name, $_POST['post_type'] ) ) { $post_type_options[ $post_type_obj->name ] = $falang_post->get_post_type_options( $post_type_obj->name ); // use filter to set default options $post_type_options[ $post_type_obj->name ]['translatable'] = true; } else if ( $falang_post->is_post_type_translatable( $post_type_obj->name ) ) { $post_type_options[ $post_type_obj->name ]['translatable'] = false; } } $falang_tax = new \Falang\Core\Taxonomy( null, $this->model ); $taxonomies_options = $falang_tax->get_taxonomies_options(); $taxonomy_objs = get_taxonomies( array( 'show_ui' => true ), 'objects' ); foreach ( $taxonomy_objs as $taxonomy_obj ) { if ( isset( $_POST['taxonomy'] ) && in_array( $taxonomy_obj->name, $_POST['taxonomy'] ) ) { $taxonomies_options[ $taxonomy_obj->name ] = $falang_tax->get_taxonomy_options( $taxonomy_obj->name ); // use filter to set default options $taxonomies_options[ $taxonomy_obj->name ]['translatable'] = true; } else if ( $falang_tax->is_taxonomy_translatable( $taxonomy_obj->name ) ) { $taxonomies_options[ $taxonomy_obj->name ]['translatable'] = false; } } $options['post_type'] = $post_type_options; $options['taxonomy'] = $taxonomies_options; $options['show_slug'] = ( isset( $_POST['show_slug'] ) && $_POST['show_slug'] ) ? true : false; $options['autodetect'] = ( isset( $_POST['autodetect'] ) && $_POST['autodetect'] ) ? true : false; $options['need_flush'] = 1; $options['enable_service'] = ( isset( $_POST['enable_service'] ) && $_POST['enable_service'] ) ? true : false; $options['service_name'] = ( isset( $_POST['service_name'] ) && $_POST['service_name'] ) ? $_POST['service_name'] : ''; $options['yandex_key'] = ( isset( $_POST['yandex_key'] ) && $_POST['yandex_key'] ) ? $_POST['yandex_key'] : ''; $options['delete_trans_on_uninstall'] = ( isset( $_POST['delete_trans_on_uninstall'] ) && $_POST['delete_trans_on_uninstall'] ) ? $_POST['delete_trans_on_uninstall'] : ''; // $options['azure_key'] = (isset($_POST['azure_key']) && $_POST['azure_key']) ? $_POST['azure_key'] : ''; update_option( $this->model->option_name, $options ); return true; } } return false; } public function save_settings_options() { if ( current_user_can( 'manage_options' ) ) { if ( isset( $_POST['falang_post_option'], $_POST['post_type'] ) && wp_verify_nonce( $_POST['falang_post_option'], 'falang_action' ) ) { $post_type = esc_attr( $_POST['post_type'] ); //TODO use model or post or static $falang_post = new \Falang\Core\Post(); if ( $falang_post->is_post_type_translatable( $post_type ) ) { $translations = $this->model->get_option( 'translations', array() ); // permalinks if ( isset( $_POST['cpt'] ) ) { $cpt = isset( $_POST['cpt'] ) ? array_map( array( $this, 'sanitize_slug' ), $_POST['cpt'] ) : array(); if ( ! isset( $translations['post_type'][ $post_type ] ) || $translations['post_type'][ $post_type ] !== $cpt ) { $translations['post_type'][ $post_type ] = $cpt; $this->model->update_option( 'translations', $translations ); $this->model->update_option( 'need_flush', 1 ); } } // custom post type archive if ( isset( $_POST['cpt_archive'] ) ) { $cpt_archive = isset( $_POST['cpt_archive'] ) ? array_map( array( $this, 'sanitize_slug' ), $_POST['cpt_archive'] ) : array(); if ( ! isset( $translations['cpt_archive'][ $post_type ] ) || $translations['cpt_archive'][ $post_type ] !== $cpt_archive ) { $translations['cpt_archive'][ $post_type ] = $cpt_archive; $this->model->update_option( 'translations', $translations ); $this->model->update_option( 'need_flush', 1 ); } } //Todo put this in options or model $post_types_options = $falang_post->get_post_types_options(); // fields $fields = isset( $_POST['fields'] ) ? array_map( 'esc_attr', $_POST['fields'] ) : array(); $post_types_options[ $post_type ]['fields'] = $fields; // meta $meta_keys = isset( $_POST['meta_keys'] ) ? array_map( 'esc_attr', $_POST['meta_keys'] ) : array(); $post_types_options[ $post_type ]['meta_keys'] = $meta_keys; // advanced options $exclude_untranslated = isset( $_POST['exclude_untranslated'] ) && $_POST['exclude_untranslated']; $post_types_options[ $post_type ]['exclude_untranslated'] = $exclude_untranslated; // @from 2.5 gutenberg metabox_compat option if ( isset( $_POST['gutenberg_async_switch'] ) && $_POST['gutenberg_async_switch'] ) { unset( $post_types_options[ $post_type ]['gutenberg_metabox_compat'] ); } else { $post_types_options[ $post_type ]['gutenberg_metabox_compat'] = 1; } // @from 2.6 Revision if ( isset( $_POST['enable_revisions'] ) && $_POST['enable_revisions'] ) { $post_types_options[ $post_type ]['enable_revisions'] = 1; } else { unset( $post_types_options[ $post_type ]['enable_revisions'] ); } $this->model->update_option( 'post_type', $post_types_options ); } return true; } else if ( isset( $_POST['falang_taxonomy_option'], $_POST['taxonomy'] ) && wp_verify_nonce( $_POST['falang_taxonomy_option'], 'falang_action' ) ) { $taxonomy = esc_attr( $_POST['taxonomy'] ); $Taxonomy = new \Falang\Core\Taxonomy( null, $this->model ); if ( $Taxonomy->is_taxonomy_translatable( $taxonomy ) ) { // permalinks if ( isset( $_POST['tax'] ) ) { $translations = $this->model->get_option( 'translations', array() ); $tax = isset( $_POST['tax'] ) ? array_map( array( $this, 'sanitize_slug' ), $_POST['tax'] ) : array(); if ( ! isset( $translations['taxonomy'][ $taxonomy ] ) || $translations['taxonomy'][ $taxonomy ] !== $tax ) { $translations['taxonomy'][ $taxonomy ] = $tax; $this->model->update_option( 'translations', $translations ); $this->model->update_option( 'need_flush', 1 ); } $this->model->update_option( 'translations', $translations ); } $taxonomies_options = $Taxonomy->get_taxonomies_options(); // fields $fields = isset( $_POST['fields'] ) ? array_map( 'esc_attr', $_POST['fields'] ) : array(); $taxonomies_options[ $taxonomy ]['fields'] = $fields; // meta $meta_keys = isset( $_POST['meta_keys'] ) ? array_map( 'esc_attr', $_POST['meta_keys'] ) : array(); $taxonomies_options[ $taxonomy ]['meta_keys'] = $meta_keys; $this->model->update_option( 'taxonomy', $taxonomies_options ); } return true; } } return false; } /** * Sanitize slug * * @from 2.3 */ public function sanitize_slug( $slug ) { return sanitize_title( $slug ); } public function save_term_translation() { $taxonomy = empty( $_POST['taxonomy'] ) ? '' : $_POST['taxonomy']; $falang_taxonomy = new \Falang\Core\Taxonomy( null, $this->model ); //TODO display a message when taxonomy is not translatable (or disable edit button) if ( $falang_taxonomy->is_taxonomy_translatable( $taxonomy ) && isset( $_POST['falang_term_nonce'] ) && wp_verify_nonce( $_POST['falang_term_nonce'], 'falang_action' ) ) { $context = empty( $_POST['context'] ) ? '' : $_POST['context']; $term_id = empty( $_POST['term_id'] ) ? '' : $_POST['term_id']; $language_locale = empty( $_POST['target_language'] ) ? '' : $_POST['target_language']; $published = $this->get_value_from_post( 'published', '' ) == '1' ? '1' : '0'; //get translatable field $taxonomy_fields = $falang_taxonomy->get_taxonomy_fields( $taxonomy ); //update published $this->update_term_translation( $term_id, array( 'published' => $published ), $language_locale ); foreach ( $taxonomy_fields as $field ) { $this->update_term_translation( $term_id, array( $field => $_POST[ $field ] ), $language_locale ); } //TODO delete non translatable field in case already in database } return true; } public function save_string_translation() { $row = empty( $_POST['row'] ) ? '' : sanitize_text_field($_POST['row']); $translation = empty( $_POST['translation'] ) ? '' : sanitize_textarea_field($_POST['translation']); $orginal_value = empty( $_POST['fake_original_value'] ) ? '' : sanitize_textarea_field($_POST['fake_original_value']); $language_locale = empty( $_POST['target_language'] ) ? '' : sanitize_html_class($_POST['target_language']); $context = empty( $_POST['context'] ) ? '' : sanitize_text_field($_POST['context']); $language = $this->model->get_language_by_locale( $language_locale ); $falang_mo = new Falang_Mo(); $falang_mo->import_from_db( $language ); /** * Filter the string translation before it is saved in DB * Allows to sanitize strings registered with falang_register_string * * @since 1.6 * * @param string $translation the string translation * @param string $name the name as defined in falang_register_string * @param string $context the context as defined in falang_register_string */ //$translation = apply_filters( 'falang_sanitize_string_translation', $translation, $this->strings[ $key ]['name'], $context ); $falang_mo->add_entry( $falang_mo->make_entry( $orginal_value, $translation ) ); // Clean database ( removes all strings which were registered some day but are no more ) //TODO check if working if ( ! empty( $_POST['clean'] ) ) { $new_mo = new Falang_Mo(); $strings = FString::get_strings(); foreach ( $strings as $string ) { $new_mo->add_entry( $falang_mo->make_entry( $string['string'], $falang_mo->translate( $string['string'] ) ) ); } } isset( $new_mo ) ? $new_mo->export_to_db( $language ) : $falang_mo->export_to_db( $language ); add_settings_error( 'general', 'falang_strings_translations_updated', __( 'Translations updated.', 'falang' ), 'updated' ); /** * Fires after the strings translations are saved in DB * * @since 1.2 */ do_action( 'falang_save_strings_translations' ); return true; } public function save_option_translation(){ $option_name = empty( $_POST['name'] ) ? '' : sanitize_text_field($_POST['name']); ; $transation = empty( $_POST['translation'] ) ? '' : sanitize_textarea_field($_POST['translation']); ; $language_locale = empty( $_POST['target_language'] ) ? '' : sanitize_html_class($_POST['target_language']); //case multiple if (isset($_POST['falang_option_translation'])) { $option_tree = $this->map_deep($_POST['falang_option_translation'], array($this, 'format_option')); //simple } else { $option_tree = array ($language_locale => array( $option_name => $transation)); } $this->update_option_translations($option_tree); return true; } /** * Update option translations * * @from 1.5 * * @return array */ private function update_option_translations($option_tree) { $translations = $this->model->get_option('translations', array()); if (empty($translations['option'])) { $translations['option'] = array(); } $translations['option'] = array_replace_recursive($translations['option'], $option_tree); // only PHP 5.3 ! // clean array $translations['option'] = $this->clean_translations($translations['option']); $this->model->update_option('translations', $translations); } /** * Clean array deep. Callback for array_reduce * * @from 1.5 */ private function clean_translations($node) { if (is_array($node)) { $clean_node = array(); foreach ($node as $key => $child) { $child = $this->clean_translations($child); if ($child !== '' && !(is_array($child) && !$child)) { $clean_node[$key] = $child; } } return $clean_node; } return $node; } /** * Format option * * @from 1.5.3 add stripslashes * @from 1.5.2 remove default html escaping * @from 1.5 */ public function format_option($value) { $value = stripslashes(trim($value)); switch ($value) { case 'false': return false; case 'true': return true; } return $value; } /** * Map deep. Copied from wp-includes/formatting.php * * @from 1.5 */ private function map_deep( $value, $callback ) { if ( is_array( $value ) || is_object( $value ) ) { foreach ( $value as &$item ) { $item = $this->map_deep( $item, $callback ); } return $value; } else { return call_user_func( $callback, $value ); } } public function ajax_falang_term_delete_translation() { $response = new stdClass(); $response->success = true; $response->success = $this->term_delete_translation(); if ( $response->success ) { $response->message = esc_html__( 'Term translation deleted', 'falang' ); } else { $response->message = esc_html__( 'Term translation not deleted', 'falang' ); } $this->return_json( $response ); exit(); } public function term_delete_translation() { $taxonomy = empty( $_GET['taxonomy'] ) ? '' : sanitize_key($_GET['taxonomy']); $term_id = empty( $_GET['term_id'] ) ? '' : sanitize_key($_GET['term_id']); $language_locale = empty( $_GET['language'] ) ? '' : sanitize_html_class($_GET['language']); if ( wp_verify_nonce( $_GET['_wpnonce'], 'delete-term-translation' ) && ! empty( $taxonomy ) && ! empty( $term_id ) && ! empty( $language_locale ) ) { //get translatable field $falang_taxonomy = new \Falang\Core\Taxonomy( null, $this->model ); //TODO get the list of all field not only the enabled $taxonomy_fields = $falang_taxonomy->get_taxonomy_fields( $taxonomy ); //add published to the list of $taxonomy fields $taxonomy_fields[] = 'published'; foreach ( $taxonomy_fields as $field ) { delete_term_meta( $term_id, Falang_Core::get_prefix( $language_locale ) . $field ); } return true; } else { return false; } } /** * Update term translation * * @param int $term_id Term ID * @param array $data { * List of field to save. * @string $name Name * @string $slug Slug * @string $description Description * @string $custom_meta_key Post meta * } * @param object $locale Language. * * @from 1.0 */ public function update_term_translation( $term_id, $data, $locale ) { // if (empty($language)) { // // $language = $this->get_language(); // // } // if ($this->is_sub($language)) { foreach ( $data as $field => $value ) { if ( $field === 'slug' ) { $value = $this->sanitize_slug( $value ); } /** * Filter before a term translation field is updated. * * @param int $post_id . Original post id. * @param string $field . Field name. * @param string $value . Value. * * @from 2.0 */ update_term_meta( $term_id, Falang_Core::get_prefix( $locale ) . $field, apply_filters( 'falang_admin_update_term', $value, $term_id, $field ) ); } // } } /** * Display the ajax string translation popup */ public function ajax_falang_string_translation() { //TODO throw error if no context $row = empty( $_GET['row'] ) ? '' : sanitize_key($_GET['row']); $language = empty($_GET['language']) ? '' : sanitize_html_class($_GET['language']); //locale $aData = array( 'form_action' => 'admin.php?page=falang-strings', 'cancel_action' => 'admin.php?page=falang-strings', 'row' => $row, 'target_language_locale' => $language, ); $this->display( 'falang_string_translation_page', $aData ); wp_die(); } /** * Display the ajax string translation popup */ public function ajax_falang_option_translation() { $name = empty( $_GET['name'] ) ? '' : sanitize_key($_GET['name']); $language = empty( $_GET['language'] ) ? '' : sanitize_html_class($_GET['language']); //locale $aData = array( 'form_action' => 'admin.php?page=falang-options', 'cancel_action' => 'admin.php?page=falang-options', 'name' => $name, 'target_language_locale' => $language, ); $this->display( 'falang_option_translation_page', $aData ); wp_die(); } public function display_edit_language_page( $edit_lang_id = null ) { $falangModel = new \Falang\Model\Falang_Model(); $aData = array( 'form_action' => 'admin.php?page=falang-language', 'cancel_action' => 'admin.php?page=falang-language&action=cancel', 'predefined_language_list' => $falangModel->get_predefined_languages(), 'flag_list' => $falangModel->get_flags_list() ); if ( ! empty( $edit_lang_id ) ) { $language = $falangModel->get_language( (int) $edit_lang_id ); //add language to the data on edit $aData['language'] = $language; $aData['delete_action'] = 'admin.php?page=falang-language&action=delete&language=' . $edit_lang_id; } $this->display( 'language_edit_page', $aData ); } public function display_edit_post_page() { $post_id = intval($_GET['post_id']); $post = get_post( $post_id ); $target_language_locale = sanitize_html_class($_GET['language']);//local in the url $falang_post = new \Falang\Core\Post( $post_id ); $translated_metakeys = $falang_post->get_post_type_metakeys($post->post_type); $aData = array( 'form_action' => 'admin.php?page=falang-translation', 'cancel_action' => 'admin.php?page=falang-translation', 'target_language_locale' => $target_language_locale, 'original_post_id' => $post_id, 'post' => $falang_post, 'translated_metakeys' => $translated_metakeys ); $this->display( 'edit_post_page', $aData ); } public function display_edit_menu_page() { $post_id = intval($_GET['post_id']); $post = get_post( $post_id ); $target_language_locale = sanitize_html_class( $_GET['language'] );//local in the url $falang_post = new \Falang\Core\Post( $post_id ); $translated_metakeys = $falang_post->get_post_type_option( $post->post_type, 'fields', $falang_post->fields );//TODO remove the $this->fields must be in option $aData = array( 'form_action' => 'admin.php?page=falang-menus', 'cancel_action' => 'admin.php?page=falang-menus', 'target_language_locale' => $target_language_locale, 'original_post_id' => $post_id, 'post' => $falang_post ); $this->display( 'edit_menu_page', $aData ); } public function display_settings_page() { $aData = array( 'form_action' => 'admin.php?page=falang-settings', 'version' => $this->get_version(), ); $this->display( 'settings_page', $aData ); } public function display_strings_page() { $aData = array( 'form_action' => 'admin.php?page=falang-strings' ); $this->display( 'strings_page', $aData ); } public function display_options_page() { $aData = array( 'form_action' => 'admin.php?page=falang-options' ); $this->display( 'options_page', $aData ); } // display view //TODO put in parent class public function display( $insView, $inaData = array(), $echo = true ) { $sFile = FALANG_ADMIN . '/views/' . $insView . '.php'; if ( ! is_file( $sFile ) ) { //display message return false; } if ( count( $inaData ) > 0 ) { extract( $inaData, EXTR_PREFIX_ALL, 'falang' ); } include_once $sFile; // ob_start(); // include( $sFile ); // $sContents = ob_get_contents(); // ob_end_clean(); // // if ($echo) { // echo $sContents; // return true; // } else { // return $sContents; // } } /** * Re-register translatable custom post without admin UI * * @hook 'init' * @from 1.5 */ // //TODO not used yet // public function register_extra_post() { // // $cpts = get_post_types( array( // 'show_ui' => false // ), 'objects' ); // // foreach ( $cpts as $cpt ) { // // if ( $this->model->is_post_type_translatable( $cpt->name ) ) { // } // } // } //ajax show post settings options public function ajax_settings_post_options() { $falang_post = new \Falang\Core\Post(); $post_type = empty( $_GET['post_type'] ) ? 'post' : sanitize_key($_GET['post_type']); $post_type_obj = get_post_type_object( $post_type ); $meta_keys = $falang_post->query_post_type_metakeys( $post_type ); $registered_meta_keys = get_registered_meta_keys( 'posts' ); //if ( ! current_user_can('manage_falang_options') ) return; $aData = array( 'form_action' => 'admin.php?page=falang-settings', 'post_type' => $post_type, 'post_type_obj' => $post_type_obj, 'meta_keys' => $meta_keys, 'registered_meta_keys' => $registered_meta_keys ); $this->display( 'settings_post_option_page', $aData ); exit(); } public function ajax_settings_taxonmy_options() { if ( isset( $_GET['taxonomy'] ) ) { $falang_taxonomy = new Falang\Core\Taxonomy( null, $this->model ); $taxonomy = sanitize_key( $_GET['taxonomy'] ); $taxonomy_obj = get_taxonomy( $taxonomy ); $meta_keys = $falang_taxonomy->query_taxonomy_metakeys( $taxonomy ); $registered_meta_keys = get_registered_meta_keys( 'term' ); $aData = array( 'form_action' => 'admin.php?page=falang-settings', 'taxonomy' => $taxonomy, 'taxonomy_obj' => $taxonomy_obj, 'meta_keys' => $meta_keys, 'registered_meta_keys' => $registered_meta_keys ); $this->display( 'settings_taxonomy_option_page', $aData ); exit(); } } /** * Display the ajax term translation popup */ public function ajax_falang_term_translation() { //TODO throw error if no context $context = empty( $_GET['context'] ) ? '' : sanitize_key($_GET['context']); $taxonomy = empty( $_GET['taxonomy'] ) ? '' : sanitize_key($_GET['taxonomy']); $id = empty( $_GET['id'] ) ? '' : intval($_GET['id']); $language = empty( $_GET['language'] ) ? '' : sanitize_html_class($_GET['language']); //locale $falang_taxonomy = new \Falang\Core\Taxonomy( null, $this->model ); $taxonomy_fields = $falang_taxonomy->get_taxonomy_fields( $taxonomy ); $aData = array( 'form_action' => 'admin.php?page=falang-terms', 'cancel_action' => 'admin.php?page=falang-terms', 'context' => $context, 'taxonomy' => $taxonomy, 'id' => $id, 'target_language_locale' => $language, 'taxonomy_fields' => $taxonomy_fields, ); $this->display( 'falang_term_translation_page', $aData ); wp_die(); } //ajax update post options public function ajax_update_settings_post_options() { // build response $response = new stdClass(); $response->success = $this->save_settings_options(); $this->return_json( $response ); exit(); } public function return_json( $data ) { header( 'content-type: application/json; charset=utf-8' ); echo json_encode( $data ); } public function ajax_update_settings_taxonomy_options() { // build response $response = new stdClass(); $response->success = $this->save_settings_options(); $this->return_json( $response ); exit(); } public function ajax_update_term_translation() { // build response $response = new stdClass(); $response->success = $this->save_term_translation(); $this->return_json( $response ); exit(); } public function ajax_update_string_translation() { // build response $response = new stdClass(); $response->success = $this->save_string_translation(); $this->return_json( $response ); exit(); } public function ajax_update_option_translation() { // build response $response = new stdClass(); $response->success = $this->save_option_translation(); $this->return_json( $response ); exit(); } /** * set post_type translatable by default * * @filter "falang_default-$post_type" * @from 1.0 */ public function set_post_type_translatable( $defaults ) { $defaults['translatable'] = true; return $defaults; } /** * Flush rewrite rules * * @hook 'wp_loaded' * @from 2.0 */ public function flush_rewrite_rules() { if ($this->model->get_option('need_flush')) { $this->disable_translate_home_url = true; flush_rewrite_rules(); $this->disable_translate_home_url = false; } } /** * Remove 'need_flush' flag after flush * * @hook for 'generate_rewrite_rules' * @from 2.0 */ public function generate_rewrite_rules($wp_rewrite) { $this->model->update_option('need_flush', 0); } /** * set default nav_menu_item options * * @filter "falang_default-$post_type" * @from 1.0 */ public function nav_menu_item_default_options( $defaults ) { $defaults['meta_keys'] = array( 'falang_hide', '_menu_item_url' ); return $defaults; } /** * Force 'falang_hide' to appear in meta_keys list * * @filter "falang_post_type_metakeys" * @from 1.0 */ public function nav_menu_item_metakeys( $value, $post_type ) { if ( $post_type === 'nav_menu_item' && empty( $value['falang_hide'] ) ) { $value['falang_hide'] = true; } return $value; } /* * Get the list of the term to be translated * $args taxonomies filter * */ //TODO not working yet. public function get_terms_to_translate( $args = array()) { $items = array(); remove_filter( 'get_term', '' ); foreach ( (array) get_taxonomies( array(), 'objects' ) as $taxonomy ) { $tax_labels = get_taxonomy_labels( $taxonomy ); $terms = get_terms( array( 'taxonomy' => $taxonomy->name, 'orderby' => 'slug', 'hide_empty' => false, ) ); foreach ( (array) $terms as $term ) { $name = sprintf( '%s:%d', $taxonomy->name, $term->term_id ); $items[] = array( 'id' => $term->term_id, 'name' => $name, 'original' => $term->name, // 'translated' => bogo_translate( $name, $taxonomy->name, // $term->name ), 'context' => $tax_labels->name, 'taxonomy' => $term->taxonomy, ); } } //add_filter( 'get_term', 'falang_get_term_filter', 10, 2 ); //$items = apply_filters( 'bogo_terms_translation', $items, $locale ); //TODO put this before with stdclass //convert array to object $items = json_decode( json_encode( $items ), false ); return $items; } public function get_options_to_translate() { global $wpdb; $sql_blacklist = "option_name NOT IN ('" . implode( "', '", array_map( 'esc_sql', $this->get_options_blacklist() ) ) . "')"; $options = $wpdb->get_results( " SELECT option_name as name, option_value as value FROM $wpdb->options WHERE option_name NOT LIKE '_transient%' AND option_name NOT LIKE '_site_transient%' AND $sql_blacklist ORDER BY option_name" ); return $options; } /* * List of options that should not be translated * * @from 1.0 */ private function get_options_blacklist() { return apply_filters( 'falang_options_blacklist', array( 'falang', 'active_plugins', 'admin_email', 'auto_core_update_notified', 'avatar_default', 'avatar_rating', 'blacklist_keys', 'blog_charset', 'blog_public', 'can_compress_scripts', 'category_base', 'close_comments_days_old', 'close_comments_for_old_posts', 'comment_max_links', 'comment_moderation', 'comment_order', 'comment_registration', 'comment_whitelist', 'comments_notify', 'comments_per_page', 'cron', 'db_upgraded', 'db_version', 'default_category', 'default_comment_status', 'default_comments_page', 'default_email_category', 'default_link_category', 'default_ping_status', 'default_pingback_flag', 'default_post_format', 'default_role', 'finished_splitting_shared_terms', 'gmt_offset', 'hack_file', 'home', 'html_type', 'image_default_align', 'image_default_link_type', 'image_default_size', 'initial_db_version', 'large_size_h', 'large_size_w', 'link_manager_enabled', 'links_updated_date_format', 'mailserver_login', 'mailserver_pass', 'mailserver_port', 'mailserver_url', 'medium_large_size_h', 'medium_large_size_w', 'medium_size_h', 'medium_size_w', 'moderation_keys', 'moderation_notify', 'nav_menu_options', 'page_comments', 'page_for_posts', 'page_on_front', 'permalink_structure', 'ping_sites', 'posts_per_page', 'posts_per_rss', 'recently_activated', 'recently_edited', 'require_name_email', 'rewrite_rules', 'rss_use_excerpt', 'show_avatars', 'show_on_front', 'sidebars_widgets', 'site_icon', 'siteurl', 'start_of_week', 'sticky_posts', 'stylesheet', 'sublanguage_options', 'sublanguage_translations', 'tag_base', 'template', 'theme_mods_twentyfifteen', 'thread_comments', 'thread_comments_depth', 'thumbnail_crop', 'thumbnail_size_h', 'thumbnail_size_w', 'timezone_string', 'uninstall_plugins', 'upload_path', 'upload_url_path', 'uploads_use_yearmonth_folders', 'use_balanceTags', 'use_smilies', 'use_trackback', 'users_can_register', 'wp_user_roles', 'WPLANG' ) ); } }