first commit

This commit is contained in:
2023-09-12 21:41:04 +02:00
commit 3361a7f053
13284 changed files with 2116755 additions and 0 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,90 @@
<?php
namespace WPML\TM\Menu\TranslationServices;
use WPML\DocPage;
use WPML\LIB\WP\Nonce;
use WPML\Setup\Option;
use WPML\TM\Menu\TranslationServices\Endpoints\Activate;
use WPML\TM\Menu\TranslationServices\Endpoints\Deactivate;
use WPML\TM\Menu\TranslationServices\Endpoints\Select;
use WPML\UIPage;
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();
$translationServicesUrl = 'https://wpml.org/documentation/translating-your-contents/professional-translation-via-wpml/?utm_source=plugin&utm_medium=gui&utm_campaign=wpmltm';
/* Translators: %s is documentation link for Translation Services */
$sectionDescription = sprintf(
'WPML integrates with dozens of professional <a target="_blank" href="%s">translation services</a>. Connect to your preferred service to send and receive translation jobs from directly within WPML.',
$translationServicesUrl
);
return [
'active_service' => $activeServiceRenderer(),
'services' => $services,
'has_preferred_service' => $hasPreferredService,
'has_services' => ! empty( $services ),
'translate_everything' => Option::shouldTranslateEverything(),
'nonces' => [
ActivationAjax::NONCE_ACTION => wp_create_nonce( ActivationAjax::NONCE_ACTION ),
AuthenticationAjax::AJAX_ACTION => wp_create_nonce( AuthenticationAjax::AJAX_ACTION ),
],
'settings_url' => UIPage::getSettings(),
'lsp_logo_placeholder' => WPML_TM_URL . '/res/img/lsp-logo-placeholder.png',
'strings' => [
'translation_services' => __( 'Translation Services', 'wpml-translation-management' ),
'translation_services_description' => __( $sectionDescription, 'wpml-translation-management' ),
'ts' => [
'different' => __( 'Looking for a different translation service?', 'wpml-translation-management' ),
'tell_us_url' => DocPage::addTranslationServiceForm(),
'tell_us' => __( 'Tell us which one', 'wpml-translation-management' ),
],
],
'endpoints' => [
'selectService' => [
'endpoint' => Select::class,
'nonce' => Nonce::create( Select::class )
],
'deactivateService' => [
'nonce' => Nonce::create( Deactivate::class ),
'endpoint' => Deactivate::class
],
'activateService' => [
'nonce' => Nonce::create( Activate::class ),
'endpoint' => Activate::class
],
],
];
}
}

View File

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

View File

@@ -0,0 +1,57 @@
<?php
namespace WPML\TM\Menu\TranslationServices;
class Resources implements \IWPML_Backend_Action {
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
);
wp_enqueue_style(
'wpml-tm-translation-services',
WPML_TM_URL . '/dist/css/translationServices/styles.css',
[],
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_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'] ) && 'translators' === $_GET['sm'];
}
}

View File

@@ -0,0 +1,111 @@
<?php
namespace WPML\TM\Menu\TranslationServices;
class Section implements \IWPML_TM_Admin_Section {
const SLUG = 'translators';
/**
* 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 ) {}
}

View File

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

View File

@@ -0,0 +1,36 @@
<?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 ) {
$isActive = $service->get_id() === $getActiveServiceId();
if ( $isActive ) {
$service->set_custom_fields_data();
}
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' => $isActive ? '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(),
'is_authorized' => ! empty( $service->get_custom_fields_data() ),
'client_create_account_page_url' => $service->get_client_create_account_page_url(),
'custom_fields' => $service->get_custom_fields(),
'countries' => $service->get_countries(),
'url' => $service->get_url(),
];
}
}

View File

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

View File

@@ -0,0 +1,35 @@
<?php
namespace WPML\TM\Menu\TranslationServices\Endpoints;
use WPML\Ajax\IHandler;
use WPML\Collect\Support\Collection;
use WPML\FP\Either;
use WPML\FP\Logic;
use WPML\FP\Obj;
use WPML\TM\TranslationProxy\Services\AuthorizationFactory;
class Activate implements IHandler {
public function run( Collection $data ) {
$serviceId = $data->get( 'service_id' );
$apiTokenData = $data->get( 'api_token' );
$authorize = function ( $serviceId ) use ( $apiTokenData ) {
$authorization = ( new AuthorizationFactory )->create();
try {
$authorization->authorize( (object) Obj::pickBy( Logic::isNotEmpty(), $apiTokenData ) );
return Either::of( $serviceId );
} catch ( \Exception $e ) {
$authorization->deauthorize();
return Either::left( $e->getMessage() );
}
};
return Either::of( $serviceId )
->chain( [ Select::class, 'select' ] )
->chain( $authorize );
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace WPML\TM\Menu\TranslationServices\Endpoints;
use WPML\Ajax\IHandler;
use WPML\Collect\Support\Collection;
use WPML\FP\Either;
class Deactivate implements IHandler {
public function run( Collection $data ) {
if ( \TranslationProxy::get_current_service_id() ) {
\TranslationProxy::clear_preferred_translation_service();
\TranslationProxy::deselect_active_service();
}
return Either::of( 'OK' );
}
}

View File

@@ -0,0 +1,42 @@
<?php
namespace WPML\TM\Menu\TranslationServices\Endpoints;
use WPML\Ajax\IHandler;
use WPML\Collect\Support\Collection;
use WPML\FP\Either;
class Select implements IHandler {
public function run( Collection $data ) {
$serviceId = $data->get( 'service_id' );
return self::select( $serviceId );
}
public static function select( $serviceId ) {
$deactivateOldService = function () {
\TranslationProxy::clear_preferred_translation_service();
\TranslationProxy::deselect_active_service();
};
$activateService = function ( $serviceId ) {
$result = \TranslationProxy::select_service( $serviceId );
return \is_wp_error( $result ) ? Either::left( $result->get_error_message() ) : Either::of( $serviceId );
};
$currentServiceId = \TranslationProxy::get_current_service_id();
if ( $currentServiceId ) {
if ( $currentServiceId === $serviceId ) {
return Either::of( $serviceId );
} else {
$deactivateOldService();
}
}
return $activateService( $serviceId );
}
}

View File

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

View File

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