first commit
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
class WPML_TM_AMS_ATE_Console_Section_Factory implements IWPML_TM_Admin_Section_Factory {
|
||||
|
||||
/**
|
||||
* Returns an instance of a class implementing \IWPML_TM_Admin_Section.
|
||||
*
|
||||
* @return \IWPML_TM_Admin_Section
|
||||
*/
|
||||
public function create() {
|
||||
if ( wpml_tm_ams_ate_factories()->is_ate_active() ) {
|
||||
return WPML\Container\make( 'WPML_TM_AMS_ATE_Console_Section' );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
<?php
|
||||
|
||||
use WPML\FP\Fns;
|
||||
use WPML\FP\Obj;
|
||||
|
||||
/**
|
||||
* It handles the TM section responsible for displaying the AMS/ATE console.
|
||||
*
|
||||
* This class takes care of the following:
|
||||
* - enqueuing the external script which holds the React APP
|
||||
* - adding the ID to the enqueued script (as it's required by the React APP)
|
||||
* - adding an inline script to initialize the React APP
|
||||
*
|
||||
* @author OnTheGo Systems
|
||||
*/
|
||||
class WPML_TM_AMS_ATE_Console_Section implements IWPML_TM_Admin_Section {
|
||||
const ATE_APP_ID = 'eate_widget';
|
||||
const TAB_ORDER = 10000;
|
||||
const CONTAINER_SELECTOR = '#ams-ate-console';
|
||||
const TAB_SELECTOR = '.wpml-tabs .nav-tab.nav-tab-active.nav-tab-ate-ams';
|
||||
const SLUG = 'ate-ams';
|
||||
|
||||
/**
|
||||
* An instance of \SitePress.
|
||||
*
|
||||
* @var SitePress The instance of \SitePress.
|
||||
*/
|
||||
private $sitepress;
|
||||
/**
|
||||
* Instance of WPML_TM_ATE_AMS_Endpoints.
|
||||
*
|
||||
* @var WPML_TM_ATE_AMS_Endpoints
|
||||
*/
|
||||
private $endpoints;
|
||||
|
||||
/**
|
||||
* Instance of WPML_TM_ATE_Authentication.
|
||||
*
|
||||
* @var WPML_TM_ATE_Authentication
|
||||
*/
|
||||
private $auth;
|
||||
|
||||
/**
|
||||
* Instance of WPML_TM_AMS_API.
|
||||
*
|
||||
* @var WPML_TM_AMS_API
|
||||
*/
|
||||
private $ams_api;
|
||||
|
||||
/**
|
||||
* WPML_TM_AMS_ATE_Console_Section constructor.
|
||||
*
|
||||
* @param SitePress $sitepress The instance of \SitePress.
|
||||
* @param WPML_TM_ATE_AMS_Endpoints $endpoints The instance of WPML_TM_ATE_AMS_Endpoints.
|
||||
* @param WPML_TM_ATE_Authentication $auth The instance of WPML_TM_ATE_Authentication.
|
||||
* @param WPML_TM_AMS_API $ams_api The instance of WPML_TM_AMS_API.
|
||||
*/
|
||||
public function __construct( SitePress $sitepress, WPML_TM_ATE_AMS_Endpoints $endpoints, WPML_TM_ATE_Authentication $auth, WPML_TM_AMS_API $ams_api ) {
|
||||
$this->sitepress = $sitepress;
|
||||
$this->endpoints = $endpoints;
|
||||
$this->auth = $auth;
|
||||
$this->ams_api = $ams_api;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a value which will be used for sorting the sections.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function get_order() {
|
||||
return self::TAB_ORDER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the unique slug of the sections which is used to build the URL for opening this section.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_slug() {
|
||||
return self::SLUG;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns one or more capabilities required to display this section.
|
||||
*
|
||||
* @return string|array
|
||||
*/
|
||||
public function get_capabilities() {
|
||||
return array( WPML_Manage_Translations_Role::CAPABILITY, 'manage_options' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the caption to display in the section.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_caption() {
|
||||
return __( 'Translation Tools', 'wpml-translation-management' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the callback responsible for rendering the content of the section.
|
||||
*
|
||||
* @return callable
|
||||
*/
|
||||
public function get_callback() {
|
||||
return array( $this, 'render' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to extend the logic for displaying/hiding the section.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_visible() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the content of the section.
|
||||
*/
|
||||
public function render() {
|
||||
echo '<div id="ams-ate-console"></div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is hooked to the `admin_enqueue_scripts` action.
|
||||
*
|
||||
* @param string $hook The current page.
|
||||
*/
|
||||
public function admin_enqueue_scripts( $hook ) {
|
||||
if ( ! $this->is_ate_console_tab() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
wp_enqueue_script( self::ATE_APP_ID, $this->endpoints->get_base_url( WPML_TM_ATE_AMS_Endpoints::SERVICE_AMS ) . '/mini_app/main.js', [], WPML_TM_VERSION, true );
|
||||
$this->add_initialization_script();
|
||||
}
|
||||
|
||||
/**
|
||||
* It returns true if the current page and tab are the ATE Console.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_ate_console_tab() {
|
||||
return array_key_exists( 'sm', $_GET ) && array_key_exists( 'page', $_GET )
|
||||
&& filter_var( $_GET['sm'], FILTER_SANITIZE_STRING ) === self::SLUG
|
||||
&& filter_var( $_GET['page'], FILTER_SANITIZE_STRING ) === WPML_TM_FOLDER . '/menu/main.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* It returns the list of all translatable post types.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_post_types_data() {
|
||||
$translatable_types = $this->sitepress->get_translatable_documents( true );
|
||||
|
||||
$data = [];
|
||||
if ( $translatable_types ) {
|
||||
foreach ( $translatable_types as $name => $post_type ) {
|
||||
$data[ esc_js( $name ) ] = [
|
||||
'labels' => [
|
||||
'name' => esc_js( $post_type->labels->name ),
|
||||
'singular_name' => esc_js( $post_type->labels->singular_name ),
|
||||
],
|
||||
'description' => esc_js( $post_type->description ),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* It returns the current user's language.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function get_user_admin_language() {
|
||||
return $this->sitepress->get_user_admin_language( wp_get_current_user()->ID );
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the React APP.
|
||||
*/
|
||||
private function add_initialization_script() {
|
||||
$registration_data = $this->ams_api->get_registration_data();
|
||||
|
||||
$fields = [ 'code', 'english_name', 'native_name', 'default_locale', 'encode_url', 'tag' ];
|
||||
$languages = Fns::map( Obj::pick( $fields ), $this->sitepress->get_active_languages() );
|
||||
|
||||
$app_constructor = [
|
||||
'host' => esc_js( $this->endpoints->get_base_url( WPML_TM_ATE_AMS_Endpoints::SERVICE_AMS ) ),
|
||||
'wpml_host' => esc_js( get_site_url() ),
|
||||
'wpml_home' => esc_js( get_home_url() ),
|
||||
'secret_key' => esc_js( $registration_data['secret'] ),
|
||||
'shared_key' => esc_js( $registration_data['shared'] ),
|
||||
'status' => esc_js( $registration_data['status'] ),
|
||||
'tm_email' => esc_js( wp_get_current_user()->user_email ),
|
||||
'website_uuid' => esc_js( $this->auth->get_site_id() ),
|
||||
'site_key' => esc_js( WP_Installer()->get_site_key( 'wpml' ) ),
|
||||
'tab' => self::TAB_SELECTOR,
|
||||
'container' => self::CONTAINER_SELECTOR,
|
||||
'post_types' => $this->get_post_types_data(),
|
||||
'ui_language' => esc_js( $this->get_user_admin_language() ),
|
||||
'restNonce' => wp_create_nonce( 'wp_rest' ),
|
||||
'languages' => $languages,
|
||||
];
|
||||
|
||||
wp_add_inline_script( self::ATE_APP_ID, 'LoadEateWidget(' . wp_json_encode( $app_constructor, JSON_PRETTY_PRINT ) . ');', 'after' );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
|
||||
use \WPML\TM\Menu\TranslationServices\SectionFactory;
|
||||
|
||||
/**
|
||||
* It handles the admin sections shown in the TM page.
|
||||
*
|
||||
* @author OnTheGo Systems
|
||||
*/
|
||||
class WPML_TM_Admin_Sections {
|
||||
/**
|
||||
* It stores the tab items.
|
||||
*
|
||||
* @var array The tab items.
|
||||
*/
|
||||
private $tab_items = array();
|
||||
|
||||
/**
|
||||
* It stores the tab items.
|
||||
*
|
||||
* @var IWPML_TM_Admin_Section[] The admin sections.
|
||||
*/
|
||||
private $admin_sections = array();
|
||||
/** @var array */
|
||||
private $items_urls = array();
|
||||
|
||||
/**
|
||||
* It adds the hooks.
|
||||
*/
|
||||
public function init_hooks() {
|
||||
/**
|
||||
* We have to defer creating of Admin Section to be sure that `WP_Installer` class is already loaded
|
||||
*/
|
||||
add_action( 'init', [ $this, 'init_sections' ] );
|
||||
}
|
||||
|
||||
public function init_sections() {
|
||||
foreach ( $this->get_admin_sections() as $section ) {
|
||||
$this->tab_items[ $section->get_slug() ] = [
|
||||
'caption' => $section->get_caption(),
|
||||
'current_user_can' => $section->get_capabilities(),
|
||||
'callback' => $section->get_callback(),
|
||||
'order' => $section->get_order(),
|
||||
];
|
||||
add_action( 'admin_enqueue_scripts', [ $section, 'admin_enqueue_scripts' ] );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \IWPML_TM_Admin_Section[]
|
||||
*/
|
||||
private function get_admin_sections() {
|
||||
if ( ! $this->admin_sections ) {
|
||||
foreach ( $this->get_admin_section_factories() as $factory ) {
|
||||
if ( in_array( 'IWPML_TM_Admin_Section_Factory', class_implements( $factory ), true ) ) {
|
||||
$sections_factory = new $factory();
|
||||
/**
|
||||
* Sections are defined through classes extending `\IWPML_TM_Admin_Section_Factory`.
|
||||
*
|
||||
* @var \IWPML_TM_Admin_Section_Factory $sections_factory An instance of the section factory.
|
||||
*/
|
||||
$section = $sections_factory->create();
|
||||
|
||||
if ( $section && in_array( 'IWPML_TM_Admin_Section', class_implements( $section ), true ) && $section->is_visible() ) {
|
||||
$this->admin_sections[ $section->get_slug() ] = $section;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->admin_sections;
|
||||
}
|
||||
|
||||
/**
|
||||
* It returns the tab items.
|
||||
*
|
||||
* @return array The tab items.
|
||||
*/
|
||||
public function get_tab_items() {
|
||||
return $this->tab_items;
|
||||
}
|
||||
|
||||
/**
|
||||
* It returns and filters the admin sections in the TM page.
|
||||
*
|
||||
* @return array<\WPML\TM\Menu\TranslationServices\SectionFactory|\WPML_TM_AMS_ATE_Console_Section_Factory|\WPML_TM_Translation_Roles_Section_Factory>
|
||||
*/
|
||||
private function get_admin_section_factories() {
|
||||
$admin_sections_factories = array(
|
||||
WPML_TM_Translation_Roles_Section_Factory::class,
|
||||
SectionFactory::class,
|
||||
WPML_TM_AMS_ATE_Console_Section_Factory::class,
|
||||
);
|
||||
|
||||
return apply_filters( 'wpml_tm_admin_sections_factories', $admin_sections_factories );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the URL of a tab item or an empty string if it cannot be found.
|
||||
*
|
||||
* @param string $slug
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_item_url( $slug ) {
|
||||
if ( $this->get_section( $slug ) ) {
|
||||
if ( ! array_key_exists( $slug, $this->items_urls ) ) {
|
||||
$this->items_urls[ $slug ] = admin_url( 'admin.php?page=' . WPML_TM_FOLDER . WPML_Translation_Management::PAGE_SLUG_MANAGEMENT . '&sm=' . $slug );
|
||||
}
|
||||
|
||||
return $this->items_urls[ $slug ];
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance of IWPML_TM_Admin_Section from its slug or null if it cannot be found.
|
||||
*
|
||||
* @param string $slug
|
||||
*
|
||||
* @return \IWPML_TM_Admin_Section|null
|
||||
*/
|
||||
public function get_section( $slug ) {
|
||||
$sections = $this->get_admin_sections();
|
||||
if ( array_key_exists( $slug, $sections ) ) {
|
||||
return $sections[ $slug ];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,280 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @author OnTheGo Systems
|
||||
*/
|
||||
class WPML_TM_Scripts_Factory {
|
||||
private $ate;
|
||||
private $ams_api;
|
||||
private $auth;
|
||||
private $endpoints;
|
||||
private $http;
|
||||
private $strings;
|
||||
|
||||
public function init_hooks() {
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
|
||||
add_filter( 'wpml_tm_translators_view_strings', array( $this, 'filter_translators_view_strings' ), 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function admin_enqueue_scripts() {
|
||||
$this->register_otgs_notices();
|
||||
|
||||
wp_register_script(
|
||||
'wpml-tm-settings',
|
||||
WPML_TM_URL . '/dist/js/settings/app.js',
|
||||
array(),
|
||||
WPML_TM_VERSION
|
||||
);
|
||||
wp_register_script(
|
||||
'ate-translation-queue',
|
||||
WPML_TM_URL . '/dist/js/translationQueue/app.js',
|
||||
array(),
|
||||
false,
|
||||
true
|
||||
);
|
||||
wp_register_script(
|
||||
'ate-translation-editor-classic',
|
||||
WPML_TM_URL . '/dist/js/ate-translation-editor-classic/app.js',
|
||||
array(),
|
||||
false,
|
||||
true
|
||||
);
|
||||
|
||||
if ( WPML_TM_Page::is_tm_dashboard() ) {
|
||||
$this->localize_script( 'wpml-tm-dashboard' );
|
||||
wp_enqueue_script( 'wpml-tm-dashboard' );
|
||||
}
|
||||
if ( WPML_TM_Page::is_settings() || WPML_TM_Page::is_tm_translators() ) {
|
||||
wp_enqueue_style( 'otgs-notices' );
|
||||
$this->localize_script( 'wpml-tm-settings' );
|
||||
wp_enqueue_script( 'wpml-tm-settings' );
|
||||
|
||||
$this->create_ate()
|
||||
->init_hooks();
|
||||
}
|
||||
if ( WPML_TM_Page::is_translation_queue() && WPML_TM_ATE_Status::is_enabled() ) {
|
||||
$this->localize_script( 'ate-translation-queue' );
|
||||
wp_enqueue_script( 'ate-translation-queue' );
|
||||
wp_enqueue_script( 'ate-translation-editor-classic' );
|
||||
wp_enqueue_style( 'otgs-notices' );
|
||||
}
|
||||
|
||||
if ( WPML_TM_Page::is_job_list() ) {
|
||||
$this->localize_jobs_list();
|
||||
}
|
||||
if ( WPML_TM_Page::is_dashboard() ) {
|
||||
$this->load_pick_up_box_scripts();
|
||||
}
|
||||
|
||||
if ( WPML_TM_Page::is_settings() ) {
|
||||
wp_enqueue_style(
|
||||
'wpml-tm-multilingual-content-setup',
|
||||
WPML_TM_URL . '/res/css/multilingual-content-setup.css',
|
||||
array(),
|
||||
WPML_TM_VERSION
|
||||
);
|
||||
}
|
||||
|
||||
if ( WPML_TM_Page::is_notifications_page() ) {
|
||||
wp_enqueue_style(
|
||||
'wpml-tm-translation-notifications',
|
||||
WPML_TM_URL . '/res/css/translation-notifications.css',
|
||||
array(),
|
||||
WPML_TM_VERSION
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function load_pick_up_box_scripts() {
|
||||
wp_enqueue_style( 'otgs-notices' );
|
||||
|
||||
wp_register_script(
|
||||
'wpml-tm-dashboard',
|
||||
WPML_TM_URL . '/dist/js/translationDashboard/app.js',
|
||||
array(),
|
||||
false,
|
||||
true
|
||||
);
|
||||
|
||||
global $iclTranslationManagement;
|
||||
|
||||
$this->localize_script(
|
||||
'wpml-tm-dashboard',
|
||||
array(
|
||||
'strings' => array(
|
||||
'numberOfTranslationStringsSingle' => __( '%d translation job', 'wpml-translation-management' ),
|
||||
'numberOfTranslationStringsMulti' => __( '%d translation jobs', 'wpml-translation-management' ),
|
||||
'stringsSentToTranslationSingle' => __(
|
||||
'%s has been sent to remote translators',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
'stringsSentToTranslationMulti' => __(
|
||||
'%s have been sent to remote translators',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
|
||||
'buttonText' => __( 'Check status and get translations', 'wpml-translation-management' ),
|
||||
'progressText' => __(
|
||||
"Checking translation jobs status. Please don't close this page!",
|
||||
'wpml-translation-management'
|
||||
),
|
||||
'progressJobsCount' => __( 'You are downloading %d jobs', 'wpml-translation-management' ),
|
||||
|
||||
'statusChecked' => __( 'Status checked:', 'wpml-translation-management' ),
|
||||
'dismissNotice' => __( 'Dismiss this notice.', 'wpml-translation-management' ),
|
||||
'noTranslationsDownloaded' => __(
|
||||
'none of your translation jobs have been completed',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
'translationsDownloaded' => __(
|
||||
'%d translation jobs have been finished and applied.',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
|
||||
'errorMessage' => __(
|
||||
'A communication error has appeared. Please wait a few minutes and try again.',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
|
||||
'lastCheck' => __( 'Last check: %s', 'wpml-translation-management' ),
|
||||
'never' => __( 'never', 'wpml-translation-management' ),
|
||||
),
|
||||
'debug' => defined( 'WPML_POLLING_BOX_DEBUG_MODE' ) && WPML_POLLING_BOX_DEBUG_MODE,
|
||||
'statusIcons' => array(
|
||||
'completed' => $iclTranslationManagement->status2icon_class( ICL_TM_COMPLETE, false ),
|
||||
'canceled' => $iclTranslationManagement->status2icon_class( ICL_TM_NOT_TRANSLATED, false ),
|
||||
'progress' => $iclTranslationManagement->status2icon_class( ICL_TM_IN_PROGRESS, false ),
|
||||
'needsUpdate' => $iclTranslationManagement->status2icon_class( ICL_TM_NEEDS_UPDATE, false ),
|
||||
),
|
||||
)
|
||||
);
|
||||
wp_enqueue_script( 'wpml-tm-dashboard' );
|
||||
}
|
||||
|
||||
public function register_otgs_notices() {
|
||||
if ( ! wp_style_is( 'otgs-notices', 'registered' ) ) {
|
||||
wp_register_style(
|
||||
'otgs-notices',
|
||||
ICL_PLUGIN_URL . '/res/css/otgs-notices.css',
|
||||
array( 'sitepress-style' )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function localize_jobs_list() {
|
||||
$script_data = new WPML_TM_Jobs_List_Script_Data();
|
||||
|
||||
$this->localize_script( 'translation-remote-jobs', $script_data->get() );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $handle
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function localize_script( $handle, $additional_data = array() ) {
|
||||
$data = array(
|
||||
'hasATEEnabled' => WPML_TM_ATE_Status::is_enabled(),
|
||||
'restUrl' => untrailingslashit( rest_url() ),
|
||||
'restNonce' => wp_create_nonce( 'wp_rest' ),
|
||||
'ate' => $this->create_ate()
|
||||
->get_script_data(),
|
||||
'currentUser' => null,
|
||||
);
|
||||
|
||||
$data = array_merge( $data, $additional_data );
|
||||
|
||||
$current_user = wp_get_current_user();
|
||||
if ( $current_user && $current_user->ID > 0 ) {
|
||||
$data['currentUser'] = $current_user;
|
||||
}
|
||||
|
||||
wp_localize_script( $handle, 'WPML_TM_SETTINGS', $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return WPML_TM_MCS_ATE
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function create_ate() {
|
||||
if ( ! $this->ate ) {
|
||||
$this->ate = new WPML_TM_MCS_ATE(
|
||||
$this->get_authentication(),
|
||||
$this->get_endpoints(),
|
||||
$this->create_ate_strings()
|
||||
);
|
||||
}
|
||||
|
||||
return $this->ate;
|
||||
}
|
||||
|
||||
private function get_authentication() {
|
||||
if ( ! $this->auth ) {
|
||||
$this->auth = new WPML_TM_ATE_Authentication();
|
||||
}
|
||||
|
||||
return $this->auth;
|
||||
}
|
||||
|
||||
private function get_endpoints() {
|
||||
if ( ! $this->endpoints ) {
|
||||
$this->endpoints = WPML\Container\make( 'WPML_TM_ATE_AMS_Endpoints' );
|
||||
}
|
||||
|
||||
return $this->endpoints;
|
||||
}
|
||||
|
||||
private function create_ate_strings() {
|
||||
if ( ! $this->strings ) {
|
||||
$this->strings = new WPML_TM_MCS_ATE_Strings( $this->get_authentication(), $this->get_endpoints() );
|
||||
}
|
||||
|
||||
return $this->strings;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $strings
|
||||
* @param bool $all_users_have_subscription
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function filter_translators_view_strings( array $strings, $all_users_have_subscription ) {
|
||||
if ( WPML_TM_ATE_Status::is_enabled() ) {
|
||||
$strings['ate'] = $this->create_ate_strings()
|
||||
->get_status_HTML(
|
||||
$this->get_ate_activation_status(),
|
||||
$all_users_have_subscription
|
||||
);
|
||||
}
|
||||
|
||||
return $strings;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
private function get_ate_activation_status() {
|
||||
$status = $this->create_ate_strings()
|
||||
->get_status();
|
||||
if ( $status !== WPML_TM_ATE_Authentication::AMS_STATUS_ACTIVE ) {
|
||||
$status = $this->fetch_and_update_ate_activation_status();
|
||||
}
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
private function fetch_and_update_ate_activation_status() {
|
||||
$ams_api = WPML\Container\make( WPML_TM_AMS_API::class );
|
||||
$ams_api->get_status();
|
||||
|
||||
return $this->create_ate_strings()
|
||||
->get_status();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @author OnTheGo Systems
|
||||
*/
|
||||
class WPML_TM_Custom_XML_AJAX extends WPML_TM_AJAX {
|
||||
const AJAX_ACTION_BASE = 'wpml-tm-custom-xml';
|
||||
|
||||
private $custom_xml;
|
||||
private $reload_config_callback;
|
||||
private $validate;
|
||||
|
||||
function __construct( WPML_Custom_XML $custom_xml, WPML_XML_Config_Validate $validate, $reload_config_callback = null ) {
|
||||
$this->custom_xml = $custom_xml;
|
||||
$this->validate = $validate;
|
||||
$this->reload_config_callback = $reload_config_callback;
|
||||
}
|
||||
|
||||
function validate_content() {
|
||||
if ( $this->is_valid_request() ) {
|
||||
$content = $this->get_content();
|
||||
|
||||
if ( $content && ! $this->validate->from_string( $content ) && $this->validate->get_errors() ) {
|
||||
$xml_errors = $this->validate->get_errors();
|
||||
foreach ( $xml_errors as $index => $xml_error ) {
|
||||
if ( 'The document has no document element.' === trim( $xml_error->message ) ) {
|
||||
unset( $xml_errors[ $index ] );
|
||||
}
|
||||
}
|
||||
$errors = array( __( 'The XML is not valid:', 'wpml-translation-management' ), $xml_errors );
|
||||
wp_send_json_error( $errors );
|
||||
} else {
|
||||
wp_send_json_success( __( 'The XML is valid.', 'wpml-translation-management' ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function save_content() {
|
||||
if ( $this->is_valid_request() ) {
|
||||
$content = $this->get_content();
|
||||
|
||||
if ( null !== $content ) {
|
||||
$this->custom_xml->set( $content );
|
||||
if ( $this->reload_config_callback ) {
|
||||
call_user_func( $this->reload_config_callback );
|
||||
}
|
||||
wp_send_json_success( __( 'The XML has been saved.', 'wpml-translation-management' ) );
|
||||
} else {
|
||||
wp_send_json_error( __( 'The XML could not be saved.', 'wpml-translation-management' ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function get_content() {
|
||||
$content = null;
|
||||
|
||||
if ( array_key_exists( 'content', $_POST ) ) {
|
||||
$content = stripcslashes( $_POST['content'] );
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @author OnTheGo Systems
|
||||
*/
|
||||
class WPML_TM_Custom_XML_Factory {
|
||||
|
||||
private $custom_xml;
|
||||
|
||||
function __construct() {
|
||||
$this->custom_xml = new WPML_Custom_XML();
|
||||
}
|
||||
|
||||
public function create_ui() {
|
||||
$template_paths = array(
|
||||
WPML_TM_PATH . '/templates/custom-xml/',
|
||||
);
|
||||
|
||||
$template_loader = new WPML_Twig_Template_Loader( $template_paths );
|
||||
|
||||
return new WPML_TM_Custom_XML_UI( $this->custom_xml, $template_loader->get_template() );
|
||||
}
|
||||
|
||||
public function create_resources( WPML_WP_API $wpml_wp_api ) {
|
||||
return new WPML_TM_Custom_XML_UI_Resources( $wpml_wp_api );
|
||||
}
|
||||
|
||||
public function create_ajax() {
|
||||
return new WPML_TM_Custom_XML_AJAX(
|
||||
$this->custom_xml,
|
||||
new WPML_XML_Config_Validate( WPML_PLUGIN_PATH . '/res/xsd/wpml-config.xsd' ),
|
||||
array( 'WPML_Config', 'load_config_run' )
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @author OnTheGo Systems
|
||||
*/
|
||||
class WPML_TM_Custom_XML_UI_Hooks {
|
||||
private $ajax;
|
||||
private $resources;
|
||||
private $ui;
|
||||
|
||||
function __construct( WPML_TM_Custom_XML_UI $ui, WPML_TM_Custom_XML_UI_Resources $resources, WPML_TM_Custom_XML_AJAX $ajax ) {
|
||||
$this->ui = $ui;
|
||||
$this->resources = $resources;
|
||||
$this->ajax = $ajax;
|
||||
}
|
||||
|
||||
function init() {
|
||||
add_filter( 'wpml_tm_tab_items', array( $this, 'add_items' ) );
|
||||
add_action( 'admin_enqueue_scripts', array( $this->resources, 'admin_enqueue_scripts' ) );
|
||||
add_action( 'wp_ajax_' . WPML_TM_Custom_XML_AJAX::AJAX_ACTION_BASE . '-validate', array( $this->ajax, 'validate_content' ) );
|
||||
add_action( 'wp_ajax_' . WPML_TM_Custom_XML_AJAX::AJAX_ACTION_BASE . '-save', array( $this->ajax, 'save_content' ) );
|
||||
}
|
||||
|
||||
function add_items( $tab_items ) {
|
||||
$tab_items['custom-xml-config']['caption'] = __( 'Custom XML Configuration', 'wpml-translation-management' );
|
||||
$tab_items['custom-xml-config']['callback'] = array( $this, 'build_content' );
|
||||
$tab_items['custom-xml-config']['current_user_can'] = 'manage_options';
|
||||
|
||||
return $tab_items;
|
||||
}
|
||||
|
||||
public function build_content() {
|
||||
echo $this->ui->show();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @author OnTheGo Systems
|
||||
*/
|
||||
class WPML_TM_Custom_XML_UI_Resources {
|
||||
private $wpml_wp_api;
|
||||
|
||||
private $wpml_tm_path;
|
||||
private $wpml_tm_url;
|
||||
|
||||
private $code_mirror_installed;
|
||||
private $vk_beautify_installed;
|
||||
|
||||
function __construct( WPML_WP_API $wpml_wp_api ) {
|
||||
$this->wpml_wp_api = $wpml_wp_api;
|
||||
$this->wpml_tm_path = $this->wpml_wp_api->constant( 'WPML_TM_PATH' );
|
||||
$this->wpml_tm_url = $this->wpml_wp_api->constant( 'WPML_TM_URL' );
|
||||
}
|
||||
|
||||
function admin_enqueue_scripts() {
|
||||
if ( $this->wpml_wp_api->is_tm_page( 'custom-xml-config', 'settings' ) ) {
|
||||
$this->add_vk_beautify();
|
||||
$this->add_code_mirror();
|
||||
|
||||
$wpml_tm_custom_xml_config_dependencies = array( 'jquery' );
|
||||
if ( $this->is_code_mirror_installed() && $this->is_syntax_highlighting_enabled() ) {
|
||||
$wpml_tm_custom_xml_config_dependencies[] = 'wpml-tm-custom-xml-editor';
|
||||
}
|
||||
|
||||
wp_register_script( 'wpml-tm-custom-xml-config', $this->wpml_tm_url . '/res/js/custom-xml-config/wpml-tm-custom-xml.js', $wpml_tm_custom_xml_config_dependencies, WPML_TM_VERSION );
|
||||
wp_register_style( 'wpml-tm-custom-xml-config', $this->wpml_tm_url . '/res/css/custom-xml.css', array(), WPML_TM_VERSION );
|
||||
|
||||
wp_enqueue_style( 'wpml-tm-custom-xml-config' );
|
||||
wp_enqueue_script( 'wpml-tm-custom-xml-config' );
|
||||
}
|
||||
}
|
||||
|
||||
private function add_code_mirror() {
|
||||
if ( $this->is_code_mirror_installed() && $this->is_syntax_highlighting_enabled() ) {
|
||||
$this->add_code_mirror_scripts();
|
||||
$this->add_code_mirror_styles();
|
||||
}
|
||||
}
|
||||
|
||||
private function add_code_mirror_scripts() {
|
||||
$this->register_main_codemirror_script();
|
||||
|
||||
$wpml_tm_custom_xml_editor_dependencies = array( $this->get_codemirror_resource_handle() );
|
||||
if ( $this->is_vk_beautify_installed() ) {
|
||||
$wpml_tm_custom_xml_editor_dependencies[] = 'vkbeautify';
|
||||
}
|
||||
|
||||
wp_register_script( 'wpml-tm-custom-xml-editor', $this->wpml_tm_url . '/res/js/custom-xml-config/wpml-tm-custom-xml-editor.js', $wpml_tm_custom_xml_editor_dependencies, WPML_TM_VERSION );
|
||||
|
||||
$modes = array(
|
||||
'xml' => 'xml/xml.js',
|
||||
);
|
||||
|
||||
$addons = array(
|
||||
'foldcode' => 'fold/foldcode.js',
|
||||
'foldgutter' => 'fold/foldgutter.js',
|
||||
'brace-fold' => 'fold/brace-fold.js',
|
||||
'xml-fold' => 'fold/xml-fold.js',
|
||||
'matchtag' => 'edit/matchtags.js',
|
||||
'matchbrackets' => 'edit/matchbrackets.js',
|
||||
'closebrackets' => 'edit/closebrackets.js',
|
||||
'closetag' => 'edit/closetag.js',
|
||||
'show-hint' => 'hint/show-hint.js',
|
||||
'xml-hint' => 'hint/xml-hint.js',
|
||||
'display-panel' => 'display/panel.js',
|
||||
);
|
||||
|
||||
foreach ( $modes as $mode => $path ) {
|
||||
wp_enqueue_script( 'codemirror-mode-' . $mode, $this->get_code_mirror_directory_url() . '/mode/' . $path, array( 'wpml-tm-custom-xml-config' ), WPML_TM_VERSION );
|
||||
}
|
||||
|
||||
foreach ( $addons as $addon => $path ) {
|
||||
wp_enqueue_script( 'codemirror-addon-' . $addon, $this->get_code_mirror_directory_url() . '/addon/' . $path, array( 'wpml-tm-custom-xml-config' ), WPML_TM_VERSION );
|
||||
}
|
||||
}
|
||||
|
||||
private function add_code_mirror_styles() {
|
||||
$this->register_main_codemirror_style();
|
||||
|
||||
$addons = array(
|
||||
'foldgutter' => 'fold/foldgutter.css',
|
||||
'xml-hint' => 'hint/show-hint.css',
|
||||
);
|
||||
foreach ( $addons as $addon => $path ) {
|
||||
wp_enqueue_style( 'codemirror-addon-' . $addon, $this->get_code_mirror_directory_url() . '/addon/' . $path, array( $this->get_codemirror_resource_handle() ), WPML_TM_VERSION );
|
||||
}
|
||||
}
|
||||
|
||||
private function add_vk_beautify() {
|
||||
if ( $this->is_vk_beautify_installed() && $this->is_syntax_highlighting_enabled() ) {
|
||||
wp_enqueue_script( 'vkbeautify', $this->wpml_tm_url . '/libraries/vkBeautify/vkbeautify.js', array(), WPML_TM_VERSION );
|
||||
}
|
||||
}
|
||||
|
||||
private function register_main_codemirror_style() {
|
||||
if ( ! $this->is_codemirror_in_wp_core() ) {
|
||||
wp_register_style( $this->get_codemirror_resource_handle(), $this->get_code_mirror_directory_url() . '/lib/codemirror.css', array( 'wpml-tm-custom-xml-config' ), WPML_TM_VERSION );
|
||||
}
|
||||
wp_enqueue_style( 'codemirror-theme', $this->get_code_mirror_directory_url() . '/theme/dracula.css', array( $this->get_codemirror_resource_handle() ), WPML_TM_VERSION );
|
||||
}
|
||||
|
||||
private function register_main_codemirror_script() {
|
||||
if ( ! $this->is_codemirror_in_wp_core() ) {
|
||||
wp_register_script( $this->get_codemirror_resource_handle(), $this->get_code_mirror_directory_url() . '/lib/codemirror.js', array(), WPML_TM_VERSION );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
private function get_code_mirror_directory_url() {
|
||||
return $this->wpml_tm_url . '/libraries/CodeMirror';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
private function get_codemirror_resource_handle() {
|
||||
if ( $this->is_codemirror_in_wp_core() ) {
|
||||
return 'wp-codemirror';
|
||||
}
|
||||
|
||||
return 'codemirror';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
private function is_code_mirror_installed() {
|
||||
if ( null === $this->code_mirror_installed ) {
|
||||
$this->code_mirror_installed = $this->is_codemirror_in_wp_core() || file_exists( $this->wpml_tm_path . '/libraries/CodeMirror/lib/codemirror.js' );
|
||||
}
|
||||
|
||||
return $this->code_mirror_installed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
private function is_vk_beautify_installed() {
|
||||
if ( null === $this->vk_beautify_installed ) {
|
||||
$this->vk_beautify_installed = file_exists( $this->wpml_tm_path . '/libraries/vkBeautify/vkbeautify.js' );
|
||||
}
|
||||
|
||||
return $this->vk_beautify_installed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
private function is_codemirror_in_wp_core() {
|
||||
global $wp_version;
|
||||
|
||||
return $this->wpml_wp_api->version_compare_naked( $wp_version, '4.9', '>=' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
private function is_syntax_highlighting_enabled() {
|
||||
return 'true' === get_user_meta( get_current_user_id(), 'syntax_highlighting', true );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @author OnTheGo Systems
|
||||
*/
|
||||
class WPML_TM_Custom_XML_UI {
|
||||
private $custom_xml;
|
||||
private $template_service;
|
||||
|
||||
function __construct( WPML_Custom_XML $custom_xml, IWPML_Template_Service $template_service ) {
|
||||
$this->custom_xml = $custom_xml;
|
||||
$this->template_service = $template_service;
|
||||
}
|
||||
|
||||
public function show() {
|
||||
$model = $this->get_model();
|
||||
|
||||
return $this->template_service->show( $model, 'main.twig' );
|
||||
}
|
||||
|
||||
private function get_model() {
|
||||
return array(
|
||||
'strings' => array(
|
||||
'content' => __( 'Custom XML configuration', 'wpml-translation-management' ),
|
||||
'save' => __( 'Save', 'wpml-translation-management' ),
|
||||
'shortcuts' => __( 'Shortcuts', 'wpml-translation-management' ),
|
||||
'keysmap' => array(
|
||||
'Ctrl-K/CMD-K' => __( 'Fold/Unfold code', 'wpml-translation-management' ),
|
||||
'Ctrl-F/CMD-F' => __( 'Autoformat', 'wpml-translation-management' ),
|
||||
'Ctrl-S/CMD-S' => __( 'Save', 'wpml-translation-management' ),
|
||||
),
|
||||
'documentation' => __( 'How to write Language Configuration Files', 'wpml-translation-management' ),
|
||||
),
|
||||
'data' => array(
|
||||
'action' => WPML_TM_Custom_XML_AJAX::AJAX_ACTION_BASE,
|
||||
'nonceValidate' => wp_create_nonce( WPML_TM_Custom_XML_AJAX::AJAX_ACTION_BASE . '-validate' ),
|
||||
'nonceSave' => wp_create_nonce( WPML_TM_Custom_XML_AJAX::AJAX_ACTION_BASE . '-save' ),
|
||||
),
|
||||
'links' => array(
|
||||
'documentation' => 'https://wpml.org/documentation/support/language-configuration-files',
|
||||
),
|
||||
'content' => $this->custom_xml->get(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
namespace WPML\TM\Menu\Dashboard;
|
||||
|
||||
class PostJobsRepository {
|
||||
|
||||
/**
|
||||
* @param int $original_element_id
|
||||
* @param string $element_type
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getJobsGroupedByLang( $original_element_id, $element_type ) {
|
||||
return $this->getJobsFor( $original_element_id, $element_type )
|
||||
->map( [ $this, 'mapJob' ] )
|
||||
->keyBy( 'targetLanguage' )
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $original_element_id
|
||||
* @param string $element_type
|
||||
*
|
||||
* @return \WPML\Collect\Support\Collection
|
||||
*/
|
||||
private function getJobsFor( $original_element_id, $element_type ) {
|
||||
return \wpml_collect(
|
||||
wpml_tm_get_jobs_repository()->get( $this->buildSearchParams( $original_element_id, $element_type ) )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $original_element_id
|
||||
* @param string $element_type
|
||||
*
|
||||
* @return \WPML_TM_Jobs_Search_Params
|
||||
*/
|
||||
private function buildSearchParams( $original_element_id, $element_type ) {
|
||||
$params = new \WPML_TM_Jobs_Search_Params();
|
||||
$params->set_original_element_id( $original_element_id );
|
||||
$params->set_job_types( $element_type );
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \WPML_TM_Post_Job_Entity $job
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function mapJob( \WPML_TM_Post_Job_Entity $job ) {
|
||||
return [
|
||||
'entity_id' => $job->get_id(),
|
||||
'job_id' => $job->get_translate_job_id(),
|
||||
'type' => $job->get_type(),
|
||||
'status' => $this->getJobStatus( $job ),
|
||||
'targetLanguage' => $job->get_target_language(),
|
||||
'isLocal' => 'local' === $job->get_translation_service(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \WPML_TM_Job_Entity $job
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
private function getJobStatus( \WPML_TM_Job_Entity $job ) {
|
||||
if ( $job->does_need_update() ) {
|
||||
return ICL_TM_NEEDS_UPDATE;
|
||||
}
|
||||
|
||||
if ( $this->postHasTranslationButLatestJobCancelled( $job ) ) {
|
||||
return ICL_TM_COMPLETE;
|
||||
}
|
||||
|
||||
return $job->get_status();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \WPML_TM_Job_Entity $job
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function postHasTranslationButLatestJobCancelled( \WPML_TM_Job_Entity $job ) {
|
||||
return $job->get_status() === ICL_TM_NOT_TRANSLATED && $job->has_completed_translation();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,347 @@
|
||||
<?php
|
||||
|
||||
class WPML_TM_Dashboard_Display_Filter {
|
||||
|
||||
const PARENT_TAXONOMY_CONTAINER = 'parent-taxonomy-container';
|
||||
const PARENT_SELECT_ID = 'parent-filter-control';
|
||||
const PARENT_SELECT_NAME = 'filter[parent_type]';
|
||||
const PARENT_OR_TAXONOMY_ITEM_CONTAINER = 'parent-taxonomy-item-container';
|
||||
|
||||
private $active_languages = array();
|
||||
private $translation_filter;
|
||||
private $post_types;
|
||||
private $post_statuses;
|
||||
private $source_language_code;
|
||||
private $priorities;
|
||||
|
||||
/** @var wpdb $wpdb */
|
||||
private $wpdb;
|
||||
|
||||
public function __construct(
|
||||
$active_languages,
|
||||
$source_language_code,
|
||||
$translation_filter,
|
||||
$post_types,
|
||||
$post_statuses,
|
||||
array $priorities,
|
||||
wpdb $wpdb
|
||||
) {
|
||||
$this->active_languages = $active_languages;
|
||||
$this->translation_filter = $translation_filter;
|
||||
$this->source_language_code = $source_language_code;
|
||||
$this->post_types = $post_types;
|
||||
$this->post_statuses = $post_statuses;
|
||||
$this->priorities = $priorities;
|
||||
$this->wpdb = $wpdb;
|
||||
}
|
||||
|
||||
private function from_lang_select() {
|
||||
$select_attributes_id = 'icl_language_selector';
|
||||
$select_attributes_name = 'filter[from_lang]';
|
||||
$select_attributes_label = __( 'in', 'wpml-translation-management' );
|
||||
|
||||
?>
|
||||
<label for="<?php echo esc_attr( $select_attributes_id ); ?>">
|
||||
<?php echo esc_html( $select_attributes_label ); ?>
|
||||
</label>
|
||||
<?php
|
||||
if ( $this->source_language_code ) {
|
||||
$tooltip = $this->get_from_language_filter_lock_message_if_required();
|
||||
?>
|
||||
<input type="hidden" id="<?php echo esc_attr( $select_attributes_id ); ?>" name="<?php echo esc_attr( $select_attributes_name ); ?>" value="<?php echo esc_attr( $this->get_language_from() ); ?>"/>
|
||||
<span class="wpml-tm-filter-disabled js-otgs-popover-tooltip" title="<?php echo esc_attr( $tooltip ); ?>"><?php echo esc_html( $this->active_languages[ $this->get_language_from() ]['display_name'] ); ?></span>
|
||||
<?php
|
||||
} else {
|
||||
?>
|
||||
<select id="<?php echo esc_attr( $select_attributes_id ); ?>" name="<?php echo esc_attr( $select_attributes_name ); ?>" title="<?php echo esc_attr( $select_attributes_label ); ?>">
|
||||
<?php
|
||||
foreach ( $this->active_languages as $lang ) {
|
||||
$selected = '';
|
||||
|
||||
if ( $this->get_language_from() && $lang['code'] == $this->get_language_from() ) {
|
||||
$selected = 'selected="selected"';
|
||||
}
|
||||
?>
|
||||
<option value="<?php echo esc_attr( $lang['code'] ); ?>" <?php echo $selected; ?>>
|
||||
<?php
|
||||
echo esc_html( $lang['display_name'] );
|
||||
?>
|
||||
</option>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
<?php
|
||||
}
|
||||
|
||||
private function get_language_from() {
|
||||
$languages_from = $this->source_language_code;
|
||||
if ( ! $languages_from ) {
|
||||
$languages_from = $this->get_language_from_filter();
|
||||
}
|
||||
|
||||
return $languages_from;
|
||||
}
|
||||
|
||||
private function get_language_from_filter() {
|
||||
if ( array_key_exists( 'from_lang', $this->translation_filter ) && $this->translation_filter['from_lang'] ) {
|
||||
return $this->translation_filter['from_lang'];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function to_lang_select() {
|
||||
?>
|
||||
<label for="filter_to_lang">
|
||||
<?php esc_html_e( 'translated to', 'wpml-translation-management' ); ?>
|
||||
</label>
|
||||
<select id="filter_to_lang" name="filter[to_lang]">
|
||||
<option value=""><?php esc_html_e( 'Any language', 'wpml-translation-management' ); ?></option>
|
||||
<?php
|
||||
foreach ( $this->active_languages as $lang ) {
|
||||
$selected = selected( $this->translation_filter['to_lang'], $lang['code'], false );
|
||||
?>
|
||||
<option value="<?php echo esc_attr( $lang['code'] ); ?>" <?php echo $selected; ?>>
|
||||
<?php echo esc_html( $lang['display_name'] ); ?>
|
||||
</option>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
<?php
|
||||
}
|
||||
|
||||
private function translation_status_select() {
|
||||
?>
|
||||
<select id="filter_tstatus" name="filter[tstatus]" title="<?php esc_attr_e( 'Translation status', 'wpml-translation-management' ); ?>">
|
||||
<?php
|
||||
$option_status = array(
|
||||
-1 => esc_html__( 'All translation statuses', 'wpml-translation-management' ),
|
||||
ICL_TM_NOT_TRANSLATED => esc_html__(
|
||||
'Not translated or needs updating',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
ICL_TM_NEEDS_UPDATE => esc_html__( 'Needs updating', 'wpml-translation-management' ),
|
||||
ICL_TM_IN_PROGRESS => esc_html__( 'Translation in progress', 'wpml-translation-management' ),
|
||||
ICL_TM_COMPLETE => esc_html__( 'Translation complete', 'wpml-translation-management' ),
|
||||
);
|
||||
foreach ( $option_status as $status_key => $status_value ) {
|
||||
$selected = selected( $this->translation_filter['tstatus'], $status_key, false );
|
||||
|
||||
?>
|
||||
<option value="<?php echo $status_key; ?>" <?php echo $selected; ?>><?php echo $status_value; ?></option>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
<?php
|
||||
}
|
||||
|
||||
private function get_from_language_filter_lock_message_if_required() {
|
||||
$basket_locked_string = null;
|
||||
if ( $this->source_language_code && isset( $this->active_languages[ $this->source_language_code ] ) ) {
|
||||
$language_name = $this->active_languages[ $this->source_language_code ]['display_name'];
|
||||
$basket_locked_string = '<p>';
|
||||
$basket_locked_string .= sprintf(
|
||||
esc_html__(
|
||||
'Language filtering has been disabled because you already have items in %s in the basket.',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
$language_name
|
||||
);
|
||||
$basket_locked_string .= '<br/>';
|
||||
$basket_locked_string .= esc_html__(
|
||||
'To re-enable it, please empty the basket or send it for translation.',
|
||||
'wpml-translation-management'
|
||||
);
|
||||
$basket_locked_string .= '</p>';
|
||||
|
||||
}
|
||||
|
||||
return $basket_locked_string;
|
||||
}
|
||||
|
||||
private function display_post_type_select() {
|
||||
$selected_type = isset( $this->translation_filter['type'] ) ? $this->translation_filter['type'] : false;
|
||||
?>
|
||||
<select id="filter_type" name="filter[type]" title="<?php esc_attr_e( 'Element type', 'wpml-translation-management' ); ?>">
|
||||
<option value=""><?php esc_html_e( 'All types', 'wpml-translation-management' ); ?></option>
|
||||
<?php
|
||||
foreach ( $this->post_types as $post_type_key => $post_type ) {
|
||||
$filter_type_selected = selected( $selected_type, $post_type_key, false );
|
||||
$hierarchical = is_post_type_hierarchical( $post_type_key ) ? 'true' : 'false';
|
||||
$taxonomy_string = '';
|
||||
foreach ( get_object_taxonomies( $post_type_key, 'objects' ) as $taxonomy => $taxonomy_object ) {
|
||||
if ( $this->has_taxonomy_terms_in_any_language( $taxonomy ) ) {
|
||||
if ( $taxonomy_string ) {
|
||||
$taxonomy_string .= ',';
|
||||
}
|
||||
$taxonomy_string .= $taxonomy . '=' . $taxonomy_object->label;
|
||||
}
|
||||
}
|
||||
?>
|
||||
<option
|
||||
value="<?php echo $post_type_key; ?>"
|
||||
data-parent="<?php echo $hierarchical; ?>"
|
||||
data-taxonomy="<?php echo $taxonomy_string; ?>"
|
||||
<?php echo $filter_type_selected; ?>
|
||||
>
|
||||
<?php
|
||||
echo $post_type->labels->singular_name != '' ? $post_type->labels->singular_name
|
||||
: $post_type->labels->name;
|
||||
?>
|
||||
</option>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
<?php
|
||||
}
|
||||
|
||||
private function display_parent_taxonomy_controls() {
|
||||
?>
|
||||
|
||||
<span id="<?php echo self::PARENT_TAXONOMY_CONTAINER; ?>" style="display:none;">
|
||||
<label for="<?php echo self::PARENT_SELECT_ID; ?>">
|
||||
<?php esc_html_e( 'parent', 'wpml-translation-management' ); ?>
|
||||
</label>
|
||||
<select
|
||||
id="<?php echo self::PARENT_SELECT_ID; ?>"
|
||||
name="<?php echo self::PARENT_SELECT_NAME; ?>"
|
||||
data-original="<?php echo isset( $this->translation_filter['parent_type'] ) ? $this->translation_filter['parent_type'] : 'any'; ?>"
|
||||
>
|
||||
</select>
|
||||
|
||||
<span name="<?php echo self::PARENT_OR_TAXONOMY_ITEM_CONTAINER; ?>" class="<?php echo self::PARENT_OR_TAXONOMY_ITEM_CONTAINER; ?>">
|
||||
<input type="hidden" name="filter[parent_id]" value="<?php echo isset( $this->translation_filter['parent_id'] ) ? $this->translation_filter['parent_id'] : ''; ?>"/>
|
||||
</span>
|
||||
</span>
|
||||
<?php
|
||||
}
|
||||
|
||||
private function filter_title_textbox() {
|
||||
$title = isset( $this->translation_filter['title'] ) ? $this->translation_filter['title'] : '';
|
||||
?>
|
||||
<input type="text" id="filter_title" name="filter[title]"
|
||||
value="<?php echo esc_attr( $title ); ?>"
|
||||
placeholder="<?php esc_attr_e( 'Title', 'wpml-translation-management' ); ?>"
|
||||
/>
|
||||
<?php
|
||||
}
|
||||
|
||||
private function display_post_statuses_select() {
|
||||
$filter_post_status = isset( $this->translation_filter['status'] ) ? $this->translation_filter['status']
|
||||
: false;
|
||||
|
||||
?>
|
||||
<select id="filter_status" name="filter[status]" title="<?php esc_attr_e( 'Publish status', 'wpml-translation-management' ); ?>">
|
||||
<option value=""><?php esc_html_e( 'All statuses', 'wpml-translation-management' ); ?></option>
|
||||
<?php
|
||||
foreach ( $this->post_statuses as $post_status_k => $post_status ) {
|
||||
$post_status_selected = selected( $filter_post_status, $post_status_k, false );
|
||||
?>
|
||||
<option value="<?php echo $post_status_k; ?>" <?php echo $post_status_selected; ?>>
|
||||
<?php echo $post_status; ?>
|
||||
</option>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
<?php
|
||||
}
|
||||
|
||||
private function display_post_translation_priority_select() {
|
||||
$filter_translation_priority = isset( $this->translation_filter['translation_priority'] ) ? $this->translation_filter['translation_priority']
|
||||
: false;
|
||||
|
||||
?>
|
||||
<select id="filter_translation_priority" name="filter[translation_priority]" title="<?php esc_attr_e( 'Priority', 'wpml-translation-management' ); ?>">
|
||||
<option value=""><?php esc_html_e( 'All Translation Priorities', 'wpml-translation-management' ); ?></option>
|
||||
<?php
|
||||
foreach ( $this->priorities as $priority ) {
|
||||
$translation_priority_selected = selected( $filter_translation_priority, $priority->term_id, false );
|
||||
?>
|
||||
<option value="<?php echo esc_attr( $priority->term_id ); ?>" <?php echo $translation_priority_selected; ?>>
|
||||
<?php echo esc_html( $priority->name ); ?>
|
||||
</option>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
<?php
|
||||
}
|
||||
|
||||
private function display_button() {
|
||||
$reset_url = $this->get_admin_page_url(
|
||||
array(
|
||||
'page' => WPML_TM_FOLDER . '/menu/main.php',
|
||||
'sm' => 'dashboard',
|
||||
'icl_tm_action' => 'reset_dashboard_filters',
|
||||
)
|
||||
);
|
||||
?>
|
||||
<input id="translation_dashboard_filter" name="translation_dashboard_filter"
|
||||
class="button-secondary" type="submit"
|
||||
value="<?php esc_attr_e( 'Filter', 'wpml-translation-management' ); ?>"/>
|
||||
|
||||
<a type="reset" href="<?php echo esc_url( $reset_url ); ?>" class="wpml-reset-filter"><i class="otgs-ico-close"> </i><?php esc_html_e( 'Reset filter', 'wpml-translation-management' ); ?></a>
|
||||
|
||||
<?php
|
||||
}
|
||||
|
||||
public function display() {
|
||||
$form_url = $this->get_admin_page_url(
|
||||
array(
|
||||
'page' => WPML_TM_FOLDER . '/menu/main.php',
|
||||
'sm' => 'dashboard',
|
||||
)
|
||||
);
|
||||
?>
|
||||
<form method="post" name="translation-dashboard-filter" class="wpml-tm-dashboard-filter" action="<?php echo esc_url( $form_url ); ?>">
|
||||
<input type="hidden" name="icl_tm_action" value="dashboard_filter"/>
|
||||
|
||||
<?php
|
||||
|
||||
do_action( 'display_basket_notification', 'tm_dashboard_top' );
|
||||
$this->heading( __( '1. Select items for translation', 'wpml-translation-management' ) );
|
||||
$this->display_post_type_select();
|
||||
$this->display_parent_taxonomy_controls();
|
||||
$this->from_lang_select();
|
||||
$this->to_lang_select();
|
||||
$this->translation_status_select();
|
||||
$this->display_post_statuses_select();
|
||||
$this->display_post_translation_priority_select();
|
||||
$this->filter_title_textbox();
|
||||
$this->display_button();
|
||||
|
||||
?>
|
||||
</form>
|
||||
<?php
|
||||
}
|
||||
|
||||
private function has_taxonomy_terms_in_any_language( $taxonomy ) {
|
||||
return $this->wpdb->get_var(
|
||||
$this->wpdb->prepare(
|
||||
"SELECT COUNT(translation_id) FROM {$this->wpdb->prefix}icl_translations WHERE element_type=%s",
|
||||
'tax_' . $taxonomy
|
||||
)
|
||||
) > 0;
|
||||
}
|
||||
|
||||
private function heading( $text ) {
|
||||
?>
|
||||
<h3 class="wpml-tm-section-header"><?php echo esc_html( $text ); ?></h3>
|
||||
<?php
|
||||
}
|
||||
|
||||
private function get_admin_page_url( array $query_args ) {
|
||||
$base_url = admin_url( 'admin.php' );
|
||||
|
||||
return add_query_arg( $query_args, $base_url );
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,356 @@
|
||||
<?php
|
||||
|
||||
use WPML\TM\Menu\Dashboard\PostJobsRepository;
|
||||
|
||||
class WPML_TM_Dashboard_Document_Row {
|
||||
|
||||
/** @var stdClass $data */
|
||||
private $data;
|
||||
private $post_types;
|
||||
private $translation_filter;
|
||||
private $active_languages;
|
||||
private $selected;
|
||||
private $note_text;
|
||||
private $note_icon_class;
|
||||
private $post_statuses;
|
||||
/** @var SitePress $sitepress */
|
||||
private $sitepress;
|
||||
/** @var WPML_TM_Translatable_Element_Provider $translatable_element_provider */
|
||||
private $translatable_element_provider;
|
||||
|
||||
public function __construct(
|
||||
$doc_data,
|
||||
$translation_filter,
|
||||
$post_types,
|
||||
$post_statuses,
|
||||
$active_languages,
|
||||
$selected,
|
||||
SitePress $sitepress,
|
||||
WPML_TM_Translatable_Element_Provider $translatable_element_provider
|
||||
) {
|
||||
$this->data = $doc_data;
|
||||
$this->post_statuses = $post_statuses;
|
||||
$this->selected = $selected;
|
||||
$this->post_types = $post_types;
|
||||
$this->active_languages = $active_languages;
|
||||
$this->translation_filter = $translation_filter;
|
||||
$this->sitepress = $sitepress;
|
||||
$this->translatable_element_provider = $translatable_element_provider;
|
||||
}
|
||||
|
||||
public function get_word_count() {
|
||||
$current_document = $this->data;
|
||||
$type = 'post';
|
||||
|
||||
if ( $this->is_external_type() ) {
|
||||
$type = 'package';
|
||||
}
|
||||
|
||||
$translatable_element = $this->translatable_element_provider->get_from_type( $type, $current_document->ID );
|
||||
|
||||
return apply_filters(
|
||||
'wpml_tm_estimated_words_count',
|
||||
$translatable_element->get_words_count(),
|
||||
$current_document
|
||||
);
|
||||
}
|
||||
|
||||
public function get_title() {
|
||||
return $this->data->title ? $this->data->title : __( '(missing title)', 'wpml-translation-management' );
|
||||
}
|
||||
|
||||
private function is_external_type() {
|
||||
$doc = $this->data;
|
||||
|
||||
return strpos( $doc->translation_element_type, 'post_' ) !== 0;
|
||||
}
|
||||
|
||||
public function get_type_prefix() {
|
||||
$type = $this->data->translation_element_type;
|
||||
$type = explode( '_', $type );
|
||||
if ( count( $type ) > 1 ) {
|
||||
$type = $type[0];
|
||||
}
|
||||
|
||||
return $type;
|
||||
}
|
||||
|
||||
public function get_type() {
|
||||
$type = $this->data->translation_element_type;
|
||||
$type = explode( '_', $type );
|
||||
if ( count( $type ) > 1 ) {
|
||||
unset( $type[0] );
|
||||
}
|
||||
$type = join( '_', $type );
|
||||
|
||||
return $type;
|
||||
}
|
||||
|
||||
public function display() {
|
||||
global $iclTranslationManagement;
|
||||
$current_document = $this->data;
|
||||
$count = $this->get_word_count();
|
||||
$post_actions = array();
|
||||
$post_actions_link = '';
|
||||
$element_type = $this->get_type_prefix();
|
||||
$check_field_name = $element_type;
|
||||
$post_title = $this->get_title();
|
||||
|
||||
$post_view_link = '';
|
||||
$post_edit_link = '';
|
||||
if ( ! $this->is_external_type() ) {
|
||||
$post_link_factory = new WPML_TM_Post_Link_Factory( $this->sitepress );
|
||||
$post_edit_link = $post_link_factory->edit_link_anchor( $current_document->ID, __( 'Edit', 'wpml-translation-management' ) );
|
||||
$post_view_link = $post_link_factory->view_link_anchor( $current_document->ID, __( 'View', 'wpml-translation-management' ) );
|
||||
}
|
||||
|
||||
$jobs = ( new PostJobsRepository() )->getJobsGroupedByLang( $current_document->ID, $element_type );
|
||||
|
||||
$post_edit_link = apply_filters( 'wpml_document_edit_item_link', $post_edit_link, __( 'Edit', 'wpml-translation-management' ), $current_document, $element_type, $this->get_type() );
|
||||
if ( $post_edit_link ) {
|
||||
$post_actions[] = "<span class='edit'>" . $post_edit_link . '</span>';
|
||||
}
|
||||
|
||||
$post_view_link = apply_filters( 'wpml_document_view_item_link', $post_view_link, __( 'View', 'wpml-translation-management' ), $current_document, $element_type, $this->get_type() );
|
||||
if ( $post_view_link ) {
|
||||
$post_actions[] = "<span class='view'>" . $post_view_link . '</span>';
|
||||
}
|
||||
|
||||
if ( $post_actions ) {
|
||||
$post_actions_link .= '<div class="row-actions">' . implode( ' | ', $post_actions ) . '</div>';
|
||||
}
|
||||
|
||||
$row_data = apply_filters( 'wpml_translation_dashboard_row_data', array( 'word_count' => $count ), $this->data );
|
||||
$row_data_str = '';
|
||||
foreach ( $row_data as $key => $value ) {
|
||||
$row_data_str .= 'data-' . esc_attr( $key ) . '="' . esc_attr( $value ) . '" ';
|
||||
}
|
||||
?>
|
||||
<tr id="row_<?php echo sanitize_html_class( $current_document->ID ); ?>" <?php echo $row_data_str; ?>>
|
||||
<td scope="row">
|
||||
<?php
|
||||
$checked = checked( true, isset( $_GET['post_id'] ) || $this->selected, false );
|
||||
?>
|
||||
<input type="checkbox" value="<?php echo $current_document->ID; ?>" name="<?php echo $check_field_name; ?>[<?php echo $current_document->ID; ?>][checked]" <?php echo $checked; ?> />
|
||||
<input type="hidden" value="<?php echo $element_type; ?>" name="<?php echo $check_field_name; ?>[<?php echo $current_document->ID; ?>][type]"/>
|
||||
</td>
|
||||
<td scope="row" class="post-title column-title">
|
||||
<?php
|
||||
echo esc_html( $post_title );
|
||||
echo $post_actions_link;
|
||||
?>
|
||||
<div class="icl_post_note" id="icl_post_note_<?php echo $current_document->ID; ?>">
|
||||
<?php
|
||||
$note = '';
|
||||
if ( ! $current_document->is_translation ) {
|
||||
$note = WPML_TM_Translator_Note::get( $current_document->ID );
|
||||
$this->note_text = '';
|
||||
if ( $note ) {
|
||||
$this->note_text = __( 'Edit note for the translators', 'wpml-translation-management' );
|
||||
$this->note_icon_class = 'otgs-ico-note-edit-o';
|
||||
} else {
|
||||
$this->note_text = __( 'Add note for the translators', 'wpml-translation-management' );
|
||||
$this->note_icon_class = 'otgs-ico-note-add-o';
|
||||
}
|
||||
}
|
||||
?>
|
||||
<label for="post_note_<?php echo $current_document->ID; ?>">
|
||||
<?php _e( 'Note for the translators', 'wpml-translation-management' ); ?>
|
||||
</label>
|
||||
<textarea id="post_note_<?php echo $current_document->ID; ?>" rows="5"><?php echo $note; ?></textarea>
|
||||
<table width="100%">
|
||||
<tr>
|
||||
<td style="border-bottom:none">
|
||||
<input type="button" class="icl_tn_cancel button" value="<?php _e( 'Cancel', 'wpml-translation-management' ); ?>" />
|
||||
<input class="icl_tn_post_id" type="hidden" value="<?php echo $current_document->ID; ?>"/>
|
||||
</td>
|
||||
<td align="right" style="border-bottom:none">
|
||||
<input type="button" class="icl_tn_save button-primary" value="<?php _e( 'Save', 'wpml-translation-management' ); ?>"/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</td>
|
||||
<td scope="row" class="manage-column wpml-column-type">
|
||||
<?php
|
||||
if ( isset( $this->post_types[ $this->get_type() ] ) ) {
|
||||
$custom_post_type_labels = $this->post_types[ $this->get_type() ]->labels;
|
||||
if ( $custom_post_type_labels->singular_name != '' ) {
|
||||
echo $custom_post_type_labels->singular_name;
|
||||
} else {
|
||||
echo $custom_post_type_labels->name;
|
||||
}
|
||||
} else {
|
||||
echo $this->get_type();
|
||||
}
|
||||
?>
|
||||
</td>
|
||||
<td scope="row" class="manage-column column-active-languages wpml-col-languages">
|
||||
<?php
|
||||
$has_jobs_in_progress = false;
|
||||
|
||||
foreach ( $this->active_languages as $code => $lang ) {
|
||||
if ( $code == $this->data->language_code ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( isset( $jobs[ $code ] ) ) {
|
||||
$job = $jobs[ $code ];
|
||||
$status = $job['status'] ?: $this->get_status_in_lang( $code );
|
||||
$job_entity_id = $job['entity_id'];
|
||||
$job_id = $job['job_id'];
|
||||
} else {
|
||||
$status = $this->get_status_in_lang( $code );
|
||||
$job_entity_id = $job_id = 0;
|
||||
}
|
||||
|
||||
switch ( $status ) {
|
||||
case ICL_TM_NOT_TRANSLATED:
|
||||
$translation_status_text = esc_attr( __( 'Not translated', 'wpml-translation-management' ) );
|
||||
break;
|
||||
case ICL_TM_WAITING_FOR_TRANSLATOR:
|
||||
$translation_status_text = esc_attr( __( 'Waiting for translator', 'wpml-translation-management' ) );
|
||||
break;
|
||||
case ICL_TM_IN_BASKET:
|
||||
$translation_status_text = esc_attr( __( 'In basket', 'wpml-translation-management' ) );
|
||||
break;
|
||||
case ICL_TM_IN_PROGRESS:
|
||||
$translation_status_text = esc_attr( __( 'In progress', 'wpml-translation-management' ) );
|
||||
$has_jobs_in_progress = true;
|
||||
break;
|
||||
case ICL_TM_TRANSLATION_READY_TO_DOWNLOAD:
|
||||
$translation_status_text = esc_attr(
|
||||
__(
|
||||
'Translation ready to download',
|
||||
'wpml-translation-management'
|
||||
)
|
||||
);
|
||||
$has_jobs_in_progress = true;
|
||||
break;
|
||||
case ICL_TM_DUPLICATE:
|
||||
$translation_status_text = esc_attr( __( 'Duplicate', 'wpml-translation-management' ) );
|
||||
break;
|
||||
case ICL_TM_COMPLETE:
|
||||
$translation_status_text = esc_attr( __( 'Complete', 'wpml-translation-management' ) );
|
||||
break;
|
||||
case ICL_TM_NEEDS_UPDATE:
|
||||
$translation_status_text = ' - ' . esc_attr( __( 'needs update', 'wpml-translation-management' ) );
|
||||
break;
|
||||
default:
|
||||
$translation_status_text = '';
|
||||
}
|
||||
|
||||
$status_icon_class = $iclTranslationManagement->status2icon_class( $status, ICL_TM_NEEDS_UPDATE === (int) $status );
|
||||
?>
|
||||
|
||||
<span data-document_status="<?php echo $status; ?>"
|
||||
id="wpml-job-status-<?php echo esc_attr( $job_entity_id ); ?>"
|
||||
class="js-wpml-translate-link"
|
||||
data-tm-job-id="<?php echo esc_attr( $job_id ); ?>"
|
||||
>
|
||||
<i class="<?php echo esc_attr( $status_icon_class ); ?>"
|
||||
title="<?php echo esc_attr( $lang['display_name'] ); ?>: <?php echo $translation_status_text; ?>"></i>
|
||||
</span>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</td>
|
||||
<td scope="row" class="post-date column-date">
|
||||
<?php
|
||||
$element_date = $this->get_date();
|
||||
if ( $element_date ) {
|
||||
echo date( 'Y-m-d', strtotime( $element_date ) );
|
||||
}
|
||||
echo '<br />';
|
||||
echo $this->get_general_status();
|
||||
?>
|
||||
</td>
|
||||
<td class="column-actions" scope="row" >
|
||||
<?php
|
||||
if ( ! $current_document->is_translation ) {
|
||||
?>
|
||||
<a title="<?php echo $this->note_text; ?>" href="#" class="icl_tn_link" id="icl_tn_link_<?php echo $current_document->ID; ?>" >
|
||||
<i class="<?php echo $this->note_icon_class; ?>"></i>
|
||||
</a>
|
||||
|
||||
<?php
|
||||
if ( $has_jobs_in_progress && $this->has_remote_jobs( $jobs ) ) {
|
||||
?>
|
||||
<a class="otgs-ico-refresh wpml-sync-and-download-translation"
|
||||
data-element-id="<?php echo $current_document->ID; ?>"
|
||||
data-element-type="<?php echo esc_attr( $element_type ); ?>"
|
||||
data-jobs="<?php echo htmlspecialchars( json_encode( array_values( $jobs ) ) ); ?>"
|
||||
data-icons="
|
||||
<?php
|
||||
echo htmlspecialchars(
|
||||
json_encode(
|
||||
array(
|
||||
'completed' => $iclTranslationManagement->status2icon_class( ICL_TM_COMPLETE, false ),
|
||||
'canceled' => $iclTranslationManagement->status2icon_class( ICL_TM_NOT_TRANSLATED, false ),
|
||||
'progress' => $iclTranslationManagement->status2icon_class( ICL_TM_IN_PROGRESS, false ),
|
||||
)
|
||||
)
|
||||
)
|
||||
?>
|
||||
"
|
||||
title="<?php esc_attr_e( 'Check status and get translations', 'wpml-translation-management' ); ?>"
|
||||
</a>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php
|
||||
}
|
||||
|
||||
private function get_date() {
|
||||
if ( ! $this->is_external_type() ) {
|
||||
/** @var WP_Post $post */
|
||||
$post = get_post( $this->data->ID );
|
||||
$date = get_post_time( 'U', false, $post );
|
||||
} else {
|
||||
$date = apply_filters(
|
||||
'wpml_tm_dashboard_date',
|
||||
time(),
|
||||
$this->data->ID,
|
||||
$this->data->translation_element_type
|
||||
);
|
||||
}
|
||||
$date = date( 'y-m-d', $date );
|
||||
|
||||
return $date;
|
||||
}
|
||||
|
||||
private function has_remote_jobs( $jobs ) {
|
||||
foreach ( $jobs as $job ) {
|
||||
if ( ! $job['isLocal'] ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function get_general_status() {
|
||||
if ( ! $this->is_external_type() ) {
|
||||
$status = get_post_status( $this->data->ID );
|
||||
$status_text = isset( $this->post_statuses[ $status ] ) ? $this->post_statuses[ $status ] : $status;
|
||||
} else {
|
||||
$status_text = apply_filters(
|
||||
'wpml_tm_dashboard_status',
|
||||
'external',
|
||||
$this->data->ID,
|
||||
$this->data->translation_element_type
|
||||
);
|
||||
}
|
||||
|
||||
return $status_text;
|
||||
}
|
||||
|
||||
private function get_status_in_lang( $language_code ) {
|
||||
$status_helper = wpml_get_post_status_helper();
|
||||
|
||||
return $status_helper->get_status( false, $this->data->trid, $language_code );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Class WPML_TM_Dashboard_Pagination
|
||||
*/
|
||||
class WPML_TM_Dashboard_Pagination {
|
||||
|
||||
public function add_hooks() {
|
||||
add_filter( 'wpml_tm_dashboard_post_query_args', array( $this, 'filter_dashboard_post_query_args_for_pagination' ), 10, 2 );
|
||||
add_action( 'wpml_tm_dashboard_pagination', array( $this, 'add_tm_dashboard_pagination' ), 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $query_args
|
||||
* @param array $args
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function filter_dashboard_post_query_args_for_pagination( $query_args, $args ) {
|
||||
if ( ! empty( $args['type'] ) ) {
|
||||
unset( $query_args['no_found_rows'] );
|
||||
}
|
||||
|
||||
return $query_args;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param integer $posts_per_page
|
||||
* @param integer $found_documents
|
||||
*/
|
||||
public function add_tm_dashboard_pagination( $posts_per_page, $found_documents ) {
|
||||
$found_documents = $found_documents;
|
||||
$total_pages = ceil( $found_documents / $posts_per_page );
|
||||
$paged = array_key_exists( 'paged', $_GET ) ? filter_var( $_GET['paged'], FILTER_SANITIZE_NUMBER_INT ) : false;
|
||||
$paged = $paged ? $paged : 1;
|
||||
$page_links = paginate_links(
|
||||
array(
|
||||
'base' => add_query_arg( 'paged', '%#%' ),
|
||||
'format' => '',
|
||||
'prev_text' => '«',
|
||||
'next_text' => '»',
|
||||
'total' => $total_pages,
|
||||
'current' => $paged,
|
||||
)
|
||||
);
|
||||
if ( $page_links ) {
|
||||
?>
|
||||
<div class="tablenav-pages">
|
||||
<?php
|
||||
$page_from = number_format_i18n( ( $paged - 1 ) * $posts_per_page + 1 );
|
||||
$page_to = number_format_i18n( min( $paged * $posts_per_page, $found_documents ) );
|
||||
$page_total = number_format_i18n( $found_documents );
|
||||
?>
|
||||
<span class="displaying-num">
|
||||
<?php echo sprintf( esc_html__( 'Displaying %1$s–%2$s of %3$s', 'wpml-translation-management' ), $page_from, $page_to, $page_total ); ?>
|
||||
</span>
|
||||
<?php echo $page_links; ?>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: bruce
|
||||
* Date: 20/04/17
|
||||
* Time: 11:39 AM
|
||||
*/
|
||||
class WPML_TM_WP_Query extends WP_Query {
|
||||
|
||||
public function get_found_count() {
|
||||
return $this->found_posts;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
interface IWPML_TM_Admin_Section_Factory {
|
||||
|
||||
/**
|
||||
* Returns an instance of a class implementing \IWPML_TM_Admin_Section.
|
||||
*
|
||||
* @return \IWPML_TM_Admin_Section
|
||||
*/
|
||||
public function create();
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
interface IWPML_TM_Admin_Section {
|
||||
|
||||
/**
|
||||
* Returns a value which will be used for sorting the sections.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function get_order();
|
||||
|
||||
/**
|
||||
* Returns the unique slug of the sections which is used to build the URL for opening this section.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_slug();
|
||||
|
||||
/**
|
||||
* Returns one or more capabilities required to display this section.
|
||||
*
|
||||
* @return string|array
|
||||
*/
|
||||
public function get_capabilities();
|
||||
|
||||
/**
|
||||
* Returns the caption to display in the section.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_caption();
|
||||
|
||||
/**
|
||||
* Returns the callback responsible for rendering the content of the section.
|
||||
*
|
||||
* @return callable
|
||||
*/
|
||||
public function get_callback();
|
||||
|
||||
/**
|
||||
* This method is hooked to the `admin_enqueue_scripts` action.
|
||||
*
|
||||
* @param string $hook The current page.
|
||||
*/
|
||||
public function admin_enqueue_scripts( $hook );
|
||||
|
||||
/**
|
||||
* Used to extend the logic for displaying/hiding the section.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_visible();
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
<?php
|
||||
|
||||
class WPML_TM_Jobs_List_Script_Data {
|
||||
/** @var WPML_TM_Rest_Jobs_Language_Names */
|
||||
private $language_names;
|
||||
|
||||
/** @var WPML_TM_Jobs_List_Translated_By_Filters */
|
||||
private $translated_by_filter;
|
||||
|
||||
/** @var WPML_TM_Jobs_List_Translators */
|
||||
private $translators;
|
||||
|
||||
/** @var WPML_TM_Jobs_List_Services */
|
||||
private $services;
|
||||
|
||||
/**
|
||||
* @param WPML_TM_Rest_Jobs_Language_Names|null $language_names
|
||||
* @param WPML_TM_Jobs_List_Translated_By_Filters|null $translated_by_filters
|
||||
* @param WPML_TM_Jobs_List_Translators|null $translators
|
||||
* @param WPML_TM_Jobs_List_Services|null $services
|
||||
*/
|
||||
public function __construct(
|
||||
WPML_TM_Rest_Jobs_Language_Names $language_names = null,
|
||||
WPML_TM_Jobs_List_Translated_By_Filters $translated_by_filters = null,
|
||||
WPML_TM_Jobs_List_Translators $translators = null,
|
||||
WPML_TM_Jobs_List_Services $services = null
|
||||
) {
|
||||
if ( ! $language_names ) {
|
||||
global $sitepress;
|
||||
|
||||
$language_names = new WPML_TM_Rest_Jobs_Language_Names( $sitepress );
|
||||
}
|
||||
$this->language_names = $language_names;
|
||||
|
||||
if ( ! $translators ) {
|
||||
global $wpdb;
|
||||
$translators = new WPML_TM_Jobs_List_Translators(
|
||||
new WPML_Translator_Records(
|
||||
$wpdb,
|
||||
new WPML_WP_User_Query_Factory(),
|
||||
wp_roles()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ( ! $services ) {
|
||||
$services = new WPML_TM_Jobs_List_Services( WPML_TM_Rest_Jobs_Translation_Service::create() );
|
||||
}
|
||||
|
||||
if ( ! $translated_by_filters ) {
|
||||
$translated_by_filters = new WPML_TM_Jobs_List_Translated_By_Filters( $services, $translators );
|
||||
}
|
||||
|
||||
$this->translated_by_filter = $translated_by_filters;
|
||||
$this->translators = $translators;
|
||||
$this->services = $services;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function get() {
|
||||
$translation_service = TranslationProxy::get_current_service();
|
||||
if ( $translation_service ) {
|
||||
$translation_service = [
|
||||
'id' => $translation_service->id,
|
||||
'name' => $translation_service->name,
|
||||
];
|
||||
} else {
|
||||
$translation_service = [
|
||||
'id' => 0,
|
||||
'name' => '?',
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'strings' => [
|
||||
'bulkActions' => __( 'Bulk actions', 'wpml-translation-management' ),
|
||||
'cancelJobs' => __( 'Cancel jobs', 'wpml-translation-management' ),
|
||||
'getTranslations' => __( 'Get translations', 'wpml-translation-management' ),
|
||||
'apply' => __( 'Apply', 'wpml-translation-management' ),
|
||||
'firstPage' => __( 'First page', 'wpml-translation-management' ),
|
||||
'previousPage' => __( 'Previous page', 'wpml-translation-management' ),
|
||||
'nextPage' => __( 'Next page', 'wpml-translation-management' ),
|
||||
'lastPage' => __( 'Last page', 'wpml-translation-management' ),
|
||||
'listNavigation' => __( 'Navigation', 'wpml-translation-management' ),
|
||||
'totalItemsText' => __( '0 items', 'wpml-translation-management' ),
|
||||
'of' => __( 'of', 'wpml-translation-management' ),
|
||||
'selectTranslator' => __( 'Select translator', 'wpml-translation-management' ),
|
||||
'assignTranslator' => __( 'Assign', 'wpml-translation-management' ),
|
||||
'externalActions' => __( 'External Actions', 'wpml-translation-management' ),
|
||||
'actions' => __( 'Actions', 'wpml-translation-management' ),
|
||||
'externalActionsTooltip' => __(
|
||||
'Actions that will happen on the translation service',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
'selectAll' => __( 'Select all', 'wpml-translation-management' ),
|
||||
'filters' => [
|
||||
'title' => __( 'Title', 'wpml-translation-management' ),
|
||||
'batch_name' => __( 'Batch name', 'wpml-translation-management' ),
|
||||
'anyLanguage' => __( 'Any language', 'wpml-translation-management' ),
|
||||
'languageFrom' => __( 'from', 'wpml-translation-management' ),
|
||||
'languageTo' => __( 'to', 'wpml-translation-management' ),
|
||||
'filter' => __( 'Filter', 'wpml-translation-management' ),
|
||||
'asc' => __( 'Ascending', 'wpml-translation-management' ),
|
||||
'desc' => __( 'Descending', 'wpml-translation-management' ),
|
||||
'sort' => __( 'Sort', 'wpml-translation-management' ),
|
||||
'direction' => __( 'Direction', 'wpml-translation-management' ),
|
||||
'firstSort' => __( 'sort by', 'wpml-translation-management' ),
|
||||
'secondSort' => __( 'then sort by', 'wpml-translation-management' ),
|
||||
'translatedBy' => __( 'translated by', 'wpml-translation-management' ),
|
||||
'with' => __( 'with', 'wpml-translation-management' ),
|
||||
'sentBetween' => __( 'sent between', 'wpml-translation-management' ),
|
||||
'deadline' => __( 'with deadline between', 'wpml-translation-management' ),
|
||||
'reset' => __( 'Reset filters', 'wpml-translation-management' ),
|
||||
'selectStatus' => __( 'Select status', 'wpml-translation-management' ),
|
||||
],
|
||||
'progressMessages' => [
|
||||
'loadingJobs' => __( 'Loading jobs...', 'wpml-translation-management' ),
|
||||
'applyingTranslations' => __( 'Downloading translations...', 'wpml-translation-management' ),
|
||||
'syncBatch' => __( 'Synchronizing batch...', 'wpml-translation-management' ),
|
||||
'cancelJobs' => __( 'Canceling jobs...', 'wpml-translation-management' ),
|
||||
'assignTranslator' => __( 'Assigning translator...', 'wpml-translation-management' ),
|
||||
'downloadingXliff' => __( 'Downloading XLIFF file...', 'wpml-translation-management' ),
|
||||
],
|
||||
'confirmations' => [
|
||||
'applyingTranslations' => __( 'Translations downloaded', 'wpml-translation-management' ),
|
||||
'syncBatch' => __(
|
||||
'Batch synchronization has been sent',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
'cancelJobs' => __( 'Jobs canceled', 'wpml-translation-management' ),
|
||||
'assignTranslator' => __( 'Translator assigned', 'wpml-translation-management' ),
|
||||
'downloadingXliff' => __( 'XLIFF file downloaded', 'wpml-translation-management' ),
|
||||
],
|
||||
'jobActions' => [
|
||||
'checkStatus' => [
|
||||
'active' => __( 'Check status', 'wpml-translation-management' ),
|
||||
'completed' => __( 'Translation is ready, no need to check its status', 'wpml-translation-management' ),
|
||||
'cancelled' => __( 'Translation is cancelled, no need to check its status', 'wpml-translation-management' ),
|
||||
'readyToDownload' => __( 'Translation is ready to download, no need to check its status', 'wpml-translation-management' ),
|
||||
'alreadySynced' => __( 'Translation has already been synchronized, no need to check its status', 'wpml-translation-management' ),
|
||||
'local' => __( 'This is a local job, no need to check its status', 'wpml-translation-management' ),
|
||||
],
|
||||
|
||||
'downloadXLIFF' => [
|
||||
'activeIconText' => __( 'Download the translated XLIFF file', 'wpml-translation-management' ),
|
||||
'localJob' => __(
|
||||
'This is a local job so it does not have an XLIFF file to download',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
'notReady' => __(
|
||||
'You cannot download the translation as it has not been completed',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
'canceled' => __(
|
||||
'You cannot download the XLIFF file because TS_NAME has canceled the job',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
],
|
||||
|
||||
'cancel' => [
|
||||
'activeIconText' => __( 'Cancel job', 'wpml-translation-management' ),
|
||||
// translators: "%s" is replaced with the name of the translation service associated with the job.
|
||||
'notLocalJob' => sprintf( __( 'Contact %s for cancellation', 'wpml-translation-management' ), $translation_service['name'] ),
|
||||
'alreadyFinished' => __( 'The job is already finished', 'wpml-translation-management' ),
|
||||
'alreadyCanceled' => __( 'The job is already canceled', 'wpml-translation-management' ),
|
||||
],
|
||||
|
||||
'getTranslations' => [
|
||||
'activeIconText' => __( 'Get translations', 'wpml-translation-management' ),
|
||||
'localJob' => __(
|
||||
'You cannot download the translation for a local job',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
'notReady' => __(
|
||||
'You cannot download the translation as it has not been completed',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
'completed' => __(
|
||||
'You have already downloaded this job',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
'canceled' => __(
|
||||
'You cannot download this job because TS_NAME has canceled it',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
],
|
||||
],
|
||||
'tpJobId' => 'TP ID: %d',
|
||||
],
|
||||
'settings' => [
|
||||
'columns' => WPML_TM_Rest_Jobs_Columns::get_columns(),
|
||||
'sortable' => WPML_TM_Rest_Jobs_Columns::get_sortable(),
|
||||
'pageSize' => 10,
|
||||
],
|
||||
'jobStatuses' => WPML_TM_Jobs_List_Status_Names::get_statuses(),
|
||||
'languages' => $this->language_names->get_active_languages(),
|
||||
'translatedByFilters' => $this->translated_by_filter->get(),
|
||||
'localTranslators' => $this->translators->get(),
|
||||
'translationService' => $translation_service,
|
||||
'siteKey' => WP_Installer::instance()->get_site_key( 'wpml' ),
|
||||
'batchUrl' => OTG_TRANSLATION_PROXY_URL . '/projects/%d/external',
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
class WPML_TM_Jobs_List_Services {
|
||||
/** @var wpdb */
|
||||
private $wpdb;
|
||||
|
||||
/** @var WPML_TM_Rest_Jobs_Translation_Service */
|
||||
private $service_names;
|
||||
|
||||
/** @var array|null */
|
||||
private $cache;
|
||||
|
||||
public function __construct( WPML_TM_Rest_Jobs_Translation_Service $service_names ) {
|
||||
global $wpdb;
|
||||
$this->wpdb = $wpdb;
|
||||
$this->service_names = $service_names;
|
||||
}
|
||||
|
||||
public function get() {
|
||||
if ( $this->cache === null ) {
|
||||
$sql = "
|
||||
SELECT *
|
||||
FROM
|
||||
(
|
||||
(
|
||||
SELECT translation_service
|
||||
FROM {$this->wpdb->prefix}icl_translation_status
|
||||
) UNION (
|
||||
SELECT translation_service
|
||||
FROM {$this->wpdb->prefix}icl_string_translations
|
||||
)
|
||||
) as services
|
||||
WHERE translation_service != 'local' AND translation_service != ''
|
||||
";
|
||||
|
||||
$this->cache = array_map( array( $this, 'map' ), $this->wpdb->get_col( $sql ) );
|
||||
}
|
||||
|
||||
return $this->cache;
|
||||
}
|
||||
|
||||
private function map( $translation_service_id ) {
|
||||
return array(
|
||||
'value' => $translation_service_id,
|
||||
'label' => $this->service_names->get_name( $translation_service_id ),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
class WPML_TM_Jobs_List_Status_Names {
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function get_statuses() {
|
||||
return array(
|
||||
ICL_TM_NOT_TRANSLATED => __( 'Cancelled', 'wpml-translation-management' ),
|
||||
ICL_TM_WAITING_FOR_TRANSLATOR => __( 'Waiting for translator', 'wpml-translation-management' ),
|
||||
ICL_TM_IN_PROGRESS => __( 'In progress', 'wpml-translation-management' ),
|
||||
ICL_TM_NEEDS_UPDATE => __( 'Needs update', 'wpml-translation-management' ),
|
||||
ICL_TM_TRANSLATION_READY_TO_DOWNLOAD => __(
|
||||
'Translation ready to download',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
ICL_TM_COMPLETE => __( 'Completed', 'wpml-translation-management' ),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
class WPML_TM_Jobs_List_Translated_By_Filters {
|
||||
/** @var WPML_TM_Jobs_List_Services */
|
||||
private $services;
|
||||
|
||||
/** @var WPML_TM_Jobs_List_Translators */
|
||||
private $translators;
|
||||
|
||||
/**
|
||||
* @param WPML_TM_Jobs_List_Services $services
|
||||
* @param WPML_TM_Jobs_List_Translators $translators
|
||||
*/
|
||||
public function __construct( WPML_TM_Jobs_List_Services $services, WPML_TM_Jobs_List_Translators $translators ) {
|
||||
$this->services = $services;
|
||||
$this->translators = $translators;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function get() {
|
||||
$options = array(
|
||||
array(
|
||||
'value' => 'any',
|
||||
'label' => __( 'Anyone', 'wpml-translation-management' ),
|
||||
),
|
||||
);
|
||||
|
||||
$services = $this->services->get();
|
||||
if ( $services ) {
|
||||
$options[] = array(
|
||||
'value' => 'any-service',
|
||||
'label' => __( 'Any Translation Service', 'wpml-translation-management' ),
|
||||
);
|
||||
}
|
||||
|
||||
$translators = $this->translators->get();
|
||||
if ( $translators ) {
|
||||
$options[] = array(
|
||||
'value' => 'any-local-translator',
|
||||
'label' => __( 'Any WordPress Translator', 'wpml-translation-management' ),
|
||||
);
|
||||
}
|
||||
|
||||
return array(
|
||||
'options' => $options,
|
||||
'services' => $services,
|
||||
'translators' => $translators,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
use \WPML\FP\Fns;
|
||||
use \WPML\FP\Lst;
|
||||
use function \WPML\FP\flip;
|
||||
use function \WPML\FP\curryN;
|
||||
|
||||
class WPML_TM_Jobs_List_Translators {
|
||||
/** @var WPML_Translator_Records */
|
||||
private $translator_records;
|
||||
|
||||
/**
|
||||
* @param WPML_Translator_Records $translator_records
|
||||
*/
|
||||
public function __construct( WPML_Translator_Records $translator_records ) {
|
||||
$this->translator_records = $translator_records;
|
||||
}
|
||||
|
||||
|
||||
public function get() {
|
||||
$translators = $this->translator_records->get_users_with_capability();
|
||||
|
||||
return array_map( [ $this, 'getTranslatorData' ], $translators );
|
||||
}
|
||||
|
||||
private function getTranslatorData( $translator ) {
|
||||
return [
|
||||
'value' => $translator->ID,
|
||||
'label' => $translator->display_name,
|
||||
'languagePairs' => $this->getLanguagePairs( $translator ),
|
||||
];
|
||||
}
|
||||
|
||||
private function getLanguagePairs( $translator ) {
|
||||
|
||||
$isValidLanguage = Lst::includes( Fns::__, array_values( icl_get_languages_codes() ) );
|
||||
$sourceIsValidLanguage = flip( $isValidLanguage );
|
||||
$getValidTargets = Fns::filter( $isValidLanguage );
|
||||
|
||||
$makePair = curryN(
|
||||
2,
|
||||
function ( $source, $target ) {
|
||||
return [
|
||||
'source' => $source,
|
||||
'target' => $target,
|
||||
];
|
||||
}
|
||||
);
|
||||
|
||||
$getAsPair = curryN(
|
||||
3,
|
||||
function ( $makePair, $targets, $source ) {
|
||||
return Fns::map( $makePair( $source ), $targets );
|
||||
}
|
||||
);
|
||||
|
||||
return \wpml_collect( $translator->language_pairs )
|
||||
->filter( $sourceIsValidLanguage )
|
||||
->map( $getValidTargets )
|
||||
->map( $getAsPair( $makePair ) )
|
||||
->flatten( 1 )
|
||||
->toArray();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @author OnTheGo Systems
|
||||
*/
|
||||
class WPML_TM_MCS_ATE_Strings {
|
||||
|
||||
const AMS_STATUS_ACTIVE_NOT_ALL_SUBSCRIBED = 'active-not-all-subscribed';
|
||||
/**
|
||||
* @var WPML_TM_ATE_Authentication
|
||||
*/
|
||||
private $authentication;
|
||||
private $authentication_data;
|
||||
/**
|
||||
* @var WPML_TM_ATE_AMS_Endpoints
|
||||
*/
|
||||
private $endpoints;
|
||||
private $statuses;
|
||||
|
||||
/**
|
||||
* WPML_TM_MCS_ATE constructor.
|
||||
*
|
||||
* @param WPML_TM_ATE_Authentication $authentication
|
||||
* @param WPML_TM_ATE_AMS_Endpoints $endpoints
|
||||
*/
|
||||
public function __construct( WPML_TM_ATE_Authentication $authentication, WPML_TM_ATE_AMS_Endpoints $endpoints ) {
|
||||
$this->authentication = $authentication;
|
||||
$this->endpoints = $endpoints;
|
||||
|
||||
$this->authentication_data = get_option( WPML_TM_ATE_Authentication::AMS_DATA_KEY, array() );
|
||||
|
||||
$this->statuses = array(
|
||||
WPML_TM_ATE_Authentication::AMS_STATUS_NON_ACTIVE => array(
|
||||
'type' => 'error',
|
||||
'message' => array(
|
||||
'status' => __( 'Advanced Translation Editor is not active yet', 'wpml-translation-management' ),
|
||||
'text' => __(
|
||||
'Request activation to receive an email with directions to activate the service.',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
),
|
||||
'button' => __( 'Request activation', 'wpml-translation-management' ),
|
||||
),
|
||||
WPML_TM_ATE_Authentication::AMS_STATUS_ENABLED => array(
|
||||
'type' => 'info',
|
||||
'message' => array(
|
||||
'status' => __( 'Advanced Translation Editor is being activated', 'wpml-translation-management' ),
|
||||
'text' => '',
|
||||
),
|
||||
'button' => '',
|
||||
),
|
||||
WPML_TM_ATE_Authentication::AMS_STATUS_ACTIVE => array(
|
||||
'type' => 'success',
|
||||
'message' => array(
|
||||
'status' => __( 'Advanced Translation Editor is enabled and active', 'wpml-translation-management' ),
|
||||
'text' => '',
|
||||
),
|
||||
'button' => __( 'Advanced Translation Editor is active', 'wpml-translation-management' ),
|
||||
),
|
||||
self::AMS_STATUS_ACTIVE_NOT_ALL_SUBSCRIBED => array(
|
||||
'type' => 'success',
|
||||
'message' => array(
|
||||
'status' => __( "WPML's Advanced Translation Editor is enabled, but not all your translators can use it.", 'wpml-translation-management' ),
|
||||
'text' => '',
|
||||
),
|
||||
'button' => __( 'Advanced Translation Editor is active', 'wpml-translation-management' ),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|WP_Error
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function get_auto_login() {
|
||||
$shared = null;
|
||||
if ( array_key_exists( 'shared', $this->authentication_data ) ) {
|
||||
$shared = $this->authentication_data['shared'];
|
||||
}
|
||||
|
||||
if ( $shared ) {
|
||||
$url = $this->endpoints->get_ams_auto_login();
|
||||
|
||||
$user = wp_get_current_user();
|
||||
|
||||
$user_email = $user->user_email;
|
||||
|
||||
return $this->authentication->get_signed_url_with_parameters(
|
||||
'GET',
|
||||
$url,
|
||||
array(
|
||||
'translation_manager' => $user_email,
|
||||
'return_url' => WPML_TM_Page::get_translators_url( array( 'refresh_subscriptions' => '1' ) ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return '#';
|
||||
}
|
||||
|
||||
public function get_status_HTML( $status, $all_users_have_subscription = true ) {
|
||||
if ( $status === WPML_TM_ATE_Authentication::AMS_STATUS_ACTIVE && ! $all_users_have_subscription ) {
|
||||
$status = self::AMS_STATUS_ACTIVE_NOT_ALL_SUBSCRIBED;
|
||||
}
|
||||
$message = $this->get_status_attribute( $status, 'message' );
|
||||
|
||||
return '<strong>' . $message['status'] . '</strong>' . $message['text'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function get_status() {
|
||||
$ate_status = WPML_TM_ATE_Authentication::AMS_STATUS_NON_ACTIVE;
|
||||
if ( array_key_exists( 'status', $this->authentication_data ) ) {
|
||||
$ate_status = $this->authentication_data['status'];
|
||||
}
|
||||
|
||||
return $ate_status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $attribute
|
||||
* @param null|mixed $default
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get_current_status_attribute( $attribute, $default = null ) {
|
||||
return $this->get_status_attribute( $this->get_status(), $attribute, $default );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $status
|
||||
* @param string $attribute
|
||||
* @param null|mixed $default
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get_status_attribute( $status, $attribute, $default = null ) {
|
||||
$status_attributes = $this->statuses[ $status ];
|
||||
|
||||
if ( array_key_exists( $attribute, $status_attributes ) ) {
|
||||
return $status_attributes[ $attribute ];
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
public function get_statuses() {
|
||||
return $this->statuses;
|
||||
}
|
||||
|
||||
public function get_synchronize_button_text() {
|
||||
return __( 'Synchronize translators and translation managers', 'wpml-translation-management' );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
|
||||
use WPML\TM\ATE\ClonedSites\Lock;
|
||||
|
||||
/**
|
||||
* @author OnTheGo Systems
|
||||
*/
|
||||
class WPML_TM_MCS_ATE extends WPML_Twig_Template_Loader {
|
||||
/**
|
||||
* @var WPML_TM_ATE_Authentication
|
||||
*/
|
||||
private $authentication;
|
||||
private $authentication_data;
|
||||
/**
|
||||
* @var WPML_TM_ATE_AMS_Endpoints
|
||||
*/
|
||||
private $endpoints;
|
||||
/**
|
||||
* @var WPML_TM_MCS_ATE_Strings
|
||||
*/
|
||||
private $strings;
|
||||
|
||||
private $model = array();
|
||||
|
||||
/**
|
||||
*
|
||||
* /**
|
||||
* WPML_TM_MCS_ATE constructor.
|
||||
*
|
||||
* @param WPML_TM_ATE_Authentication $authentication
|
||||
* @param WPML_TM_ATE_AMS_Endpoints $endpoints
|
||||
*
|
||||
* @param WPML_TM_MCS_ATE_Strings $strings
|
||||
*/
|
||||
public function __construct(
|
||||
WPML_TM_ATE_Authentication $authentication,
|
||||
WPML_TM_ATE_AMS_Endpoints $endpoints,
|
||||
WPML_TM_MCS_ATE_Strings $strings
|
||||
) {
|
||||
parent::__construct(
|
||||
array(
|
||||
$this->get_template_path(),
|
||||
)
|
||||
);
|
||||
|
||||
$this->authentication = $authentication;
|
||||
$this->endpoints = $endpoints;
|
||||
$this->strings = $strings;
|
||||
$this->authentication_data = get_option( WPML_TM_ATE_Authentication::AMS_DATA_KEY, array() );
|
||||
|
||||
$wpml_support = esc_html__( 'WPML support', 'wpml-translation-management' );
|
||||
$wpml_support_link = '<a target="_blank" rel="noopener" href="https://wpml.org/forums/forum/english-support/">' . $wpml_support . '</a>';
|
||||
|
||||
$this->model = [
|
||||
'status_button_text' => $this->get_status_button_text(),
|
||||
'synchronize_button_text' => $this->strings->get_synchronize_button_text(),
|
||||
'is_ate_communication_locked' => Lock::isLocked(),
|
||||
'strings' => [
|
||||
'error_help' => sprintf( esc_html__( 'Please try again in a few minutes. If the problem persists, please contact %s.', 'wpml-translation-management' ), $wpml_support_link ),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function get_template_path() {
|
||||
return WPML_TM_PATH . '/templates/ATE';
|
||||
}
|
||||
|
||||
public function init_hooks() {
|
||||
add_action( 'wpml_tm_mcs_' . ICL_TM_TMETHOD_ATE, array( $this, 'render' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $args
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_model( array $args = array() ) {
|
||||
if ( array_key_exists( 'wizard', $args ) ) {
|
||||
$this->model['strings']['error_help'] = esc_html__( 'You can continue the Translation Management configuration later by going to WPML -> Settings -> How to translate posts and pages.', 'wpml-translation-management' );
|
||||
}
|
||||
|
||||
return $this->model;
|
||||
}
|
||||
|
||||
public function render() {
|
||||
echo $this->get_template()
|
||||
->show( $this->get_model(), 'mcs-ate-controls.twig' );
|
||||
}
|
||||
|
||||
public function get_strings() {
|
||||
return $this->strings;
|
||||
}
|
||||
|
||||
private function has_translators() {
|
||||
/** @var TranslationManagement $iclTranslationManagement */
|
||||
global $iclTranslationManagement;
|
||||
|
||||
return $iclTranslationManagement->has_translators();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
private function get_status_button_text() {
|
||||
return $this->strings->get_current_status_attribute( 'button' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function get_script_data() {
|
||||
return array(
|
||||
'hasTranslators' => $this->has_translators(),
|
||||
'currentStatus' => $this->strings->get_status(),
|
||||
'statuses' => $this->strings->get_statuses(),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
class WPML_TM_MCS_Custom_Field_Settings_Menu_Factory {
|
||||
|
||||
/** @var WPML_Custom_Field_Setting_Factory $setting_factory */
|
||||
private $setting_factory;
|
||||
|
||||
/** @var WPML_UI_Unlock_Button $unlock_button */
|
||||
private $unlock_button;
|
||||
|
||||
/** @var WPML_Custom_Field_Setting_Query_Factory $query_factory */
|
||||
private $query_factory;
|
||||
|
||||
/**
|
||||
* @return WPML_TM_MCS_Post_Custom_Field_Settings_Menu
|
||||
*/
|
||||
public function create_post() {
|
||||
return new WPML_TM_MCS_Post_Custom_Field_Settings_Menu(
|
||||
$this->get_setting_factory(),
|
||||
$this->get_unlock_button(),
|
||||
$this->get_query_factory()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return WPML_TM_MCS_Term_Custom_Field_Settings_Menu
|
||||
*/
|
||||
public function create_term() {
|
||||
return new WPML_TM_MCS_Term_Custom_Field_Settings_Menu(
|
||||
$this->get_setting_factory(),
|
||||
$this->get_unlock_button(),
|
||||
$this->get_query_factory()
|
||||
);
|
||||
}
|
||||
|
||||
private function get_setting_factory() {
|
||||
global $iclTranslationManagement;
|
||||
|
||||
if ( null === $this->setting_factory ) {
|
||||
$this->setting_factory = new WPML_Custom_Field_Setting_Factory( $iclTranslationManagement );
|
||||
$this->setting_factory->show_system_fields = array_key_exists( 'show_system_fields', $_GET )
|
||||
? (bool) $_GET['show_system_fields'] : false;
|
||||
}
|
||||
|
||||
return $this->setting_factory;
|
||||
}
|
||||
|
||||
private function get_unlock_button() {
|
||||
if ( null === $this->unlock_button ) {
|
||||
$this->unlock_button = new WPML_UI_Unlock_Button();
|
||||
}
|
||||
|
||||
return $this->unlock_button;
|
||||
}
|
||||
|
||||
private function get_query_factory() {
|
||||
if ( null === $this->query_factory ) {
|
||||
$this->query_factory = new WPML_Custom_Field_Setting_Query_Factory();
|
||||
}
|
||||
|
||||
return $this->query_factory;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,293 @@
|
||||
<?php
|
||||
|
||||
abstract class WPML_TM_MCS_Custom_Field_Settings_Menu {
|
||||
|
||||
/** @var WPML_Custom_Field_Setting_Factory $settings_factory */
|
||||
protected $settings_factory;
|
||||
|
||||
/** @var WPML_UI_Unlock_Button $unlock_button_ui */
|
||||
private $unlock_button_ui;
|
||||
|
||||
/** @var WPML_Custom_Field_Setting_Query_Factory $query_factory */
|
||||
private $query_factory;
|
||||
|
||||
/** @var WPML_Custom_Field_Setting_Query $query */
|
||||
private $query;
|
||||
|
||||
/** @var string[] Custom field keys */
|
||||
private $custom_fields_keys;
|
||||
|
||||
/** @var int $total_keys */
|
||||
private $total_keys;
|
||||
|
||||
/** @var array Custom field options */
|
||||
private $custom_field_options;
|
||||
|
||||
/** @var int Initial setting of items per page */
|
||||
const ITEMS_PER_PAGE = 20;
|
||||
|
||||
public function __construct(
|
||||
WPML_Custom_Field_Setting_Factory $settings_factory,
|
||||
WPML_UI_Unlock_Button $unlock_button_ui,
|
||||
WPML_Custom_Field_Setting_Query_Factory $query_factory
|
||||
) {
|
||||
$this->settings_factory = $settings_factory;
|
||||
$this->unlock_button_ui = $unlock_button_ui;
|
||||
$this->query_factory = $query_factory;
|
||||
|
||||
$this->custom_field_options = array(
|
||||
WPML_IGNORE_CUSTOM_FIELD => __( "Don't translate", 'wpml-translation-management' ),
|
||||
WPML_COPY_CUSTOM_FIELD => __( 'Copy from original to translation', 'wpml-translation-management' ),
|
||||
WPML_COPY_ONCE_CUSTOM_FIELD => __( 'Copy once', 'wpml-translation-management' ),
|
||||
WPML_TRANSLATE_CUSTOM_FIELD => __( 'Translate', 'wpml-translation-management' ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* This will fetch the data from DB
|
||||
* depending on the user inputs (pagination/search)
|
||||
*
|
||||
* @param array $args
|
||||
*/
|
||||
public function init_data( array $args = array() ) {
|
||||
if ( null === $this->custom_fields_keys ) {
|
||||
$args = array_merge(
|
||||
array(
|
||||
'hide_system_fields' => ! $this->settings_factory->show_system_fields,
|
||||
'items_per_page' => self::ITEMS_PER_PAGE,
|
||||
'page' => 1,
|
||||
),
|
||||
$args
|
||||
);
|
||||
|
||||
$this->custom_fields_keys = $this->get_query()->get( $args );
|
||||
$this->total_keys = $this->get_query()->get_total_rows();
|
||||
|
||||
if ( $this->custom_fields_keys ) {
|
||||
natcasesort( $this->custom_fields_keys );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function render() {
|
||||
ob_start();
|
||||
?>
|
||||
<div class="wpml-section wpml-section-<?php echo esc_attr( $this->kind_shorthand() ); ?>-translation"
|
||||
id="ml-content-setup-sec-<?php echo esc_attr( $this->kind_shorthand() ); ?>">
|
||||
<div class="wpml-section-header">
|
||||
<h3><?php echo esc_html( $this->get_title() ); ?></h3>
|
||||
<p>
|
||||
<?php
|
||||
// We need htmlspecialchars() here only for testing, as DOMDocument::loadHTML() cannot parse url with '&'.
|
||||
$toggle_system_fields = array(
|
||||
'url' => htmlspecialchars(
|
||||
add_query_arg(
|
||||
array(
|
||||
'show_system_fields' => ! $this->settings_factory->show_system_fields,
|
||||
),
|
||||
admin_url( 'admin.php?page=' . WPML_TM_FOLDER . WPML_Translation_Management::PAGE_SLUG_SETTINGS ) . '#ml-content-setup-sec-' . $this->kind_shorthand()
|
||||
)
|
||||
),
|
||||
'text' => $this->settings_factory->show_system_fields ?
|
||||
__( 'Hide system fields', 'wpml-translation-management' ) :
|
||||
__( 'Show system fields', 'wpml-translation-management' ),
|
||||
);
|
||||
?>
|
||||
<a href="<?php echo esc_url( $toggle_system_fields['url'] ); ?>"><?php echo esc_html( $toggle_system_fields['text'] ); ?></a>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
<div class="wpml-section-content wpml-section-content-wide">
|
||||
<form
|
||||
id="icl_<?php echo esc_attr( $this->kind_shorthand() ); ?>_translation"
|
||||
data-type="<?php echo esc_attr( $this->kind_shorthand() ); ?>"
|
||||
name="icl_<?php echo esc_attr( $this->kind_shorthand() ); ?>_translation"
|
||||
class="wpml-custom-fields-settings" action="">
|
||||
<?php wp_nonce_field( 'icl_' . $this->kind_shorthand() . '_translation_nonce', '_icl_nonce' ); ?>
|
||||
<?php
|
||||
if ( empty( $this->custom_fields_keys ) ) {
|
||||
?>
|
||||
<p class="no-data-found">
|
||||
<?php echo esc_html( $this->get_no_data_message() ); ?>
|
||||
</p>
|
||||
<?php
|
||||
} else {
|
||||
?>
|
||||
|
||||
<div class="wpml-flex-table wpml-translation-setup-table wpml-margin-top-sm">
|
||||
|
||||
<?php echo $this->render_heading(); ?>
|
||||
|
||||
<div class="wpml-flex-table-body">
|
||||
<?php
|
||||
$this->render_body();
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
$this->render_pagination( self::ITEMS_PER_PAGE, 1 );
|
||||
?>
|
||||
|
||||
<p class="buttons-wrap">
|
||||
<span class="icl_ajx_response"
|
||||
id="icl_ajx_response_<?php echo esc_attr( $this->kind_shorthand() ); ?>"></span>
|
||||
<input type="submit" class="button-primary"
|
||||
value="<?php echo esc_attr__( 'Save', 'wpml-translation-management' ); ?>"/>
|
||||
</p>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</form>
|
||||
</div>
|
||||
<!-- .wpml-section-content -->
|
||||
</div> <!-- .wpml-section -->
|
||||
<?php
|
||||
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
abstract protected function kind_shorthand();
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
abstract protected function get_title();
|
||||
|
||||
abstract protected function get_meta_type();
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
*
|
||||
* @return WPML_Custom_Field_Setting
|
||||
*/
|
||||
abstract protected function get_setting( $key );
|
||||
|
||||
private function render_radio( $cf_key, $html_disabled, $status, $ref_status ) {
|
||||
ob_start();
|
||||
?>
|
||||
<input type="radio" name="<?php echo $this->get_radio_name( $cf_key ); ?>"
|
||||
value="<?php echo esc_attr( $ref_status ); ?>"
|
||||
title="<?php echo esc_attr( $ref_status ); ?>" <?php echo $html_disabled; ?>
|
||||
<?php
|
||||
if ( $status == $ref_status ) :
|
||||
?>
|
||||
checked<?php endif; ?> />
|
||||
<?php
|
||||
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
private function get_radio_name( $cf_key ) {
|
||||
return 'cf[' . esc_attr( base64_encode( $cf_key ) ) . ']';
|
||||
}
|
||||
|
||||
private function get_unlock_name( $cf_key ) {
|
||||
return 'cf_unlocked[' . esc_attr( base64_encode( $cf_key ) ) . ']';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string header and footer of the setting table
|
||||
*/
|
||||
private function render_heading() {
|
||||
ob_start();
|
||||
?>
|
||||
<div class="wpml-flex-table-header wpml-flex-table-sticky">
|
||||
<?php $this->render_search(); ?>
|
||||
<div class="wpml-flex-table-row">
|
||||
<div class="wpml-flex-table-cell name">
|
||||
<?php echo esc_html( $this->get_column_header( 'name' ) ); ?>
|
||||
</div>
|
||||
<div class="wpml-flex-table-cell text-center">
|
||||
<?php echo esc_html__( "Don't translate", 'wpml-translation-management' ); ?>
|
||||
</div>
|
||||
<div class="wpml-flex-table-cell text-center">
|
||||
<?php echo esc_html_x( 'Copy', 'Verb', 'wpml-translation-management' ); ?>
|
||||
</div>
|
||||
<div class="wpml-flex-table-cell text-center">
|
||||
<?php echo esc_html__( 'Copy once', 'wpml-translation-management' ); ?>
|
||||
</div>
|
||||
<div class="wpml-flex-table-cell text-center">
|
||||
<?php echo esc_html__( 'Translate', 'wpml-translation-management' ); ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Render search box for Custom Field Settings.
|
||||
*
|
||||
* @param string $search_string Search String.
|
||||
*/
|
||||
public function render_search( $search_string = '' ) {
|
||||
$search = new WPML_TM_MCS_Search_Factory();
|
||||
echo $search->create( $search_string )->render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Render body of Custom Field Settings.
|
||||
*/
|
||||
public function render_body() {
|
||||
foreach ( $this->custom_fields_keys as $cf_key ) {
|
||||
$setting = $this->get_setting( $cf_key );
|
||||
$status = $setting->status();
|
||||
$html_disabled = $setting->is_read_only() && ! $setting->is_unlocked() ? 'disabled="disabled"' : '';
|
||||
?>
|
||||
<div class="wpml-flex-table-row">
|
||||
<div class="wpml-flex-table-cell name">
|
||||
<?php
|
||||
$this->unlock_button_ui->render( $setting->is_read_only(), $setting->is_unlocked(), $this->get_radio_name( $cf_key ), $this->get_unlock_name( $cf_key ) );
|
||||
echo esc_html( $cf_key );
|
||||
?>
|
||||
</div>
|
||||
<?php
|
||||
foreach ( $this->custom_field_options as $ref_status => $title ) {
|
||||
?>
|
||||
<div class="wpml-flex-table-cell text-center">
|
||||
<?php
|
||||
echo $this->render_radio( $cf_key, $html_disabled, $status, $ref_status );
|
||||
?>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render pagination for Custom Field Settings.
|
||||
*
|
||||
* @param int $items_per_page Items per page to display.
|
||||
* @param int $current_page Which page to display.
|
||||
*/
|
||||
public function render_pagination( $items_per_page, $current_page ) {
|
||||
$pagination = new WPML_TM_MCS_Pagination_Render_Factory( $items_per_page );
|
||||
echo $pagination->create( $this->total_keys, $current_page )->render();
|
||||
}
|
||||
|
||||
abstract public function get_no_data_message();
|
||||
|
||||
abstract public function get_column_header( $id );
|
||||
|
||||
/**
|
||||
* @return WPML_Custom_Field_Setting_Query
|
||||
*/
|
||||
private function get_query() {
|
||||
if ( null === $this->query ) {
|
||||
$this->query = $this->query_factory->create( $this->get_meta_type() );
|
||||
}
|
||||
|
||||
return $this->query;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Class WPML_TM_MCS_Pagination_Ajax_Factory
|
||||
*/
|
||||
class WPML_TM_MCS_Pagination_Ajax_Factory implements IWPML_AJAX_Action_Loader {
|
||||
|
||||
/**
|
||||
* Create MCS Pagination.
|
||||
*
|
||||
* @return WPML_TM_MCS_Pagination_Ajax
|
||||
*/
|
||||
public function create() {
|
||||
return new WPML_TM_MCS_Pagination_Ajax( new WPML_TM_MCS_Custom_Field_Settings_Menu_Factory() );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Class WPML_TM_MCS_Pagination_Ajax
|
||||
*/
|
||||
class WPML_TM_MCS_Pagination_Ajax {
|
||||
|
||||
/** @var WPML_TM_MCS_Custom_Field_Settings_Menu_Factory */
|
||||
private $menu_factory;
|
||||
|
||||
public function __construct( WPML_TM_MCS_Custom_Field_Settings_Menu_Factory $menu_factory ) {
|
||||
$this->menu_factory = $menu_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define Ajax hooks.
|
||||
*/
|
||||
public function add_hooks() {
|
||||
add_action( 'wp_ajax_wpml_update_mcs_cf', array( $this, 'update_mcs_cf' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Update custom fields form.
|
||||
*/
|
||||
public function update_mcs_cf() {
|
||||
if ( isset( $_POST['nonce'] ) && wp_verify_nonce( $_POST['nonce'], 'icl_' . $_POST['type'] . '_translation_nonce' ) ) {
|
||||
$args = array(
|
||||
'items_per_page' => intval( $_POST['items_per_page'] ),
|
||||
'page' => intval( $_POST['paged'] ),
|
||||
'search' => isset( $_POST['search'] ) ? sanitize_text_field( $_POST['search'] ) : '',
|
||||
'hide_system_fields' => ! isset( $_POST['show_system_fields'] ) || ! filter_var( $_POST['show_system_fields'], FILTER_VALIDATE_BOOLEAN ),
|
||||
);
|
||||
|
||||
$menu_item = null;
|
||||
|
||||
if ( 'cf' === $_POST['type'] ) {
|
||||
$menu_item = $this->menu_factory->create_post();
|
||||
} elseif ( 'tcf' === $_POST['type'] ) {
|
||||
$menu_item = $this->menu_factory->create_term();
|
||||
}
|
||||
|
||||
if ( $menu_item ) {
|
||||
$result = array();
|
||||
ob_start();
|
||||
$menu_item->init_data( $args );
|
||||
$menu_item->render_body();
|
||||
$result['body'] = ob_get_clean();
|
||||
|
||||
ob_start();
|
||||
$menu_item->render_pagination( $args['items_per_page'], $args['page'] );
|
||||
$result['pagination'] = ob_get_clean();
|
||||
|
||||
wp_send_json_success( $result );
|
||||
}
|
||||
}
|
||||
wp_send_json_error(
|
||||
array(
|
||||
'message' => __( 'Invalid Request.', 'wpml-translation-management' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Class WPML_TM_MCS_Pagination_Render_Factory
|
||||
*/
|
||||
class WPML_TM_MCS_Pagination_Render_Factory {
|
||||
/**
|
||||
* @var int Items per page
|
||||
*/
|
||||
private $items_per_page;
|
||||
|
||||
/**
|
||||
* WPML_TM_MCS_Pagination_Render_Factory constructor.
|
||||
*
|
||||
* @param int $items_per_page
|
||||
*/
|
||||
public function __construct( $items_per_page ) {
|
||||
$this->items_per_page = $items_per_page;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $items_per_page
|
||||
* @param $total_items
|
||||
* @param int $current_page
|
||||
*
|
||||
* @return WPML_TM_MCS_Pagination_Render
|
||||
*/
|
||||
public function create( $total_items, $current_page = 1 ) {
|
||||
$pagination = new WPML_Admin_Pagination();
|
||||
$pagination->set_items_per_page( $this->items_per_page );
|
||||
$pagination->set_total_items( $total_items );
|
||||
$pagination->set_current_page( $current_page );
|
||||
|
||||
$template = new WPML_Twig_Template_Loader(
|
||||
array(
|
||||
WPML_TM_PATH . '/templates/menus/mcsetup',
|
||||
)
|
||||
);
|
||||
|
||||
return new WPML_TM_MCS_Pagination_Render( $template->get_template(), $pagination );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
/**
|
||||
* WPML_TM_MCS_Pagination_Render class file.
|
||||
*
|
||||
* @package wpml-translation-management
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class WPML_TM_MCS_Pagination_Render
|
||||
*/
|
||||
class WPML_TM_MCS_Pagination_Render {
|
||||
|
||||
/**
|
||||
* Twig template path.
|
||||
*/
|
||||
const TM_MCS_PAGINATION_TEMPLATE = 'tm-mcs-pagination.twig';
|
||||
|
||||
/**
|
||||
* Twig template service.
|
||||
*
|
||||
* @var IWPML_Template_Service
|
||||
*/
|
||||
private $template;
|
||||
|
||||
/**
|
||||
* Admin pagination instance.
|
||||
*
|
||||
* @var WPML_Admin_Pagination
|
||||
*/
|
||||
private $pagination;
|
||||
|
||||
/**
|
||||
* Items per page.
|
||||
*
|
||||
* @var int Items per page
|
||||
*/
|
||||
private $items_per_page;
|
||||
|
||||
/**
|
||||
* Total items.
|
||||
*
|
||||
* @var int Total items
|
||||
*/
|
||||
private $total_items;
|
||||
|
||||
/**
|
||||
* Current page number.
|
||||
*
|
||||
* @var int Current page
|
||||
*/
|
||||
private $current_page;
|
||||
|
||||
/**
|
||||
* Total number of pages.
|
||||
*
|
||||
* @var int Total pages
|
||||
*/
|
||||
private $total_pages;
|
||||
|
||||
/**
|
||||
* WPML_TM_MCS_Pagination_Render constructor.
|
||||
*
|
||||
* @param IWPML_Template_Service $template Twig template service.
|
||||
* @param WPML_Admin_Pagination $pagination Admin pagination object.
|
||||
*/
|
||||
public function __construct( IWPML_Template_Service $template, WPML_Admin_Pagination $pagination ) {
|
||||
$this->template = $template;
|
||||
$this->pagination = $pagination;
|
||||
$this->items_per_page = $pagination->get_items_per_page();
|
||||
$this->total_items = $pagination->get_total_items();
|
||||
$this->current_page = $pagination->get_current_page();
|
||||
$this->total_pages = $pagination->get_total_pages();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get twig model.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_model() {
|
||||
$from = min( ( $this->current_page - 1 ) * $this->items_per_page + 1, $this->total_items );
|
||||
$to = min( $this->current_page * $this->items_per_page, $this->total_items );
|
||||
if ( - 1 === $this->current_page ) {
|
||||
$from = min( 1, $this->total_items );
|
||||
$to = $this->total_items;
|
||||
}
|
||||
|
||||
$model = array(
|
||||
'strings' => array(
|
||||
'displaying' => __( 'Displaying', 'wpml-translation-management' ),
|
||||
'of' => __( 'of', 'wpml-translation-management' ),
|
||||
'display_all' => __( 'Display all results', 'wpml-translation-management' ),
|
||||
'display_less' => __( 'Display 20 results per page', 'wpml-translation-management' ),
|
||||
'nothing_found' => __( 'Nothing found', 'wpml-translation-management' ),
|
||||
),
|
||||
'pagination' => $this->pagination,
|
||||
'from' => number_format_i18n( $from ),
|
||||
'to' => number_format_i18n( $to ),
|
||||
'current_page' => $this->current_page,
|
||||
'total_items' => $this->total_items,
|
||||
'total_items_i18n' => number_format_i18n( $this->total_items ),
|
||||
'total_pages' => $this->total_pages,
|
||||
'paginate_links' => $this->paginate_links(),
|
||||
'select' => array( 10, 20, 50, 100 ),
|
||||
'select_value' => $this->items_per_page,
|
||||
);
|
||||
|
||||
return $model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render model via twig.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function render() {
|
||||
return $this->template->show( $this->get_model(), self::TM_MCS_PAGINATION_TEMPLATE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Paginate links.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function paginate_links() {
|
||||
$page_links = array();
|
||||
|
||||
if ( - 1 === $this->current_page ) {
|
||||
return $page_links;
|
||||
}
|
||||
|
||||
if ( $this->total_pages < 2 ) {
|
||||
return $page_links;
|
||||
}
|
||||
|
||||
$end_size = 1;
|
||||
$mid_size = 2;
|
||||
$dots = false;
|
||||
|
||||
for ( $n = 1; $n <= $this->total_pages; $n ++ ) {
|
||||
if ( $n === $this->current_page ) {
|
||||
$page_links[] = array(
|
||||
'class' => 'current',
|
||||
'number' => number_format_i18n( $n ),
|
||||
);
|
||||
} else {
|
||||
if (
|
||||
$n <= $end_size ||
|
||||
( $this->current_page && $n >= $this->current_page - $mid_size && $n <= $this->current_page + $mid_size ) ||
|
||||
$n > $this->total_pages - $end_size
|
||||
) {
|
||||
$page_links[] = array(
|
||||
'class' => '',
|
||||
'number' => number_format_i18n( $n ),
|
||||
);
|
||||
$dots = true;
|
||||
} elseif ( $dots ) {
|
||||
$page_links[] = array(
|
||||
'class' => 'dots',
|
||||
'number' => '…',
|
||||
);
|
||||
$dots = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $page_links;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
class WPML_TM_MCS_Post_Custom_Field_Settings_Menu extends WPML_TM_MCS_Custom_Field_Settings_Menu {
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function get_meta_type() {
|
||||
return WPML_Custom_Field_Setting_Query_Factory::TYPE_POSTMETA;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
*
|
||||
* @return WPML_Post_Custom_Field_Setting
|
||||
*/
|
||||
protected function get_setting( $key ) {
|
||||
return $this->settings_factory->post_meta_setting( $key );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function get_title() {
|
||||
return __( 'Custom Fields Translation', 'wpml-translation-management' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function kind_shorthand() {
|
||||
return 'cf';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function get_no_data_message() {
|
||||
return __( 'No custom fields found. It is possible that they will only show up here after you add more posts after installing a new plugin.', 'wpml-translation-management' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $id
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_column_header( $id ) {
|
||||
$header = $id;
|
||||
if('name' === $id) {
|
||||
$header = __( 'Custom fields', 'wpml-translation-management' );
|
||||
}
|
||||
return $header;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Class WPML_TM_MCS_Search_Factory
|
||||
*/
|
||||
class WPML_TM_MCS_Search_Factory {
|
||||
/**
|
||||
* Create MCS Search.
|
||||
*
|
||||
* @param string $search_string
|
||||
*
|
||||
* @return WPML_TM_MCS_Search_Render
|
||||
*/
|
||||
public function create( $search_string = '' ) {
|
||||
$template = new WPML_Twig_Template_Loader(
|
||||
array(
|
||||
WPML_TM_PATH . '/templates/menus/mcsetup',
|
||||
)
|
||||
);
|
||||
|
||||
return new WPML_TM_MCS_Search_Render( $template->get_template(), $search_string );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Class WPML_TM_MCS_Search_Render
|
||||
*/
|
||||
class WPML_TM_MCS_Search_Render {
|
||||
|
||||
/**
|
||||
* Twig template path.
|
||||
*/
|
||||
const TM_MCS_SEARCH_TEMPLATE = 'tm-mcs-search.twig';
|
||||
|
||||
/**
|
||||
* @var IWPML_Template_Service
|
||||
*/
|
||||
private $template;
|
||||
|
||||
/**
|
||||
* @var string Search string
|
||||
*/
|
||||
private $search_string;
|
||||
|
||||
/**
|
||||
* WPML_TM_MCS_Search_Render constructor.
|
||||
*
|
||||
* @param IWPML_Template_Service $template Twig template service.
|
||||
* @param string $search_string Search string.
|
||||
*/
|
||||
public function __construct( IWPML_Template_Service $template, $search_string ) {
|
||||
$this->template = $template;
|
||||
$this->search_string = $search_string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get twig model.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_model() {
|
||||
$model = array(
|
||||
'strings' => array(
|
||||
'search_for' => __( 'Search for', 'wpml-translation-management' ),
|
||||
),
|
||||
'search_string' => $this->search_string,
|
||||
);
|
||||
|
||||
return $model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render model via twig.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function render() {
|
||||
return $this->template->show( $this->get_model(), self::TM_MCS_SEARCH_TEMPLATE );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
abstract class WPML_TM_MCS_Section_UI {
|
||||
|
||||
private $id;
|
||||
private $title;
|
||||
|
||||
public function __construct( $id, $title ) {
|
||||
$this->id = $id;
|
||||
$this->title = $title;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function get_id() {
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function add_hooks() {
|
||||
add_filter( 'wpml_mcsetup_navigation_links', array( $this, 'mcsetup_navigation_links' ) );
|
||||
}
|
||||
|
||||
public function mcsetup_navigation_links( array $mcsetup_sections ) {
|
||||
$mcsetup_sections[ $this->id ] = esc_html( $this->title );
|
||||
|
||||
return $mcsetup_sections;
|
||||
}
|
||||
|
||||
public function render() {
|
||||
$output = '';
|
||||
|
||||
$output .= '<div class="wpml-section" id="' . esc_attr( $this->id ) . '">';
|
||||
$output .= '<div class="wpml-section-header">';
|
||||
$output .= '<h3>' . esc_html( $this->title ) . '</h3>';
|
||||
$output .= '</div>';
|
||||
$output .= '<div class="wpml-section-content">';
|
||||
$output .= $this->render_content();
|
||||
$output .= '</div>';
|
||||
$output .= '</div>';
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
abstract protected function render_content();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
class WPML_TM_MCS_Term_Custom_Field_Settings_Menu extends WPML_TM_MCS_Custom_Field_Settings_Menu {
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function get_meta_type() {
|
||||
return WPML_Custom_Field_Setting_Query_Factory::TYPE_TERMMETA;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
*
|
||||
* @return WPML_Term_Custom_Field_Setting
|
||||
*/
|
||||
protected function get_setting( $key ) {
|
||||
return $this->settings_factory->term_meta_setting( $key );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function get_title() {
|
||||
return __( 'Custom Term Meta Translation', 'wpml-translation-management' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function kind_shorthand() {
|
||||
return 'tcf';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function get_no_data_message() {
|
||||
return __( 'No term meta found. It is possible that they will only show up here after you add/create them.', 'wpml-translation-management' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $id
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_column_header( $id ) {
|
||||
$header = $id;
|
||||
if ( 'name' === $id ) {
|
||||
$header = __( 'Term Meta', 'wpml-translation-management' );
|
||||
}
|
||||
return $header;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
class WPML_TM_Options_Ajax {
|
||||
|
||||
const NONCE_TRANSLATED_DOCUMENT = 'wpml-translated-document-options-nonce';
|
||||
|
||||
private $sitepress;
|
||||
|
||||
public function __construct( SitePress $sitepress ) {
|
||||
$this->sitepress = $sitepress;
|
||||
}
|
||||
|
||||
public function ajax_hooks() {
|
||||
add_action( 'wp_ajax_wpml_translated_document_options', array( $this, 'wpml_translated_document_options' ) );
|
||||
}
|
||||
|
||||
public function wpml_translated_document_options() {
|
||||
|
||||
if ( ! $this->is_valid_request() ) {
|
||||
wp_send_json_error();
|
||||
} else {
|
||||
$settings = $this->sitepress->get_settings();
|
||||
|
||||
if ( array_key_exists( 'document_status', $_POST ) ) {
|
||||
$settings['translated_document_status'] = filter_var( $_POST['document_status'], FILTER_SANITIZE_NUMBER_INT, FILTER_NULL_ON_FAILURE );
|
||||
}
|
||||
if ( array_key_exists( 'page_url', $_POST ) ) {
|
||||
$settings['translated_document_page_url'] = filter_var( $_POST['page_url'], FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_NULL_ON_FAILURE );
|
||||
}
|
||||
if ( $settings ) {
|
||||
$this->sitepress->save_settings( $settings );
|
||||
}
|
||||
|
||||
wp_send_json_success();
|
||||
}
|
||||
}
|
||||
|
||||
private function is_valid_request() {
|
||||
$valid_request = true;
|
||||
if ( ! array_key_exists( 'nonce', $_POST ) ) {
|
||||
$valid_request = false;
|
||||
}
|
||||
if ( $valid_request ) {
|
||||
$nonce = $_POST['nonce'];
|
||||
$nonce_is_valid = wp_verify_nonce( $nonce, self::NONCE_TRANSLATED_DOCUMENT );
|
||||
if ( ! $nonce_is_valid ) {
|
||||
$valid_request = false;
|
||||
}
|
||||
}
|
||||
return $valid_request;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
class WPML_TM_Pickup_Mode_Ajax {
|
||||
|
||||
const NONCE_PICKUP_MODE = 'wpml_save_translation_pickup_mode';
|
||||
|
||||
/**
|
||||
* @var SitePress
|
||||
*/
|
||||
private $sitepress;
|
||||
|
||||
/**
|
||||
* @var WPML_Update_PickUp_Method
|
||||
*/
|
||||
private $update_pickup_mode;
|
||||
|
||||
/**
|
||||
* @var WPML_Pro_Translation
|
||||
*/
|
||||
private $icl_pro_translation;
|
||||
|
||||
public function __construct( SitePress $sitepress, WPML_Pro_Translation $icl_pro_translation ) {
|
||||
$this->sitepress = $sitepress;
|
||||
$this->icl_pro_translation = $icl_pro_translation;
|
||||
$this->update_pickup_mode = new WPML_Update_PickUp_Method( $this->sitepress );
|
||||
}
|
||||
|
||||
public function ajax_hooks() {
|
||||
add_action( 'wp_ajax_wpml_save_translation_pickup_mode', array( $this, 'wpml_save_translation_pickup_mode' ) );
|
||||
}
|
||||
|
||||
public function wpml_save_translation_pickup_mode() {
|
||||
try {
|
||||
if ( ! $this->is_valid_request() ) {
|
||||
throw new InvalidArgumentException('Request is not valid');
|
||||
}
|
||||
|
||||
if ( ! array_key_exists('pickup_mode', $_POST ) ) {
|
||||
throw new InvalidArgumentException();
|
||||
}
|
||||
|
||||
$available_pickup_modes = array( 0, 1 );
|
||||
$pickup_mode = filter_var( $_POST['pickup_mode'], FILTER_SANITIZE_NUMBER_INT, FILTER_NULL_ON_FAILURE );
|
||||
|
||||
if ( ! in_array( (int) $pickup_mode, $available_pickup_modes, true ) ) {
|
||||
throw new InvalidArgumentException();
|
||||
}
|
||||
|
||||
$data['icl_translation_pickup_method'] = $pickup_mode;
|
||||
$this->update_pickup_mode->update_pickup_method( $data, $this->icl_pro_translation->get_current_project() );
|
||||
|
||||
wp_send_json_success();
|
||||
} catch ( InvalidArgumentException $e ) {
|
||||
wp_send_json_error();
|
||||
}
|
||||
}
|
||||
|
||||
private function is_valid_request() {
|
||||
$valid_request = true;
|
||||
if ( ! array_key_exists( 'nonce', $_POST ) ) {
|
||||
$valid_request = false;
|
||||
}
|
||||
if ( $valid_request ) {
|
||||
$nonce = $_POST['nonce'];
|
||||
$nonce_is_valid = wp_verify_nonce( $nonce, self::NONCE_PICKUP_MODE );
|
||||
if ( ! $nonce_is_valid ) {
|
||||
$valid_request = false;
|
||||
}
|
||||
}
|
||||
return $valid_request;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
class WPML_Translate_Link_Targets_UI extends WPML_TM_MCS_Section_UI {
|
||||
const ID = 'ml-content-setup-sec-links-target';
|
||||
|
||||
/** @var WPDB $wpdb */
|
||||
private $wpdb;
|
||||
/** @var WPML_Pro_Translation $pro_translation */
|
||||
private $pro_translation;
|
||||
/** @var WPML_WP_API $wp_api */
|
||||
private $wp_api;
|
||||
/** @var SitePress $sitepress */
|
||||
private $sitepress;
|
||||
|
||||
public function __construct( $title, $wpdb, $sitepress, $pro_translation ) {
|
||||
parent::__construct( self::ID, $title );
|
||||
$this->wpdb = $wpdb;
|
||||
$this->pro_translation = $pro_translation;
|
||||
$this->sitepress = $sitepress;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function render_content() {
|
||||
$output = '';
|
||||
|
||||
$main_message = __( 'Adjust links in posts so they point to the translated content', 'wpml-translation-management' );
|
||||
$complete_message = __( 'All posts have been processed. %s links were changed to point to the translated content.', 'wpml-translation-management' );
|
||||
$string_count = 0;
|
||||
|
||||
$posts = new WPML_Translate_Link_Targets_In_Posts_Global( new WPML_Translate_Link_Target_Global_State( $this->sitepress ), $this->wpdb, $this->pro_translation );
|
||||
$post_count = $posts->get_number_to_be_fixed();
|
||||
|
||||
if ( defined( 'WPML_ST_VERSION' ) ) {
|
||||
$strings = new WPML_Translate_Link_Targets_In_Strings_Global( new WPML_Translate_Link_Target_Global_State( $this->sitepress ), $this->wpdb, $this->wp_api, $this->pro_translation );
|
||||
$string_count = $strings->get_number_to_be_fixed();
|
||||
$main_message = __( 'Adjust links in posts and strings so they point to the translated content', 'wpml-translation-management' );
|
||||
$complete_message = __( 'All posts and strings have been processed. %s links were changed to point to the translated content.', 'wpml-translation-management' );
|
||||
}
|
||||
|
||||
$data_attributes = array(
|
||||
'post-message' => esc_attr__( 'Processing posts... %1$s of %2$s done.', 'wpml-translation-management' ),
|
||||
'post-count' => $post_count,
|
||||
'string-message' => esc_attr__( 'Processing strings... %1$s of %2$s done.', 'wpml-translation-management' ),
|
||||
'string-count' => $string_count,
|
||||
'complete-message' => esc_attr( $complete_message ),
|
||||
);
|
||||
|
||||
$output .= '<p>' . $main_message . '</p>';
|
||||
$output .= '<button id="wpml-scan-link-targets" class="button-secondary"';
|
||||
|
||||
foreach ( $data_attributes as $key => $value ) {
|
||||
$output .= ' data-' . $key . '="' . $value . '"';
|
||||
}
|
||||
$output .= '>' . esc_html__( 'Scan now and adjust links', 'wpml-translation-management' ) . '</button>';
|
||||
$output .= '<span class="spinner"> </span>';
|
||||
$output .= '<p class="results"> </p>';
|
||||
$output .= wp_nonce_field( 'WPML_Ajax_Update_Link_Targets', 'wpml-translate-link-targets', true, false );
|
||||
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Class WPML_TM_Polling_Box
|
||||
*/
|
||||
class WPML_TM_Polling_Box {
|
||||
|
||||
/**
|
||||
* Renders the html for the TM polling pickup box
|
||||
* @uses $GLOBALS['sitepress']
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function render() {
|
||||
return '<div id="wpml-tm-polling-wrap"></div>';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Class WPML_TM_Last_Picked_Up
|
||||
*/
|
||||
class WPML_TM_Last_Picked_Up {
|
||||
|
||||
/**
|
||||
* @var Sitepress $sitepress
|
||||
*/
|
||||
private $sitepress;
|
||||
|
||||
/**
|
||||
* WPML_TM_Last_Picked_Up constructor.
|
||||
*
|
||||
* @param Sitepress $sitepress
|
||||
*/
|
||||
public function __construct( $sitepress ) {
|
||||
$this->sitepress = $sitepress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get last_picked_up setting.
|
||||
*
|
||||
* @return bool|mixed
|
||||
*/
|
||||
public function get() {
|
||||
return $this->sitepress->get_setting( 'last_picked_up' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get last_picked_up setting as formatted string.
|
||||
*
|
||||
* @param string $format
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_formatted( $format = 'Y, F jS @g:i a' ) {
|
||||
$last_picked_up = $this->get();
|
||||
$last_time_picked_up = ! empty( $last_picked_up ) ?
|
||||
date_i18n( $format, $last_picked_up ) :
|
||||
__( 'never', 'wpml-translation-management' );
|
||||
|
||||
return $last_time_picked_up;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set last_picked_up setting.
|
||||
*/
|
||||
public function set() {
|
||||
$this->sitepress->set_setting( 'last_picked_up', current_time( 'timestamp', true ), true );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
<?php
|
||||
|
||||
namespace WPML\TM\Menu\TranslationBasket;
|
||||
|
||||
use WPML\Notices\DismissNotices;
|
||||
|
||||
class Strings {
|
||||
|
||||
const ATE_AUTOMATIC_TRANSLATION_SUGGESTION = 'wpml-ate-automatic-translation-suggestion';
|
||||
|
||||
/** @var Utility */
|
||||
private $utility;
|
||||
|
||||
/** @var DismissNotices */
|
||||
private $dismissNotices;
|
||||
|
||||
/**
|
||||
* @param Utility $utility
|
||||
* @param DismissNotices $dismissNotices
|
||||
*/
|
||||
public function __construct( Utility $utility, DismissNotices $dismissNotices ) {
|
||||
$this->utility = $utility;
|
||||
$this->dismissNotices = $dismissNotices;
|
||||
}
|
||||
|
||||
|
||||
public function getAll() {
|
||||
$isCurrentUserOnlyTranslator = $this->utility->isTheOnlyAvailableTranslator();
|
||||
|
||||
return [
|
||||
'jobs_sent_to_local_translator' => $this->jobsSentToLocalTranslator(),
|
||||
'jobs_emails_local_did_not_sent' => $this->emailNotSentError(),
|
||||
'jobs_committed' => $isCurrentUserOnlyTranslator ? $this->jobsSentToCurrentUserWhoIsTheOnlyTranslator() : $this->jobsSentDefaultMessage(),
|
||||
'jobs_committing' => __( 'Working...', 'wpml-translation-management' ),
|
||||
'error_occurred' => __( 'An error occurred:', 'wpml-translation-management' ),
|
||||
'error_not_allowed' => __(
|
||||
'You are not allowed to run this action.',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
'batch' => __( 'Batch', 'wpml-translation-management' ),
|
||||
'error_no_translators' => __( 'No selected translators!', 'wpml-translation-management' ),
|
||||
'rollbacks' => __( 'Rollback jobs...', 'wpml-translation-management' ),
|
||||
'rolled' => __( 'Batch rolled back', 'wpml-translation-management' ),
|
||||
'errors' => __( 'Errors:', 'wpml-translation-management' ),
|
||||
'sending_batch' => $isCurrentUserOnlyTranslator ?
|
||||
__( 'Preparing your content for translation', 'wpml-translation-management' )
|
||||
: __( 'Sending your jobs to translation', 'wpml-translation-management' ),
|
||||
'sending_batch_to_ts' => __(
|
||||
'Sending your jobs to professional translation',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function duplicatePostTranslationWarning() {
|
||||
$message = esc_html_x(
|
||||
'You are about to translate duplicated posts.',
|
||||
'1/2 Confirm to disconnect duplicates',
|
||||
'wpml-translation-management'
|
||||
);
|
||||
|
||||
$message .= "\n";
|
||||
|
||||
$message .= esc_html_x(
|
||||
'These items will be automatically disconnected from originals, so translation is not lost when you update the originals.',
|
||||
'2/2 Confirm to disconnect duplicates',
|
||||
'wpml-translation-management'
|
||||
);
|
||||
|
||||
return $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function jobsSentToLocalTranslator() {
|
||||
$translation_dashboard_url = admin_url( 'admin.php?page=' . WPML_TM_FOLDER . '/menu/main.php' );
|
||||
$translation_dashboard_text = esc_html__( 'Translation Dashboard', 'wpml-translation-management' );
|
||||
$translation_dashboard_link = '<a href="' . esc_url( $translation_dashboard_url ) . '">' . $translation_dashboard_text . '</a>';
|
||||
|
||||
$translation_notifications_url = admin_url( 'admin.php?page=' . WPML_TM_FOLDER . \WPML_Translation_Management::PAGE_SLUG_SETTINGS . '&sm=notifications' );
|
||||
$translation_notifications_text = esc_html__(
|
||||
'WPML->Settings->Translation notifications',
|
||||
'wpml-translation-management'
|
||||
);
|
||||
$translation_notifications_link = '<a href="' . esc_url( $translation_notifications_url ) . '">' . $translation_notifications_text . '</a>';
|
||||
|
||||
$template = '
|
||||
<p>%1$s</p>
|
||||
<ul>
|
||||
<li>%2$s<</li>
|
||||
<li>%3$s</li>
|
||||
<li>%4$s</li>
|
||||
<li>%5$s</li>
|
||||
</ul>
|
||||
';
|
||||
|
||||
return sprintf(
|
||||
$template,
|
||||
esc_html__( 'All done. What happens next?', 'wpml-translation-management' ),
|
||||
esc_html__(
|
||||
'WPML sent emails to the translators, telling them about the new work from you.',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
sprintf(
|
||||
esc_html__(
|
||||
'Your translators should log-in to their accounts in this site and go to %1$sWPML->Translations%2$s. There, they will see the jobs that are waiting for them.',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
'<strong>',
|
||||
'</strong>'
|
||||
),
|
||||
sprintf(
|
||||
esc_html__(
|
||||
'You can always follow the progress of translation in the %1$s. For a more detailed view and to cancel jobs, visit the %2$s list.',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
$translation_dashboard_link,
|
||||
$this->getJobsLink()
|
||||
),
|
||||
sprintf(
|
||||
esc_html__(
|
||||
'You can control email notifications to translators and yourself in %s.',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
$translation_notifications_link
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function jobsSentToCurrentUserWhoIsTheOnlyTranslator() {
|
||||
return sprintf(
|
||||
'<p>%s</p><p>%s <a href="%s">%s</a></p>',
|
||||
__( 'Ready!', 'wpml-translation-management' ),
|
||||
/* translators: This text is followed by 'Translation Queue'. eg To translate those jobs, go to the Translation Queue */
|
||||
__( 'To translate those jobs, go to ', 'wpml-translation-management' ),
|
||||
admin_url( 'admin.php?page=' . WPML_TM_FOLDER . '/menu/translations-queue.php' ),
|
||||
__( 'WPML->Translation Queue', 'wpml-translation-management' )
|
||||
) . $this->automaticTranslationTip();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
private function automaticTranslationTip() {
|
||||
if (
|
||||
$this->dismissNotices->isDismissed( self::ATE_AUTOMATIC_TRANSLATION_SUGGESTION ) ||
|
||||
! \WPML_TM_ATE_Status::is_enabled_and_activated()
|
||||
) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$template = "
|
||||
<div id='wpml-tm-basket-automatic-translations-suggestion' >
|
||||
<h5>%s</h5>
|
||||
|
||||
<p>%s</p>
|
||||
|
||||
<p>%s <span>%s</span></p>
|
||||
</div>
|
||||
";
|
||||
|
||||
return sprintf(
|
||||
$template,
|
||||
esc_html__( 'Want to translate everything automatically?', 'wpml-translation-management' ),
|
||||
sprintf(
|
||||
esc_html__(
|
||||
'Go to the %s tab for automatic translation and time-saving goodies.',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
$this->getToolsLink()
|
||||
),
|
||||
$this->dismissNotices->renderCheckbox( self::ATE_AUTOMATIC_TRANSLATION_SUGGESTION ),
|
||||
__( 'Don’t offer this again', 'wpml-translation-management' )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function jobsSentDefaultMessage() {
|
||||
$message = '<p>' . esc_html__( 'Ready!', 'wpml-translation-management' ) . '</p>';
|
||||
$message .= '<p>';
|
||||
$message .= sprintf(
|
||||
esc_html__(
|
||||
'You can check the status of these jobs in %s.',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
$this->getJobsLink()
|
||||
);
|
||||
$message .= '</p>';
|
||||
|
||||
return $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
private function getJobsLink() {
|
||||
$url = admin_url( 'admin.php?page=' . WPML_TM_FOLDER . '/menu/main.php&sm=jobs' );
|
||||
$text = esc_html__( 'WPML->Translation Jobs', 'wpml-translation-management' );
|
||||
$link = '<a href="' . esc_url( $url ) . '">' . $text . '</a>';
|
||||
|
||||
return $link;
|
||||
}
|
||||
|
||||
private function getToolsLink() {
|
||||
$url = admin_url( 'admin.php?page=' . WPML_TM_FOLDER . '/menu/main.php&sm=ate-ams' );
|
||||
$text = esc_html__( 'WPML->Translation Management->Translation Tools', 'wpml-translation-management' );
|
||||
$link = '<a href="' . esc_url( $url ) . '">' . $text . '</a>';
|
||||
|
||||
return $link;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function emailNotSentError() {
|
||||
return '<li><strong>' . esc_html__(
|
||||
'WPML could not send notification emails to the translators, telling them about the new work from you.',
|
||||
'wpml-translation-management'
|
||||
) . '</strong></li>';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace WPML\TM\Menu\TranslationBasket;
|
||||
|
||||
class Utility {
|
||||
/** @var \SitePress */
|
||||
private $sitepress;
|
||||
|
||||
/** @var \WPML_Translator_Records */
|
||||
private $translatorRecords;
|
||||
|
||||
/**
|
||||
* @param \SitePress $sitepress
|
||||
* @param \WPML_Translator_Records $translatorRecords
|
||||
*/
|
||||
public function __construct( \SitePress $sitepress, \WPML_Translator_Records $translatorRecords ) {
|
||||
$this->sitepress = $sitepress;
|
||||
$this->translatorRecords = $translatorRecords;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getTargetLanguages() {
|
||||
$basketLanguages = \TranslationProxy_Basket::get_target_languages();
|
||||
|
||||
$targetLanguages = [];
|
||||
|
||||
if ( $basketLanguages ) {
|
||||
|
||||
$notBasketLanguage = function ( $lang ) use ( $basketLanguages ) {
|
||||
return ! in_array( $lang['code'], $basketLanguages, true );
|
||||
};
|
||||
$isBasketSourceLanguage = function ( $lang ) {
|
||||
return \TranslationProxy_Basket::get_source_language() === $lang['code'];
|
||||
};
|
||||
$addFlag = function ( $lang ) {
|
||||
$lang['flag'] = $this->sitepress->get_flag_img( $lang['code'] );
|
||||
|
||||
return $lang;
|
||||
};
|
||||
$targetLanguages = wpml_collect( $this->sitepress->get_active_languages() )
|
||||
->reject( $notBasketLanguage )
|
||||
->reject( $isBasketSourceLanguage )
|
||||
->map( $addFlag )
|
||||
->toArray();
|
||||
}
|
||||
|
||||
return $targetLanguages;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $targetLanguages
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isTheOnlyAvailableTranslatorForTargetLanguages( $targetLanguages ) {
|
||||
if ( \TranslationProxy::is_current_service_active_and_authenticated() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$translators = $this->translatorRecords->get_users_with_languages(
|
||||
\TranslationProxy_Basket::get_source_language(),
|
||||
array_keys( $targetLanguages ),
|
||||
false
|
||||
);
|
||||
|
||||
return count( $translators ) === 1 && $translators[0]->ID === get_current_user_id();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isTheOnlyAvailableTranslator() {
|
||||
return $this->isTheOnlyAvailableTranslatorForTargetLanguages( $this->getTargetLanguages() );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
if ( ! defined( 'WPINC' ) ) {
|
||||
die;
|
||||
}
|
||||
|
||||
class WPML_TM_Translate_Independently {
|
||||
|
||||
/** @var TranslationManagement $translation_management */
|
||||
private $translation_management;
|
||||
|
||||
/** @var WPML_Translation_Basket $translation_basket */
|
||||
private $translation_basket;
|
||||
|
||||
/** @var SitePress $sitepress */
|
||||
private $sitepress;
|
||||
|
||||
public function __construct(
|
||||
TranslationManagement $translation_management,
|
||||
WPML_Translation_Basket $translation_basket,
|
||||
SitePress $sitepress
|
||||
) {
|
||||
$this->translation_management = $translation_management;
|
||||
$this->translation_basket = $translation_basket;
|
||||
$this->sitepress = $sitepress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Init all plugin actions.
|
||||
*/
|
||||
public function init() {
|
||||
add_action( 'wp_ajax_icl_disconnect_posts', array( $this, 'ajax_disconnect_duplicates' ) );
|
||||
add_action( 'admin_footer', array( $this, 'add_hidden_field' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add hidden fields to TM basket.
|
||||
* #icl_duplicate_post_in_basket with list of duplicated ids in basket target languages.
|
||||
* #icl_disconnect_nonce nonce for AJAX call.
|
||||
*/
|
||||
public function add_hidden_field() {
|
||||
$basket = $this->translation_basket->get_basket( true );
|
||||
if ( ! isset( $basket['post'] ) ) {
|
||||
return;
|
||||
}
|
||||
$posts_ids_to_disconnect = $this->duplicates_to_disconnect( $basket['post'] );
|
||||
if ( $posts_ids_to_disconnect ) :
|
||||
?>
|
||||
<input type="hidden" value="<?php echo implode( ',', $posts_ids_to_disconnect ); ?>" id="icl_duplicate_post_in_basket">
|
||||
<input type="hidden" value="<?php echo wp_create_nonce( 'icl_disconnect_duplicates' ); ?>" id="icl_disconnect_nonce">
|
||||
<?php
|
||||
endif;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $basket_posts
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function duplicates_to_disconnect( $basket_posts ) {
|
||||
/** @var SitePress $sitepress */
|
||||
global $sitepress;
|
||||
$posts_to_disconnect = array();
|
||||
|
||||
foreach ( $basket_posts as $from_post => $data ) {
|
||||
$target_langs = array_keys( $data['to_langs'] );
|
||||
$element_type = 'post_' . get_post_type( $from_post );
|
||||
$trid = $sitepress->get_element_trid( $from_post, $element_type );
|
||||
$translations = $sitepress->get_element_translations( $trid, $element_type );
|
||||
|
||||
foreach ( $translations as $translation ) {
|
||||
if ( ! in_array( $translation->language_code, $target_langs, true ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$is_duplicate = get_post_meta( $translation->element_id, '_icl_lang_duplicate_of', true );
|
||||
|
||||
if ( $is_duplicate ) {
|
||||
$posts_to_disconnect[] = (int) $translation->element_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $posts_to_disconnect;
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX action to bulk disconnect posts before sending them to translation.
|
||||
*/
|
||||
public function ajax_disconnect_duplicates() {
|
||||
// Check nonce.
|
||||
if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( $_POST['nonce'], 'icl_disconnect_duplicates' ) ) {
|
||||
wp_send_json_error( esc_html__( 'Failed to disconnect posts', 'wpml-translation-management' ) );
|
||||
return;
|
||||
}
|
||||
|
||||
// Get post basket post ids.
|
||||
$post_ids = isset( $_POST['posts'] ) ? explode( ',', $_POST['posts'] ) : array();
|
||||
if ( empty( $post_ids ) ) {
|
||||
wp_send_json_error( esc_html__( 'No duplicate posts found to disconnect.', 'wpml-translation-management' ) );
|
||||
return;
|
||||
}
|
||||
|
||||
$post_ids = array_map( 'intval', $post_ids );
|
||||
array_walk( $post_ids, array( $this->translation_management, 'reset_duplicate_flag' ) );
|
||||
|
||||
wp_send_json_success( esc_html__( 'Successfully disconnected posts', 'wpml-translation-management' ) );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,445 @@
|
||||
<?php
|
||||
|
||||
use WPML\TM\Menu\TranslationBasket\Strings;
|
||||
use function WPML\Container\make;
|
||||
|
||||
require_once WPML_TM_PATH . '/menu/sitepress-table.class.php';
|
||||
|
||||
class SitePress_Table_Basket extends SitePress_Table {
|
||||
|
||||
public static function enqueue_js() {
|
||||
/** @var WP_Locale $wp_locale */
|
||||
global $wp_locale;
|
||||
|
||||
wp_enqueue_script(
|
||||
'wpml-tm-translation-basket-and-options',
|
||||
WPML_TM_URL . '/res/js/translation-basket-and-options.js',
|
||||
array( 'wpml-tm-scripts', 'jquery-ui-progressbar', 'jquery-ui-datepicker', 'wpml-tooltip', 'wpml-tm-progressbar' ),
|
||||
WPML_TM_VERSION
|
||||
);
|
||||
|
||||
wp_localize_script(
|
||||
'wpml-tm-translation-basket-and-options',
|
||||
'wpml_tm_translation_basket_and_options',
|
||||
array(
|
||||
'day_names' => array_values( $wp_locale->weekday ),
|
||||
'day_initials' => array_values( $wp_locale->weekday_initial ),
|
||||
'month_names' => array_values( $wp_locale->month ),
|
||||
)
|
||||
);
|
||||
|
||||
wp_enqueue_style(
|
||||
'wpml-tm-jquery-ui-datepicker',
|
||||
WPML_TM_URL . '/res/css/jquery-ui/datepicker.css',
|
||||
array( 'wpml-tooltip' ),
|
||||
WPML_TM_VERSION
|
||||
);
|
||||
|
||||
/** @var Strings $strings */
|
||||
$strings = make( Strings::class );
|
||||
|
||||
$tm_basket_data = array(
|
||||
'nonce' => array(),
|
||||
'strings' => $strings->getAll(),
|
||||
'tmi_message' => $strings->duplicatePostTranslationWarning(),
|
||||
);
|
||||
|
||||
$tm_basket_data = apply_filters( 'translation_basket_and_options_js_data', $tm_basket_data );
|
||||
wp_localize_script(
|
||||
'wpml-tm-translation-basket-and-options',
|
||||
'tm_basket_data',
|
||||
$tm_basket_data
|
||||
);
|
||||
|
||||
wp_enqueue_script( 'wpml-tm-translation-basket-and-options' );
|
||||
}
|
||||
|
||||
function prepare_items() {
|
||||
$this->action_callback();
|
||||
|
||||
$this->get_data();
|
||||
|
||||
$columns = $this->get_columns();
|
||||
$hidden = array();
|
||||
$sortable = $this->get_sortable_columns();
|
||||
$this->_column_headers = array( $columns, $hidden, $sortable );
|
||||
|
||||
if ( $this->items ) {
|
||||
usort( $this->items, array( &$this, 'usort_reorder' ) );
|
||||
}
|
||||
}
|
||||
|
||||
function get_columns() {
|
||||
$columns = array(
|
||||
'title' => __( 'Title', 'wpml-translation-management' ),
|
||||
'type' => __( 'Type', 'wpml-translation-management' ),
|
||||
'status' => __( 'Status', 'wpml-translation-management' ),
|
||||
'languages' => __( 'Languages', 'wpml-translation-management' ),
|
||||
'words' => __( 'Words to translate', 'wpml-translation-management' ),
|
||||
'delete' => '',
|
||||
);
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object $item
|
||||
* @param string $column_name
|
||||
*
|
||||
* @return mixed|string
|
||||
*/
|
||||
function column_default( $item, $column_name ) {
|
||||
/**
|
||||
* WP base class is expecting an object, but we are using an array in our implementation.
|
||||
* Casting $item into an array prevents IDE warnings.
|
||||
*/
|
||||
$item = (array) $item;
|
||||
|
||||
switch ( $column_name ) {
|
||||
case 'title':
|
||||
case 'notes':
|
||||
return $item[ $column_name ];
|
||||
break;
|
||||
case 'type':
|
||||
return $this->get_post_type_label( $item[ $column_name ], $item );
|
||||
break;
|
||||
case 'status':
|
||||
return $this->get_post_status_label( $item[ $column_name ] );
|
||||
break;
|
||||
case 'words':
|
||||
return $item[ $column_name ];
|
||||
break;
|
||||
case 'languages':
|
||||
$target_languages_data = $item['target_languages'];
|
||||
$source_language_data = $item['source_language'];
|
||||
$target_languages = explode( ',', $target_languages_data );
|
||||
$languages = sprintf(
|
||||
__( '%1$s to %2$s', 'wpml-translation-management' ),
|
||||
$source_language_data,
|
||||
$target_languages_data
|
||||
);
|
||||
if ( count( $target_languages ) > 1 ) {
|
||||
$last_target_language = $target_languages[ count( $target_languages ) - 1 ];
|
||||
$first_target_languages = array_slice( $target_languages, 0, count( $target_languages ) - 1 );
|
||||
$languages = sprintf(
|
||||
__( '%1$s to %2$s and %3$s', 'wpml-translation-management' ),
|
||||
$source_language_data,
|
||||
implode( ',', $first_target_languages ),
|
||||
$last_target_language
|
||||
);
|
||||
}
|
||||
|
||||
return $languages;
|
||||
break;
|
||||
default:
|
||||
return print_r( $item, true ); // Show the whole array for troubleshooting purposes
|
||||
}
|
||||
}
|
||||
|
||||
function column_title( $item ) {
|
||||
return esc_html( $item['title'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $item
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function column_delete( $item ) {
|
||||
$qs = $_GET;
|
||||
$qs['page'] = $_REQUEST['page'];
|
||||
$qs['action'] = 'delete';
|
||||
$qs['id'] = $item['ID'];
|
||||
$qs['item_type'] = $item['item_type'];
|
||||
|
||||
$new_qs = esc_attr( http_build_query( $qs ) );
|
||||
|
||||
return sprintf(
|
||||
'<a href="?%s" title="%s" class="otgs-ico-cancel wpml-tm-delete"></a>',
|
||||
$new_qs,
|
||||
__( 'Remove from Translation Basket', 'wpml-translation-management' )
|
||||
);
|
||||
}
|
||||
|
||||
function no_items() {
|
||||
_e( 'The basket is empty', 'wpml-translation-management' );
|
||||
}
|
||||
|
||||
function get_sortable_columns() {
|
||||
$sortable_columns = array(
|
||||
'title' => array( 'title', true ),
|
||||
'type' => array( 'type', false ),
|
||||
'status' => array( 'status', false ),
|
||||
'languages' => array( 'languages', false ),
|
||||
'words' => array( 'words', false ),
|
||||
);
|
||||
|
||||
return $sortable_columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $post_id
|
||||
* @param $data
|
||||
* @param $item_type
|
||||
*/
|
||||
private function build_basket_item( $post_id, $data, $item_type ) {
|
||||
$this->items[ $item_type . '|' . $post_id ]['ID'] = $post_id;
|
||||
$this->items[ $item_type . '|' . $post_id ]['title'] = $data['post_title'];
|
||||
$this->items[ $item_type . '|' . $post_id ]['notes'] = isset( $data['post_notes'] ) ? $data['post_notes'] : '';
|
||||
$this->items[ $item_type . '|' . $post_id ]['type'] = $data['post_type'];
|
||||
$this->items[ $item_type . '|' . $post_id ]['status'] = isset( $data['post_status'] ) ? $data['post_status'] : '';
|
||||
$this->items[ $item_type . '|' . $post_id ]['source_language'] = $data['from_lang_string'];
|
||||
$this->items[ $item_type . '|' . $post_id ]['target_languages'] = $data['to_langs_string'];
|
||||
$this->items[ $item_type . '|' . $post_id ]['item_type'] = $item_type;
|
||||
$this->items[ $item_type . '|' . $post_id ]['words'] = $this->get_words_count( $post_id, $item_type, count( $data['to_langs'] ) );
|
||||
$this->items[ $item_type . '|' . $post_id ]['auto_added'] = isset( $data['auto_added'] ) && $data['auto_added'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $element_id
|
||||
* @param $element_type
|
||||
* @param $languages_count
|
||||
*/
|
||||
private function get_words_count( $element_id, $element_type, $languages_count ) {
|
||||
$records_factory = new WPML_TM_Word_Count_Records_Factory();
|
||||
$single_process_factory = new WPML_TM_Word_Count_Single_Process_Factory();
|
||||
$st_package_factory = class_exists( 'WPML_ST_Package_Factory' ) ? new WPML_ST_Package_Factory() : null;
|
||||
$element_provider = new WPML_TM_Translatable_Element_Provider( $records_factory->create(), $single_process_factory->create(), $st_package_factory );
|
||||
$translatable_element = $element_provider->get_from_type( $element_type, $element_id );
|
||||
|
||||
$count = null !== $translatable_element ? $translatable_element->get_words_count() : 0;
|
||||
|
||||
return $count * $languages_count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $cart_items
|
||||
* @param $item_type
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function build_basket_items( $cart_items, $item_type ) {
|
||||
if ( $cart_items ) {
|
||||
foreach ( $cart_items as $post_id => $data ) {
|
||||
$this->build_basket_item( $post_id, $data, $item_type );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function usort_reorder( $a, $b ) {
|
||||
$sortable_columns_keys = array_keys( $this->get_sortable_columns() );
|
||||
$first_column_key = $sortable_columns_keys[0];
|
||||
// If no sort, default to first column
|
||||
$orderby = ( ! empty( $_GET['orderby'] ) ) ? $_GET['orderby'] : $first_column_key;
|
||||
// If no order, default to asc
|
||||
$order = ( ! empty( $_GET['order'] ) ) ? $_GET['order'] : 'asc';
|
||||
// Determine sort order
|
||||
$result = strcmp( $a[ $orderby ], $b[ $orderby ] );
|
||||
|
||||
// Send final sort direction to usort
|
||||
return ( $order === 'asc' ) ? $result : - $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $post_status
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function get_post_status_label( $post_status ) {
|
||||
static $post_status_object;
|
||||
// Get and store the post status "as verb", if available
|
||||
if ( ! isset( $post_status_object[ $post_status ] ) ) {
|
||||
$post_status_object[ $post_status ] = get_post_status_object( $post_status );
|
||||
}
|
||||
$post_status_label = ucfirst( $post_status );
|
||||
if ( isset( $post_status_object[ $post_status ] ) ) {
|
||||
$post_status_object_item = $post_status_object[ $post_status ];
|
||||
if ( isset( $post_status_object_item->label ) && $post_status_object_item->label ) {
|
||||
$post_status_label = $post_status_object_item->label;
|
||||
}
|
||||
}
|
||||
|
||||
return $post_status_label;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $post_type
|
||||
* @param array $item
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function get_post_type_label( $post_type, array $item ) {
|
||||
static $post_type_object;
|
||||
|
||||
if ( ! isset( $post_type_object[ $post_type ] ) ) {
|
||||
$post_type_object[ $post_type ] = get_post_type_object( $post_type );
|
||||
}
|
||||
$post_type_label = ucfirst( $post_type );
|
||||
|
||||
if ( isset( $post_type_object[ $post_type ] ) ) {
|
||||
$post_type_object_item = $post_type_object[ $post_type ];
|
||||
if ( isset( $post_type_object_item->labels->singular_name ) && $post_type_object_item->labels->singular_name ) {
|
||||
$post_type_label = $post_type_object_item->labels->singular_name;
|
||||
}
|
||||
}
|
||||
|
||||
if ( isset( $item['auto_added'] ) && $item['auto_added'] ) {
|
||||
if ( 'wp_block' === $post_type ) {
|
||||
$post_type_label = __( 'Reusable Block', 'wpml-translation-management' );
|
||||
}
|
||||
|
||||
$tooltip = '<a class="js-otgs-popover-tooltip otgs-ico-help"
|
||||
data-tippy-zindex="999999" tabindex="0"
|
||||
title="' . esc_attr__( "WPML added this item because it's linked to another in the basket. If you wish, you can manually remove it.", 'wpml-translation-management' ) . '"
|
||||
</a>';
|
||||
|
||||
$post_type_label .= '<br><small>'
|
||||
. sprintf( esc_html__( 'automatically added %s', 'wpml-translation-management' ), $tooltip ) .
|
||||
'</small>';
|
||||
}
|
||||
|
||||
return $post_type_label;
|
||||
}
|
||||
|
||||
private function action_callback() {
|
||||
if ( isset( $_GET['clear_basket'] ) && isset( $_GET['clear_basket_nonce'] ) && $_GET['clear_basket'] == 1 ) {
|
||||
if ( wp_verify_nonce( $_GET['clear_basket_nonce'], 'clear_basket' ) ) {
|
||||
TranslationProxy_Basket::delete_all_items_from_basket();
|
||||
}
|
||||
}
|
||||
if ( $this->current_action() == 'delete_selected' ) {
|
||||
// Delete basket items from post action
|
||||
TranslationProxy_Basket::delete_items_from_basket( $_POST['icl_translation_basket_delete'] );
|
||||
} elseif ( $this->current_action() == 'delete' && isset( $_GET['id'] ) && isset( $_GET['item_type'] ) ) {
|
||||
// Delete basket item from post action
|
||||
$delete_basket_item_id = filter_input( INPUT_GET, 'id', FILTER_SANITIZE_NUMBER_INT );
|
||||
$delete_basket_item_type = filter_input( INPUT_GET, 'item_type', FILTER_SANITIZE_STRING );
|
||||
if ( $delete_basket_item_id && $delete_basket_item_type ) {
|
||||
TranslationProxy_Basket::delete_item_from_basket(
|
||||
$delete_basket_item_id,
|
||||
$delete_basket_item_type,
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function get_data() {
|
||||
global $iclTranslationManagement;
|
||||
|
||||
$translation_jobs_basket = TranslationProxy_Basket::get_basket();
|
||||
|
||||
$basket_items_types = TranslationProxy_Basket::get_basket_items_types();
|
||||
foreach ( $basket_items_types as $item_type_name => $item_type ) {
|
||||
$translation_jobs_cart[ $item_type_name ] = false;
|
||||
if ( $item_type == 'core' ) {
|
||||
if ( ! empty( $translation_jobs_basket[ $item_type_name ] ) ) {
|
||||
$basket_type_items = $translation_jobs_basket[ $item_type_name ];
|
||||
if ( $item_type_name == 'string' ) {
|
||||
$translation_jobs_cart[ $item_type_name ] = $iclTranslationManagement->get_translation_jobs_basket_strings( $basket_type_items );
|
||||
} else {
|
||||
$translation_jobs_cart[ $item_type_name ] = $iclTranslationManagement->get_translation_jobs_basket_posts( $basket_type_items );
|
||||
}
|
||||
$this->build_basket_items( $translation_jobs_cart[ $item_type_name ], $item_type_name );
|
||||
}
|
||||
} elseif ( $item_type == 'custom' ) {
|
||||
$translation_jobs_cart_externals = apply_filters(
|
||||
'wpml_tm_translation_jobs_basket',
|
||||
array(),
|
||||
$translation_jobs_basket,
|
||||
$item_type_name
|
||||
);
|
||||
$this->build_basket_items( $translation_jobs_cart_externals, $item_type_name );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function display_tablenav( $which ) {
|
||||
return;
|
||||
}
|
||||
|
||||
function display() {
|
||||
parent::display();
|
||||
if ( TranslationProxy_Basket::get_basket_items_count() ) {
|
||||
$clear_basket_nonce = wp_create_nonce( 'clear_basket' );
|
||||
?>
|
||||
<a href="admin.php?page=<?php echo WPML_TM_FOLDER; ?>/menu/main.php&sm=basket&clear_basket=1&clear_basket_nonce=<?php echo $clear_basket_nonce; ?>"
|
||||
class="button-secondary wpml-tm-clear-basket-button" name="clear-basket">
|
||||
<i class="otgs-ico-cancel"></i>
|
||||
<?php _e( 'Clear Basket', 'wpml-translation-management' ); ?>
|
||||
</a>
|
||||
<?php
|
||||
}
|
||||
|
||||
$this->display_total_word_count_info();
|
||||
}
|
||||
|
||||
private function display_total_word_count_info() {
|
||||
|
||||
$grand_total_words_count = 0;
|
||||
|
||||
if ( $this->items ) {
|
||||
foreach ( $this->items as $item ) {
|
||||
$grand_total_words_count += $item['words'];
|
||||
}
|
||||
}
|
||||
|
||||
$service = TranslationProxy::get_current_service();
|
||||
$tm_ate = new WPML_TM_ATE();
|
||||
$is_ate_enabled = $tm_ate->is_translation_method_ate_enabled();
|
||||
$display_message = '';
|
||||
|
||||
$ate_doc_link = '';
|
||||
$ate_name = '';
|
||||
|
||||
if ( $is_ate_enabled ) {
|
||||
$ate_name = __( 'Advanced Translation Editor', 'wpml-translation-management' );
|
||||
$ate_doc_link = 'https://wpml.org/documentation/translating-your-contents/advanced-translation-editor/';
|
||||
}
|
||||
|
||||
if ( $service && $is_ate_enabled ) {
|
||||
$service_message = __( '%1$s and the %2$s use a translation memory, which can reduce the number of words you need to translate.', 'wpml-translation-management' );
|
||||
$display_message = sprintf(
|
||||
$service_message,
|
||||
'<a class="wpml-external-link" href="' . $service->doc_url . '" target="blank">' . $service->name . '</a>',
|
||||
'<a class="wpml-external-link" href="' . $ate_doc_link . '" target="blank">' . $ate_name . '</a>'
|
||||
);
|
||||
} elseif ( $service ) {
|
||||
$service_message = __( '%s uses a translation memory, which can reduce the number of words you need to translate.', 'wpml-translation-management' );
|
||||
$display_message = sprintf( $service_message, '<a class="wpml-external-link" href="' . $service->doc_url . '" target="blank">' . $service->name . '</a>' );
|
||||
} elseif ( $is_ate_enabled ) {
|
||||
$service_message = __( 'The %s uses a translation memory, which can reduce the number of words you need to translate.', 'wpml-translation-management' );
|
||||
$display_message = sprintf( $service_message, '<a class="wpml-external-link" href="' . $ate_doc_link . '" target="blank">' . $ate_name . '</a>' );
|
||||
}
|
||||
|
||||
if ( $service ) {
|
||||
$words_count_url = 'https://wpml.org/documentation/translating-your-contents/getting-a-word-count-of-your-wordpress-site/#differences-in-word-count-between-wpml-and-translation-service-providers';
|
||||
$words_count_text = '<a class="wpml-external-link" href="' . $words_count_url . '" target="_blank">';
|
||||
|
||||
// translators: "%s" is replaced by the name of a translation service.
|
||||
$words_count_text .= sprintf( __( '%s may produce a different word count', 'wpml-translation-management' ), $service->name );
|
||||
$words_count_text .= '</a>';
|
||||
|
||||
// translators: "%s" is replaced by the the previous string.
|
||||
$words_count_message = sprintf( __( 'The number of words WPML will send to translation (%s):', 'wpml-translation-management' ), $words_count_text );
|
||||
} else {
|
||||
$words_count_message = __( 'The number of words WPML will send to translation:', 'wpml-translation-management' );
|
||||
}
|
||||
|
||||
?>
|
||||
<div class="words-count-summary">
|
||||
<p class="words-count-summary-info">
|
||||
<strong><?php echo $words_count_message; ?></strong>
|
||||
<span class="words-count-total"><?php echo $grand_total_words_count; ?></span>
|
||||
</p>
|
||||
<?php if ( $display_message ) { ?>
|
||||
<p class="words-count-summary-ts">
|
||||
<?php echo $display_message; ?>
|
||||
</p>
|
||||
<?php } ?>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
<?php
|
||||
|
||||
class WPML_Basket_Tab_Ajax {
|
||||
|
||||
/** @var TranslationProxy_Project $project */
|
||||
private $project;
|
||||
|
||||
/** @var WPML_Translation_Proxy_Basket_Networking $networking */
|
||||
private $networking;
|
||||
|
||||
/** @var WPML_Translation_Basket $basket */
|
||||
private $basket;
|
||||
|
||||
/**
|
||||
* @param TranslationProxy_Project $project
|
||||
* @param WPML_Translation_Proxy_Basket_Networking $networking
|
||||
* @param WPML_Translation_Basket $basket
|
||||
*/
|
||||
function __construct( $project, $networking, $basket ) {
|
||||
$this->project = $project;
|
||||
$this->networking = $networking;
|
||||
$this->basket = $basket;
|
||||
}
|
||||
|
||||
function init() {
|
||||
$request = filter_input( INPUT_POST, 'action' );
|
||||
$nonce = filter_input( INPUT_POST, '_icl_nonce' );
|
||||
if ( $request && $nonce && wp_verify_nonce( $nonce, $request . '_nonce' ) ) {
|
||||
add_action( 'wp_ajax_send_basket_items', [ $this, 'begin_basket_commit' ] );
|
||||
add_action( 'wp_ajax_send_basket_item', [ $this, 'send_basket_chunk' ] );
|
||||
add_action( 'wp_ajax_send_basket_commit', [ $this, 'send_basket_commit' ] );
|
||||
add_action( 'wp_ajax_check_basket_name', [ $this, 'check_basket_name' ] );
|
||||
add_action( 'wp_ajax_rollback_basket', [ $this, 'rollback_basket' ] );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for the ajax call to commit a chunk of the items in a batch provided in the request.
|
||||
*
|
||||
* @uses \WPML_Translation_Proxy_Basket_Networking::commit_basket_chunk
|
||||
*/
|
||||
function send_basket_chunk() {
|
||||
$batch_factory = new WPML_TM_Translation_Batch_Factory( $this->basket );
|
||||
|
||||
try {
|
||||
$batch = $batch_factory->create( $_POST );
|
||||
list( $has_error, $data, $error ) = $this->networking->commit_basket_chunk( $batch );
|
||||
} catch ( InvalidArgumentException $e ) {
|
||||
$has_error = true;
|
||||
$data = $e->getMessage();
|
||||
}
|
||||
|
||||
if ( $has_error ) {
|
||||
wp_send_json_error( $data );
|
||||
} else {
|
||||
wp_send_json_success( $data );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajax handler for the first ajax request call in the basket commit workflow, responding with an message
|
||||
* containing information about the basket's contents.
|
||||
*
|
||||
* @uses \WPML_Basket_Tab_Ajax::create_remote_batch_message
|
||||
*/
|
||||
function begin_basket_commit() {
|
||||
$basket_name = filter_input( INPUT_POST, 'basket_name', FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES );
|
||||
|
||||
wp_send_json_success( $this->create_remote_batch_message( $basket_name ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Last ajax call in the multiple ajax calls made during the commit of a batch.
|
||||
* Empties the basket in case the commit worked error free responds to the ajax call.
|
||||
*/
|
||||
function send_basket_commit() {
|
||||
$errors = array();
|
||||
try {
|
||||
$translators = isset( $_POST['translators'] ) ? $_POST['translators'] : array();
|
||||
$has_remote_translators = $this->networking->contains_remote_translators( $translators );
|
||||
$response = $this->project && $has_remote_translators ? $this->project->commit_batch_job() : true;
|
||||
$response = ! empty( $this->project->errors ) ? false : $response;
|
||||
if ( $response !== false ) {
|
||||
|
||||
if ( is_object( $response ) ) {
|
||||
$current_service = $this->project->current_service();
|
||||
if ( $current_service->redirect_to_ts ) {
|
||||
$message = sprintf(
|
||||
__(
|
||||
'You\'ve sent the content for translation to %s. Please continue to their site, to make sure that the translation starts.',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
$current_service->name
|
||||
);
|
||||
|
||||
$link_text = sprintf(
|
||||
__( 'Continue to %s', 'wpml-translation-management' ),
|
||||
$current_service->name
|
||||
);
|
||||
} else {
|
||||
$message = sprintf(
|
||||
__(
|
||||
'You\'ve sent the content for translation to %1$s. Currently, we are processing it and delivering to %1$s.',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
$current_service->name
|
||||
);
|
||||
|
||||
$link_text = __( 'Check the batch delivery status', 'wpml-translation-management' );
|
||||
}
|
||||
|
||||
$response->call_to_action = $message;
|
||||
|
||||
$batch_url = OTG_TRANSLATION_PROXY_URL . sprintf( '/projects/%d/external', $this->project->get_batch_job_id() );
|
||||
$response->ts_batch_link = array(
|
||||
'href' => esc_url( $batch_url ),
|
||||
'text' => $link_text,
|
||||
);
|
||||
} elseif ( $this->contains_local_translators_different_than_current_user( $translators ) ) {
|
||||
$response = new stdClass();
|
||||
$response->is_local = true;
|
||||
}
|
||||
}
|
||||
|
||||
$errors = $response === false && $this->project ? $this->project->errors : $errors;
|
||||
} catch ( Exception $e ) {
|
||||
$response = false;
|
||||
$errors[] = $e->getMessage();
|
||||
}
|
||||
|
||||
do_action( 'wpml_tm_basket_committed' );
|
||||
|
||||
if ( isset( $response->is_local ) ) {
|
||||
$batch_jobs = get_option( WPML_TM_Batch_Report::BATCH_REPORT_OPTION );
|
||||
if ( $batch_jobs ) {
|
||||
$response->emails_did_not_sent = true;
|
||||
}
|
||||
}
|
||||
|
||||
$this->send_json_response( $response, $errors );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $translators
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function contains_local_translators_different_than_current_user( $translators ) {
|
||||
$is_first_available_translator = function ( $translator ) {
|
||||
return $translator === '0';
|
||||
};
|
||||
|
||||
return ! \wpml_collect( $translators )
|
||||
->reject( get_current_user_id() )
|
||||
->reject( $is_first_available_translator )
|
||||
->filter(
|
||||
function ( $translator ) {
|
||||
return is_numeric( $translator );
|
||||
}
|
||||
)
|
||||
->isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajax handler for checking if a current basket/batch name is valid for use with the currently used translation
|
||||
* service.
|
||||
*
|
||||
* @uses \WPML_Translation_Basket::check_basket_name
|
||||
*/
|
||||
function check_basket_name() {
|
||||
$basket_name_max_length = TranslationProxy::get_current_service_batch_name_max_length();
|
||||
|
||||
wp_send_json_success( $this->basket->check_basket_name( $this->get_basket_name(), $basket_name_max_length ) );
|
||||
}
|
||||
|
||||
public function rollback_basket() {
|
||||
$this->networking->rollback_basket_commit( $this->get_basket_name() );
|
||||
wp_send_json_success();
|
||||
}
|
||||
|
||||
/** @return string */
|
||||
private function get_basket_name() {
|
||||
return filter_input( INPUT_POST, 'basket_name', FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES );
|
||||
}
|
||||
|
||||
private static function sanitize_errors( $source ) {
|
||||
if ( is_array( $source ) ) {
|
||||
if ( $source && array_key_exists( 'errors', $source ) ) {
|
||||
foreach ( $source['errors'] as &$error ) {
|
||||
if ( is_array( $error ) ) {
|
||||
$error = self::sanitize_errors( $error );
|
||||
} else {
|
||||
$error = ICL_AdminNotifier::sanitize_and_format_message( $error );
|
||||
}
|
||||
}
|
||||
unset( $error );
|
||||
}
|
||||
} else {
|
||||
$source = ICL_AdminNotifier::sanitize_and_format_message( $source );
|
||||
}
|
||||
|
||||
return $source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the response to the ajax for \WPML_Basket_Tab_Ajax::send_basket_commit and rolls back the commit
|
||||
* in case of any errors.
|
||||
*
|
||||
* @see \WPML_Basket_Tab_Ajax::send_basket_commit
|
||||
* @uses \WPML_Translation_Proxy_Basket_Networking::rollback_basket_commit
|
||||
* @uses \WPML_Translation_Basket::delete_all_items
|
||||
*
|
||||
* @param object|bool $response
|
||||
* @param array $errors
|
||||
*/
|
||||
private function send_json_response( $response, $errors ) {
|
||||
$result = array(
|
||||
'result' => $response,
|
||||
'is_error' => ! ( $response && empty( $errors ) ),
|
||||
'errors' => $errors,
|
||||
);
|
||||
if ( ! empty( $errors ) ) {
|
||||
$this->networking->rollback_basket_commit( $this->get_basket_name() );
|
||||
wp_send_json_error( self::sanitize_errors( $result ) );
|
||||
} else {
|
||||
$this->basket->delete_all_items();
|
||||
|
||||
wp_send_json_success( $result );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the message that is shown before committing a batch.
|
||||
*
|
||||
* @see \WPML_Basket_Tab_Ajax::begin_basket_commit
|
||||
*
|
||||
* @param string $basket_name
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function create_remote_batch_message( $basket_name ) {
|
||||
if ( $basket_name ) {
|
||||
$this->basket->set_name( $basket_name );
|
||||
}
|
||||
$basket = $this->basket->get_basket();
|
||||
$basket_items_types = $this->basket->get_item_types();
|
||||
if ( ! $basket ) {
|
||||
$message_content = __( 'No items found in basket', 'wpml-translation-management' );
|
||||
} else {
|
||||
$total_count = 0;
|
||||
$message_content_details = '<ul>';
|
||||
foreach ( $basket_items_types as $item_type_name => $item_type ) {
|
||||
if ( isset( $basket[ $item_type_name ] ) ) {
|
||||
$count_item_type = count( $basket[ $item_type_name ] );
|
||||
$total_count += $count_item_type;
|
||||
|
||||
$message_content_details .= '<li>' . $item_type_name . 's: ' . $count_item_type . '</li>';
|
||||
}
|
||||
}
|
||||
$message_content_details .= '</ul>';
|
||||
|
||||
$message_content = sprintf( __( '%s items in basket:', 'wpml-translation-management' ), $total_count );
|
||||
$message_content .= $message_content_details;
|
||||
}
|
||||
$container = $message_content;
|
||||
|
||||
return array(
|
||||
'message' => $container,
|
||||
'basket' => $basket,
|
||||
'allowed_item_types' => array_keys( $basket_items_types ),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
class WPML_Custom_Field_Editor_Settings {
|
||||
/** @var WPML_Custom_Field_Setting_Factory */
|
||||
private $settings_factory;
|
||||
|
||||
public function __construct( WPML_Custom_Field_Setting_Factory $settingsFactory ) {
|
||||
$this->settings_factory = $settingsFactory;
|
||||
}
|
||||
|
||||
public function filter_name( $fieldType, $default ) {
|
||||
return $this->settings_factory->post_meta_setting( $this->extractTypeName( $fieldType ) )->get_editor_label() ?: $default;
|
||||
}
|
||||
|
||||
public function filter_style( $fieldType, $default ) {
|
||||
$filtered_style = $this->settings_factory->post_meta_setting( $this->extractTypeName( $fieldType ) )->get_editor_style();
|
||||
switch ( $filtered_style ) {
|
||||
case 'line':
|
||||
return 0;
|
||||
case 'textarea':
|
||||
return 1;
|
||||
case 'visual':
|
||||
return 2;
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
public function get_group( $fieldType ) {
|
||||
return $this->settings_factory->post_meta_setting( $this->extractTypeName( $fieldType ) )->get_editor_group();
|
||||
}
|
||||
|
||||
private function extractTypeName( $fieldType ) {
|
||||
return substr( $fieldType, strlen( 'field_' ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
class WPML_Editor_UI_Job {
|
||||
|
||||
private $fields = array();
|
||||
protected $job_id;
|
||||
private $job_type;
|
||||
private $job_type_title;
|
||||
private $title;
|
||||
private $view_link;
|
||||
private $source_lang;
|
||||
private $target_lang;
|
||||
private $translation_complete;
|
||||
private $duplicate;
|
||||
private $note = '';
|
||||
|
||||
function __construct(
|
||||
$job_id,
|
||||
$job_type,
|
||||
$job_type_title,
|
||||
$title,
|
||||
$view_link,
|
||||
$source_lang,
|
||||
$target_lang,
|
||||
$translation_complete,
|
||||
$duplicate
|
||||
) {
|
||||
|
||||
$this->job_id = $job_id;
|
||||
$this->job_type = $job_type;
|
||||
$this->job_type_title = $job_type_title;
|
||||
$this->title = $title;
|
||||
$this->view_link = $view_link;
|
||||
$this->source_lang = $source_lang;
|
||||
$this->target_lang = $target_lang;
|
||||
$this->translation_complete = $translation_complete;
|
||||
$this->duplicate = $duplicate;
|
||||
|
||||
}
|
||||
|
||||
public function add_field( $field ) {
|
||||
$this->fields[] = $field;
|
||||
}
|
||||
|
||||
public function add_note( $note ) {
|
||||
$this->note = $note;
|
||||
}
|
||||
|
||||
public function get_all_fields() {
|
||||
$fields = array();
|
||||
/** @var WPML_Editor_UI_Field $field */
|
||||
foreach ( $this->fields as $field ) {
|
||||
$child_fields = $field->get_fields();
|
||||
/** @var WPML_Editor_UI_Field $child_field */
|
||||
foreach ( $child_fields as $child_field ) {
|
||||
$fields[] = $child_field;
|
||||
}
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
public function get_layout_of_fields() {
|
||||
$layout = array();
|
||||
/** @var WPML_Editor_UI_Field $field */
|
||||
foreach ( $this->fields as $field ) {
|
||||
$layout[] = $field->get_layout();
|
||||
}
|
||||
|
||||
return $layout;
|
||||
}
|
||||
|
||||
public function get_target_language() {
|
||||
return $this->target_lang;
|
||||
}
|
||||
|
||||
public function is_translation_complete() {
|
||||
return $this->translation_complete;
|
||||
}
|
||||
|
||||
public function save( $data ) {
|
||||
$translations = array();
|
||||
|
||||
foreach ( $data['fields'] as $id => $field ) {
|
||||
$translations[ $this->convert_id_to_translation_key( $id ) ] = $field['data'];
|
||||
}
|
||||
|
||||
try {
|
||||
$this->save_translations( $translations );
|
||||
|
||||
return new WPML_Ajax_Response( true, true );
|
||||
} catch ( Exception $e ) {
|
||||
return new WPML_Ajax_Response( false, 0 );
|
||||
}
|
||||
}
|
||||
|
||||
private function convert_id_to_translation_key( $id ) {
|
||||
// This is to support the old api for saving translations.
|
||||
return md5( $id );
|
||||
}
|
||||
|
||||
public function requires_translation_complete_for_each_field() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function display_hide_completed_switcher() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function is_hide_empty_fields() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function save_translations( $translations ) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
class WPML_TM_Editor_Job_Save {
|
||||
|
||||
public function save( $data ) {
|
||||
/** @var WPML_Translation_Job_Factory $wpml_translation_job_factory */
|
||||
global $wpml_translation_job_factory;
|
||||
|
||||
$factory = new WPML_TM_Job_Action_Factory( $wpml_translation_job_factory );
|
||||
$action = new WPML_TM_Editor_Save_Ajax_Action( $factory, $data );
|
||||
|
||||
return $action->run();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
class WPML_TM_Editor_Save_Ajax_Action extends WPML_TM_Job_Action {
|
||||
|
||||
private $data;
|
||||
|
||||
/**
|
||||
* WPML_TM_Editor_Save_Ajax_Action constructor.
|
||||
*
|
||||
* @param WPML_TM_Job_Action_Factory $job_action_factory
|
||||
* @param array $data
|
||||
*/
|
||||
public function __construct( &$job_action_factory, array $data ) {
|
||||
parent::__construct( $job_action_factory );
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
public function run() {
|
||||
try {
|
||||
return new WPML_Ajax_Response(
|
||||
true,
|
||||
$this->job_action_factory->save_action( $this->data )->save_translation()
|
||||
);
|
||||
} catch ( Exception $e ) {
|
||||
return new WPML_Ajax_Response( false, 0 );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
class WPML_TM_Field_Type_Sanitizer {
|
||||
|
||||
|
||||
/**
|
||||
* Get elements custom field `field_type`.
|
||||
* Removes last character if it's number.
|
||||
* ex. field-custom_field-0 => field-custom_field
|
||||
*
|
||||
* @param $element
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function sanitize( $custom_field_type ) {
|
||||
$element_field_type_parts = explode( '-', $custom_field_type );
|
||||
$last_part = array_pop( $element_field_type_parts );
|
||||
|
||||
if ( empty( $element_field_type_parts ) ) {
|
||||
return $custom_field_type;
|
||||
}
|
||||
|
||||
// Re-create field.
|
||||
$field_type = implode( '-', $element_field_type_parts );
|
||||
if ( is_numeric( $last_part ) ) {
|
||||
return $field_type;
|
||||
} else {
|
||||
return $custom_field_type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
class WPML_Translation_Editor_Header {
|
||||
|
||||
private $job_instance;
|
||||
|
||||
public function __construct( $job_instance ) {
|
||||
$this->job_instance = $job_instance;
|
||||
}
|
||||
|
||||
public function get_model() {
|
||||
$type_title = esc_html( $this->job_instance->get_type_title() );
|
||||
$title = esc_html( $this->job_instance->get_title() );
|
||||
$data = array();
|
||||
$data['title'] = sprintf( __( '%1$s translation: %2$s', 'wpml-translation-management' ), $type_title, '<strong>' . $title . '</strong>' );
|
||||
$data['link_url'] = $this->job_instance->get_url( true );
|
||||
$data['link_text'] = $this->job_instance instanceof WPML_External_Translation_Job ? '' : sprintf( __( 'View %s', 'wpml-translation-management' ), $type_title );
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
class WPML_Translation_Editor_Languages extends WPML_SP_User {
|
||||
|
||||
private $job;
|
||||
|
||||
/**
|
||||
* @param SitePress $sitepress
|
||||
*/
|
||||
public function __construct( &$sitepress, $job ) {
|
||||
parent::__construct( $sitepress );
|
||||
$this->job = $job;
|
||||
}
|
||||
|
||||
public function get_model() {
|
||||
$source_lang = $this->sitepress->get_language_details( $this->job->source_language_code );
|
||||
$target_lang = $this->sitepress->get_language_details( $this->job->language_code );
|
||||
|
||||
$data = array(
|
||||
'source' => $this->job->source_language_code,
|
||||
'target' => $this->job->language_code,
|
||||
'source_lang' => $source_lang['display_name'],
|
||||
'target_lang' => $target_lang['display_name'],
|
||||
);
|
||||
$data['img'] = array(
|
||||
'source_url' => $this->sitepress->get_flag_url( $this->job->source_language_code ),
|
||||
'target_url' => $this->sitepress->get_flag_url( $this->job->language_code ),
|
||||
);
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,335 @@
|
||||
<?php
|
||||
|
||||
use \WPML\TM\Jobs\FieldId;
|
||||
|
||||
class WPML_Translation_Editor_UI {
|
||||
const MAX_ALLOWED_SINGLE_LINE_LENGTH = 50;
|
||||
|
||||
/** @var SitePress $sitepress */
|
||||
private $sitepress;
|
||||
/** @var WPDB $wpdb */
|
||||
private $wpdb;
|
||||
|
||||
/** @var array */
|
||||
private $all_translations;
|
||||
/**
|
||||
* @var WPML_Translation_Editor
|
||||
*/
|
||||
private $editor_object;
|
||||
private $job;
|
||||
private $original_post;
|
||||
private $rtl_original;
|
||||
private $rtl_original_attribute_object;
|
||||
private $rtl_translation;
|
||||
private $rtl_translation_attribute;
|
||||
private $is_duplicate = false;
|
||||
/**
|
||||
* @var TranslationManagement
|
||||
*/
|
||||
private $tm_instance;
|
||||
|
||||
/** @var WPML_Element_Translation_Job|WPML_External_Translation_Job */
|
||||
private $job_instance;
|
||||
|
||||
private $job_factory;
|
||||
private $job_layout;
|
||||
/** @var array */
|
||||
private $fields;
|
||||
|
||||
function __construct( wpdb $wpdb, SitePress $sitepress, TranslationManagement $iclTranslationManagement, WPML_Element_Translation_Job $job_instance, WPML_TM_Job_Action_Factory $job_factory, WPML_TM_Job_Layout $job_layout ) {
|
||||
$this->sitepress = $sitepress;
|
||||
$this->wpdb = $wpdb;
|
||||
|
||||
$this->tm_instance = $iclTranslationManagement;
|
||||
$this->job_instance = $job_instance;
|
||||
$this->job = $job_instance->get_basic_data();
|
||||
$this->job_factory = $job_factory;
|
||||
$this->job_layout = $job_layout;
|
||||
if ( $job_instance->get_translator_id() <= 0 ) {
|
||||
$job_instance->assign_to( $sitepress->get_wp_api()->get_current_user_id() );
|
||||
}
|
||||
}
|
||||
|
||||
function render() {
|
||||
list( $this->rtl_original, $this->rtl_translation ) = $this->init_rtl_settings();
|
||||
|
||||
require_once ABSPATH . 'wp-admin/includes/image.php';
|
||||
require_once ABSPATH . 'wp-admin/includes/file.php';
|
||||
require_once ABSPATH . 'wp-admin/includes/media.php';
|
||||
|
||||
?>
|
||||
<div class="wrap icl-translation-editor wpml-dialog-translate">
|
||||
<h1 id="wpml-translation-editor-header" class="wpml-translation-title"></h1>
|
||||
<?php
|
||||
do_action( 'icl_tm_messages' );
|
||||
do_action( 'wpml_tm_editor_messages' );
|
||||
$this->init_original_post();
|
||||
$this->init_editor_object();
|
||||
|
||||
$this->output_model();
|
||||
$this->output_ate_notice();
|
||||
$this->output_gutenberg_notice();
|
||||
$this->output_wysiwyg_editors();
|
||||
$this->output_copy_all_dialog();
|
||||
if ( $this->is_duplicate ) {
|
||||
$this->output_edit_independently_dialog();
|
||||
}
|
||||
$this->output_editor_form();
|
||||
?>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private function init_rtl_settings() {
|
||||
$this->rtl_original = $this->sitepress->is_rtl( $this->job->source_language_code );
|
||||
$this->rtl_translation = $this->sitepress->is_rtl( $this->job->language_code );
|
||||
$this->rtl_original_attribute_object = $this->rtl_original ? ' dir="rtl"' : ' dir="ltr"';
|
||||
$this->rtl_translation_attribute = $this->rtl_translation ? ' dir="rtl"' : ' dir="ltr"';
|
||||
|
||||
return array( $this->rtl_original, $this->rtl_translation );
|
||||
}
|
||||
|
||||
private function init_original_post() {
|
||||
// we do not need the original document of the job here
|
||||
// but the document with the same trid and in the $this->job->source_language_code
|
||||
$this->all_translations = $this->sitepress->get_element_translations( $this->job->trid, $this->job->original_post_type );
|
||||
$this->original_post = false;
|
||||
foreach ( (array) $this->all_translations as $t ) {
|
||||
if ( $t->language_code === $this->job->source_language_code ) {
|
||||
$this->original_post = $this->tm_instance->get_post( $t->element_id, $this->job->element_type_prefix );
|
||||
// if this fails for some reason use the original doc from which the trid originated
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( ! $this->original_post ) {
|
||||
$this->original_post = $this->tm_instance->get_post( $this->job_instance->get_original_element_id(), $this->job->element_type_prefix );
|
||||
}
|
||||
|
||||
if ( isset( $this->all_translations[ $this->job->language_code ] ) ) {
|
||||
$post_status = new WPML_Post_Status( $this->wpdb, $this->sitepress->get_wp_api() );
|
||||
$this->is_duplicate = $post_status->is_duplicate( $this->all_translations[ $this->job->language_code ]->element_id );
|
||||
}
|
||||
|
||||
return $this->original_post;
|
||||
}
|
||||
|
||||
private function init_editor_object() {
|
||||
global $wpdb;
|
||||
|
||||
$this->editor_object = new WPML_Translation_Editor( $this->sitepress, $wpdb, $this->job_instance );
|
||||
}
|
||||
|
||||
private function output_model() {
|
||||
|
||||
$model = array(
|
||||
'requires_translation_complete_for_each_field' => true,
|
||||
'hide_empty_fields' => true,
|
||||
'translation_is_complete' => ICL_TM_COMPLETE === (int) $this->job->status,
|
||||
'show_media_button' => false,
|
||||
'is_duplicate' => $this->is_duplicate,
|
||||
'display_hide_completed_switcher' => true,
|
||||
);
|
||||
|
||||
if ( ! empty( $_GET['return_url'] ) ) {
|
||||
$model['return_url'] = filter_var( $_GET['return_url'], FILTER_SANITIZE_URL );
|
||||
} else {
|
||||
$model['return_url'] = 'admin.php?page=' . WPML_TM_FOLDER . '/menu/translations-queue.php';
|
||||
}
|
||||
|
||||
$languages = new WPML_Translation_Editor_Languages( $this->sitepress, $this->job );
|
||||
$model['languages'] = $languages->get_model();
|
||||
|
||||
$header = new WPML_Translation_Editor_Header( $this->job_instance );
|
||||
$model['header'] = $header->get_model();
|
||||
|
||||
$model['note'] = $this->sitepress->get_wp_api()->get_post_meta(
|
||||
$this->job_instance->get_original_element_id(),
|
||||
WPML_TM_Translator_Note::META_FIELD_KEY,
|
||||
true
|
||||
);
|
||||
|
||||
$this->fields = $this->job_factory->field_contents( (int) $this->job_instance->get_id() )->run();
|
||||
$this->fields = $this->add_titles_and_adjust_styles( $this->fields );
|
||||
$this->fields = $this->add_rtl_attributes( $this->fields );
|
||||
$model['fields'] = $this->fields;
|
||||
$model['layout'] = $this->job_layout->run( $model['fields'], $this->tm_instance );
|
||||
$model['rtl_original'] = $this->rtl_original;
|
||||
$model['rtl_translation'] = $this->rtl_translation;
|
||||
$model['translation_memory'] = (bool) $this->sitepress->get_setting( 'translation_memory', 1 );
|
||||
|
||||
$model = $this->filter_the_model( $model );
|
||||
?>
|
||||
<script type="text/javascript">
|
||||
var WpmlTmEditorModel = <?php echo wp_json_encode( $model ); ?>;
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
|
||||
private function output_ate_notice() {
|
||||
|
||||
$html_fields = array_filter(
|
||||
$this->fields,
|
||||
function ( $field ) {
|
||||
return $field['field_style'] === '1' && strpos( $field['field_data'], '<' ) !== false;
|
||||
}
|
||||
);
|
||||
|
||||
if ( count( $html_fields ) > 0 ) {
|
||||
$link = 'https://wpml.org/documentation/translating-your-contents/advanced-translation-editor/#html-markers';
|
||||
$notice_text = esc_html__( 'We see you\'re translating content that contains HTML. Switch to the Advanced Translation Editor to translate content without the risk of breaking your HTML code.', 'wpml-translation-management' );
|
||||
echo '<div class="notice notice-info">
|
||||
<p>' . $notice_text . ' <a href="' . $link . '" class="wpml-external-link" target="_blank" rel="noopener">' . esc_html__( 'Read more...', 'wpml-translation-management' ) . '</a></p>
|
||||
</div>';
|
||||
}
|
||||
}
|
||||
|
||||
private function output_gutenberg_notice() {
|
||||
$has_gutenberg_block = false;
|
||||
|
||||
foreach ( $this->fields as $field ) {
|
||||
if ( preg_match( '#<!-- wp:#', $field['field_data'] ) ) {
|
||||
$has_gutenberg_block = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $has_gutenberg_block ) {
|
||||
echo '<div class="notice notice-info">
|
||||
<p>' . esc_html__( 'This content came from the Block editor and you need to translate it carefully so that formatting is not broken.', 'wpml-translation-management' ) . '</p>
|
||||
<p><a href="https://wpml.org/documentation/getting-started-guide/translating-content-created-using-gutenberg-editor/?utm_source=wpmlplugin&utm_campaign=gutenberg&utm_medium=translation-editor&utm_term=translating-content-created-using-gutenberg-editor" class="wpml-external-link" target="_blank" rel="noopener">' . esc_html__( 'Learn how to translate content that comes from Block editor', 'wpml-translation-management' ) . '</a></p>
|
||||
</div>';
|
||||
}
|
||||
}
|
||||
|
||||
private function output_wysiwyg_editors() {
|
||||
echo '<div style="display: none">';
|
||||
|
||||
foreach ( $this->fields as $field ) {
|
||||
if ( 2 === (int) $field['field_style'] ) {
|
||||
$this->editor_object->output_editors( $field );
|
||||
}
|
||||
}
|
||||
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
private function output_copy_all_dialog() {
|
||||
?>
|
||||
<div id="wpml-translation-editor-copy-all-dialog" class="wpml-dialog" style="display:none"
|
||||
title="<?php echo esc_attr__( 'Copy all fields from original', 'wpml-translation-management' ); ?>">
|
||||
<p class="wpml-dialog-cols-icon">
|
||||
<i class="otgs-ico-copy wpml-dialog-icon-xl"></i>
|
||||
</p>
|
||||
|
||||
<div class="wpml-dialog-cols-content">
|
||||
<p>
|
||||
<strong><?php echo esc_html__( 'Some fields in translation are already filled!', 'wpml-translation-management' ); ?></strong>
|
||||
<br/>
|
||||
<?php echo esc_html__( 'You have two ways to copy content from the original language:', 'wpml-translation-management' ); ?>
|
||||
</p>
|
||||
<ul>
|
||||
<li><?php echo esc_html__( 'copy to empty fields only', 'wpml-translation-management' ); ?></li>
|
||||
<li><?php echo esc_html__( 'copy and overwrite all fields', 'wpml-translation-management' ); ?></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="wpml-dialog-footer">
|
||||
<div class="alignleft">
|
||||
<button
|
||||
class="cancel wpml-dialog-close-button js-copy-cancel"><?php echo esc_html__( 'Cancel', 'wpml-translation-management' ); ?></button>
|
||||
</div>
|
||||
<div class="alignright">
|
||||
<button
|
||||
class="button-secondary js-copy-not-translated"><?php echo esc_html__( 'Copy to empty fields only', 'wpml-translation-management' ); ?></button>
|
||||
<button
|
||||
class="button-secondary js-copy-overwrite"><?php echo esc_html__( 'Copy & Overwrite all fields', 'wpml-translation-management' ); ?></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
private function output_edit_independently_dialog() {
|
||||
?>
|
||||
<div id="wpml-translation-editor-edit-independently-dialog" class="wpml-dialog" style="display:none"
|
||||
title="<?php echo esc_attr__( 'Edit independently', 'wpml-translation-management' ); ?>">
|
||||
<p class="wpml-dialog-cols-icon">
|
||||
<i class="otgs-ico-unlink wpml-dialog-icon-xl"></i>
|
||||
</p>
|
||||
|
||||
<div class="wpml-dialog-cols-content">
|
||||
<p><?php esc_html_e( 'This document is a duplicate of:', 'wpml-translation-management' ); ?>
|
||||
<span class="wpml-duplicated-post-title">
|
||||
<img class="wpml-title-flag" src="<?php echo esc_attr( $this->sitepress->get_flag_url( $this->job->source_language_code ) ); ?>">
|
||||
<?php echo esc_html( $this->job_instance->get_title() ); ?>
|
||||
</span>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<?php echo esc_html( sprintf( __( 'WPML will no longer synchronize this %s with the original content.', 'wpml-translation-management' ), $this->job_instance->get_type_title() ) ); ?>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="wpml-dialog-footer">
|
||||
<div class="alignleft">
|
||||
<button class="cancel wpml-dialog-close-button js-edit-independently-cancel"><?php echo esc_html__( 'Cancel', 'wpml-translation-management' ); ?></button>
|
||||
</div>
|
||||
<div class="alignright">
|
||||
<button class="button-secondary js-edit-independently"><?php echo esc_html__( 'Edit independently', 'wpml-translation-management' ); ?></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
private function output_editor_form() {
|
||||
?>
|
||||
<form id="icl_tm_editor" method="post" action="">
|
||||
<input type="hidden" name="job_post_type" value="<?php echo esc_attr( $this->job->original_post_type ); ?>"/>
|
||||
<input type="hidden" name="job_post_id" value="<?php echo esc_attr( $this->job->original_doc_id ); ?>"/>
|
||||
<input type="hidden" name="job_id" value="<?php echo esc_attr( $this->job_instance->get_id() ); ?>"/>
|
||||
|
||||
<div id="wpml-translation-editor-wrapper"></div>
|
||||
</form>
|
||||
<?php
|
||||
}
|
||||
|
||||
private function add_titles_and_adjust_styles( array $fields ) {
|
||||
return apply_filters( 'wpml_tm_adjust_translation_fields', $fields, $this->job, $this->original_post );
|
||||
}
|
||||
|
||||
private function add_rtl_attributes( array $fields ) {
|
||||
foreach ( $fields as &$field ) {
|
||||
$field['original_direction'] = $this->rtl_original ? 'dir="rtl"' : 'dir="ltr"';
|
||||
$field['translation_direction'] = $this->rtl_translation ? 'dir="rtl"' : 'dir="ltr"';
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
private function filter_the_model( array $model ) {
|
||||
$job_details = array(
|
||||
'job_type' => $this->job->original_post_type,
|
||||
'job_id' => $this->job->original_doc_id,
|
||||
'target' => $model['languages']['target'],
|
||||
);
|
||||
$job = apply_filters( 'wpml-translation-editor-fetch-job', null, $job_details );
|
||||
if ( $job ) {
|
||||
$model['requires_translation_complete_for_each_field'] = $job->requires_translation_complete_for_each_field();
|
||||
$model['hide_empty_fields'] = $job->is_hide_empty_fields();
|
||||
$model['show_media_button'] = $job->show_media_button();
|
||||
$model['display_hide_completed_switcher'] = $job->display_hide_completed_switcher();
|
||||
|
||||
$model['fields'] = $this->add_rtl_attributes( $job->get_all_fields() );
|
||||
$this->fields = $model['fields'];
|
||||
|
||||
$model['layout'] = $job->get_layout_of_fields();
|
||||
}
|
||||
|
||||
return $model;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
<?php
|
||||
|
||||
if ( ! class_exists( '_WP_Editors', false ) ) {
|
||||
require ABSPATH . WPINC . '/class-wp-editor.php';
|
||||
}
|
||||
|
||||
class WPML_Translation_Editor extends WPML_WPDB_And_SP_User {
|
||||
|
||||
/**
|
||||
* @var WPML_Element_Translation_Job $job
|
||||
*/
|
||||
private $job;
|
||||
|
||||
/**
|
||||
* @param SitePress $sitepress
|
||||
* @param wpdb $wpdb
|
||||
* @param WPML_Element_Translation_Job $job
|
||||
*/
|
||||
public function __construct(
|
||||
&$sitepress,
|
||||
&$wpdb,
|
||||
$job
|
||||
) {
|
||||
parent::__construct( $wpdb, $sitepress );
|
||||
$this->job = $job;
|
||||
|
||||
$this->add_hooks();
|
||||
$this->enqueue_js();
|
||||
}
|
||||
|
||||
public function add_hooks() {
|
||||
add_filter( 'tiny_mce_before_init', [ $this, 'filter_original_editor_buttons' ], 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues the JavaScript used by the TM editor.
|
||||
*/
|
||||
public function enqueue_js() {
|
||||
wp_enqueue_script( 'wpml-tm-editor-scripts' );
|
||||
wp_localize_script(
|
||||
'wpml-tm-editor-scripts',
|
||||
'tmEditorStrings',
|
||||
$this->get_translation_editor_strings()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
private function get_translation_editor_strings() {
|
||||
|
||||
$translation_memory_endpoint = apply_filters( 'wpml_st_translation_memory_endpoint', '' );
|
||||
|
||||
return array(
|
||||
'dontShowAgain' => __(
|
||||
"Don't show this again.",
|
||||
'wpml-translation-management'
|
||||
),
|
||||
'learnMore' => __(
|
||||
'<p>The administrator has disabled term translation from the translation editor. </p>
|
||||
<p>If your access permissions allow you can change this under "Translation Management" - "Multilingual Content Setup" - "Block translating taxonomy terms that already got translated". </p>
|
||||
<p>Please note that editing terms from the translation editor will affect all posts that have the respective terms associated.</p>',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
'warning' => __(
|
||||
"Please be advised that editing this term's translation here will change the value of the term in general. The changes made here, will not only affect this post!",
|
||||
'wpml-translation-management'
|
||||
),
|
||||
'title' => __(
|
||||
'Terms translation is disabled',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
'confirm' => __(
|
||||
'You have unsaved work. Are you sure you want to close without saving?',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
'cancel' => __(
|
||||
'Cancel',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
'save' => __(
|
||||
'Save',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
'hide_translated' => __(
|
||||
'Hide completed',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
'save_and_close' => __(
|
||||
'Save & Close',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
'loading_url' => ICL_PLUGIN_URL . '/res/img/ajax-loader.gif',
|
||||
'saving' => __(
|
||||
'Saving...',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
'translation_complete' => __(
|
||||
'Translation is complete',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
'contentNonce' => wp_create_nonce( 'wpml_save_job_nonce' ),
|
||||
'translationMemoryNonce' => \WPML\LIB\WP\Nonce::create( $translation_memory_endpoint ),
|
||||
'translationMemoryEndpoint' => $translation_memory_endpoint,
|
||||
'source_lang' => __(
|
||||
'Original',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
'target_lang' => __(
|
||||
'Translation to',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
'copy_all' => __(
|
||||
'Copy all fields from original',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
'resign' => __(
|
||||
'Resign',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
'resign_translation' => __(
|
||||
'Are you sure you want to resign from this job?',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
'resign_url' => admin_url( 'admin.php?page=' . WPML_TM_FOLDER . '/menu/translations-queue.php&icl_tm_action=save_translation&resign=1&job_id=' . $this->job->get_id() ),
|
||||
'confirmNavigate' => __(
|
||||
'You have unsaved changes!',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
'copy_from_original' => __(
|
||||
'Copy from original',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
'show_diff' => __( 'Show differences', 'wpml-translation-management' ),
|
||||
);
|
||||
}
|
||||
|
||||
public function filter_original_editor_buttons( $config, $editor_id ) {
|
||||
if ( strpos( $editor_id, '_original' ) > 0 ) {
|
||||
$config['toolbar1'] = ' ';
|
||||
$config['toolbar2'] = ' ';
|
||||
$config['readonly'] = '1';
|
||||
}
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
public function output_editors( $field ) {
|
||||
echo '<div id="' . $field['field_type'] . '_original_editor" class="original_value mce_editor_origin">';
|
||||
wp_editor(
|
||||
$field['field_data'],
|
||||
$field['field_type'] . '_original',
|
||||
array(
|
||||
'textarea_rows' => 4,
|
||||
'editor_class' => 'wpml_content_tr original_value mce_editor_origin',
|
||||
'media_buttons' => false,
|
||||
'quicktags' => array( 'buttons' => 'empty' ),
|
||||
)
|
||||
);
|
||||
echo '</div>';
|
||||
echo '<div id="' . $field['field_type'] . '_translated_editor" class="mce_editor translated_value">';
|
||||
wp_editor(
|
||||
$field['field_data_translated'],
|
||||
$field['field_type'],
|
||||
array(
|
||||
'textarea_rows' => 4,
|
||||
'editor_class' => 'wpml_content_tr translated_value',
|
||||
'media_buttons' => true,
|
||||
'textarea_name' => 'fields[' . $field['field_type'] . '][data]',
|
||||
)
|
||||
);
|
||||
echo '</div>';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
class WPML_Editor_UI_Field_Group extends WPML_Editor_UI_Fields {
|
||||
|
||||
private $title;
|
||||
private $divider;
|
||||
|
||||
function __construct( $title = '', $divider = true ) {
|
||||
$this->title = $title;
|
||||
$this->divider = $divider;
|
||||
|
||||
}
|
||||
|
||||
public function get_layout() {
|
||||
$data = array(
|
||||
'title' => $this->title,
|
||||
'divider' => $this->divider,
|
||||
'field_type' => 'tm-group',
|
||||
);
|
||||
|
||||
$data['fields'] = parent::get_layout();
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
class WPML_Editor_UI_Field_Image extends WPML_Editor_UI_Fields {
|
||||
|
||||
private $image_id;
|
||||
private $divider;
|
||||
private $group;
|
||||
|
||||
function __construct( $id, $image_id, $data, $divider = true ) {
|
||||
|
||||
$this->image_id = $image_id;
|
||||
$this->divider = $divider;
|
||||
$this->group = new WPML_Editor_UI_Field_Group( '', false );
|
||||
|
||||
$this->group->add_field( new WPML_Editor_UI_Single_Line_Field( $id . '-title', __( 'Title', 'wpml-translation-management' ), $data, false ) );
|
||||
$this->group->add_field( new WPML_Editor_UI_Single_Line_Field( $id . '-caption', __('Caption', 'wpml-translation-management' ), $data, false ) );
|
||||
$this->group->add_field( new WPML_Editor_UI_Single_Line_Field( $id . '-alt-text', __('Alt Text', 'wpml-translation-management' ), $data, false ) );
|
||||
$this->group->add_field( new WPML_Editor_UI_Single_Line_Field( $id . '-description', __('Description', 'wpml-translation-management' ), $data, false ) );
|
||||
|
||||
$this->add_field( $this->group );
|
||||
}
|
||||
|
||||
public function get_layout() {
|
||||
$image = wp_get_attachment_image_src( $this->image_id, array( 100, 100 ) );
|
||||
$data = array(
|
||||
'field_type' => 'wcml-image',
|
||||
'divider' => $this->divider,
|
||||
'image_src' => isset( $image[0] ) ? $image[0] : '',
|
||||
);
|
||||
|
||||
$data['fields'] = parent::get_layout();
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
class WPML_Editor_UI_Field_Section extends WPML_Editor_UI_Fields {
|
||||
|
||||
private $title;
|
||||
private $sub_title;
|
||||
|
||||
function __construct( $title = '', $sub_title = '' ) {
|
||||
$this->title = $title;
|
||||
$this->sub_title = $sub_title;
|
||||
}
|
||||
|
||||
public function get_layout() {
|
||||
$data = array(
|
||||
'empty_message' => '',
|
||||
'empty' => false,
|
||||
'title' => $this->title,
|
||||
'sub_title' => $this->sub_title,
|
||||
'field_type' => 'tm-section',
|
||||
);
|
||||
|
||||
$data['fields'] = parent::get_layout();
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
class WPML_Editor_UI_Field {
|
||||
|
||||
protected $id;
|
||||
protected $title;
|
||||
protected $original;
|
||||
protected $translation;
|
||||
private $requires_complete;
|
||||
protected $is_complete;
|
||||
|
||||
function __construct( $id, $title, $data, $requires_complete = false ) {
|
||||
|
||||
$this->id = $id;
|
||||
$this->title = $title ? $title : '';
|
||||
$this->original = $data[ $id ]['original'];
|
||||
$this->translation = isset( $data[ $id ]['translation'] ) ? $data[ $id ]['translation'] : '';
|
||||
$this->requires_complete = $requires_complete;
|
||||
$this->is_complete = isset( $data[ $id ]['is_complete'] ) ? $data[ $id ]['is_complete'] : false;
|
||||
|
||||
}
|
||||
|
||||
public function get_fields() {
|
||||
$field = array();
|
||||
$field['field_type'] = $this->id;
|
||||
$field['field_data'] = $this->original;
|
||||
$field['field_data_translated'] = $this->translation;
|
||||
$field['title'] = $this->title;
|
||||
$field['field_finished'] = $this->is_complete ? '1' : '0';
|
||||
$field['tid'] = '0';
|
||||
|
||||
return $field;
|
||||
}
|
||||
|
||||
public function get_layout() {
|
||||
// This is a field with no sub fields so just return the id
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
class WPML_Editor_UI_Fields {
|
||||
|
||||
private $fields = array();
|
||||
|
||||
public function add_field( $field ) {
|
||||
$this->fields[] = $field;
|
||||
}
|
||||
|
||||
public function get_fields() {
|
||||
$fields = array();
|
||||
/** @var WPML_Editor_UI_Field $field */
|
||||
foreach ( $this->fields as $field ) {
|
||||
$child_fields = $field->get_fields();
|
||||
foreach ( $child_fields as $child_field ) {
|
||||
$fields[] = $child_field;
|
||||
}
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
public function get_layout() {
|
||||
$layout = array();
|
||||
/** @var WPML_Editor_UI_Field $field */
|
||||
foreach ( $this->fields as $field ) {
|
||||
$layout[] = $field->get_layout();
|
||||
}
|
||||
|
||||
return $layout;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
class WPML_Editor_UI_Single_Line_Field extends WPML_Editor_UI_Field {
|
||||
|
||||
private $include_copy_button;
|
||||
|
||||
function __construct( $id, $title, $data, $include_copy_button, $requires_complete = false ) {
|
||||
parent::__construct( $id, $title, $data, $requires_complete );
|
||||
|
||||
$this->include_copy_button = $include_copy_button;
|
||||
}
|
||||
|
||||
public function get_fields() {
|
||||
$field = parent::get_fields();
|
||||
$field['field_style'] = '0';
|
||||
|
||||
return array( $field );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
class WPML_Editor_UI_TextArea_Field extends WPML_Editor_UI_Field {
|
||||
|
||||
private $include_copy_button;
|
||||
|
||||
function __construct( $id, $title, $data, $include_copy_button, $requires_complete = false ) {
|
||||
parent::__construct( $id, $title, $data, $requires_complete );
|
||||
|
||||
$this->include_copy_button = $include_copy_button;
|
||||
}
|
||||
|
||||
public function get_fields() {
|
||||
$field = parent::get_fields();
|
||||
$field['field_style'] = '1';
|
||||
|
||||
return array( $field );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
class WPML_Editor_UI_WYSIWYG_Field extends WPML_Editor_UI_Field {
|
||||
|
||||
private $include_copy_button;
|
||||
|
||||
function __construct( $id, $title, $data, $include_copy_button, $requires_complete = false ) {
|
||||
parent::__construct( $id, $title, $data, $requires_complete );
|
||||
|
||||
$this->include_copy_button = $include_copy_button;
|
||||
}
|
||||
|
||||
public function get_fields() {
|
||||
$field = parent::get_fields();
|
||||
$field['field_style'] = '2';
|
||||
|
||||
return array( $field );
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
class WPML_TM_Emails_Settings_Factory implements IWPML_Backend_Action_Loader {
|
||||
|
||||
/**
|
||||
* @return WPML_TM_Emails_Settings
|
||||
*/
|
||||
public function create() {
|
||||
global $iclTranslationManagement;
|
||||
|
||||
$hooks = null;
|
||||
|
||||
if ( $this->is_tm_settings_page() ) {
|
||||
$template_service = new WPML_Twig_Template_Loader( array( WPML_TM_PATH . '/templates/settings' ) );
|
||||
$hooks = new WPML_TM_Emails_Settings( $template_service->get_template(), $iclTranslationManagement );
|
||||
}
|
||||
|
||||
return $hooks;
|
||||
}
|
||||
|
||||
private function is_tm_settings_page() {
|
||||
return isset( $_GET['page'] )
|
||||
&& WPML_TM_FOLDER . WPML_Translation_Management::PAGE_SLUG_SETTINGS === filter_var( $_GET['page'], FILTER_SANITIZE_FULL_SPECIAL_CHARS );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
class WPML_TM_Emails_Settings {
|
||||
|
||||
const TEMPLATE = 'emails-settings.twig';
|
||||
const COMPLETED_JOB_FREQUENCY = 'completed_frequency';
|
||||
const NOTIFY_IMMEDIATELY = 1;
|
||||
const NOTIFY_DAILY = 2;
|
||||
const NOTIFY_WEEKLY = 3;
|
||||
|
||||
/**
|
||||
* @var IWPML_Template_Service
|
||||
*/
|
||||
private $template_service;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $tm;
|
||||
|
||||
public function __construct( IWPML_Template_Service $template_service, TranslationManagement $tm ) {
|
||||
$this->template_service = $template_service;
|
||||
$this->tm = $tm;
|
||||
}
|
||||
|
||||
public function add_hooks() {
|
||||
add_action( 'wpml_tm_translation_notification_setting_after', array( $this, 'render' ) );
|
||||
add_action( 'wpml_tm_notification_settings_saved', array( $this, 'remove_scheduled_summary_email' ) );
|
||||
}
|
||||
|
||||
public function render() {
|
||||
echo $this->template_service->show( $this->get_model(), self::TEMPLATE );
|
||||
}
|
||||
|
||||
private function get_model() {
|
||||
return array(
|
||||
'strings' => array(
|
||||
'section_title_translator' => __( 'Notification emails to translators', 'wpml-translation-management' ),
|
||||
'label_new_job' => __( 'Notify translators when new jobs are waiting for them', 'wpml-translation-management' ),
|
||||
'label_include_xliff' => __( 'Include XLIFF files in the notification emails', 'wpml-translation-management' ),
|
||||
'label_resigned_job' => __( 'Notify translators when they are removed from jobs', 'wpml-translation-management' ),
|
||||
'section_title_manager' => __( 'Notification emails to the translation manager', 'wpml-translation-management' ),
|
||||
'label_completed_job' => esc_html__( 'Notify the translation manager when jobs are completed %s', 'wpml-translation-management' ),
|
||||
'label_overdue_job' => esc_html__( 'Notify the translation manager when jobs are late by %s days', 'wpml-translation-management' ),
|
||||
),
|
||||
'settings' => array(
|
||||
'new_job' => array(
|
||||
'value' => self::NOTIFY_IMMEDIATELY,
|
||||
'checked' => checked( self::NOTIFY_IMMEDIATELY, $this->tm->settings['notification']['new-job'], false ),
|
||||
),
|
||||
'include_xliff' => array(
|
||||
'value' => 1,
|
||||
'checked' => checked( 1, $this->tm->settings['notification']['include_xliff'], false ),
|
||||
'disabled' => disabled( 0, $this->tm->settings['notification']['new-job'], false ),
|
||||
),
|
||||
'resigned' => array(
|
||||
'value' => self::NOTIFY_IMMEDIATELY,
|
||||
'checked' => checked( self::NOTIFY_IMMEDIATELY, $this->tm->settings['notification']['resigned'], false ),
|
||||
),
|
||||
'completed' => array(
|
||||
'value' => 1,
|
||||
'checked' => checked( self::NOTIFY_IMMEDIATELY, $this->tm->settings['notification']['completed'], false ),
|
||||
),
|
||||
'completed_frequency' => array(
|
||||
'options' => array(
|
||||
array(
|
||||
'label' => __( 'immediately', 'wpml-translation-management' ),
|
||||
'value' => self::NOTIFY_IMMEDIATELY,
|
||||
'checked' => selected( self::NOTIFY_IMMEDIATELY, $this->tm->settings['notification'][ self::COMPLETED_JOB_FREQUENCY ], false ),
|
||||
),
|
||||
array(
|
||||
'label' => __( 'once a day', 'wpml-translation-management' ),
|
||||
'value' => self::NOTIFY_DAILY,
|
||||
'checked' => selected( self::NOTIFY_DAILY, $this->tm->settings['notification'][ self::COMPLETED_JOB_FREQUENCY ], false ),
|
||||
),
|
||||
array(
|
||||
'label' => __( 'once a week', 'wpml-translation-management' ),
|
||||
'value' => self::NOTIFY_WEEKLY,
|
||||
'checked' => selected( self::NOTIFY_WEEKLY, $this->tm->settings['notification'][ self::COMPLETED_JOB_FREQUENCY ], false ),
|
||||
),
|
||||
),
|
||||
'disabled' => disabled( 0, $this->tm->settings['notification']['completed'], false ),
|
||||
),
|
||||
'overdue' => array(
|
||||
'value' => 1,
|
||||
'checked' => checked( self::NOTIFY_IMMEDIATELY, $this->tm->settings['notification']['overdue'], false ),
|
||||
),
|
||||
'overdue_offset' => array(
|
||||
'value' => $this->tm->settings['notification']['overdue_offset'],
|
||||
'disabled' => disabled( 0, $this->tm->settings['notification']['overdue'], false ),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function remove_scheduled_summary_email() {
|
||||
wp_clear_scheduled_hook( WPML_TM_Jobs_Summary_Report_Hooks::EVENT_HOOK );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace WPML\TM\Menu\TranslationQueue;
|
||||
|
||||
use WPML\FP\Obj;
|
||||
use WPML_Element_Translation_Job;
|
||||
use WPML_TM_Editors;
|
||||
use WPML_TM_ATE_Jobs;
|
||||
use WPML\TM\ATE\JobRecords;
|
||||
use WPML_TM_ATE_API;
|
||||
|
||||
class CloneJobs {
|
||||
/**
|
||||
* @var WPML_TM_ATE_Jobs
|
||||
*/
|
||||
private $ateJobs;
|
||||
|
||||
/**
|
||||
* @var WPML_TM_ATE_API
|
||||
*/
|
||||
private $apiClient;
|
||||
|
||||
/**
|
||||
* @param WPML_TM_ATE_Jobs $ateJobs
|
||||
* @param WPML_TM_ATE_API $apiClient
|
||||
*/
|
||||
public function __construct( WPML_TM_ATE_Jobs $ateJobs, WPML_TM_ATE_API $apiClient ) {
|
||||
$this->ateJobs = $ateJobs;
|
||||
$this->apiClient = $apiClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $wpmlJobId
|
||||
* @param WPML_Element_Translation_Job $jobObject
|
||||
*/
|
||||
public function cloneCompletedJob( $wpmlJobId, WPML_Element_Translation_Job $jobObject ) {
|
||||
if (
|
||||
wpml_tm_load_old_jobs_editor()->get( $wpmlJobId ) === WPML_TM_Editors::ATE
|
||||
&& (int) $jobObject->get_status_value() === ICL_TM_COMPLETE
|
||||
) {
|
||||
$ateJobId = $this->ateJobs->get_ate_job_id( $jobObject->get_id() );
|
||||
$result = $this->apiClient->clone_job( $ateJobId, $jobObject );
|
||||
if ( $result ) {
|
||||
$this->ateJobs->store( $wpmlJobId, [ JobRecords::FIELD_ATE_JOB_ID => $result['id'] ] );
|
||||
$this->ateJobs->set_wpml_status_from_ate( $wpmlJobId, $result['ate_status'] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function maybeCloneWPMLJob( $wpmlJobId ) {
|
||||
$jobsEditor = wpml_tm_load_old_jobs_editor();
|
||||
if (
|
||||
\WPML_TM_ATE_Status::is_enabled_and_activated() &&
|
||||
$jobsEditor->get( $wpmlJobId ) === WPML_TM_Editors::ATE &&
|
||||
! $this->ateJobs->get_ate_job_id( $wpmlJobId )
|
||||
) {
|
||||
$rid = wpml_tm_get_records()->icl_translate_job_by_job_id( $wpmlJobId )->rid();
|
||||
|
||||
$params = json_decode( wp_json_encode( [
|
||||
'jobs' => [ wpml_tm_create_ATE_job_creation_model( $wpmlJobId, $rid ) ]
|
||||
] ), true );
|
||||
|
||||
$response = $this->apiClient->create_jobs( $params );
|
||||
|
||||
if ( ! is_wp_error( $response ) && Obj::prop( 'jobs', $response ) ) {
|
||||
$this->ateJobs->store( $wpmlJobId, [ JobRecords::FIELD_ATE_JOB_ID => Obj::path( [ 'jobs', $rid ], $response ) ] );
|
||||
$jobsEditor->set( $wpmlJobId, WPML_TM_Editors::ATE );
|
||||
$this->ateJobs->warm_cache( [ $wpmlJobId ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use function WPML\Container\make;
|
||||
use WPML\TM\Menu\TranslationQueue\CloneJobs;
|
||||
|
||||
/**
|
||||
* @author OnTheGo Systems
|
||||
*/
|
||||
class WPML_Translations_Queue_Factory {
|
||||
/**
|
||||
* @return \WPML_Translations_Queue|null
|
||||
*/
|
||||
public function create() {
|
||||
global $sitepress;
|
||||
|
||||
if ( ! $sitepress ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( ! class_exists( 'WP_List_Table' ) ) {
|
||||
require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
|
||||
}
|
||||
|
||||
$screen_options_factory = new WPML_UI_Screen_Options_Factory( $sitepress );
|
||||
|
||||
return new WPML_Translations_Queue(
|
||||
$sitepress,
|
||||
$screen_options_factory,
|
||||
make( CloneJobs::class )
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
<?php
|
||||
|
||||
use WPML\TM\Jobs\Utils\ElementLink;
|
||||
use WPML\FP\Obj;
|
||||
|
||||
/**
|
||||
* Created by OnTheGo Systems
|
||||
*/
|
||||
class WPML_Translations_Queue_Jobs_Model {
|
||||
|
||||
/** @var TranslationManagement $tm_instance */
|
||||
private $tm_instance;
|
||||
|
||||
/** @var array $translation_jobs */
|
||||
private $translation_jobs;
|
||||
|
||||
/** @var WPML_TM_API $tm_api */
|
||||
private $tm_api;
|
||||
|
||||
/** @var array $post_types */
|
||||
private $post_types;
|
||||
|
||||
private $post_type_names = array();
|
||||
|
||||
/** @var ElementLink $element_link */
|
||||
private $element_link;
|
||||
|
||||
/**
|
||||
* WPML_Translations_Queue_Jobs_Model constructor.
|
||||
*
|
||||
* @param SitePress $sitepress
|
||||
* @param TranslationManagement $tm_instance
|
||||
* @param WPML_TM_API $tm_api
|
||||
* @param ElementLink $elemen_link
|
||||
* @param array $translation_jobs
|
||||
*/
|
||||
public function __construct(
|
||||
SitePress $sitepress,
|
||||
TranslationManagement $tm_instance,
|
||||
WPML_TM_API $tm_api,
|
||||
ElementLink $elemen_link,
|
||||
array $translation_jobs
|
||||
) {
|
||||
$this->tm_instance = $tm_instance;
|
||||
$this->tm_api = $tm_api;
|
||||
$this->element_link = $elemen_link;
|
||||
$this->translation_jobs = $translation_jobs;
|
||||
|
||||
$this->post_types = $sitepress->get_translatable_documents( true );
|
||||
$this->post_types = apply_filters( 'wpml_get_translatable_types', $this->post_types );
|
||||
}
|
||||
|
||||
public function get() {
|
||||
$model = array();
|
||||
|
||||
$model['strings'] = array(
|
||||
'job_id' => __( 'Job ID', 'wpml-translation-management' ),
|
||||
'title' => __( 'Title', 'wpml-translation-management' ),
|
||||
'type' => __( 'Type', 'wpml-translation-management' ),
|
||||
'language' => __( 'Language', 'wpml-translation-management' ),
|
||||
'status' => __( 'Translation status', 'wpml-translation-management' ),
|
||||
'deadline' => __( 'Deadline', 'wpml-translation-management' ),
|
||||
'check_all' => __( 'Check all', 'wpml-translation-management' ),
|
||||
'confirm' => __( 'Are you sure you want to resign from this job?', 'wpml-translation-management' ),
|
||||
);
|
||||
|
||||
$model['jobs'] = array();
|
||||
|
||||
foreach ( $this->translation_jobs as $job ) {
|
||||
$job->post_title = apply_filters( 'the_title', $job->post_title, $job->original_doc_id );
|
||||
$job->tm_post_link = $this->get_post_link( $job );
|
||||
$job->post_type = $this->get_post_type( $job );
|
||||
$job->icon = $this->tm_instance->status2icon_class( $job->status, $job->needs_update );
|
||||
$job->status_text = $this->get_status_text( $job );
|
||||
$job->edit_url = $this->get_edit_url( $job );
|
||||
$job->button_text = $this->get_button_text( $job );
|
||||
$job->is_doing_job = $this->is_doing_job( $job );
|
||||
$job->resign_text = $this->get_resign_text( $job );
|
||||
$job->resign_url = $this->get_resign_url( $job );
|
||||
$job->view_link = $this->get_view_translation_link( $job );
|
||||
|
||||
$model['jobs'][] = $job;
|
||||
}
|
||||
|
||||
return $model;
|
||||
}
|
||||
|
||||
private function get_post_link( $job ) {
|
||||
return $this->element_link->getOriginal( $job );
|
||||
}
|
||||
|
||||
private function get_view_translation_link( $job ) {
|
||||
return $this->element_link->getTranslation( $job );
|
||||
}
|
||||
|
||||
private function get_post_type( $job ) {
|
||||
if ( ! isset( $this->post_type_names[ $job->original_post_type ] ) ) {
|
||||
$type = $job->original_post_type;
|
||||
$name = $type;
|
||||
switch ( $job->element_type_prefix ) {
|
||||
case 'post':
|
||||
$type = substr( $type, 5 );
|
||||
break;
|
||||
|
||||
case 'package':
|
||||
$type = substr( $type, 8 );
|
||||
break;
|
||||
|
||||
case 'st-batch':
|
||||
$name = __( 'Strings', 'wpml-translation-management' );
|
||||
break;
|
||||
}
|
||||
|
||||
$this->post_type_names[ $job->original_post_type ] =
|
||||
Obj::pathOr( $name, [ $type, 'labels', 'singular_name' ], $this->post_types );;
|
||||
}
|
||||
|
||||
return $this->post_type_names[ $job->original_post_type ];
|
||||
}
|
||||
|
||||
private function get_status_text( $job ) {
|
||||
$status = $this->tm_api->get_translation_status_label( $job->status );
|
||||
if ( $job->needs_update ) {
|
||||
$status .= __( ' - (needs update)', 'wpml-translation-management' );
|
||||
}
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
private function get_edit_url( $job ) {
|
||||
$edit_url = '';
|
||||
if ( $job->original_doc_id ) {
|
||||
$translation_queue_page = admin_url( 'admin.php?page='
|
||||
. WPML_TM_FOLDER
|
||||
. '/menu/translations-queue.php&job_id='
|
||||
. $job->job_id );
|
||||
$edit_url = apply_filters( 'icl_job_edit_url', $translation_queue_page, $job->job_id );
|
||||
}
|
||||
|
||||
return $edit_url;
|
||||
}
|
||||
|
||||
private function get_button_text( $job ) {
|
||||
|
||||
$job_status = (int) $job->status;
|
||||
$needs_edit = in_array( $job_status,
|
||||
array( ICL_TM_WAITING_FOR_TRANSLATOR, ICL_TM_IN_PROGRESS, ICL_TM_COMPLETE ),
|
||||
true );
|
||||
$is_editable = ( (int) $job->translator_id ) > 0 && $needs_edit;
|
||||
if ( $is_editable ) {
|
||||
if ( $job_status == ICL_TM_COMPLETE ) {
|
||||
$button_text = __( 'Edit', 'wpml-translation-management' );
|
||||
} else {
|
||||
$button_text = __( 'Translate', 'wpml-translation-management' );
|
||||
}
|
||||
} else {
|
||||
$button_text = __( 'Take and translate', 'wpml-translation-management' );
|
||||
}
|
||||
|
||||
return $button_text;
|
||||
}
|
||||
|
||||
private function get_resign_text( $job ) {
|
||||
return $this->is_doing_job( $job ) ? __( 'Resign', 'wpml-translation-management' ) : '';
|
||||
}
|
||||
|
||||
private function is_doing_job( $job ) {
|
||||
return $job->translator_id > 0
|
||||
&& ( $job->status == ICL_TM_WAITING_FOR_TRANSLATOR
|
||||
|| $job->status == ICL_TM_IN_PROGRESS );
|
||||
}
|
||||
|
||||
private function get_resign_url( $job ) {
|
||||
return admin_url( 'admin.php?page='
|
||||
. WPML_TM_FOLDER
|
||||
. '/menu/translations-queue.php&icl_tm_action=save_translation&resign=1&job_id='
|
||||
. $job->job_id );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Created by OnTheGo Systems
|
||||
*/
|
||||
class WPML_Translations_Queue_Pagination_UI {
|
||||
|
||||
private $translation_jobs;
|
||||
private $jobs_per_page;
|
||||
|
||||
function __construct( $translation_jobs, $jobs_per_page ) {
|
||||
$this->translation_jobs = $translation_jobs;
|
||||
$this->jobs_per_page = $jobs_per_page;
|
||||
}
|
||||
|
||||
public function show() {
|
||||
$total_count = count( $this->translation_jobs );
|
||||
$paginate = new WPML_UI_Pagination( $total_count, $this->jobs_per_page );
|
||||
|
||||
$paginate->show();
|
||||
}
|
||||
|
||||
public function get_paged_jobs() {
|
||||
$paged = isset( $_GET['paged'] ) ? filter_var(
|
||||
$_GET['paged'],
|
||||
FILTER_SANITIZE_NUMBER_INT
|
||||
) : '';
|
||||
$paged = $paged ? $paged : 1;
|
||||
$per_page = $this->jobs_per_page;
|
||||
|
||||
return array_slice(
|
||||
$this->translation_jobs,
|
||||
( $paged - 1 ) * $per_page,
|
||||
$per_page
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,838 @@
|
||||
<?php
|
||||
|
||||
use WPML\FP\Fns;
|
||||
use WPML\FP\Maybe;
|
||||
use WPML\FP\Obj;
|
||||
use function WPML\Container\make;
|
||||
use WPML\TM\Menu\TranslationQueue\CloneJobs;
|
||||
use WPML\Element\API\PostTranslations;
|
||||
|
||||
class WPML_Translations_Queue {
|
||||
|
||||
/** @var SitePress $sitepress */
|
||||
private $sitepress;
|
||||
|
||||
/* @var WPML_UI_Screen_Options_Pagination */
|
||||
private $screen_options;
|
||||
|
||||
/** @var WPML_Admin_Table_Sort $table_sort */
|
||||
private $table_sort;
|
||||
|
||||
private $must_render_the_editor = false;
|
||||
|
||||
/** @var WPML_Translation_Editor_UI */
|
||||
private $translation_editor;
|
||||
|
||||
/**
|
||||
* @var CloneJobs
|
||||
*/
|
||||
private $clone_jobs;
|
||||
|
||||
/**
|
||||
* @param SitePress $sitepress
|
||||
* @param WPML_UI_Screen_Options_Factory $screen_options_factory
|
||||
* @param CloneJobs $clone_jobs
|
||||
*/
|
||||
public function __construct(
|
||||
$sitepress,
|
||||
$screen_options_factory,
|
||||
CloneJobs $clone_jobs
|
||||
) {
|
||||
$this->sitepress = $sitepress;
|
||||
$this->screen_options = $screen_options_factory->create_pagination(
|
||||
'tm_translations_queue_per_page',
|
||||
ICL_TM_DOCS_PER_PAGE
|
||||
);
|
||||
$this->table_sort = $screen_options_factory->create_admin_table_sort();
|
||||
$this->clone_jobs = $clone_jobs;
|
||||
}
|
||||
|
||||
public function init_hooks() {
|
||||
add_action( 'current_screen', array( $this, 'load' ) );
|
||||
}
|
||||
|
||||
public function load() {
|
||||
if ( $this->must_open_the_editor() ) {
|
||||
$job_id = $this->get_job_id_from_request();
|
||||
$job_object = wpml_tm_load_job_factory()->get_translation_job( $job_id, false, 0, true );
|
||||
if ( $job_object ) {
|
||||
if ( $job_object->get_translator_id() <= 0 ) {
|
||||
$job_object->assign_to( $this->sitepress->get_wp_api()->get_current_user_id() );
|
||||
}
|
||||
|
||||
$isDuplicate = $this->isDuplicate( $job_object );
|
||||
if ( $isDuplicate ) {
|
||||
$this->mark_job_as( $job_object, ICL_TM_DUPLICATE );
|
||||
} elseif ( (int) $job_object->get_status_value() !== ICL_TM_COMPLETE ) {
|
||||
$this->mark_job_as( $job_object, ICL_TM_IN_PROGRESS );
|
||||
}
|
||||
|
||||
if ( $job_object->user_can_translate( wp_get_current_user() ) ) {
|
||||
$this->clone_jobs->cloneCompletedJob( $job_id, $job_object );
|
||||
$this->clone_jobs->maybeCloneWPMLJob( $job_id );
|
||||
|
||||
$this->attempt_opening_ATE( $job_id );
|
||||
|
||||
wpml_tm_load_old_jobs_editor()->set( $job_id, WPML_TM_Editors::WPML );
|
||||
|
||||
global $wpdb;
|
||||
$this->must_render_the_editor = true;
|
||||
$this->translation_editor = new WPML_Translation_Editor_UI(
|
||||
$wpdb,
|
||||
$this->sitepress,
|
||||
wpml_load_core_tm(),
|
||||
$job_object,
|
||||
new WPML_TM_Job_Action_Factory( wpml_tm_load_job_factory() ),
|
||||
new WPML_TM_Job_Layout( $wpdb, $this->sitepress->get_wp_api() )
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $icl_translation_filter
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function display( array $icl_translation_filter = array() ) {
|
||||
if ( $this->must_render_the_editor ) {
|
||||
$this->translation_editor->render();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @var TranslationManagement $iclTranslationManagement
|
||||
* @var WPML_Translation_Job_Factory $wpml_translation_job_factory
|
||||
*/
|
||||
global $iclTranslationManagement, $wpml_translation_job_factory;
|
||||
|
||||
$translation_jobs = array();
|
||||
$job_types = array();
|
||||
$langs_from = array();
|
||||
$lang_from = array();
|
||||
$langs_to = array();
|
||||
$lang_to = array();
|
||||
$job_id = null;
|
||||
|
||||
if ( ! empty( $_GET['resigned'] ) ) {
|
||||
$iclTranslationManagement->add_message(
|
||||
array(
|
||||
'type' => 'updated',
|
||||
'text' => __(
|
||||
"You've resigned from this job.",
|
||||
'wpml-translation-management'
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$cookie_filters = self::get_cookie_filters();
|
||||
|
||||
if ( $cookie_filters ) {
|
||||
$icl_translation_filter = $cookie_filters;
|
||||
}
|
||||
|
||||
$current_translator = $iclTranslationManagement->get_current_translator();
|
||||
$can_translate = $current_translator && $current_translator->ID > 0 && $current_translator->language_pairs;
|
||||
$post_link_factory = new WPML_TM_Post_Link_Factory( $this->sitepress );
|
||||
if ( $can_translate ) {
|
||||
$icl_translation_filter['translator_id'] = $current_translator->ID;
|
||||
$icl_translation_filter['include_unassigned'] = true;
|
||||
|
||||
$element_type_prefix = isset( $_GET['element_type'] ) ? $_GET['element_type'] : 'post';
|
||||
if ( isset( $_GET['updated'] ) && $_GET['updated'] ) {
|
||||
$tm_post_link_updated = $post_link_factory->view_link( $_GET['updated'] );
|
||||
if ( $iclTranslationManagement->is_external_type( $element_type_prefix ) ) {
|
||||
$tm_post_link_updated = apply_filters(
|
||||
'wpml_external_item_link',
|
||||
$tm_post_link_updated,
|
||||
$_GET['updated'],
|
||||
false
|
||||
);
|
||||
}
|
||||
$user_message = __( 'Translation updated: ', 'wpml-translation-management' ) . $tm_post_link_updated;
|
||||
$iclTranslationManagement->add_message(
|
||||
array(
|
||||
'type' => 'updated',
|
||||
'text' => $user_message,
|
||||
)
|
||||
);
|
||||
} elseif ( isset( $_GET['added'] ) && $_GET['added'] ) {
|
||||
$tm_post_link_added = $post_link_factory->view_link( $_GET['added'] );
|
||||
if ( $iclTranslationManagement->is_external_type( $element_type_prefix ) ) {
|
||||
$tm_post_link_added = apply_filters(
|
||||
'wpml_external_item_link',
|
||||
$tm_post_link_added,
|
||||
$_GET['added'],
|
||||
false
|
||||
);
|
||||
}
|
||||
$user_message = __( 'Translation added: ', 'wpml-translation-management' ) . $tm_post_link_added;
|
||||
$iclTranslationManagement->add_message(
|
||||
array(
|
||||
'type' => 'updated',
|
||||
'text' => $user_message,
|
||||
)
|
||||
);
|
||||
} elseif ( isset( $_GET['job-cancelled'] ) ) {
|
||||
$user_message = __( 'Translation has been removed by admin', 'wpml-translation-management' );
|
||||
$iclTranslationManagement->add_message(
|
||||
array(
|
||||
'type' => 'error',
|
||||
'text' => $user_message,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ( isset( $_GET['title'] ) && $_GET['title'] ) {
|
||||
$icl_translation_filter['title'] = filter_var( $_GET['title'], FILTER_SANITIZE_FULL_SPECIAL_CHARS );
|
||||
}
|
||||
|
||||
if ( ! empty( $current_translator->language_pairs ) ) {
|
||||
$_langs_to = array();
|
||||
if ( 1 < count( $current_translator->language_pairs ) ) {
|
||||
foreach ( $current_translator->language_pairs as $lang => $to ) {
|
||||
$langs_from[] = $this->sitepress->get_language_details( $lang );
|
||||
$_langs_to = array_merge( (array) $_langs_to, array_keys( $to ) );
|
||||
}
|
||||
$_langs_to = array_unique( $_langs_to );
|
||||
} else {
|
||||
$_langs_to = array_keys( current( $current_translator->language_pairs ) );
|
||||
$lang_from = $this->sitepress->get_language_details( key( $current_translator->language_pairs ) );
|
||||
$icl_translation_filter['from'] = $lang_from['code'];
|
||||
}
|
||||
|
||||
if ( 1 < count( $_langs_to ) ) {
|
||||
foreach ( $_langs_to as $lang ) {
|
||||
$langs_to[] = $this->sitepress->get_language_details( $lang );
|
||||
}
|
||||
} else {
|
||||
$lang_to = $this->sitepress->get_language_details( current( $_langs_to ) );
|
||||
$icl_translation_filter['to'] = $lang_to['code'];
|
||||
}
|
||||
$job_types = $wpml_translation_job_factory->get_translation_job_types_filter(
|
||||
array(),
|
||||
array(
|
||||
'translator_id' => $current_translator->ID,
|
||||
'include_unassigned' => true,
|
||||
)
|
||||
);
|
||||
|
||||
if ( isset( $_GET['orderby'] ) ) {
|
||||
$icl_translation_filter['order_by'] = filter_var( $_GET['orderby'], FILTER_SANITIZE_STRING );
|
||||
}
|
||||
|
||||
if ( isset( $_GET['order'] ) ) {
|
||||
$icl_translation_filter['order'] = filter_var( $_GET['order'], FILTER_SANITIZE_STRING );
|
||||
}
|
||||
|
||||
$translation_jobs = $wpml_translation_job_factory->get_translation_jobs( (array) $icl_translation_filter );
|
||||
}
|
||||
}
|
||||
?>
|
||||
<div class="wrap">
|
||||
<h2><?php echo __( 'Translations queue', 'wpml-translation-management' ); ?></h2>
|
||||
|
||||
<?php if ( empty( $current_translator->language_pairs ) ) : ?>
|
||||
<div class="error below-h2"><p><?php _e( 'No translation languages configured for this user.', 'wpml-translation-management' ); ?></p></div>
|
||||
<?php endif; ?>
|
||||
<?php do_action( 'icl_tm_messages' ); ?>
|
||||
|
||||
<?php if ( ! empty( $current_translator->language_pairs ) ) : ?>
|
||||
|
||||
<div class="alignright">
|
||||
<form method="post"
|
||||
name="translation-jobs-filter"
|
||||
id="tm-queue-filter"
|
||||
action="admin.php?page=<?php echo WPML_TM_FOLDER; ?>/menu/translations-queue.php">
|
||||
<input type="hidden" name="icl_tm_action" value="ujobs_filter"/>
|
||||
<table class="">
|
||||
<tbody>
|
||||
<tr valign="top">
|
||||
<td>
|
||||
<select name="filter[type]">
|
||||
<option value=""><?php _e( 'All types', 'wpml-translation-management' ); ?></option>
|
||||
<?php foreach ( $job_types as $job_type => $job_type_name ) : ?>
|
||||
<option value="<?php echo $job_type; ?>"
|
||||
<?php
|
||||
if ( ! empty( $icl_translation_filter['type'] )
|
||||
&& $icl_translation_filter['type']
|
||||
=== $job_type ) :
|
||||
|
||||
?>
|
||||
selected="selected"<?php endif; ?>><?php echo $job_type_name; ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<label>
|
||||
<strong><?php _e( 'From', 'wpml-translation-management' ); ?></strong>
|
||||
<?php
|
||||
if ( 1 < count( $current_translator->language_pairs ) ) {
|
||||
|
||||
$from_select = new WPML_Simple_Language_Selector( $this->sitepress );
|
||||
echo $from_select->render(
|
||||
array(
|
||||
'name' => 'filter[from]',
|
||||
'please_select_text' => __(
|
||||
'Any language',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
'style' => '',
|
||||
'languages' => $langs_from,
|
||||
'selected' => isset( $icl_translation_filter['from'] )
|
||||
? $icl_translation_filter['from'] : '',
|
||||
)
|
||||
);
|
||||
} else {
|
||||
?>
|
||||
<input type="hidden"
|
||||
name="filter[from]"
|
||||
value="<?php echo esc_attr( $lang_from['code'] ); ?>"/>
|
||||
<?php
|
||||
echo $this->sitepress->get_flag_img( $lang_from['code'] )
|
||||
. ' '
|
||||
. $lang_from['display_name'];
|
||||
?>
|
||||
<?php } ?>
|
||||
</label>
|
||||
<label>
|
||||
<strong><?php _e( 'To', 'wpml-translation-management' ); ?></strong>
|
||||
<?php
|
||||
if ( 1 < @count( $langs_to ) ) {
|
||||
$to_select = new WPML_Simple_Language_Selector( $this->sitepress );
|
||||
echo $to_select->render(
|
||||
array(
|
||||
'name' => 'filter[to]',
|
||||
'please_select_text' => __(
|
||||
'Any language',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
'style' => '',
|
||||
'languages' => $langs_to,
|
||||
'selected' => isset( $icl_translation_filter['to'] )
|
||||
? $icl_translation_filter['to'] : '',
|
||||
)
|
||||
);
|
||||
} else {
|
||||
?>
|
||||
<input type="hidden" name="filter[to]"
|
||||
value="<?php echo esc_attr( $lang_to['code'] ); ?>"/>
|
||||
<?php
|
||||
echo $this->sitepress->get_flag_img( $lang_to['code'] ) . ' ' . $lang_to['display_name'];
|
||||
}
|
||||
|
||||
$translation_filter_status = null;
|
||||
if ( array_key_exists( 'status', $icl_translation_filter ) ) {
|
||||
$translation_filter_status = (int) $icl_translation_filter['status'];
|
||||
}
|
||||
|
||||
?>
|
||||
</label>
|
||||
|
||||
<select name="filter[status]">
|
||||
<option value=""><?php _e( 'All statuses', 'wpml-translation-management' ) ?></option>
|
||||
<option value="<?php echo ICL_TM_COMPLETE; ?>"
|
||||
<?php
|
||||
if ( $translation_filter_status === ICL_TM_COMPLETE ) :
|
||||
|
||||
?>
|
||||
selected="selected"<?php endif; ?>><?php echo TranslationManagement::status2text( ICL_TM_COMPLETE ); ?></option>
|
||||
<option value="<?php echo ICL_TM_IN_PROGRESS; ?>"
|
||||
<?php
|
||||
if ( $translation_filter_status
|
||||
=== ICL_TM_IN_PROGRESS ) :
|
||||
|
||||
?>
|
||||
selected="selected"<?php endif; ?>><?php echo TranslationManagement::status2text( ICL_TM_IN_PROGRESS ); ?></option>
|
||||
<option value="<?php echo ICL_TM_WAITING_FOR_TRANSLATOR; ?>"
|
||||
<?php
|
||||
if ( $translation_filter_status === ICL_TM_WAITING_FOR_TRANSLATOR ) :
|
||||
|
||||
?>
|
||||
selected="selected"<?php endif; ?>><?php _e( 'Available to translate', 'wpml-translation-management' ) ?></option>
|
||||
</select>
|
||||
|
||||
<input class="button-secondary"
|
||||
type="submit"
|
||||
value="<?php _e( 'Filter', 'wpml-translation-management' ); ?>"/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
<?php
|
||||
$actions = apply_filters( 'wpml_translation_queue_actions', array() );
|
||||
|
||||
/**
|
||||
* @deprecated Use 'wpml_translation_queue_actions' instead
|
||||
*/
|
||||
$actions = apply_filters( 'WPML_translation_queue_actions', $actions );
|
||||
?>
|
||||
<?php if ( count( $actions ) > 0 ) : ?>
|
||||
<form method="post" name="translation-jobs-action" action="admin.php?page=<?php echo WPML_TM_FOLDER; ?>/menu/translations-queue.php">
|
||||
<?php endif; ?>
|
||||
|
||||
<?php
|
||||
do_action( 'wpml_xliff_select_actions', $actions, 'action', $translation_jobs );
|
||||
|
||||
/**
|
||||
* @deprecated Use 'wpml_xliff_select_actions' instead
|
||||
*/
|
||||
do_action( 'WPML_xliff_select_actions', $actions, 'action', $translation_jobs );
|
||||
?>
|
||||
|
||||
<?php
|
||||
|
||||
$translation_queue_pagination = new WPML_Translations_Queue_Pagination_UI(
|
||||
$translation_jobs,
|
||||
$this->screen_options->get_items_per_page()
|
||||
);
|
||||
$translation_jobs = $translation_queue_pagination->get_paged_jobs();
|
||||
|
||||
?>
|
||||
<?php // pagination - end ?>
|
||||
|
||||
<?php
|
||||
$blog_translators = wpml_tm_load_blog_translators();
|
||||
$tm_api = new WPML_TM_API( $blog_translators, $iclTranslationManagement );
|
||||
|
||||
$translation_queue_jobs_model = new WPML_Translations_Queue_Jobs_Model(
|
||||
$this->sitepress,
|
||||
$iclTranslationManagement,
|
||||
$tm_api,
|
||||
\WPML\TM\Jobs\Utils\ElementLinkFactory::create(),
|
||||
$translation_jobs
|
||||
);
|
||||
$translation_jobs = $translation_queue_jobs_model->get();
|
||||
|
||||
$this->show_table( $translation_jobs, count( $actions ) > 0, $job_id );
|
||||
?>
|
||||
|
||||
<div id="tm-queue-pagination" class="tablenav">
|
||||
<?php $translation_queue_pagination->show(); ?>
|
||||
|
||||
<?php
|
||||
do_action( 'wpml_xliff_select_actions', $actions, 'action2', $translation_jobs );
|
||||
|
||||
/**
|
||||
* @deprecated Use 'wpml_xliff_select_actions' instead
|
||||
*/
|
||||
do_action( 'WPML_xliff_select_actions', $actions, 'action2', $translation_jobs );
|
||||
?>
|
||||
</div>
|
||||
<?php // pagination - end ?>
|
||||
|
||||
<?php if ( count( $actions ) > 0 ) : ?>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php do_action( 'wpml_translation_queue_after_display', $translation_jobs ); ?>
|
||||
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
// Check for any bulk actions
|
||||
if ( isset( $_POST['action'] ) || isset( $_POST['action2'] ) ) {
|
||||
$xliff_version = isset( $_POST['doaction'] ) ? $_POST['action'] : $_POST['action2'];
|
||||
do_action( 'wpml_translation_queue_do_actions_export_xliff', $_POST, $xliff_version );
|
||||
|
||||
/**
|
||||
* @deprecated Use 'wpml_translation_queue_do_actions_export_xliff' instead
|
||||
*/
|
||||
do_action( 'WPML_translation_queue_do_actions_export_xliff', $_POST, $xliff_version );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $translation_jobs
|
||||
* @param $has_actions
|
||||
* @param $open_job
|
||||
*/
|
||||
public function show_table( $translation_jobs, $has_actions, $open_job ) {
|
||||
?>
|
||||
<table class="widefat striped icl-translation-jobs" id="icl-translation-jobs" cellspacing="0"
|
||||
data-string-complete="<?php esc_attr_e( 'Complete', 'wpml-translation-management' ); ?>"
|
||||
data-string-edit="<?php esc_attr_e( 'Edit', 'wpml-translation-management' ); ?>"
|
||||
>
|
||||
<?php foreach ( array( 'thead', 'tfoot' ) as $element_type ) { ?>
|
||||
<<?php echo $element_type; ?>>
|
||||
<tr>
|
||||
<?php if ( $has_actions ) { ?>
|
||||
<td class="manage-column column-cb check-column js-check-all" scope="col">
|
||||
<input title="<?php echo esc_attr( $translation_jobs['strings']['check_all'] ); ?>"
|
||||
type="checkbox"/>
|
||||
</td>
|
||||
<?php } ?>
|
||||
<th scope="col" class="cloumn-job_id <?php echo $this->table_sort->get_column_classes( 'job_id' ); ?>">
|
||||
<a href="<?php echo $this->table_sort->get_column_url( 'job_id' ); ?>">
|
||||
<span><?php echo esc_html( $translation_jobs['strings']['job_id'] ); ?></span>
|
||||
<span class="sorting-indicator"></span>
|
||||
</a>
|
||||
</th>
|
||||
<th scope="col"
|
||||
class="column-title"><?php echo esc_html( $translation_jobs['strings']['title'] ); ?></th>
|
||||
<th scope="col"
|
||||
class="column-type"><?php echo esc_html( $translation_jobs['strings']['type'] ); ?></th>
|
||||
<th scope="col"
|
||||
class="column-language"><?php echo esc_html( $translation_jobs['strings']['language'] ); ?></th>
|
||||
<th scope="col"
|
||||
class="column-status"><?php echo esc_html( $translation_jobs['strings']['status'] ); ?></th>
|
||||
<th scope="col"
|
||||
class="column-deadline <?php echo $this->table_sort->get_column_classes( 'deadline' ); ?>">
|
||||
<a href="<?php echo $this->table_sort->get_column_url( 'deadline' ); ?>">
|
||||
<span><?php echo esc_html( $translation_jobs['strings']['deadline'] ); ?></span>
|
||||
<span class="sorting-indicator"></span>
|
||||
</a>
|
||||
</th>
|
||||
<th scope="col"
|
||||
class="column-actions"></th>
|
||||
</tr>
|
||||
</<?php echo $element_type; ?>>
|
||||
<?php } ?>
|
||||
|
||||
<tbody>
|
||||
<?php if ( empty( $translation_jobs['jobs'] ) ) { ?>
|
||||
<tr>
|
||||
<td colspan="7"
|
||||
align="center"><?php _e( 'No translation jobs found', 'wpml-translation-management' ); ?></td>
|
||||
</tr>
|
||||
<?php
|
||||
} else {
|
||||
|
||||
$ate_jobs = apply_filters( 'wpml_tm_ate_jobs_data', array(), $translation_jobs['jobs'] );
|
||||
|
||||
foreach ( $translation_jobs['jobs'] as $index => $job ) {
|
||||
?>
|
||||
<tr<?php echo $this->get_row_css_attribute( $job ); ?>>
|
||||
<?php if ( $has_actions ) { ?>
|
||||
<td>
|
||||
<input type="checkbox" name="job[<?php echo $job->job_id; ?>]" value="1"/>
|
||||
</td>
|
||||
<?php } ?>
|
||||
<td class="column-job_id"><?php echo $job->job_id; ?></td>
|
||||
<td class="column-title">
|
||||
<?php echo esc_html( $job->post_title ); ?>
|
||||
<div class="row-actions">
|
||||
<span class="view"><?php echo $job->tm_post_link; ?></span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="column-type" data-colname=""><?php echo esc_html( $job->post_type ); ?></td>
|
||||
<td class="column-languages"><?php echo $job->lang_text_with_flags; ?></td>
|
||||
<td class="column-status"><span><i class="<?php echo esc_attr( $job->icon ); ?>"></i><?php echo esc_html( $job->status_text ); ?></span></td>
|
||||
<td class="column-deadline">
|
||||
<?php
|
||||
if ( $job->deadline_date ) {
|
||||
if ( '0000-00-00 00:00:00' === $job->deadline_date ) {
|
||||
$deadline_day = __( 'Not set', 'wpml-translation-management' );
|
||||
} else {
|
||||
$deadline_day = date( 'Y-m-d', strtotime( $job->deadline_date ) );
|
||||
}
|
||||
echo esc_html( $deadline_day );
|
||||
}
|
||||
?>
|
||||
</td>
|
||||
<td class="column-actions">
|
||||
<?php
|
||||
if ( $job->original_doc_id ) {
|
||||
$ate_job_id = null;
|
||||
if ( array_key_exists( $job->job_id, $ate_jobs ) ) {
|
||||
if ( array_key_exists( 'ate_job_id', $ate_jobs[ $job->job_id ] ) ) {
|
||||
$ate_job_id = $ate_jobs[ $job->job_id ]['ate_job_id'];
|
||||
}
|
||||
}
|
||||
?>
|
||||
<a class="button-secondary js-translation-queue-edit"
|
||||
href="<?php echo esc_attr( $job->edit_url ); ?>"
|
||||
data-job-id="<?php echo $job->job_id; ?>"
|
||||
data-ate-job-id="<?php echo esc_attr( $ate_job_id ); ?>"
|
||||
data-ate-job-url="<?php echo $job->edit_url; ?>"
|
||||
data-ate-auto-open="<?php echo $job->job_id === $open_job; ?>"
|
||||
>
|
||||
<?php echo $job->button_text; ?>
|
||||
</a>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
<?php if ( $job->is_doing_job ) { ?>
|
||||
<br>
|
||||
<a class="link-resign"
|
||||
href="<?php echo esc_attr( $job->resign_url ); ?>"
|
||||
onclick="if ( !confirm( '<?php echo esc_js( $translation_jobs['strings']['confirm'] ); ?>' ) ) { return false; }">
|
||||
<?php echo $job->resign_text; ?>
|
||||
</a>
|
||||
<?php } ?>
|
||||
<?php
|
||||
if ( $job->view_link ) {
|
||||
echo '<br>';
|
||||
echo $job->view_link;
|
||||
}
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php
|
||||
}
|
||||
|
||||
private function get_job_id_from_request() {
|
||||
/**
|
||||
* @var TranslationManagement $iclTranslationManagement
|
||||
* @var WPML_Post_Translation $wpml_post_translations
|
||||
* @var WPML_Translation_Job_Factory $wpml_translation_job_factory
|
||||
*/
|
||||
global $iclTranslationManagement, $wpml_post_translations, $wpml_translation_job_factory, $sitepress;
|
||||
|
||||
$job_id = filter_var( isset( $_GET['job_id'] ) ? $_GET['job_id'] : '', FILTER_SANITIZE_NUMBER_INT );
|
||||
|
||||
list( $trid, $update_needed, $language_code, $element_type ) = $this->get_job_data_for_restore( $job_id );
|
||||
$source_language_code = filter_var( isset( $_GET['source_language_code'] ) ? $_GET['source_language_code'] : '', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
|
||||
|
||||
if ( $trid && $language_code ) {
|
||||
if ( ! $job_id ) {
|
||||
$job_id = $iclTranslationManagement->get_translation_job_id(
|
||||
$trid,
|
||||
$language_code
|
||||
);
|
||||
if ( ! $job_id ) {
|
||||
if ( ! $source_language_code ) {
|
||||
$post_id = SitePress::get_original_element_id_by_trid( $trid );
|
||||
} else {
|
||||
$posts_in_trid = $wpml_post_translations->get_element_translations(
|
||||
false,
|
||||
$trid
|
||||
);
|
||||
$post_id = isset( $posts_in_trid[ $source_language_code ] ) ? $posts_in_trid[ $source_language_code ] : false;
|
||||
}
|
||||
$blog_translators = wpml_tm_load_blog_translators();
|
||||
$args = array(
|
||||
'lang_from' => $source_language_code,
|
||||
'lang_to' => $language_code,
|
||||
'job_id' => $job_id,
|
||||
);
|
||||
if ( $post_id && $blog_translators->is_translator(
|
||||
$sitepress->get_current_user()->ID,
|
||||
$args
|
||||
)
|
||||
) {
|
||||
$job_id = $wpml_translation_job_factory->create_local_post_job(
|
||||
$post_id,
|
||||
$language_code
|
||||
);
|
||||
}
|
||||
}
|
||||
} elseif ( $update_needed ) {
|
||||
$element_id = SitePress::get_original_element_id_by_trid( $trid );
|
||||
$job_id = $wpml_translation_job_factory->create_local_job( $element_id, $language_code, null, $element_type );
|
||||
}
|
||||
}
|
||||
|
||||
return $job_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $job_id
|
||||
*
|
||||
* @return array ( trid, updated_needed, language_code )
|
||||
*/
|
||||
private function get_job_data_for_restore( $job_id ) {
|
||||
$fields = array( 'trid', 'update_needed', 'language_code', 'element_type' );
|
||||
$result = array_fill_keys( $fields, false );
|
||||
|
||||
if ( isset( $_GET['trid'] ) ) {
|
||||
$result['trid'] = filter_var( $_GET['trid'], FILTER_SANITIZE_NUMBER_INT );
|
||||
}
|
||||
if ( isset( $_GET['update_needed'] ) ) {
|
||||
$result['update_needed'] = filter_var( $_GET['update_needed'], FILTER_SANITIZE_NUMBER_INT );
|
||||
}
|
||||
if ( isset( $_GET['language_code'] ) ) {
|
||||
$result['language_code'] = filter_var( $_GET['language_code'], FILTER_SANITIZE_FULL_SPECIAL_CHARS );
|
||||
}
|
||||
|
||||
$wpdb = $this->sitepress->get_wpdb();
|
||||
|
||||
if ( isset( $result['trid'] ) ) {
|
||||
$element_type_query = $wpdb->prepare(
|
||||
"SELECT element_type FROM {$wpdb->prefix}icl_translations WHERE trid = %d LIMIT 1",
|
||||
$result['trid']
|
||||
);
|
||||
|
||||
$result['element_type'] = $wpdb->get_var( $element_type_query );
|
||||
}
|
||||
|
||||
if ( ! $job_id || isset( $_GET['trid'], $_GET['update_needed'], $_GET['language_code'], $result['element_type'] ) ) {
|
||||
return array(
|
||||
$result['trid'],
|
||||
$result['update_needed'],
|
||||
$result['language_code'],
|
||||
$result['element_type'],
|
||||
);
|
||||
}
|
||||
|
||||
$sql = "
|
||||
SELECT t.trid, ts.needs_update as update_needed, t.language_code, t.element_type
|
||||
FROM {$wpdb->prefix}icl_translations t
|
||||
INNER JOIN {$wpdb->prefix}icl_translation_status ts on ts.translation_id = t.translation_id
|
||||
INNER JOIN {$wpdb->prefix}icl_translate_job j ON j.rid = ts.rid
|
||||
WHERE j.job_id = %d;
|
||||
";
|
||||
|
||||
$db_result = $wpdb->get_row( $wpdb->prepare( $sql, $job_id ), ARRAY_A );
|
||||
|
||||
foreach ( $fields as $field ) {
|
||||
if ( ! isset( $_GET[ $field ] ) ) {
|
||||
$result[ $field ] = $db_result[ $field ];
|
||||
}
|
||||
}
|
||||
|
||||
return array( $result['trid'], $result['update_needed'], $result['language_code'], $result['element_type'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param stdClass $job
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function get_row_css_attribute( $job ) {
|
||||
$classes = [ 'js-wpml-job-row' ];
|
||||
|
||||
if ( isset( $job->deadline_date ) && ICL_TM_COMPLETE !== (int) $job->status ) {
|
||||
$deadline_day = date( 'Y-m-d', strtotime( $job->deadline_date ) );
|
||||
$today = date( 'Y-m-d' );
|
||||
|
||||
if ( $deadline_day < $today ) {
|
||||
$classes[] = 'overdue';
|
||||
}
|
||||
}
|
||||
|
||||
return ' class="' . esc_attr( implode( ' ', $classes ) ) . '" data-job-id="' . $job->job_id . '"';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
private function must_open_the_editor() {
|
||||
return ( isset( $_GET['job_id'] ) && $_GET['job_id'] > 0 )
|
||||
|| ( isset( $_GET['trid'] ) && $_GET['trid'] > 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $job_id
|
||||
*/
|
||||
private function attempt_opening_ATE( $job_id ) {
|
||||
if ( ! WPML_TM_ATE_Status::is_enabled_and_activated() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// a job already exists
|
||||
if ( isset( $_GET['job_id'] ) && $_GET['job_id'] > 0 ) {
|
||||
$current_editor = wpml_tm_load_old_jobs_editor()->get( $job_id );
|
||||
if ( WPML_TM_Editors::ATE !== $current_editor && WPML_TM_Editors::NONE !== $current_editor ) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$editor_url = apply_filters( 'wpml_tm_ate_jobs_editor_url', null, $job_id, $this->get_return_url() );
|
||||
|
||||
if ( $editor_url ) {
|
||||
make( \WPML\TM\ATE\Sync\Trigger::class )->setSyncRequiredForCurrentUser();
|
||||
wpml_tm_load_old_jobs_editor()->set( $job_id, WPML_TM_Editors::ATE );
|
||||
|
||||
if ( wp_safe_redirect( $editor_url, 302, 'WPML' ) ) {
|
||||
exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed|null|string
|
||||
*/
|
||||
private function get_return_url() {
|
||||
$return_url = null;
|
||||
|
||||
if ( array_key_exists( 'return_url', $_GET ) ) {
|
||||
$return_url = filter_var( $_GET['return_url'], FILTER_SANITIZE_URL );
|
||||
|
||||
$return_url_parts = wp_parse_url( $return_url );
|
||||
|
||||
$admin_url = get_admin_url();
|
||||
$admin_url_parts = wp_parse_url( $admin_url );
|
||||
|
||||
if ( strpos( $return_url_parts['path'], $admin_url_parts['path'] ) === 0 ) {
|
||||
$admin_url_parts['path'] = $return_url_parts['path'];
|
||||
} else {
|
||||
$admin_url_parts = $return_url_parts;
|
||||
}
|
||||
|
||||
if ( array_key_exists( 'query', $return_url_parts ) ) {
|
||||
$admin_url_parts['query'] = $this->filterQueryParameters( $return_url_parts['query'] );
|
||||
}
|
||||
|
||||
$return_url = http_build_url( $admin_url_parts );
|
||||
}
|
||||
|
||||
return $return_url;
|
||||
}
|
||||
|
||||
private function filterQueryParameters( $query ) {
|
||||
$parameters = [];
|
||||
parse_str( $query, $parameters );
|
||||
|
||||
unset( $parameters['ate_original_id'] );
|
||||
unset( $parameters['back'] );
|
||||
unset( $parameters['complete'] );
|
||||
|
||||
return http_build_query( $parameters );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function get_cookie_filters() {
|
||||
$filters = array();
|
||||
|
||||
if ( isset( $_COOKIE['wp-translation_ujobs_filter'] ) ) {
|
||||
parse_str( $_COOKIE['wp-translation_ujobs_filter'], $filters );
|
||||
|
||||
$filters = filter_var_array(
|
||||
$filters,
|
||||
array(
|
||||
'type' => FILTER_SANITIZE_STRING,
|
||||
'from' => FILTER_SANITIZE_STRING,
|
||||
'to' => FILTER_SANITIZE_STRING,
|
||||
'status' => FILTER_SANITIZE_NUMBER_INT,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return $filters;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \WPML_Translation_Job $job_object
|
||||
* @param int $status
|
||||
*/
|
||||
private function mark_job_as( \WPML_Translation_Job $job_object, $status ) {
|
||||
wpml_load_core_tm()->update_translation_status(
|
||||
[
|
||||
'translation_id' => $job_object->get_translation_id(),
|
||||
'status' => $status,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
private function isDuplicate( \WPML_Translation_Job $jobObject ) {
|
||||
return Maybe::of( $jobObject->get_original_element_id() )
|
||||
->map( PostTranslations::get() )
|
||||
->map( Obj::prop( $jobObject->get_language_code() ) )
|
||||
->map( Obj::prop( 'element_id' ) )
|
||||
->map( [ wpml_get_post_status_helper(), 'is_duplicate' ] )
|
||||
->getOrElse( false );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace WPML\TM\Menu\TranslationRoles;
|
||||
|
||||
class RoleValidator {
|
||||
/**
|
||||
* @param string $roleName
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public static function getTheHighestPossibleIfNotValid( $roleName ) {
|
||||
$wp_role = get_role( $roleName );
|
||||
$user = wp_get_current_user();
|
||||
if ( \WPML_WP_Roles::get_highest_level( $wp_role->capabilities ) > \WPML_WP_Roles::get_user_max_level( $user ) ) {
|
||||
$wp_role = current( \WPML_WP_Roles::get_roles_up_to_user_level( $user ) );
|
||||
if ( ! $wp_role ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$roleName = $wp_role->name;
|
||||
}
|
||||
|
||||
return $roleName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
class WPML_TM_Translation_Roles_Section_Factory implements IWPML_TM_Admin_Section_Factory {
|
||||
|
||||
/**
|
||||
* @return \WPML_TM_Admin_Section|\WPML_TM_Translation_Roles_Section
|
||||
*/
|
||||
public function create() {
|
||||
global $wpdb;
|
||||
|
||||
$user_query_factory = new WPML_WP_User_Query_Factory();
|
||||
|
||||
$translation_manager_settings = new WPML_Translation_Manager_Settings(
|
||||
new WPML_Translation_Manager_View(),
|
||||
new WPML_Translation_Manager_Records( $wpdb, $user_query_factory, wp_roles() )
|
||||
);
|
||||
|
||||
$translator_settings = new WPML_Translator_Settings_Proxy( array( $this, 'create_translator_settings' ) );
|
||||
|
||||
return new WPML_TM_Translation_Roles_Section( $translation_manager_settings, $translator_settings );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \WPML_Translator_Settings
|
||||
*/
|
||||
public function create_translator_settings() {
|
||||
global $wpdb, $sitepress;
|
||||
|
||||
do_action( 'wpml_tm_ate_synchronize_translators' );
|
||||
|
||||
$user_query_factory = new WPML_WP_User_Query_Factory();
|
||||
|
||||
return new WPML_Translator_Settings(
|
||||
new WPML_Translator_Records( $wpdb, $user_query_factory, wp_roles() ),
|
||||
new WPML_Language_Collection( $sitepress, array_keys( $sitepress->get_active_languages() ) ),
|
||||
$sitepress->get_default_language()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
class WPML_TM_Translation_Roles_Section implements IWPML_TM_Admin_Section {
|
||||
const SLUG = 'translators';
|
||||
|
||||
/**
|
||||
* The WPML_Translation_Manager_Settings instance.
|
||||
*
|
||||
* @var WPML_Translator_Settings_Interface $translator_settings
|
||||
*/
|
||||
private $translator_settings;
|
||||
|
||||
/**
|
||||
* The WPML_Translator_Settings_Interface instance.
|
||||
*
|
||||
* @var WPML_Translation_Manager_Settings $translation_manager_settings
|
||||
*/
|
||||
private $translation_manager_settings;
|
||||
|
||||
/**
|
||||
* WPML_TM_Translation_Roles_Section constructor.
|
||||
*
|
||||
* @param \WPML_Translation_Manager_Settings $translation_manager_settings The WPML_Translation_Manager_Settings instance.
|
||||
* @param \WPML_Translator_Settings_Interface $translator_settings The WPML_Translator_Settings_Interface instance.
|
||||
*/
|
||||
public function __construct(
|
||||
WPML_Translation_Manager_Settings $translation_manager_settings,
|
||||
WPML_Translator_Settings_Interface $translator_settings
|
||||
) {
|
||||
$this->translation_manager_settings = $translation_manager_settings;
|
||||
$this->translator_settings = $translator_settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a value which will be used for sorting the sections.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function get_order() {
|
||||
return 300;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the unique slug of the sections which is used to build the URL for opening this section.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_slug() {
|
||||
return self::SLUG;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns one or more capabilities required to display this section.
|
||||
*
|
||||
* @return string|array
|
||||
*/
|
||||
public function get_capabilities() {
|
||||
return array( WPML_Manage_Translations_Role::CAPABILITY, 'manage_options' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the caption to display in the section.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_caption() {
|
||||
return current_user_can( 'manage_options' ) ?
|
||||
__( 'Translation Roles', 'wpml-translation-management' ) :
|
||||
__( 'Translators', 'wpml-translation-management' );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the callback responsible for rendering the content of the section.
|
||||
*
|
||||
* @return callable
|
||||
*/
|
||||
public function get_callback() {
|
||||
return array( $this, 'render' );
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is hooked to the `admin_enqueue_scripts` action.
|
||||
*
|
||||
* @param string $hook The current page.
|
||||
*/
|
||||
public function admin_enqueue_scripts( $hook ) {}
|
||||
|
||||
/**
|
||||
* Used to extend the logic for displaying/hiding the section.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_visible() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the content of the section.
|
||||
*/
|
||||
public function render() {
|
||||
echo $this->translator_settings->render();
|
||||
echo $this->translation_manager_settings->render();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
|
||||
abstract class WPML_TM_Translators_View extends WPML_Twig_Template_Loader {
|
||||
|
||||
protected $model = array();
|
||||
|
||||
/** @var WPML_Translator_Records $user_records */
|
||||
private $user_records;
|
||||
|
||||
/** @var WPML_Language_Collection $active_languages */
|
||||
private $active_languages;
|
||||
|
||||
/** @var WPML_Language_Collection $source_languages */
|
||||
private $source_languages;
|
||||
|
||||
private $default_language;
|
||||
|
||||
public function __construct(
|
||||
WPML_Translator_Records $user_records,
|
||||
WPML_Language_Collection $active_languages,
|
||||
$default_language
|
||||
) {
|
||||
parent::__construct(
|
||||
array_merge(
|
||||
array(
|
||||
WPML_TM_PATH . '/templates/translators',
|
||||
WPML_TM_PATH . '/templates/menus/translation-managers',
|
||||
),
|
||||
$this->get_template_paths()
|
||||
)
|
||||
);
|
||||
|
||||
$this->user_records = $user_records;
|
||||
$this->active_languages = $active_languages;
|
||||
$this->default_language = $default_language;
|
||||
|
||||
$this->source_languages = apply_filters( 'wpml_tm_allowed_source_languages', clone $this->active_languages );
|
||||
}
|
||||
|
||||
public function render( $wizard_mode = false ) {
|
||||
$this->add_users();
|
||||
$this->add_strings();
|
||||
$this->add_add_translator_dialog();
|
||||
$this->add_edit_translator_languages_dialog();
|
||||
$this->add_roles();
|
||||
$this->add_languages();
|
||||
$this->add_nonce();
|
||||
$this->add_capability( $wizard_mode );
|
||||
|
||||
return $this->get_template()->show( $this->model, $this->get_twig_template() );
|
||||
}
|
||||
|
||||
public function add_strings() {
|
||||
|
||||
$this->model['strings'] = array(
|
||||
'title' => __( 'Translators', 'wpml-translation-management' ),
|
||||
'sub_title' => __( 'WPML’s Translation Editor makes it easy for your own translators to translate content in your site. You can create accounts for new translators or use existing WordPress users as your translators.', 'wpml-translation-management' ),
|
||||
'columns' => array(
|
||||
'name' => __( 'Name', 'wpml-translation-management' ),
|
||||
'email' => __( 'Email', 'wpml-translation-management' ),
|
||||
'language_pairs' => __( 'Language Pairs', 'wpml-translation-management' ),
|
||||
),
|
||||
'add_translator' => __( 'Add a Translator', 'wpml-translation-management' ),
|
||||
'go_back' => __( 'Go back', 'wpml-translation-management' ),
|
||||
'continue' => __( 'Continue', 'wpml-translation-management' ),
|
||||
'first_name' => __( 'First name:', 'wpml-translation-management' ),
|
||||
'last_name' => __( 'Last name:', 'wpml-translation-management' ),
|
||||
'email' => __( 'Email:', 'wpml-translation-management' ),
|
||||
'user_name' => __( 'User name:', 'wpml-translation-management' ),
|
||||
'wp_role' => __( 'WordPress role:', 'wpml-translation-management' ),
|
||||
'remove' => __( 'Remove', 'wpml-translation-management' ),
|
||||
'edit_languages' => __( 'Edit Languages', 'wpml-translation-management' ),
|
||||
'skip' => __( "Skip this step - I don't need to add more translators", 'wpml-translation-management' ),
|
||||
'no_capability' => __( "Only Translation Managers can add translators to the site. You can assign a different WordPress user to be the site's Translation Manager or make yourself a Translation Manager.", 'wpml-translation-management' ),
|
||||
'yes' => __( 'Yes', 'wpml-translation-management' ),
|
||||
'no' => __( 'No', 'wpml-translation-management' ),
|
||||
'only_i' => __( 'You can translate to and from all languages', 'wpml-translation-management' ),
|
||||
'remove_me' => __( 'Remove me as a translator', 'wpml-translation-management' ),
|
||||
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
private function add_roles() {
|
||||
$this->model['wp_roles'] = WPML_WP_Roles::get_roles_up_to_user_level( wp_get_current_user() );
|
||||
}
|
||||
|
||||
public function add_add_translator_dialog() {
|
||||
$this->model['add_translator_dialog'] = array(
|
||||
'id' => 'js-add-translator-dialog',
|
||||
'class' => '',
|
||||
'strings' => array(
|
||||
'title' => __( 'Add a Translator', 'wpml-translation-management' ),
|
||||
'set_languages_text' => __( 'Set language pair(s)', 'wpml-translation-management' ),
|
||||
'save_translator_text' => __( 'Save', 'wpml-translation-management' ),
|
||||
'previous_text' => __( 'Previous', 'wpml-translation-management' ),
|
||||
'cancel_text' => __( 'Cancel', 'wpml-translation-management' ),
|
||||
'existing_user' => __( 'Select an existing user and set as Translator', 'wpml-translation-management' ),
|
||||
'new_user' => __( 'Create a new user and set as Translator', 'wpml-translation-management' ),
|
||||
'set_lang' => __( 'Set language pair(s) for Translator %USERNAME%', 'wpml-translation-management' ),
|
||||
'from' => __( 'From', 'wpml-translation-management' ),
|
||||
'to' => __( 'to', 'wpml-translation-management' ),
|
||||
'choose_language' => __( '--Choose language--', 'wpml-translation-management' ),
|
||||
'add_lang_pair_text' => __( 'Add more language pairs for this translator', 'wpml-translation-management' ),
|
||||
),
|
||||
'languages' => $this->active_languages,
|
||||
'source_languages' => $this->source_languages,
|
||||
'default_language' => $this->default_language,
|
||||
'nonce' => $this->get_nonce(),
|
||||
);
|
||||
}
|
||||
|
||||
public function add_edit_translator_languages_dialog() {
|
||||
$this->model['edit_translator_languages_dialog'] = array(
|
||||
'id' => 'js-edit-translator-languages-dialog',
|
||||
'class' => '',
|
||||
'strings' => array(
|
||||
'title' => __( 'Edit language pair(s) for translator', 'wpml-translation-management' ),
|
||||
'save_text' => __( 'Save', 'wpml-translation-management' ),
|
||||
'cancel_text' => __( 'Cancel', 'wpml-translation-management' ),
|
||||
'from' => __( 'From', 'wpml-translation-management' ),
|
||||
'to' => __( 'to', 'wpml-translation-management' ),
|
||||
'choose_language' => __( '--Choose language--', 'wpml-translation-management' ),
|
||||
'add_lang_pair_text' => __( 'Add more language pairs for this translator', 'wpml-translation-management' ),
|
||||
),
|
||||
'languages' => $this->active_languages,
|
||||
'source_languages' => $this->source_languages,
|
||||
'nonce' => $this->get_nonce(),
|
||||
);
|
||||
}
|
||||
|
||||
public function add_users() {
|
||||
global $wpdb;
|
||||
$language_pair_records = new WPML_Language_Pair_Records( $wpdb, new WPML_Language_Records( $wpdb ) );
|
||||
|
||||
$this->model['users'] = array();
|
||||
|
||||
$users = $this->user_records->get_users_with_capability();
|
||||
foreach ( $users as $user ) {
|
||||
$user->language_pairs = $language_pair_records->get( $user->ID );
|
||||
if ( get_user_meta( $user->ID, WPML_TM_Wizard_Options::ONLY_I_USER_META, true ) ) {
|
||||
$user->avatar = get_avatar( $user->ID, 70 );
|
||||
$this->model['only_i_user'] = $user;
|
||||
} else {
|
||||
$this->model['users'][] = $user;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function add_languages() {
|
||||
$this->model['languages'] = $this->source_languages;
|
||||
}
|
||||
|
||||
public function add_nonce() {
|
||||
$this->model['nonce'] = $this->get_nonce();
|
||||
}
|
||||
|
||||
public function add_capability( $wizard_mode ) {
|
||||
$this->model['can_add_translators'] = $wizard_mode || current_user_can( WPML_Manage_Translations_Role::CAPABILITY );
|
||||
}
|
||||
|
||||
private function get_nonce() {
|
||||
return wp_create_nonce( WPML_Translator_Ajax::NONCE_ACTION );
|
||||
}
|
||||
|
||||
abstract public function get_twig_template();
|
||||
|
||||
abstract public function get_template_paths();
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
class WPML_Translation_Manager_Ajax extends WPML_Translation_Roles_Ajax {
|
||||
|
||||
const TRANSLATION_MANAGER_INSTRUCTIONS_TEMPLATE = 'notification/translation-manager-instructions.twig';
|
||||
|
||||
/** @var WPML_TM_Email_Notification_View $email_view */
|
||||
private $email_view;
|
||||
|
||||
public function __construct(
|
||||
IWPML_Translation_Roles_View $view,
|
||||
WPML_Translation_Roles_Records $records,
|
||||
WPML_Super_Globals_Validation $post_vars,
|
||||
WPML_WP_User_Factory $user_factory,
|
||||
WPML_TM_Email_Notification_View $email_view
|
||||
) {
|
||||
parent::__construct( $view, $records, $post_vars, $user_factory );
|
||||
$this->email_view = $email_view;
|
||||
}
|
||||
|
||||
public function get_role() {
|
||||
return 'manager';
|
||||
}
|
||||
|
||||
public function get_nonce() {
|
||||
return WPML_Translation_Manager_Settings::NONCE_ACTION;
|
||||
}
|
||||
|
||||
public function get_capability() {
|
||||
return WPML_Manage_Translations_Role::CAPABILITY;
|
||||
}
|
||||
|
||||
public function get_user_row_template() {
|
||||
return 'translation-managers-row.twig';
|
||||
}
|
||||
|
||||
public function on_user_created( WP_User $user ) {
|
||||
|
||||
}
|
||||
|
||||
public function on_remove_role( WP_User $user ) {
|
||||
|
||||
}
|
||||
|
||||
public function send_instructions_to_user( WP_User $user ) {
|
||||
$site_name = get_option( 'blogname' );
|
||||
$translation_setup_url = admin_url( 'admin.php?page=' . WPML_TM_FOLDER . '/menu/main.php' );
|
||||
$admin_user = wp_get_current_user();
|
||||
|
||||
$model = array(
|
||||
'setup_url' => esc_url( $translation_setup_url ),
|
||||
'username' => $user->display_name,
|
||||
'intro_message_1' => sprintf( __( 'You are the Translation Manager for %s. This role lets you manage everything related to translation for this site.', 'wpml-translation-management' ), $site_name ),
|
||||
'intro_message_2' => __( 'Before you can start sending content to translation, you need to complete a short setup.', 'wpml-translation-management' ),
|
||||
'setup' => __( 'Set-up the translation', 'wpml-translation-management' ),
|
||||
'reminder' => sprintf( __( '* Remember, your login name for %1$s is %2$s. If you need help with your password, use the password reset in the login page.', 'wpml-translation-management' ), $site_name, $user->user_login ),
|
||||
'at_your_service' => __( 'At your service', 'wpml-translation-management' ),
|
||||
'admin_name' => $admin_user->display_name,
|
||||
'admin_for_site' => sprintf( __( 'Administrator for %s', 'wpml-translation-management' ), $site_name ),
|
||||
);
|
||||
|
||||
$to = $user->display_name . ' <' . $user->user_email . '>';
|
||||
$message = $this->email_view->render_model( $model, self::TRANSLATION_MANAGER_INSTRUCTIONS_TEMPLATE );
|
||||
$subject = sprintf( __( 'You are now the Translation Manager for %s - action needed', 'wpml-translation-management' ), $site_name );
|
||||
|
||||
$headers = array(
|
||||
'MIME-Version: 1.0',
|
||||
'Content-type: text/html; charset=UTF-8',
|
||||
'Reply-To: ' . $admin_user->display_name . ' <' . $admin_user->user_email . '>',
|
||||
);
|
||||
|
||||
add_filter( 'wp_mail_from_name', array( $this, 'wp_mail_from_name_filter' ), 10, 1 );
|
||||
wp_mail( $to, $subject, $message, $headers );
|
||||
remove_filter( 'wp_mail_from_name', array( $this, 'wp_mail_from_name_filter' ), 10 );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function wp_mail_from_name_filter( $from_name ) {
|
||||
$admin_user = wp_get_current_user();
|
||||
|
||||
return $admin_user->display_name;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
class WPML_Translation_Manager_Settings {
|
||||
|
||||
const MAIN_TEMPLATE = 'translation-managers.twig';
|
||||
const NONCE_ACTION = 'wpml_translation_manager_actions';
|
||||
|
||||
/** @var WPML_Translation_Manager_View $view */
|
||||
private $view;
|
||||
|
||||
/** @var WPML_Translation_Manager_Records $records */
|
||||
private $records;
|
||||
|
||||
public function __construct(
|
||||
WPML_Translation_Manager_View $view,
|
||||
WPML_Translation_Manager_Records $records
|
||||
) {
|
||||
$this->view = $view;
|
||||
$this->records = $records;
|
||||
}
|
||||
|
||||
public function render() {
|
||||
if ( current_user_can( 'manage_options' ) ) {
|
||||
return $this->view->show( $this->get_model(), self::MAIN_TEMPLATE );
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
public function get_model() {
|
||||
$current_user = wp_get_current_user();
|
||||
|
||||
return array(
|
||||
'translation_managers' => $this->get_translation_managers(),
|
||||
'nonce' => wp_create_nonce( self::NONCE_ACTION ),
|
||||
'user_id' => $current_user->ID,
|
||||
'wp_roles' => WPML_WP_Roles::get_editor_roles(),
|
||||
);
|
||||
}
|
||||
|
||||
private function get_translation_managers() {
|
||||
$users = $this->records->get_users_with_capability();
|
||||
|
||||
foreach ( $users as $user ) {
|
||||
$user->edit_link = esc_url(
|
||||
add_query_arg(
|
||||
'wp_http_referer',
|
||||
urlencode( esc_url( stripslashes( $_SERVER['REQUEST_URI'] ) ) ),
|
||||
"user-edit.php?user_id={$user->ID}"
|
||||
)
|
||||
);
|
||||
$user->avatar = get_avatar( $user->ID, 70 );
|
||||
}
|
||||
|
||||
return $users;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
class WPML_Translation_Manager_View extends WPML_Twig_Template_Loader implements IWPML_Translation_Roles_View {
|
||||
|
||||
const TEMPLATE_PATH = '/templates/menus/translation-managers';
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct( array( WPML_TM_PATH . self::TEMPLATE_PATH ) );
|
||||
}
|
||||
|
||||
public function show( $model, $template ) {
|
||||
$model['strings'] = self::get_strings();
|
||||
$model['dialog'] = self::add_add_translation_manager_dialog();
|
||||
|
||||
return $this->get_template()->show( $model, $template );
|
||||
}
|
||||
|
||||
public static function get_strings() {
|
||||
return array(
|
||||
'title' => __( 'Translation Managers', 'wpml-translation-management' ),
|
||||
'column_name' => __( 'Name', 'wpml-translation-management' ),
|
||||
'edit' => __( 'Edit user', 'wpml-translation-management' ),
|
||||
'remove' => __( 'Remove Translation Manager', 'wpml-translation-management' ),
|
||||
'no_users' => __( "This site doesn't yet have a Translation Manager.", 'wpml-translation-management' ),
|
||||
'placeholder' => __( 'Search for user', 'wpml-translation-management' ),
|
||||
'add_button' => __( 'Add a Translation Manager', 'wpml-translation-management' ),
|
||||
'first_name' => __( 'First name:', 'wpml-translation-management' ),
|
||||
'last_name' => __( 'Last name:', 'wpml-translation-management' ),
|
||||
'email' => __( 'Email:', 'wpml-translation-management' ),
|
||||
'user_name' => __( 'User name:', 'wpml-translation-management' ),
|
||||
'wp_role' => __( 'WordPress role:', 'wpml-translation-management' ),
|
||||
'existing_user' => __( 'Select an existing WordPress user (needs to be an editor or above)', 'wpml-translation-management' ),
|
||||
'new_user' => __( 'Create a new user to be the Translation Manager', 'wpml-translation-management' ),
|
||||
'set_user' => __( 'Set a Translation Manager', 'wpml-translation-management' ),
|
||||
'add_user' => __( 'Add a Translation Manager', 'wpml-translation-management' ),
|
||||
);
|
||||
}
|
||||
|
||||
public static function add_add_translation_manager_dialog() {
|
||||
return array(
|
||||
'id' => 'js-add-translation-manager-dialog',
|
||||
'class' => '',
|
||||
'nonce' => wp_create_nonce( WPML_Translation_Manager_Settings::NONCE_ACTION ),
|
||||
'strings' => array(
|
||||
'title' => __( 'Add a Translation Manager', 'wpml-translation-management' ),
|
||||
'add_text' => __( 'Add a Translation Manager', 'wpml-translation-management' ),
|
||||
'cancel_text' => __( 'Cancel', 'wpml-translation-management' ),
|
||||
'existing_user' => __( 'Select an existing user and set as Translation Manager', 'wpml-translation-management' ),
|
||||
'new_user' => __( 'Create a new user and set as Translation Manager', 'wpml-translation-management' ),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
<?php
|
||||
|
||||
abstract class WPML_Translation_Roles_Ajax extends WPML_TM_AJAX implements IWPML_Action {
|
||||
|
||||
const USER_SEARCH_LIMIT = 10;
|
||||
|
||||
/** @var IWPML_Translation_Roles_View $view */
|
||||
private $view;
|
||||
|
||||
/** @var WPML_Translation_Roles_Records $records */
|
||||
private $records;
|
||||
|
||||
/** @var WPML_Super_Globals_Validation $post_vars */
|
||||
protected $post_vars;
|
||||
|
||||
/** @var WPML_WP_User_Factory $user_factory */
|
||||
private $user_factory;
|
||||
|
||||
public function __construct(
|
||||
IWPML_Translation_Roles_View $view,
|
||||
WPML_Translation_Roles_Records $records,
|
||||
WPML_Super_Globals_Validation $post_vars,
|
||||
WPML_WP_User_Factory $user_factory
|
||||
) {
|
||||
$this->view = $view;
|
||||
$this->records = $records;
|
||||
$this->post_vars = $post_vars;
|
||||
$this->user_factory = $user_factory;
|
||||
}
|
||||
|
||||
public function add_hooks() {
|
||||
$role = $this->get_role();
|
||||
add_action( 'wp_ajax_wpml_remove_translation_' . $role, array( $this, 'remove_translation_role' ) );
|
||||
add_action( 'wp_ajax_wpml_search_translation_' . $role, array( $this, 'search_for_translation_roles' ) );
|
||||
add_action( 'wp_ajax_wpml_add_translation_' . $role, array( $this, 'add_translation_role' ) );
|
||||
add_action( 'wp_ajax_wpml_send_instructions_to_translation_' . $role, array( $this, 'send_instructions' ) );
|
||||
}
|
||||
|
||||
public function remove_translation_role() {
|
||||
if ( $this->is_valid_request( $this->get_nonce() ) && $user = $this->get_user() ) {
|
||||
$user->remove_cap( $this->get_capability() );
|
||||
$this->on_remove_role( $user );
|
||||
do_action( 'wpml_tm_ate_synchronize_' . $this->get_role() . 's' );
|
||||
do_action( 'wpml_tm_remove_translation_role', $user, $this->get_capability() );
|
||||
wp_send_json_success();
|
||||
} else {
|
||||
wp_send_json_error( __( 'Could not find user!', 'wpml-translation-management' ) );
|
||||
}
|
||||
}
|
||||
|
||||
public function search_for_translation_roles() {
|
||||
if ( $this->is_valid_request( $this->get_nonce() ) ) {
|
||||
$results = $this->records->search_for_users_without_capability( $this->post_vars->post( 'search' ), self::USER_SEARCH_LIMIT );
|
||||
|
||||
wp_send_json_success( $results );
|
||||
}
|
||||
}
|
||||
|
||||
public function add_translation_role() {
|
||||
if ( $this->is_valid_request( $this->get_nonce() ) && $user = $this->get_user() ) {
|
||||
|
||||
if ( ! is_wp_error( $user ) ) {
|
||||
$user->add_cap( $this->get_capability() );
|
||||
|
||||
$this->on_user_created( $user );
|
||||
|
||||
$user->data->edit_link = esc_url( "user-edit.php?user_id={$user->ID}" );
|
||||
$user->data->avatar = get_avatar( $user->ID );
|
||||
|
||||
$new_row = $this->view->show(
|
||||
array(
|
||||
'user' => (array) $user->data,
|
||||
),
|
||||
$this->get_user_row_template()
|
||||
);
|
||||
|
||||
do_action( 'wpml_tm_ate_synchronize_' . $this->get_role() . 's' );
|
||||
do_action( 'wpml_tm_add_translation_role', $user, $this->get_capability() );
|
||||
|
||||
if ( $this->post_vars->post( 'sendEmail', FILTER_VALIDATE_BOOLEAN ) ) {
|
||||
$this->send_instructions_to_user( $user );
|
||||
}
|
||||
|
||||
wp_send_json_success( $new_row );
|
||||
} else {
|
||||
wp_send_json_error( $user->get_error_message() );
|
||||
}
|
||||
} else {
|
||||
wp_send_json_error( __( 'Could not find user!', 'wpml-translation-management' ) );
|
||||
}
|
||||
}
|
||||
|
||||
public function send_instructions() {
|
||||
if ( $this->is_valid_request( $this->get_nonce() ) && $user = $this->get_user() ) {
|
||||
if ( $this->send_instructions_to_user( $user ) ) {
|
||||
wp_send_json_success( sprintf( __( 'An email has been sent to %s', 'wpml-translation-management' ), $user->user_login ) );
|
||||
} else {
|
||||
wp_send_json_error( __( 'Failed to send email', 'wpml-translation-management' ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function get_user() {
|
||||
$user_id = $this->post_vars->post( 'user_id', FILTER_SANITIZE_NUMBER_INT );
|
||||
if ( $user_id ) {
|
||||
return $this->user_factory->create( $user_id );
|
||||
} else {
|
||||
return $this->create_new_wp_user();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null|WP_Error|WP_User
|
||||
*/
|
||||
private function create_new_wp_user() {
|
||||
$first_name = $this->post_vars->post( 'first' );
|
||||
$last_name = $this->post_vars->post( 'last' );
|
||||
$email = $this->post_vars->post( 'email', FILTER_SANITIZE_EMAIL );
|
||||
$user_name = $this->post_vars->post( 'user' );
|
||||
$role = \WPML\TM\Menu\TranslationRoles\RoleValidator::getTheHighestPossibleIfNotValid(
|
||||
$this->post_vars->post( 'role' )
|
||||
);
|
||||
|
||||
if ( $email && $user_name && $role ) {
|
||||
$user_id = wp_insert_user(
|
||||
array(
|
||||
'first_name' => $first_name,
|
||||
'last_name' => $last_name,
|
||||
'user_email' => $email,
|
||||
'user_login' => $user_name,
|
||||
'role' => $role,
|
||||
'user_pass' => wp_generate_password(),
|
||||
)
|
||||
);
|
||||
|
||||
if ( ! is_wp_error( $user_id ) ) {
|
||||
wp_send_new_user_notifications( $user_id );
|
||||
|
||||
return $this->user_factory->create( $user_id );
|
||||
} else {
|
||||
return $user_id;
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
abstract public function get_role();
|
||||
abstract public function get_nonce();
|
||||
abstract public function get_capability();
|
||||
abstract public function get_user_row_template();
|
||||
abstract public function on_user_created( WP_User $user );
|
||||
abstract public function on_remove_role( WP_User $user );
|
||||
abstract public function send_instructions_to_user( WP_User $user );
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
class WPML_Translation_Roles_Ajax_Factory implements IWPML_AJAX_Action_Loader {
|
||||
|
||||
/**
|
||||
* @return \IWPML_Action|\IWPML_Action[]
|
||||
*/
|
||||
public function create() {
|
||||
global $wpdb, $sitepress;
|
||||
|
||||
$email_twig_factory = new WPML_TM_Email_Twig_Template_Factory();
|
||||
|
||||
$hooks = array(
|
||||
|
||||
new WPML_Translation_Manager_Ajax(
|
||||
new WPML_Translation_Manager_View(),
|
||||
new WPML_Translation_Manager_Records( $wpdb, new WPML_WP_User_Query_Factory(), wp_roles() ),
|
||||
new WPML_Super_Globals_Validation(),
|
||||
new WPML_WP_User_Factory(),
|
||||
new WPML_TM_Email_Notification_View( $email_twig_factory->create() )
|
||||
),
|
||||
|
||||
new WPML_Translator_Ajax(
|
||||
new WPML_Translator_View(
|
||||
apply_filters(
|
||||
'wpml_tm_allowed_source_languages',
|
||||
new WPML_Language_Collection( $sitepress, array_keys( $sitepress->get_active_languages() ) )
|
||||
)
|
||||
),
|
||||
new WPML_Translator_Records( $wpdb, new WPML_WP_User_Query_Factory(), wp_roles() ),
|
||||
new WPML_Super_Globals_Validation(),
|
||||
new WPML_WP_User_Factory(),
|
||||
new WPML_Language_Pair_Records( $wpdb, new WPML_Language_Records( $wpdb ) )
|
||||
),
|
||||
|
||||
);
|
||||
|
||||
return $hooks;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
interface IWPML_Translation_Roles_View {
|
||||
|
||||
public function show( $model, $template );
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
class WPML_Translator_Ajax extends WPML_Translation_Roles_Ajax {
|
||||
|
||||
const NONCE_ACTION = 'wpml_translator_actions';
|
||||
|
||||
/** @var WPML_Language_Pair_Records $language_pair_records */
|
||||
private $language_pair_records;
|
||||
|
||||
public function __construct(
|
||||
IWPML_Translation_Roles_View $view,
|
||||
WPML_Translation_Roles_Records $records,
|
||||
WPML_Super_Globals_Validation $post_vars,
|
||||
WPML_WP_User_Factory $user_factory,
|
||||
WPML_Language_Pair_Records $language_pair_records
|
||||
) {
|
||||
parent::__construct( $view, $records, $post_vars, $user_factory );
|
||||
$this->language_pair_records = $language_pair_records;
|
||||
}
|
||||
|
||||
public function get_role() {
|
||||
return 'translator';
|
||||
}
|
||||
|
||||
public function get_nonce() {
|
||||
return self::NONCE_ACTION;
|
||||
}
|
||||
|
||||
public function get_capability() {
|
||||
return WPML_Translator_Role::CAPABILITY;
|
||||
}
|
||||
|
||||
public function get_user_row_template() {
|
||||
return 'translators-row.twig';
|
||||
}
|
||||
|
||||
public function on_user_created( WP_User $user ) {
|
||||
$language_pairs = $this->post_vars->post( 'languagePairs' );
|
||||
$this->language_pair_records->store( $user->ID, $language_pairs );
|
||||
$user->data->language_pairs = $this->post_vars->post( 'languagePairs' );
|
||||
}
|
||||
|
||||
public function on_remove_role( WP_User $user ) {
|
||||
delete_user_meta( $user->ID, WPML_TM_Wizard_Options::ONLY_I_USER_META );
|
||||
}
|
||||
|
||||
public function send_instructions_to_user( WP_User $user ) {
|
||||
// Not needed at this stage.
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
class WPML_Translator_View extends WPML_Twig_Template_Loader implements IWPML_Translation_Roles_View {
|
||||
|
||||
const TEMPLATE_PATH = '/templates/translators';
|
||||
|
||||
/** @var WPML_Language_Collection $languages */
|
||||
private $languages;
|
||||
|
||||
public function __construct( WPML_Language_Collection $languages ) {
|
||||
$this->languages = $languages;
|
||||
parent::__construct( array( WPML_TM_PATH . self::TEMPLATE_PATH ) );
|
||||
}
|
||||
|
||||
public function show( $model, $template ) {
|
||||
$model['strings'] = self::get_strings();
|
||||
$model['languages'] = $this->languages;
|
||||
|
||||
$model['strings'] = apply_filters(
|
||||
'wpml_tm_translators_view_strings',
|
||||
$model['strings'],
|
||||
true
|
||||
);
|
||||
|
||||
return $this->get_template()->show( $model, $template );
|
||||
}
|
||||
|
||||
public static function get_strings() {
|
||||
return array(
|
||||
'edit' => __( 'Edit user', 'wpml-translation-management' ),
|
||||
'remove' => __( 'Remove', 'wpml-translation-management' ),
|
||||
'edit_languages' => __( 'Edit Languages', 'wpml-translation-management' ),
|
||||
'no' => __( 'No', 'wpml-translation-management' ),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
interface WPML_Translator_Settings_Interface {
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function render();
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
class WPML_Translator_Settings_Proxy implements WPML_Translator_Settings_Interface {
|
||||
/** @var callable */
|
||||
private $create_callback;
|
||||
|
||||
/**
|
||||
* @param callable $create_callback
|
||||
*/
|
||||
public function __construct( $create_callback ) {
|
||||
$this->create_callback = $create_callback;
|
||||
}
|
||||
|
||||
public function render() {
|
||||
$translator_settings = call_user_func( $this->create_callback );
|
||||
if ( ! $translator_settings instanceof WPML_Translator_Settings_Interface ) {
|
||||
throw new RuntimeException( 'Factory method created an invalid object.' );
|
||||
}
|
||||
|
||||
return $translator_settings->render();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
class WPML_Translator_Settings extends WPML_TM_Translators_View implements WPML_Translator_Settings_Interface {
|
||||
|
||||
public function get_twig_template() {
|
||||
return 'translators.twig';
|
||||
}
|
||||
|
||||
public function get_template_paths() {
|
||||
return array();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
<?php
|
||||
|
||||
namespace WPML\TM\Menu\TranslationServices;
|
||||
|
||||
use TranslationProxy;
|
||||
|
||||
class ActivationAjax {
|
||||
|
||||
const NONCE_ACTION = 'translation_service_toggle';
|
||||
const REFRESH_TS_INFO_ACTION = 'refresh_ts_info';
|
||||
|
||||
/** @var \WPML_TP_Client */
|
||||
private $tp_client;
|
||||
|
||||
public function __construct( \WPML_TP_Client $tp_client ) {
|
||||
$this->tp_client = $tp_client;
|
||||
}
|
||||
|
||||
public function add_hooks() {
|
||||
add_action( 'wp_ajax_translation_service_toggle', array( $this, 'translation_service_toggle' ) );
|
||||
add_action( 'wp_ajax_refresh_ts_info', array( $this, 'refresh_ts_info' ) );
|
||||
}
|
||||
|
||||
public function translation_service_toggle() {
|
||||
if ( $this->is_valid_request( self::NONCE_ACTION ) ) {
|
||||
|
||||
if ( ! isset( $_POST['service_id'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$service_id = (int) filter_var( $_POST['service_id'], FILTER_SANITIZE_NUMBER_INT );
|
||||
$enable = false;
|
||||
$response = false;
|
||||
|
||||
if ( isset( $_POST['enable'] ) ) {
|
||||
$enable = filter_var( $_POST['enable'], FILTER_SANITIZE_NUMBER_INT );
|
||||
}
|
||||
|
||||
if ( $enable ) {
|
||||
if ( $service_id !== TranslationProxy::get_current_service_id() ) {
|
||||
$response = $this->activate_service( $service_id );
|
||||
} else {
|
||||
$response = array( 'activated' => true );
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $enable && $service_id === TranslationProxy::get_current_service_id() ) {
|
||||
TranslationProxy::clear_preferred_translation_service();
|
||||
$response = $this->deactivate_service();
|
||||
}
|
||||
|
||||
wp_send_json_success( $response );
|
||||
return;
|
||||
}
|
||||
|
||||
$this->send_invalid_nonce_error();
|
||||
}
|
||||
|
||||
public function refresh_ts_info() {
|
||||
if ( $this->is_valid_request( self::REFRESH_TS_INFO_ACTION ) ) {
|
||||
$active_service = $this->tp_client->services()->get_active( true );
|
||||
|
||||
if ( $active_service ) {
|
||||
$active_service = (object) (array) $active_service;
|
||||
TranslationProxy::build_and_store_active_translation_service( $active_service, $active_service->custom_fields_data );
|
||||
|
||||
$activeServiceTemplateRender = ActiveServiceTemplateFactory::createRenderer();
|
||||
|
||||
wp_send_json_success( [ 'active_service_block' => $activeServiceTemplateRender() ] );
|
||||
return;
|
||||
}
|
||||
|
||||
wp_send_json_error(
|
||||
array( 'message' => __( 'It was not possible to refresh the active translation service information.', 'wpml-translation-management' ) )
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->send_invalid_nonce_error();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $service_id
|
||||
*
|
||||
* @return array
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
private function activate_service( $service_id ) {
|
||||
$result = TranslationProxy::select_service( $service_id );
|
||||
$message = '';
|
||||
if ( is_wp_error( $result ) ) {
|
||||
$message = $result->get_error_message();
|
||||
}
|
||||
|
||||
return array(
|
||||
'message' => $message,
|
||||
'reload' => 1,
|
||||
'activated' => 1,
|
||||
);
|
||||
}
|
||||
|
||||
private function deactivate_service() {
|
||||
TranslationProxy::deselect_active_service();
|
||||
|
||||
return array(
|
||||
'message' => '',
|
||||
'reload' => 1,
|
||||
'activated' => 0,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $action
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_valid_request( $action ) {
|
||||
if ( ! isset( $_POST['nonce'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return wp_verify_nonce( filter_var( $_POST['nonce'], FILTER_SANITIZE_FULL_SPECIAL_CHARS ), $action );
|
||||
}
|
||||
|
||||
private function send_invalid_nonce_error() {
|
||||
$response = array(
|
||||
'message' => __( 'You are not allowed to perform this action.', 'wpml-translation-management' ),
|
||||
'reload' => 0,
|
||||
);
|
||||
|
||||
wp_send_json_error( $response );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace WPML\TM\Menu\TranslationServices;
|
||||
|
||||
class ActivationAjaxFactory implements \IWPML_Backend_Action_Loader {
|
||||
|
||||
/**
|
||||
* @return ActivationAjax
|
||||
*/
|
||||
public function create() {
|
||||
$tp_client_factory = new \WPML_TP_Client_Factory();
|
||||
return new ActivationAjax( $tp_client_factory->create() );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace WPML\TM\Menu\TranslationServices;
|
||||
|
||||
|
||||
use WPML\FP\Maybe;
|
||||
use function WPML\FP\invoke;
|
||||
|
||||
class ActiveServiceRepository {
|
||||
/**
|
||||
* @return \WPML_TP_Service|null
|
||||
*/
|
||||
public static function get() {
|
||||
global $sitepress;
|
||||
|
||||
$active_service = $sitepress->get_setting( 'translation_service' );
|
||||
|
||||
return $active_service ? new \WPML_TP_Service( $active_service ) : null;
|
||||
}
|
||||
|
||||
public static function getId() {
|
||||
return Maybe::fromNullable( self::get() )
|
||||
->map( invoke( 'get_id' ) )
|
||||
->getOrElse( null );
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
|
||||
namespace WPML\TM\Menu\TranslationServices;
|
||||
|
||||
class ActiveServiceTemplate {
|
||||
|
||||
const ACTIVE_SERVICE_TEMPLATE = 'active-service.twig';
|
||||
const HOURS_BEFORE_TS_REFRESH = 24;
|
||||
|
||||
/**
|
||||
* @param callable $templateRenderer
|
||||
* @param \WPML_TP_Service $active_service
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function render( $templateRenderer, \WPML_TP_Service $active_service ) {
|
||||
return $templateRenderer( self::getModel( $active_service ), self::ACTIVE_SERVICE_TEMPLATE );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private static function getModel( \WPML_TP_Service $active_service ) {
|
||||
$model = [
|
||||
'strings' => [
|
||||
'title' => __( 'Active service:', 'wpml-translation-management' ),
|
||||
'deactivate' => __( 'Deactivate', 'wpml-translation-management' ),
|
||||
'modal_header' => sprintf(
|
||||
__(
|
||||
'Enter here your %s authentication details',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
$active_service->get_name()
|
||||
),
|
||||
'modal_tip' => $active_service->get_popup_message() ?
|
||||
$active_service->get_popup_message() :
|
||||
__( 'You can find API token at %s site', 'wpml-translation-management' ),
|
||||
'modal_title' => sprintf(
|
||||
__( '%s authentication', 'wpml-translation-management' ),
|
||||
$active_service->get_name()
|
||||
),
|
||||
'refresh_language_pairs' => __( 'Refresh language pairs', 'wpml-translation-management' ),
|
||||
'refresh_ts_info' => __( 'Refresh information', 'wpml-translation-management' ),
|
||||
'documentation_lower' => __( 'documentation', 'wpml-translation-management' ),
|
||||
'refreshing_ts_message' => __(
|
||||
'Refreshing translation service information...',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
],
|
||||
'active_service' => $active_service,
|
||||
'nonces' => [
|
||||
\WPML_TP_Refresh_Language_Pairs::AJAX_ACTION => wp_create_nonce( \WPML_TP_Refresh_Language_Pairs::AJAX_ACTION ),
|
||||
ActivationAjax::REFRESH_TS_INFO_ACTION => wp_create_nonce( ActivationAjax::REFRESH_TS_INFO_ACTION ),
|
||||
],
|
||||
'needs_info_refresh' => self::shouldRefreshData( $active_service ),
|
||||
];
|
||||
|
||||
$authentication_message = [];
|
||||
/* translators: sentence 1/3: create account with the translation service ("%1$s" is the service name) */
|
||||
$authentication_message[] = __(
|
||||
'To send content for translation to %1$s, you need to have an %1$s account.',
|
||||
'wpml-translation-management'
|
||||
);
|
||||
/* translators: sentence 2/3: create account with the translation service ("one" is "one account) */
|
||||
$authentication_message[] = __(
|
||||
"If you don't have one, you can create it after clicking the authenticate button.",
|
||||
'wpml-translation-management'
|
||||
);
|
||||
/* translators: sentence 3/3: create account with the translation service ("%2$s" is "documentation") */
|
||||
$authentication_message[] = __(
|
||||
'Please, check the %2$s page for more details.',
|
||||
'wpml-translation-management'
|
||||
);
|
||||
|
||||
$model['strings']['authentication'] = [
|
||||
'description' => implode( ' ', $authentication_message ),
|
||||
'authenticate_button' => __( 'Authenticate', 'wpml-translation-management' ),
|
||||
'de_authorize_button' => __( 'De-authorize', 'wpml-translation-management' ),
|
||||
'update_credentials_button' => __( 'Update credentials', 'wpml-translation-management' ),
|
||||
'is_authorized' => self::isAuthorizedText( $active_service->get_name() ),
|
||||
];
|
||||
|
||||
return $model;
|
||||
}
|
||||
|
||||
private static function isAuthorizedText( $serviceName ) {
|
||||
$query_args = [
|
||||
'page' => WPML_TM_FOLDER . \WPML_Translation_Management::PAGE_SLUG_MANAGEMENT,
|
||||
'sm' => 'dashboard',
|
||||
];
|
||||
|
||||
$href = add_query_arg( $query_args, admin_url( 'admin.php' ) );
|
||||
|
||||
$dashboard = '<a href="' . $href . '">' .
|
||||
__( 'Translation Dashboard', 'wpml-translation-management' ) .
|
||||
'</a>';
|
||||
|
||||
$isAuthorized = sprintf(
|
||||
__( 'Success! You can now send content to %s.', 'wpml-translation-management' ),
|
||||
$serviceName
|
||||
);
|
||||
$isAuthorized .= '<br/>';
|
||||
// translators: "%s" is replaced with the link to the "Translation Dashboard"
|
||||
$isAuthorized .= sprintf(
|
||||
__( 'Go to the %s to choose the content and send it to translation.', 'wpml-translation-management' ),
|
||||
$dashboard
|
||||
);
|
||||
|
||||
return $isAuthorized;
|
||||
}
|
||||
|
||||
private static function shouldRefreshData( \WPML_TP_Service $active_service ) {
|
||||
$refresh_time = time() - ( self::HOURS_BEFORE_TS_REFRESH * HOUR_IN_SECONDS );
|
||||
|
||||
return ! $active_service->get_last_refresh() || $active_service->get_last_refresh() < $refresh_time;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace WPML\TM\Menu\TranslationServices;
|
||||
|
||||
use function WPML\Container\make;
|
||||
use function WPML\FP\partial;
|
||||
|
||||
class ActiveServiceTemplateFactory {
|
||||
/**
|
||||
* @return \Closure
|
||||
*/
|
||||
public static function createRenderer() {
|
||||
$activeService = ActiveServiceRepository::get();
|
||||
if ( $activeService ) {
|
||||
$templateRenderer = self::getTemplateRenderer();
|
||||
|
||||
return partial( ActiveServiceTemplate::class . '::render', [ $templateRenderer, 'show' ], $activeService );
|
||||
}
|
||||
|
||||
return function () {
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \WPML_Twig_Template
|
||||
*/
|
||||
private static function getTemplateRenderer() {
|
||||
$paths = [ WPML_TM_PATH . '/templates/menus/translation-services/' ];
|
||||
$twigLoader = make( \WPML_Twig_Template_Loader::class, [ ':paths' => $paths ] );
|
||||
|
||||
return $twigLoader->get_template();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
<?php
|
||||
|
||||
namespace WPML\TM\Menu\TranslationServices;
|
||||
|
||||
use WPML\TM\TranslationProxy\Services\AuthorizationFactory;
|
||||
|
||||
class AuthenticationAjax {
|
||||
|
||||
const AJAX_ACTION = 'translation_service_authentication';
|
||||
|
||||
/** @var AuthorizationFactory */
|
||||
protected $authorize_factory;
|
||||
|
||||
/**
|
||||
* @param AuthorizationFactory $authorize_factory
|
||||
*/
|
||||
public function __construct( AuthorizationFactory $authorize_factory ) {
|
||||
$this->authorize_factory = $authorize_factory;
|
||||
}
|
||||
|
||||
public function add_hooks() {
|
||||
add_action( 'wp_ajax_translation_service_authentication', [ $this, 'authenticate_service' ] );
|
||||
add_action( 'wp_ajax_translation_service_update_credentials', [ $this, 'update_credentials' ] );
|
||||
add_action( 'wp_ajax_translation_service_invalidation', [ $this, 'invalidate_service' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function authenticate_service() {
|
||||
return $this->handle_action(
|
||||
function () {
|
||||
$this->authorize_factory->create()->authorize(
|
||||
json_decode( stripslashes( $_POST['custom_fields'] ) )
|
||||
);
|
||||
},
|
||||
[ $this, 'is_valid_request_with_params' ],
|
||||
__( 'Service activated.', 'wpml-translation-management' ),
|
||||
__(
|
||||
'The authentication didn\'t work. Please make sure you entered your details correctly and try again.',
|
||||
'wpml-translation-management'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function update_credentials() {
|
||||
return $this->handle_action(
|
||||
function () {
|
||||
$this->authorize_factory->create()->updateCredentials(
|
||||
json_decode( stripslashes( $_POST['custom_fields'] ) )
|
||||
);
|
||||
},
|
||||
[ $this, 'is_valid_request_with_params' ],
|
||||
__( 'Service credentials updated.', 'wpml-translation-management' ),
|
||||
__(
|
||||
'The authentication didn\'t work. Please make sure you entered your details correctly and try again.',
|
||||
'wpml-translation-management'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function invalidate_service() {
|
||||
return $this->handle_action(
|
||||
function () {
|
||||
$this->authorize_factory->create()->deauthorize();
|
||||
},
|
||||
[ $this, 'is_valid_request' ],
|
||||
__( 'Service invalidated.', 'wpml-translation-management' ),
|
||||
__( 'Unable to invalidate this service. Please contact WPML support.', 'wpml-translation-management' )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callable $action
|
||||
* @param callable $request_validation
|
||||
* @param string $success_message
|
||||
* @param string $failure_message
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function handle_action(
|
||||
callable $action,
|
||||
callable $request_validation,
|
||||
$success_message,
|
||||
$failure_message
|
||||
) {
|
||||
if ( $request_validation() ) {
|
||||
try {
|
||||
$action();
|
||||
|
||||
return $this->send_success_response( $success_message );
|
||||
} catch ( \Exception $e ) {
|
||||
return $this->send_error_message( $failure_message );
|
||||
}
|
||||
} else {
|
||||
return $this->send_error_message( __( 'Invalid Request', 'wpml-translation-management' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $msg
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function send_success_response( $msg ) {
|
||||
wp_send_json_success(
|
||||
[
|
||||
'errors' => 0,
|
||||
'message' => $msg,
|
||||
'reload' => 1,
|
||||
]
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $msg
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function send_error_message( $msg ) {
|
||||
wp_send_json_error(
|
||||
[
|
||||
'errors' => 1,
|
||||
'message' => $msg,
|
||||
'reload' => 0,
|
||||
]
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function is_valid_request() {
|
||||
return isset( $_POST['nonce'] ) && wp_verify_nonce( $_POST['nonce'], self::AJAX_ACTION );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function is_valid_request_with_params() {
|
||||
return isset( $_POST['service_id'], $_POST['custom_fields'] ) && $this->is_valid_request();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace WPML\TM\Menu\TranslationServices;
|
||||
|
||||
use function WPML\Container\make;
|
||||
|
||||
class AuthenticationAjaxFactory implements \IWPML_Backend_Action_Loader {
|
||||
/**
|
||||
* @return AuthenticationAjax
|
||||
*/
|
||||
public function create() {
|
||||
return make( AuthenticationAjax::class );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace WPML\TM\Menu\TranslationServices;
|
||||
|
||||
class MainLayoutTemplate {
|
||||
|
||||
const SERVICES_LIST_TEMPLATE = 'services-layout.twig';
|
||||
|
||||
/**
|
||||
* @param callable $templateRenderer
|
||||
* @param callable $activeServiceRenderer
|
||||
* @param bool $hasPreferredService
|
||||
* @param callable $retrieveServiceTabsData
|
||||
*/
|
||||
public static function render(
|
||||
$templateRenderer,
|
||||
$activeServiceRenderer,
|
||||
$hasPreferredService,
|
||||
$retrieveServiceTabsData
|
||||
) {
|
||||
echo $templateRenderer(
|
||||
self::getModel( $activeServiceRenderer, $hasPreferredService, $retrieveServiceTabsData ),
|
||||
self::SERVICES_LIST_TEMPLATE
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callable $activeServiceRenderer
|
||||
* @param bool $hasPreferredService
|
||||
* @param callable $retrieveServiceTabsData
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private static function getModel( $activeServiceRenderer, $hasPreferredService, $retrieveServiceTabsData ) {
|
||||
$services = $retrieveServiceTabsData();
|
||||
|
||||
return [
|
||||
'active_service' => $activeServiceRenderer(),
|
||||
'services' => $services,
|
||||
'has_preferred_service' => $hasPreferredService,
|
||||
'has_services' => ! empty( $services ),
|
||||
'nonces' => [
|
||||
ActivationAjax::NONCE_ACTION => wp_create_nonce( ActivationAjax::NONCE_ACTION ),
|
||||
AuthenticationAjax::AJAX_ACTION => wp_create_nonce( AuthenticationAjax::AJAX_ACTION ),
|
||||
],
|
||||
'strings' => [
|
||||
'no_service_found' => [
|
||||
__(
|
||||
'WPML cannot load the list of translation services. This can be a connection problem. Please wait a minute and reload this page.',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
__( 'If the problem continues, please contact %s.', 'wpml-translation-management' ),
|
||||
],
|
||||
'wpml_support' => 'WPML support',
|
||||
'support_link' => 'https://wpml.org/forums/forum/english-support/',
|
||||
'activate' => __( 'Activate', 'wpml-translation-management' ),
|
||||
'documentation' => __( 'Documentation', 'wpml-translation-management' ),
|
||||
'ts' => [
|
||||
'different' => __(
|
||||
'Looking for a different translation service?',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
'tell_us_url' => 'https://wpml.org/documentation/content-translation/how-to-add-translation-services-to-wpml/#add-service-form',
|
||||
'tell_us' => __( 'Tell us which one', 'wpml-translation-management' ),
|
||||
],
|
||||
'filter' => [
|
||||
'search' => __( 'Search', 'wpml-translation-management' ),
|
||||
'clean_search' => __( 'Clear search', 'wpml-translation-management' ),
|
||||
],
|
||||
'columns' => [
|
||||
'name' => __( 'Name', 'wpml-translation-management' ),
|
||||
'description' => __( 'Description', 'wpml-translation-management' ),
|
||||
'ranking' => __( 'Ranking', 'wpml-translation-management' ),
|
||||
],
|
||||
'pagination' => \WPML_Admin_Pagination_Render::get_strings( 10 ),
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace WPML\TM\Menu\TranslationServices;
|
||||
|
||||
class NoSiteKeyTemplate {
|
||||
|
||||
const TEMPLATE = 'no-site-key.twig';
|
||||
|
||||
/**
|
||||
* @param callable $templateRenderer
|
||||
*/
|
||||
public static function render( $templateRenderer ) {
|
||||
echo $templateRenderer( self::get_no_site_key_model(), self::TEMPLATE );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private static function get_no_site_key_model() {
|
||||
return [
|
||||
'registration' => [
|
||||
'link' => admin_url( 'plugin-install.php?tab=commercial#repository-wpml' ),
|
||||
'text' => __(
|
||||
'Please register WPML to enable the professional translation option',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace WPML\TM\Menu\TranslationServices;
|
||||
|
||||
class Resources {
|
||||
|
||||
public function add_hooks() {
|
||||
if ( $this->is_active() ) {
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_styles' ) );
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
|
||||
}
|
||||
}
|
||||
|
||||
public function enqueue_styles() {
|
||||
wp_enqueue_style(
|
||||
'wpml-tm-ts-admin-section',
|
||||
WPML_TM_URL . '/res/css/admin-sections/translation-services.css',
|
||||
array(),
|
||||
WPML_TM_VERSION
|
||||
);
|
||||
}
|
||||
|
||||
public function enqueue_scripts() {
|
||||
|
||||
wp_enqueue_script(
|
||||
'wpml-tm-ts-admin-section',
|
||||
WPML_TM_URL . '/res/js/translation-services.js',
|
||||
array(),
|
||||
WPML_TM_VERSION
|
||||
);
|
||||
|
||||
wp_enqueue_script(
|
||||
'wpml-tm-translation-services',
|
||||
WPML_TM_URL . '/dist/js/translationServices/app.js',
|
||||
array(),
|
||||
WPML_TM_VERSION
|
||||
);
|
||||
wp_localize_script(
|
||||
'wpml-tm-translation-services',
|
||||
'WPML_Translation_Services',
|
||||
[
|
||||
'logoPlaceholder' => WPML_TM_URL . '/res/img/lsp-logo-placeholder.png',
|
||||
]
|
||||
);
|
||||
|
||||
wp_enqueue_script(
|
||||
'wpml-tp-api',
|
||||
WPML_TM_URL . '/res/js/wpml-tp-api.js',
|
||||
array( 'jquery', 'wp-util' ),
|
||||
WPML_TM_VERSION
|
||||
);
|
||||
}
|
||||
|
||||
private function is_active() {
|
||||
return isset( $_GET['sm'] ) && 'translation-services' === $_GET['sm'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace WPML\TM\Menu\TranslationServices;
|
||||
|
||||
class ResourcesFactory implements \IWPML_Backend_Action_Loader {
|
||||
/**
|
||||
* @return Resources
|
||||
*/
|
||||
public function create() {
|
||||
return new Resources();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
namespace WPML\TM\Menu\TranslationServices;
|
||||
|
||||
class Section implements \IWPML_TM_Admin_Section {
|
||||
const SLUG = 'translation-services';
|
||||
|
||||
/**
|
||||
* The SitePress instance.
|
||||
*
|
||||
* @var \SitePress
|
||||
*/
|
||||
private $sitepress;
|
||||
|
||||
/**
|
||||
* The WPML_WP_API instance.
|
||||
*
|
||||
* @var \WPML_WP_API
|
||||
*/
|
||||
private $wp_api;
|
||||
|
||||
/**
|
||||
* The template to use.
|
||||
*
|
||||
* @var mixed $template
|
||||
*/
|
||||
private $template;
|
||||
|
||||
/**
|
||||
* WPML_TM_Translation_Services_Admin_Section constructor.
|
||||
*
|
||||
* @param \SitePress $sitepress The SitePress instance.
|
||||
* @param callable $template The template to use.
|
||||
*/
|
||||
public function __construct(
|
||||
\SitePress $sitepress,
|
||||
$template
|
||||
) {
|
||||
$this->sitepress = $sitepress;
|
||||
$this->wp_api = $sitepress->get_wp_api();
|
||||
$this->template = $template;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a value which will be used for sorting the sections.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function get_order() {
|
||||
return 400;
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the content of the section.
|
||||
*/
|
||||
public function render() {
|
||||
call_user_func( $this->template );
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to extend the logic for displaying/hiding the section.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_visible() {
|
||||
return ! $this->wp_api->constant( 'ICL_HIDE_TRANSLATION_SERVICES' ) && ( $this->wp_api->constant( 'WPML_BYPASS_TS_CHECK' ) || ! $this->sitepress->get_setting( 'translation_service_plugin_activated' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the unique slug of the sections which is used to build the URL for opening this section.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_slug() {
|
||||
return self::SLUG;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns one or more capabilities required to display this section.
|
||||
*
|
||||
* @return string|array
|
||||
*/
|
||||
public function get_capabilities() {
|
||||
return [ \WPML_Manage_Translations_Role::CAPABILITY, 'manage_options' ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the caption to display in the section.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_caption() {
|
||||
return __( 'Translation Services', 'wpml-translation-management' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the callback responsible for rendering the content of the section.
|
||||
*
|
||||
* @return callable
|
||||
*/
|
||||
public function get_callback() {
|
||||
return array( $this, 'render' );
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is hooked to the `admin_enqueue_scripts` action.
|
||||
*
|
||||
* @param string $hook The current page.
|
||||
*/
|
||||
public function admin_enqueue_scripts( $hook ) {}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
namespace WPML\TM\Menu\TranslationServices;
|
||||
|
||||
use WPML\LIB\WP\Http;
|
||||
use WPML\TM\Geolocalization;
|
||||
use function WPML\Container\make;
|
||||
use function WPML\FP\partial;
|
||||
use function WPML\FP\partialRight;
|
||||
|
||||
class SectionFactory implements \IWPML_TM_Admin_Section_Factory {
|
||||
/**
|
||||
* @return Section
|
||||
*/
|
||||
public function create() {
|
||||
global $sitepress;
|
||||
|
||||
return new Section(
|
||||
$sitepress,
|
||||
$this->site_key_exists() ?
|
||||
$this->createServicesListRenderer() :
|
||||
partial( NoSiteKeyTemplate::class . '::render', $this->getTemplateRenderer() )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool|string
|
||||
*/
|
||||
private function site_key_exists() {
|
||||
$site_key = false;
|
||||
|
||||
if ( class_exists( 'WP_Installer' ) ) {
|
||||
$repository_id = 'wpml';
|
||||
$site_key = \WP_Installer()->get_site_key( $repository_id );
|
||||
}
|
||||
|
||||
return $site_key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \WPML_Twig_Template_Loader $twig_loader
|
||||
* @param \WPML_TP_Client $tp_client
|
||||
*
|
||||
* @return callable
|
||||
*/
|
||||
private function createServicesListRenderer() {
|
||||
/**
|
||||
* Section: "Partner services", "Other services" and "Translation Management Services"
|
||||
*/
|
||||
$getServicesTabs = partial(
|
||||
ServicesRetriever::class . '::get',
|
||||
$this->getTpApiServices(),
|
||||
Geolocalization::getCountryByIp( Http::post() ),
|
||||
partialRight(
|
||||
[ ServiceMapper::class, 'map' ],
|
||||
[ ActiveServiceRepository::class, 'getId' ]
|
||||
)
|
||||
);
|
||||
|
||||
return partial(
|
||||
MainLayoutTemplate::class . '::render',
|
||||
$this->getTemplateRenderer(),
|
||||
ActiveServiceTemplateFactory::createRenderer(),
|
||||
\TranslationProxy::has_preferred_translation_service(),
|
||||
$getServicesTabs
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return callable
|
||||
*/
|
||||
private function getTemplateRenderer() {
|
||||
$template = make(
|
||||
\WPML_Twig_Template_Loader::class,
|
||||
[
|
||||
':paths' => [
|
||||
WPML_TM_PATH . '/templates/menus/translation-services/',
|
||||
WPML_PLUGIN_PATH . '/templates/pagination/',
|
||||
],
|
||||
]
|
||||
)->get_template();
|
||||
|
||||
return [ $template, 'show' ];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \WPML_TP_API_Services
|
||||
*/
|
||||
private function getTpApiServices() {
|
||||
return make( \WPML_TP_Client_Factory::class )->create()->services();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace WPML\TM\Menu\TranslationServices;
|
||||
|
||||
class ServiceMapper {
|
||||
/**
|
||||
* @param \WPML_TP_Service $service
|
||||
* @param callable $getActiveServiceId
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function map( \WPML_TP_Service $service, $getActiveServiceId ) {
|
||||
return [
|
||||
'id' => $service->get_id(),
|
||||
'logo_url' => $service->get_logo_preview_url(),
|
||||
'name' => $service->get_name(),
|
||||
'description' => $service->get_description(),
|
||||
'doc_url' => $service->get_doc_url(),
|
||||
'active' => $service->get_id() === $getActiveServiceId() ? 'active' : 'inactive',
|
||||
'rankings' => $service->get_rankings(),
|
||||
'how_to_get_credentials_desc' => $service->get_how_to_get_credentials_desc(),
|
||||
'how_to_get_credentials_url' => $service->get_how_to_get_credentials_url(),
|
||||
'client_create_account_page_url' => $service->get_client_create_account_page_url(),
|
||||
'custom_fields' => $service->get_custom_fields(),
|
||||
'countries' => $service->get_countries(),
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
namespace WPML\TM\Menu\TranslationServices;
|
||||
|
||||
use WPML\FP\Fns;
|
||||
use WPML\FP\Logic;
|
||||
use WPML\FP\Lst;
|
||||
use WPML\FP\Obj;
|
||||
use WPML\FP\Relation;
|
||||
use function WPML\FP\curryN;
|
||||
use function WPML\FP\invoke;
|
||||
use function WPML\FP\partialRight;
|
||||
use function WPML\FP\pipe;
|
||||
|
||||
class ServicesRetriever {
|
||||
public static function get( \WPML_TP_API_Services $servicesAPI, $getUserCountry, $mapService ) {
|
||||
$userCountry = $getUserCountry( isset( $_SERVER['REMOTE_ADDR'] ) ? $_SERVER['REMOTE_ADDR'] : null );
|
||||
|
||||
// $buildSection :: $services -> $header -> $showPopularity -> string
|
||||
$buildSection = self::buildSection( $mapService );
|
||||
|
||||
// $otherSection :: $services -> $header -> string
|
||||
$otherSection = $buildSection( Fns::__, Fns::__, false, true );
|
||||
|
||||
$buildPartnerServicesSections = self::buildPartnerServicesSections( $buildSection, $userCountry );
|
||||
$partnerServices = $servicesAPI->get_translation_services( true );
|
||||
$services = $buildPartnerServicesSections( $partnerServices );
|
||||
|
||||
$services[] = $otherSection(
|
||||
$servicesAPI->get_translation_services( false ),
|
||||
__( 'Other Translation Services', 'wpml-translation-management' )
|
||||
);
|
||||
|
||||
$services[] = $otherSection(
|
||||
$servicesAPI->get_translation_management_systems(),
|
||||
__( 'Translation Management System', 'wpml-translation-management' )
|
||||
);
|
||||
|
||||
return $services;
|
||||
}
|
||||
|
||||
// buildPartnerServicesSections :: \WPML_TP_Services[] -> string[]
|
||||
private static function buildPartnerServicesSections( $buildSection, $userCountry ) {
|
||||
$headers = [
|
||||
'regular' => __( 'Partner Translation Services', 'wpml-translation-management' ),
|
||||
'inCountry' => __(
|
||||
sprintf(
|
||||
'Partner Translation Services in %s',
|
||||
isset( $userCountry['name'] ) ? $userCountry['name'] : ''
|
||||
),
|
||||
'wpml-translation-management'
|
||||
),
|
||||
'otherCountries' => __(
|
||||
'Other Partner Translation Services from Around the World',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
];
|
||||
|
||||
// $partnerSection :: $services -> $header -> string
|
||||
$partnerSection = $buildSection( Fns::__, Fns::__, true, Fns::__ );
|
||||
|
||||
// $regularPartnerSection :: $services -> string
|
||||
$regularPartnerSection = $partnerSection( Fns::__, $headers['regular'], true );
|
||||
|
||||
// $partnersInCountry :: $services -> string
|
||||
$partnersInCountry = $partnerSection( Fns::__, $headers['inCountry'], false );
|
||||
|
||||
// $partnersOther :: $services -> string
|
||||
$partnersOther = $partnerSection( Fns::__, $headers['otherCountries'], true );
|
||||
|
||||
// $getServicesFromCountry :: [$servicesFromCountry, $otherServices] -> $servicesFromCountry
|
||||
$inUserCountry = Lst::nth( 0 );
|
||||
// $getServicesFromOtherCountries :: [$servicesFromCountry, $otherServices] -> $otherServices
|
||||
$inOtherCountries = Lst::nth( 1 );
|
||||
|
||||
// $splitSections :: [$servicesFromCountry, $otherServices] -> [string, string]
|
||||
$splitSections = Fns::converge(
|
||||
Lst::make(),
|
||||
[
|
||||
pipe( $inUserCountry, $partnersInCountry ),
|
||||
pipe( $inOtherCountries, $partnersOther ),
|
||||
]
|
||||
);
|
||||
|
||||
// $hasUserCountry :: [$servicesFromCountry, $otherServices] -> bool
|
||||
$hasUserCountry = pipe( $inUserCountry, Logic::isEmpty(), Logic::not() );
|
||||
|
||||
return pipe(
|
||||
Lst::partition( self::belongToUserCountry( $userCountry ) ),
|
||||
Logic::ifElse(
|
||||
$hasUserCountry,
|
||||
$splitSections,
|
||||
pipe( $inOtherCountries, $regularPartnerSection, Lst::make() )
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callable $mapService
|
||||
*
|
||||
* @return callable
|
||||
*/
|
||||
private static function buildSection( $mapService ) {
|
||||
return curryN(
|
||||
4,
|
||||
function ( $services, $header, $showPopularity, $pagination ) use ( $mapService ) {
|
||||
return [
|
||||
'services' => Fns::map( Fns::unary( $mapService ), $services ),
|
||||
'header' => $header,
|
||||
'showPopularity' => $showPopularity,
|
||||
'pagination' => $pagination,
|
||||
];
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// belongToUserCountry :: \WPML_TP_Service -> bool
|
||||
private static function belongToUserCountry( $userCountry ) {
|
||||
return pipe(
|
||||
invoke( 'get_countries' ),
|
||||
Fns::map( Obj::prop( 'code' ) ),
|
||||
Lst::find( Relation::equals( Obj::prop( 'code', $userCountry ) ) ),
|
||||
Logic::isNotNull()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
namespace WPML\TM\Menu\TranslationServices\Troubleshooting;
|
||||
|
||||
class RefreshServices {
|
||||
|
||||
const TEMPLATE = 'refresh-services.twig';
|
||||
const AJAX_ACTION = 'wpml_tm_refresh_services';
|
||||
|
||||
/**
|
||||
* @var \IWPML_Template_Service
|
||||
*/
|
||||
private $template;
|
||||
|
||||
/**
|
||||
* @var \WPML_TP_API_Services
|
||||
*/
|
||||
private $tp_services;
|
||||
|
||||
public function __construct( \IWPML_Template_Service $template, \WPML_TP_API_Services $tp_services ) {
|
||||
$this->template = $template;
|
||||
$this->tp_services = $tp_services;
|
||||
}
|
||||
|
||||
public function add_hooks() {
|
||||
add_action( 'after_setup_complete_troubleshooting_functions', array( $this, 'render' ), 1 );
|
||||
add_action( 'wp_ajax_' . self::AJAX_ACTION, array( $this, 'refresh_services_ajax_handler' ) );
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
|
||||
}
|
||||
|
||||
public function render() {
|
||||
echo $this->template->show( $this->get_model(), self::TEMPLATE );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private function get_model() {
|
||||
return array(
|
||||
'button_text' => __( 'Refresh Translation Services', 'wpml-translation-management' ),
|
||||
'nonce' => wp_create_nonce( self::AJAX_ACTION ),
|
||||
);
|
||||
}
|
||||
|
||||
public function refresh_services_ajax_handler() {
|
||||
if ( $this->is_valid_request() ) {
|
||||
if ( $this->refresh_services() ) {
|
||||
wp_send_json_success(
|
||||
array(
|
||||
'message' => __( 'Services Refreshed.', 'wpml-translation-management' ),
|
||||
)
|
||||
);
|
||||
} else {
|
||||
wp_send_json_error(
|
||||
array(
|
||||
'message' => __(
|
||||
'WPML cannot load the list of translation services. This can be a connection problem. Please wait a minute and reload this page.
|
||||
If the problem continues, please contact WPML support.',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
wp_send_json_error(
|
||||
array(
|
||||
'message' => __( 'Invalid Request.', 'wpml-translation-management' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function refresh_services() {
|
||||
return $this->tp_services->refresh_cache() && $this->refresh_active_service();
|
||||
}
|
||||
|
||||
private function refresh_active_service() {
|
||||
$active_service = $this->tp_services->get_active();
|
||||
|
||||
if ( $active_service ) {
|
||||
$active_service = (object) (array) $active_service; // Cast to stdClass
|
||||
\TranslationProxy::build_and_store_active_translation_service( $active_service, $active_service->custom_fields_data );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
private function is_valid_request() {
|
||||
return isset( $_POST['nonce'] ) && wp_verify_nonce( $_POST['nonce'], self::AJAX_ACTION );
|
||||
}
|
||||
|
||||
public function enqueue_scripts() {
|
||||
wp_enqueue_script(
|
||||
'wpml-tm-refresh-services',
|
||||
WPML_TM_URL . '/res/js/refresh-services.js',
|
||||
array(),
|
||||
WPML_TM_VERSION
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace WPML\TM\Menu\TranslationServices\Troubleshooting;
|
||||
|
||||
use function WPML\Container\make;
|
||||
|
||||
class RefreshServicesFactory implements \IWPML_Backend_Action_Loader {
|
||||
|
||||
/**
|
||||
* @return RefreshServices|null
|
||||
* @throws \Auryn\InjectionException
|
||||
*/
|
||||
public function create() {
|
||||
$hooks = null;
|
||||
|
||||
if ( $this->is_visible() ) {
|
||||
$hooks = $this->create_an_instance();
|
||||
}
|
||||
|
||||
return $hooks;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return RefreshServices
|
||||
* @throws \Auryn\InjectionException
|
||||
*/
|
||||
public function create_an_instance() {
|
||||
$templateService = make(
|
||||
\WPML_Twig_Template_Loader::class,
|
||||
[ ':paths' => [ WPML_TM_PATH . '/templates/menus/translation-services' ] ]
|
||||
);
|
||||
|
||||
$tpClientFactory = make( \WPML_TP_Client_Factory::class );
|
||||
|
||||
return new RefreshServices( $templateService->get_template(), $tpClientFactory->create()->services() );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
private function is_visible() {
|
||||
return ( isset( $_GET['page'] ) && 'sitepress-multilingual-cms/menu/troubleshooting.php' === $_GET['page'] ) ||
|
||||
( isset( $_POST['action'] ) && RefreshServices::AJAX_ACTION === $_POST['action'] );
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user