first commit
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
namespace WPML\TM\Notices;
|
||||
|
||||
use WPML\TM\ATE\ClonedSites\Lock;
|
||||
use WPML\TM\Templates\Notices\AteLocked;
|
||||
|
||||
class AteLockNotice implements \IWPML_Backend_Action, \IWPML_DIC_Action {
|
||||
|
||||
/**
|
||||
* @var AteLocked
|
||||
*/
|
||||
private $templateRenderer;
|
||||
|
||||
public function __construct( AteLocked $templateRenderer ) {
|
||||
$this->templateRenderer = $templateRenderer;
|
||||
}
|
||||
|
||||
public function add_hooks() {
|
||||
add_action( 'admin_notices', [ $this, 'ateLockNotice' ] );
|
||||
add_action( 'admin_enqueue_scripts', [ $this, 'enqueueScripts' ] );
|
||||
}
|
||||
|
||||
public function enqueueScripts() {
|
||||
if ( $this->shouldRender() ) {
|
||||
wp_enqueue_script(
|
||||
'wpml-tm-ate-lock',
|
||||
WPML_TM_URL . '/res/js/ate-api-lock-notification.js',
|
||||
[],
|
||||
WPML_TM_VERSION,
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function ateLockNotice() {
|
||||
if ( $this->shouldRender() ) {
|
||||
$this->renderNotice();
|
||||
}
|
||||
}
|
||||
|
||||
private function shouldRender() {
|
||||
return Lock::isLocked() && $this->shouldDisplayOnCurrentPage();
|
||||
}
|
||||
|
||||
private function renderNotice() {
|
||||
if ( current_user_can( 'manage_options' ) ) {
|
||||
$this->renderAdminNotice();
|
||||
} else {
|
||||
$this->renderUserNotice();
|
||||
}
|
||||
}
|
||||
|
||||
private function renderAdminNotice() {
|
||||
$model = (object) [
|
||||
'title' => __( 'Site Moved or Copied - Action Required', 'wpml-translation-management' ),
|
||||
'intro' => __( 'Looks like this site is a copy of a different site, or moved to a different URL.', 'wpml-translation-management' ),
|
||||
'radio_option_1' => __( 'I moved the site to this new URL', 'wpml-translation-management' ),
|
||||
'radio_option_2' => __( 'This is a copy of my original site', 'wpml-translation-management' ),
|
||||
'btn_text' => __( 'Save', 'wpml-translation-management' ),
|
||||
'link_text' => __( 'More details', 'wpml-translation-management' ),
|
||||
'allowed_modes' => apply_filters( 'wpml_ate_locked_allow_site_move_copy', [ 'move' => true, 'copy' => true ] ),
|
||||
];
|
||||
|
||||
$this->templateRenderer->renderAdmin( $model );
|
||||
}
|
||||
|
||||
private function renderUserNotice() {
|
||||
$model = (object) [
|
||||
'title' => __( 'Site Moved or Copied - Action Required', 'wpml-translation-management' ),
|
||||
'intro' => __( 'Looks like this site is a copy of a different site, or moved to a different URL. Please contact your translation manager to update Translation Management plugin configuration.', 'wpml-translation-management' ),
|
||||
];
|
||||
|
||||
$this->templateRenderer->renderUser( $model );
|
||||
}
|
||||
|
||||
private function shouldDisplayOnCurrentPage() {
|
||||
return $this->shouldDisplayOnScreen( [ 'dashboard' ] ) || $this->shouldDisplayOnPage( $this->getPages() );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $screens
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function shouldDisplayOnScreen( array $screens ) {
|
||||
$currentScreen = get_current_screen();
|
||||
return $currentScreen instanceof \WP_Screen
|
||||
&& in_array( $currentScreen->id, $screens );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $pages
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function shouldDisplayOnPage( array $pages ) {
|
||||
return isset( $_GET['page'] ) && in_array( $_GET['page'], $pages );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|string[]
|
||||
*/
|
||||
private function getPages() {
|
||||
$pages = [
|
||||
WPML_PLUGIN_FOLDER . '/menu/languages.php',
|
||||
WPML_PLUGIN_FOLDER . '/menu/theme-localization.php',
|
||||
WPML_PLUGIN_FOLDER . '/menu/settings.php',
|
||||
WPML_PLUGIN_FOLDER . '/menu/support.php',
|
||||
WPML_TM_FOLDER . '/menu/settings',
|
||||
WPML_TM_FOLDER . '/menu/main.php',
|
||||
WPML_TM_FOLDER . '/menu/translations-queue.php',
|
||||
WPML_TM_FOLDER . '/menu/string-translation.php',
|
||||
WPML_TM_FOLDER . '/menu/settings.php',
|
||||
];
|
||||
|
||||
return $pages;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace WPML\Notices;
|
||||
|
||||
class DismissNotices implements \IWPML_Backend_Action {
|
||||
|
||||
const OPTION = 'wpml_dismiss_notice';
|
||||
const CSS_CLASS = 'wpml_dismiss_notice';
|
||||
|
||||
public function add_hooks() {
|
||||
add_action( 'wp_ajax_wpml_dismiss_notice', [ $this, 'toggleDismiss' ] );
|
||||
|
||||
add_action(
|
||||
'admin_enqueue_scripts',
|
||||
function () {
|
||||
wp_enqueue_script(
|
||||
'wpml-dismiss-notice',
|
||||
ICL_PLUGIN_URL . '/dist/js/notices/app.js',
|
||||
[],
|
||||
ICL_SITEPRESS_VERSION
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public function toggleDismiss() {
|
||||
$postData = wpml_collect( $_POST );
|
||||
$id = $postData->get( 'id', null );
|
||||
if ( ! $id ) {
|
||||
return wp_send_json_error( 'ID of notice is not defined' );
|
||||
}
|
||||
|
||||
$options = get_option( self::OPTION, [] );
|
||||
$options[ $id ] = $postData->get( 'dismiss', false ) === 'true';
|
||||
|
||||
update_option( self::OPTION, $options );
|
||||
|
||||
return wp_send_json_success();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isDismissed( $id ) {
|
||||
return wpml_collect( get_option( self::OPTION, [] ) )->get( $id, false );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function renderCheckbox( $id ) {
|
||||
return sprintf(
|
||||
'<input type="checkbox" class="%s" data-id="%s" />',
|
||||
self::CSS_CLASS,
|
||||
$id
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @author OnTheGo Systems
|
||||
*/
|
||||
class WPML_Notice_Action {
|
||||
private $dismiss;
|
||||
private $display_as_button;
|
||||
private $hide;
|
||||
private $text;
|
||||
private $url;
|
||||
private $group_to_dismiss;
|
||||
private $js_callback;
|
||||
private $dismiss_different_text;
|
||||
private $link_target;
|
||||
|
||||
/**
|
||||
* WPML_Admin_Notice_Action constructor.
|
||||
*
|
||||
* @param string $text
|
||||
* @param string $url
|
||||
* @param bool $dismiss
|
||||
* @param bool $hide
|
||||
* @param bool|string $display_as_button
|
||||
* @param bool $dismiss_different_text
|
||||
*/
|
||||
public function __construct( $text, $url = '#', $dismiss = false, $hide = false, $display_as_button = false, $dismiss_different_text = true ) {
|
||||
$this->text = $text;
|
||||
$this->url = $url;
|
||||
$this->dismiss = $dismiss;
|
||||
$this->hide = $hide;
|
||||
$this->display_as_button = $display_as_button;
|
||||
$this->dismiss_different_text = $dismiss_different_text;
|
||||
}
|
||||
|
||||
public function get_text() {
|
||||
return $this->text;
|
||||
}
|
||||
|
||||
public function get_url() {
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
public function can_dismiss() {
|
||||
return $this->dismiss;
|
||||
}
|
||||
|
||||
public function can_dismiss_different_text() {
|
||||
return $this->dismiss_different_text;
|
||||
}
|
||||
|
||||
public function can_hide() {
|
||||
return $this->hide;
|
||||
}
|
||||
|
||||
public function must_display_as_button() {
|
||||
return $this->display_as_button;
|
||||
}
|
||||
|
||||
public function set_group_to_dismiss( $group_name ) {
|
||||
$this->group_to_dismiss = $group_name;
|
||||
}
|
||||
|
||||
public function get_group_to_dismiss() {
|
||||
return $this->group_to_dismiss;
|
||||
}
|
||||
|
||||
public function set_js_callback( $js_callback ) {
|
||||
$this->js_callback = $js_callback;
|
||||
}
|
||||
|
||||
public function get_js_callback() {
|
||||
return $this->js_callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function get_link_target() {
|
||||
return $this->link_target;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $link_target
|
||||
*/
|
||||
public function set_link_target( $link_target ) {
|
||||
$this->link_target = $link_target;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,407 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @author OnTheGo Systems
|
||||
*/
|
||||
class WPML_Notice_Render {
|
||||
private $dismiss_html_added;
|
||||
private $hide_html_added;
|
||||
private $collapse_html_added;
|
||||
|
||||
public function render( WPML_Notice $notice ) {
|
||||
echo $this->get_html( $notice );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WPML_Notice $notice
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_html( WPML_Notice $notice ) {
|
||||
$result = '';
|
||||
|
||||
if ( $this->must_display_notice( $notice ) ) {
|
||||
if ( $notice->should_be_text_only() ) {
|
||||
return $notice->get_text();
|
||||
}
|
||||
|
||||
$actions_html = $this->get_actions_html( $notice );
|
||||
|
||||
$temp_types = $notice->get_css_class_types();
|
||||
foreach ( $temp_types as $temp_type ) {
|
||||
if ( strpos( $temp_type, 'notice-' ) === false ) {
|
||||
$temp_types[] = 'notice-' . $temp_type;
|
||||
}
|
||||
if ( strpos( $temp_type, 'notice-' ) === 0 ) {
|
||||
$temp_types[] = substr( $temp_type, 0, strlen( 'notice-' ) );
|
||||
}
|
||||
}
|
||||
$temp_classes = $notice->get_css_classes();
|
||||
|
||||
$classes = array_merge( $temp_classes, $temp_types );
|
||||
|
||||
if ( $this->hide_html_added || $this->dismiss_html_added || $notice->can_be_hidden() || $notice->can_be_dismissed() ) {
|
||||
$classes[] = 'is-dismissible';
|
||||
}
|
||||
$classes[] = 'notice';
|
||||
$classes[] = 'otgs-notice';
|
||||
|
||||
$classes = array_unique( $classes );
|
||||
|
||||
$class = implode( ' ', $classes );
|
||||
|
||||
$result .= '<div class="' . $class . '" data-id="' . esc_attr( $notice->get_id() ) . '" data-group="' . esc_attr( $notice->get_group() ) . '"';
|
||||
$result .= $this->get_data_nonce_attribute();
|
||||
|
||||
if ( $this->hide_html_added || $notice->can_be_hidden() ) {
|
||||
$result .= ' data-hide-text="' . __( 'Hide', 'sitepress' ) . '" ';
|
||||
}
|
||||
$result .= '>';
|
||||
|
||||
if ( $notice->can_be_collapsed() ) {
|
||||
$result .= $this->sanitize_and_format_text( $this->get_collapsed_html( $notice ) );
|
||||
} else {
|
||||
$result .= '<p>' . $this->sanitize_and_format_text( $notice->get_text() ) . '</p>';
|
||||
}
|
||||
|
||||
$this->dismiss_html_added = false;
|
||||
$this->hide_html_added = false;
|
||||
$this->collapse_html_added = false;
|
||||
|
||||
$result .= $actions_html;
|
||||
|
||||
if ( $notice->can_be_hidden() ) {
|
||||
$result .= $this->get_hide_html();
|
||||
}
|
||||
|
||||
if ( $notice->can_be_dismissed() ) {
|
||||
$result .= $this->get_dismiss_html();
|
||||
}
|
||||
|
||||
if ( $notice->can_be_collapsed() ) {
|
||||
$result .= $this->get_collapse_html();
|
||||
}
|
||||
|
||||
if ( $notice->get_nonce_action() ) {
|
||||
$result .= $this->add_nonce( $notice );
|
||||
}
|
||||
|
||||
$result .= '</div>';
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WPML_Notice $notice
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function add_nonce( $notice ) {
|
||||
return wp_nonce_field( $notice->get_nonce_action(), $notice->get_nonce_action(), true, false );
|
||||
}
|
||||
|
||||
public function must_display_notice( WPML_Notice $notice ) {
|
||||
if ( ! $notice->is_for_current_user() || ! $notice->is_user_cap_allowed() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->is_current_page_allowed( $notice ) && $this->is_allowed_by_callback( $notice );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WPML_Notice $notice
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function get_actions_html( WPML_Notice $notice ) {
|
||||
$actions_html = '';
|
||||
if ( $notice->get_actions() ) {
|
||||
$actions_html .= '<div class="otgs-notice-actions">';
|
||||
foreach ( $notice->get_actions() as $action ) {
|
||||
$actions_html .= $this->get_action_html( $action );
|
||||
}
|
||||
|
||||
$actions_html .= '</div>';
|
||||
|
||||
return $actions_html;
|
||||
}
|
||||
|
||||
return $actions_html;
|
||||
}
|
||||
|
||||
private function sanitize_and_format_text( $text ) {
|
||||
$backticks_pattern = '|`(.*)`|U';
|
||||
preg_match_all( $backticks_pattern, $text, $matches );
|
||||
|
||||
$sanitized_notice = $text;
|
||||
if ( 2 === count( $matches ) ) {
|
||||
/** @var array<string> $matches_to_sanitize */
|
||||
$matches_to_sanitize = $matches[1];
|
||||
|
||||
foreach ( $matches_to_sanitize as &$match_to_sanitize ) {
|
||||
$match_to_sanitize = '<pre>' . esc_html( $match_to_sanitize ) . '</pre>';
|
||||
}
|
||||
unset( $match_to_sanitize );
|
||||
|
||||
$sanitized_notice = str_replace( $matches[0], $matches_to_sanitize, $sanitized_notice );
|
||||
}
|
||||
|
||||
return stripslashes( $sanitized_notice );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null|string $localized_text
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function get_hide_html( $localized_text = null ) {
|
||||
$hide_html = '';
|
||||
$hide_html .= '<span class="otgs-notice-hide notice-hide"><span class="screen-reader-text">';
|
||||
if ( $localized_text ) {
|
||||
$hide_html .= esc_html( $localized_text );
|
||||
} else {
|
||||
$hide_html .= esc_html__( 'Hide this notice.', 'sitepress' );
|
||||
}
|
||||
$hide_html .= '</span></span>';
|
||||
|
||||
return $hide_html;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null|string $localized_text
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function get_dismiss_html( $localized_text = null ) {
|
||||
$dismiss_html = '';
|
||||
$dismiss_html .= '<span class="otgs-notice-dismiss notice-dismiss">';
|
||||
$dismiss_html .= '<span class="screen-reader-text"><input class="otgs-notice-dismiss-check" type="checkbox" value="1" />';
|
||||
if ( $localized_text ) {
|
||||
$dismiss_html .= esc_html( $localized_text );
|
||||
} else {
|
||||
$dismiss_html .= esc_html__( 'Dismiss this notice.', 'sitepress' );
|
||||
}
|
||||
$dismiss_html .= '</span></span>';
|
||||
|
||||
return $dismiss_html;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $localized_text
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function get_collapse_html( $localized_text = null ) {
|
||||
$hide_html = '<span class="otgs-notice-collapse-hide"><span class="screen-reader-text">';
|
||||
if ( $localized_text ) {
|
||||
$hide_html .= esc_html( $localized_text );
|
||||
} else {
|
||||
$hide_html .= esc_html__( 'Hide this notice.', 'sitepress' );
|
||||
}
|
||||
$hide_html .= '</span></span>';
|
||||
|
||||
return $hide_html;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WPML_Notice $notice
|
||||
* @param string|null $localized_text
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function get_collapsed_html( WPML_Notice $notice, $localized_text = null ) {
|
||||
$content = '
|
||||
<div class="otgs-notice-collapsed-text">
|
||||
<p>%s
|
||||
<span class="otgs-notice-collapse-show notice-collapse"><span class="screen-reader-text">
|
||||
%s
|
||||
</span></span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="otgs-notice-collapse-text">
|
||||
%s
|
||||
</div>
|
||||
';
|
||||
|
||||
$content = sprintf(
|
||||
$content,
|
||||
$notice->get_collapsed_text(),
|
||||
$localized_text ? esc_html( $localized_text ) : esc_html__( 'Show this notice.', 'sitepress' ),
|
||||
$notice->get_text()
|
||||
);
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WPML_Notice_Action $action
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function get_action_html( $action ) {
|
||||
$action_html = '';
|
||||
if ( $action->can_hide() ) {
|
||||
$action_html .= $this->get_hide_html( $action->get_text() );
|
||||
$this->hide_html_added = true;
|
||||
} elseif ( $action->can_dismiss() ) {
|
||||
$action_html .= $this->get_dismiss_html( $action->get_text() );
|
||||
$this->dismiss_html_added = true;
|
||||
} else {
|
||||
if ( $action->get_url() ) {
|
||||
$action_html .= $this->get_action_anchor( $action );
|
||||
} else {
|
||||
$action_html .= $action->get_text();
|
||||
}
|
||||
}
|
||||
|
||||
return $action_html;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WPML_Notice_Action $action
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function get_action_anchor( WPML_Notice_Action $action ) {
|
||||
$anchor_attributes = array();
|
||||
|
||||
$action_url = '<a';
|
||||
|
||||
$anchor_attributes['href'] = esc_url_raw( $action->get_url() );
|
||||
if ( $action->get_link_target() ) {
|
||||
$anchor_attributes['target'] = $action->get_link_target();
|
||||
}
|
||||
|
||||
$action_url_classes = array( 'notice-action' );
|
||||
if ( $action->must_display_as_button() ) {
|
||||
$button_style = 'button-secondary';
|
||||
if ( is_string( $action->must_display_as_button() ) ) {
|
||||
$button_style = $action->must_display_as_button();
|
||||
}
|
||||
$action_url_classes[] = esc_attr( $button_style );
|
||||
$action_url_classes[] = 'notice-action-' . esc_attr( $button_style );
|
||||
} else {
|
||||
$action_url_classes[] = 'notice-action-link';
|
||||
}
|
||||
$anchor_attributes['class'] = implode( ' ', $action_url_classes );
|
||||
|
||||
if ( $action->get_group_to_dismiss() ) {
|
||||
$anchor_attributes['data-dismiss-group'] = esc_attr( $action->get_group_to_dismiss() );
|
||||
}
|
||||
if ( $action->get_js_callback() ) {
|
||||
$anchor_attributes['data-js-callback'] = esc_attr( $action->get_js_callback() )
|
||||
. '"';
|
||||
}
|
||||
|
||||
foreach ( $anchor_attributes as $name => $value ) {
|
||||
$action_url .= ' ' . $name . '="' . $value . '"';
|
||||
}
|
||||
|
||||
$action_url .= $this->get_data_nonce_attribute();
|
||||
$action_url .= '>';
|
||||
$action_url .= $action->get_text();
|
||||
$action_url .= '</a>';
|
||||
|
||||
return $action_url;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
private function get_data_nonce_attribute() {
|
||||
return ' data-nonce="' . wp_create_nonce( WPML_Notices::NONCE_NAME ) . '"';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WPML_Notice $notice
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_current_screen_allowed( WPML_Notice $notice ) {
|
||||
$allow_current_screen = true;
|
||||
$restrict_to_screen_ids = $notice->get_restrict_to_screen_ids();
|
||||
if ( $restrict_to_screen_ids && function_exists( 'get_current_screen' ) ) {
|
||||
$screen = get_current_screen();
|
||||
$allow_current_screen = $screen && in_array( $screen->id, $restrict_to_screen_ids, true );
|
||||
}
|
||||
|
||||
return $allow_current_screen;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WPML_Notice $notice
|
||||
* @param string $current_page
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_current_page_prefix_allowed( WPML_Notice $notice, $current_page ) {
|
||||
$restrict_to_page_prefixes = $notice->get_restrict_to_page_prefixes();
|
||||
if ( $current_page && $restrict_to_page_prefixes ) {
|
||||
$allow_current_page_prefix = false;
|
||||
foreach ( $restrict_to_page_prefixes as $restrict_to_prefix ) {
|
||||
if ( stripos( $current_page, $restrict_to_prefix ) === 0 ) {
|
||||
$allow_current_page_prefix = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $allow_current_page_prefix;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WPML_Notice $notice
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_current_page_allowed( WPML_Notice $notice ) {
|
||||
$current_page = array_key_exists( 'page', $_GET ) ? $_GET['page'] : null;
|
||||
|
||||
if ( ! $this->is_current_screen_allowed( $notice ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( $current_page ) {
|
||||
|
||||
$exclude_from_pages = $notice->get_exclude_from_pages();
|
||||
if ( $exclude_from_pages && in_array( $current_page, $exclude_from_pages, true ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! $this->is_current_page_prefix_allowed( $notice, $current_page ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$restrict_to_pages = $notice->get_restrict_to_pages();
|
||||
if ( $restrict_to_pages && ! in_array( $current_page, $restrict_to_pages, true ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WPML_Notice $notice
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_allowed_by_callback( WPML_Notice $notice ) {
|
||||
$allow_by_callback = true;
|
||||
$display_callbacks = $notice->get_display_callbacks();
|
||||
if ( $display_callbacks ) {
|
||||
$allow_by_callback = false;
|
||||
foreach ( $display_callbacks as $callback ) {
|
||||
if ( is_callable( $callback ) && call_user_func( $callback ) ) {
|
||||
$allow_by_callback = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $allow_by_callback;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,401 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @author OnTheGo Systems
|
||||
*/
|
||||
class WPML_Notice {
|
||||
private $display_callbacks = array();
|
||||
private $id;
|
||||
private $text;
|
||||
private $collapsed_text;
|
||||
private $group = 'default';
|
||||
private $restricted_to_user_ids = array();
|
||||
|
||||
private $actions = array();
|
||||
/**
|
||||
* @see \WPML_Notice::set_css_class_types
|
||||
* @var array
|
||||
*/
|
||||
private $css_class_types = array();
|
||||
private $css_classes = array();
|
||||
private $dismissible = false;
|
||||
private $exclude_from_pages = array();
|
||||
private $hideable = false;
|
||||
private $collapsable = false;
|
||||
private $restrict_to_pages = array();
|
||||
private $restrict_to_page_prefixes = array();
|
||||
private $restrict_to_screen_ids = array();
|
||||
private $hide_if_notice_exists = null;
|
||||
private $dismissible_for_different_text = true;
|
||||
|
||||
private $default_group_name = 'default';
|
||||
|
||||
private $capabilities = array();
|
||||
|
||||
private $dismiss_reset = false;
|
||||
|
||||
/*
|
||||
* @var bool
|
||||
* @since 4.1.0
|
||||
*/
|
||||
private $flash = false;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $nonce_action;
|
||||
|
||||
/** @var bool */
|
||||
private $text_only = false;
|
||||
|
||||
/**
|
||||
* WPML_Admin_Notification constructor.
|
||||
*
|
||||
* @param int|string $id
|
||||
* @param string $text
|
||||
* @param string $group
|
||||
*/
|
||||
public function __construct( $id, $text, $group = 'default' ) {
|
||||
$this->id = $id;
|
||||
$this->text = $text;
|
||||
$this->group = $group ? $group : $this->default_group_name;
|
||||
}
|
||||
|
||||
public function add_action( WPML_Notice_Action $action ) {
|
||||
$this->actions[] = $action;
|
||||
|
||||
if ( $action->can_dismiss() ) {
|
||||
$this->dismissible = true;
|
||||
}
|
||||
if ( ! $action->can_dismiss_different_text() ) {
|
||||
$this->dismissible_for_different_text = false;
|
||||
}
|
||||
if ( $action->can_hide() ) {
|
||||
$this->hideable = true;
|
||||
}
|
||||
}
|
||||
|
||||
public function add_exclude_from_page( $page ) {
|
||||
$this->exclude_from_pages[] = $page;
|
||||
}
|
||||
|
||||
public function add_restrict_to_page( $page ) {
|
||||
$this->restrict_to_pages[] = $page;
|
||||
}
|
||||
|
||||
/** @param int $user_id */
|
||||
public function add_user_restriction( $user_id ) {
|
||||
$user_id = (int) $user_id;
|
||||
$this->restricted_to_user_ids[ $user_id ] = $user_id;
|
||||
}
|
||||
|
||||
/** @param int $user_id */
|
||||
public function remove_user_restriction( $user_id ) {
|
||||
unset( $this->restricted_to_user_ids[ (int) $user_id ] );
|
||||
}
|
||||
|
||||
/** @return array */
|
||||
public function get_restricted_user_ids() {
|
||||
return $this->restricted_to_user_ids;
|
||||
}
|
||||
|
||||
/** @return bool */
|
||||
public function is_user_restricted() {
|
||||
return (bool) $this->restricted_to_user_ids;
|
||||
}
|
||||
|
||||
/** @return bool */
|
||||
public function is_for_current_user() {
|
||||
return ! $this->restricted_to_user_ids
|
||||
|| array_key_exists( get_current_user_id(), $this->restricted_to_user_ids );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function is_user_cap_allowed() {
|
||||
$user_can = true;
|
||||
foreach ( $this->capabilities as $cap ) {
|
||||
$user_can = current_user_can( $cap );
|
||||
|
||||
if ( $user_can ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $user_can;
|
||||
}
|
||||
|
||||
public function can_be_dismissed() {
|
||||
return $this->dismissible;
|
||||
}
|
||||
|
||||
public function can_be_dismissed_for_different_text() {
|
||||
return $this->dismissible_for_different_text;
|
||||
}
|
||||
|
||||
public function can_be_hidden() {
|
||||
return $this->hideable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function can_be_collapsed() {
|
||||
return $this->collapsable;
|
||||
}
|
||||
|
||||
/**
|
||||
* As the notice is supposed to be serialized and stored into the DB,
|
||||
* the callback should be only a function or a static method.
|
||||
*
|
||||
* Before to use a callback, please check the existing options with:
|
||||
* - add_exclude_from_page
|
||||
* - add_restrict_to_page
|
||||
* - add_user_restriction
|
||||
* - add_capability_check
|
||||
*
|
||||
* @param callable $callback
|
||||
*/
|
||||
public function add_display_callback( $callback ) {
|
||||
if ( ! is_callable( $callback ) ) {
|
||||
throw new UnexpectedValueException( '\WPML_Notice::add_display_callback expects a callable', 1 );
|
||||
}
|
||||
$this->display_callbacks[] = $callback;
|
||||
}
|
||||
|
||||
public function add_capability_check( array $cap ) {
|
||||
$this->capabilities = $cap;
|
||||
}
|
||||
|
||||
public function get_display_callbacks() {
|
||||
return $this->display_callbacks;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<\WPML_Notice_Action>
|
||||
*/
|
||||
public function get_actions() {
|
||||
return $this->actions;
|
||||
}
|
||||
|
||||
public function get_css_classes() {
|
||||
return $this->css_classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|array $css_classes
|
||||
*/
|
||||
public function set_css_classes( $css_classes ) {
|
||||
if ( ! is_array( $css_classes ) ) {
|
||||
$css_classes = explode( ' ', $css_classes );
|
||||
}
|
||||
$this->css_classes = $css_classes;
|
||||
}
|
||||
|
||||
public function get_exclude_from_pages() {
|
||||
return $this->exclude_from_pages;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function get_group() {
|
||||
return $this->group;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int|string
|
||||
*/
|
||||
public function get_id() {
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function set_restrict_to_page_prefixes( array $page_prefixes ) {
|
||||
$this->restrict_to_page_prefixes = $page_prefixes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function get_restrict_to_page_prefixes() {
|
||||
return $this->restrict_to_page_prefixes;
|
||||
}
|
||||
|
||||
public function get_restrict_to_pages() {
|
||||
return $this->restrict_to_pages;
|
||||
}
|
||||
|
||||
public function set_restrict_to_screen_ids( array $screens ) {
|
||||
$this->restrict_to_screen_ids = $screens;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function get_restrict_to_screen_ids() {
|
||||
return $this->restrict_to_screen_ids;
|
||||
}
|
||||
|
||||
public function get_nonce_action() {
|
||||
return $this->nonce_action;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function get_text() {
|
||||
$notice = array(
|
||||
'id' => $this->get_id(),
|
||||
'group' => $this->get_group(),
|
||||
);
|
||||
$this->text = apply_filters( 'wpml_notice_text', $this->text, $notice );
|
||||
|
||||
return $this->text;
|
||||
}
|
||||
|
||||
public function get_css_class_types() {
|
||||
return $this->css_class_types;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function get_collapsed_text() {
|
||||
return $this->collapsed_text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this to set the look of the notice.
|
||||
* WordPress recognize these values:
|
||||
* - notice-error
|
||||
* - notice-warning
|
||||
* - notice-success
|
||||
* - notice-info
|
||||
* You can use the above values with or without the "notice-" prefix:
|
||||
* the prefix will be added automatically in the HTML, if missing.
|
||||
*
|
||||
* @see https://codex.wordpress.org/Plugin_API/Action_Reference/admin_notices for more details
|
||||
*
|
||||
* @param string|array $types Accepts either a space separated values string, or an array of values.
|
||||
* @return WPML_Notice
|
||||
*/
|
||||
public function set_css_class_types( $types ) {
|
||||
$this->css_class_types = is_array( $types ) ? $types : explode( ' ', $types );
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $dismissible
|
||||
*/
|
||||
public function set_dismissible( $dismissible ) {
|
||||
$this->dismissible = $dismissible;
|
||||
}
|
||||
|
||||
public function set_exclude_from_pages( array $pages ) {
|
||||
$this->exclude_from_pages = $pages;
|
||||
}
|
||||
|
||||
public function set_hide_if_notice_exists( $notice_id, $notice_group = null ) {
|
||||
$this->hide_if_notice_exists = array(
|
||||
'id' => $notice_id,
|
||||
'group' => $notice_group,
|
||||
);
|
||||
}
|
||||
|
||||
public function get_hide_if_notice_exists() {
|
||||
return $this->hide_if_notice_exists;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $hideable
|
||||
*/
|
||||
public function set_hideable( $hideable ) {
|
||||
$this->hideable = $hideable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $collapsable
|
||||
*/
|
||||
public function set_collapsable( $collapsable ) {
|
||||
$this->collapsable = $collapsable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $action
|
||||
*/
|
||||
public function set_nonce_action( $action ) {
|
||||
$this->nonce_action = $action;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $collapsed_text
|
||||
*/
|
||||
public function set_collapsed_text( $collapsed_text ) {
|
||||
$this->collapsed_text = $collapsed_text;
|
||||
}
|
||||
|
||||
public function set_restrict_to_pages( array $pages ) {
|
||||
$this->restrict_to_pages = $pages;
|
||||
}
|
||||
|
||||
public function reset_dismiss() {
|
||||
$this->dismiss_reset = true;
|
||||
}
|
||||
|
||||
public function must_reset_dismiss() {
|
||||
return $this->dismiss_reset;
|
||||
}
|
||||
|
||||
public function is_different( WPML_Notice $other_notice ) {
|
||||
return serialize( $this ) !== serialize( $other_notice );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set notice to only display once.
|
||||
*
|
||||
* @param bool $flash
|
||||
*
|
||||
* @return WPML_Notice
|
||||
* @since 4.1.0
|
||||
*/
|
||||
public function set_flash( $flash = true ) {
|
||||
$this->flash = (bool) $flash;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @since 4.1.0
|
||||
*/
|
||||
public function is_flash() {
|
||||
return $this->flash;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function should_be_text_only() {
|
||||
return $this->text_only;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $text_only
|
||||
*/
|
||||
public function set_text_only( $text_only ) {
|
||||
$this->text_only = $text_only;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|string $id
|
||||
* @param string $text
|
||||
* @param string $group
|
||||
*
|
||||
* @return WPML_Notice
|
||||
*/
|
||||
public static function make( $id, $text, $group = 'default' ) {
|
||||
return new WPML_Notice( $id, $text, $group );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,488 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @author OnTheGo Systems
|
||||
*/
|
||||
class WPML_Notices {
|
||||
|
||||
const NOTICES_OPTION_KEY = 'wpml_notices';
|
||||
const DISMISSED_OPTION_KEY = '_wpml_dismissed_notices';
|
||||
const USER_DISMISSED_KEY = '_wpml_user_dismissed_notices';
|
||||
const NONCE_NAME = 'wpml-notices';
|
||||
const DEFAULT_GROUP = 'default';
|
||||
|
||||
private $notice_render;
|
||||
/**
|
||||
* @var array<string,array<\WPML_Notice>>
|
||||
*/
|
||||
private $notices;
|
||||
/**
|
||||
* @var array<string,array<int>>
|
||||
*/
|
||||
private $notices_to_remove = array();
|
||||
private $dismissed;
|
||||
private $user_dismissed;
|
||||
private $original_notices_md5;
|
||||
|
||||
/**
|
||||
* WPML_Notices constructor.
|
||||
*
|
||||
* @param WPML_Notice_Render $notice_render
|
||||
*/
|
||||
public function __construct( WPML_Notice_Render $notice_render ) {
|
||||
$this->notice_render = $notice_render;
|
||||
$this->notices = $this->filter_invalid_notices( $this->get_all_notices() );
|
||||
$this->dismissed = $this->get_all_dismissed();
|
||||
$this->original_notices_md5 = md5( maybe_serialize( $this->notices ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function count() {
|
||||
$all_notices = $this->get_all_notices();
|
||||
$count = 0;
|
||||
foreach ( $all_notices as $group => $group_notices ) {
|
||||
$count += count( $group_notices );
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function get_all_notices() {
|
||||
$all_notices = get_option( self::NOTICES_OPTION_KEY );
|
||||
if ( ! is_array( $all_notices ) ) {
|
||||
$all_notices = array();
|
||||
}
|
||||
return $all_notices;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private function get_all_dismissed() {
|
||||
$dismissed = get_option( self::DISMISSED_OPTION_KEY );
|
||||
if ( ! is_array( $dismissed ) ) {
|
||||
$dismissed = array();
|
||||
}
|
||||
return $dismissed;
|
||||
}
|
||||
|
||||
private function init_all_user_dismissed() {
|
||||
if ( null === $this->user_dismissed ) {
|
||||
$this->user_dismissed = get_user_meta( get_current_user_id(), self::USER_DISMISSED_KEY, true );
|
||||
|
||||
if ( ! is_array( $this->user_dismissed ) ) {
|
||||
$this->user_dismissed = array();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $id
|
||||
* @param string $group
|
||||
*
|
||||
* @return null|WPML_Notice
|
||||
*/
|
||||
public function get_notice( $id, $group = 'default' ) {
|
||||
$notice = null;
|
||||
|
||||
if ( isset( $this->notices[ $group ][ $id ] ) ) {
|
||||
$notice = $this->notices[ $group ][ $id ];
|
||||
}
|
||||
|
||||
return $notice;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $id
|
||||
* @param string $text
|
||||
* @param string $group
|
||||
*
|
||||
* @return WPML_Notice
|
||||
*/
|
||||
public function create_notice( $id, $text, $group = 'default' ) {
|
||||
return new WPML_Notice( $id, $text, $group );
|
||||
}
|
||||
|
||||
public function add_notice( WPML_Notice $notice, $force_update = false ) {
|
||||
$existing_notice = $this->notice_exists( $notice ) ? $this->notices[ $notice->get_group() ][ $notice->get_id() ] : null;
|
||||
|
||||
$new_notice_is_different = null === $existing_notice || $notice->is_different( $existing_notice );
|
||||
|
||||
if ( $notice->must_reset_dismiss() && $this->is_notice_dismissed( $notice ) ) {
|
||||
$this->undismiss_notice( $notice );
|
||||
}
|
||||
|
||||
if ( ! $existing_notice || ( $new_notice_is_different || $force_update ) ) {
|
||||
$this->notices[ $notice->get_group() ][ $notice->get_id() ] = $notice;
|
||||
$this->save_notices();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $id
|
||||
* @param string $text
|
||||
* @param string $group
|
||||
*
|
||||
* @return WPML_Notice
|
||||
*/
|
||||
public function get_new_notice( $id, $text, $group = 'default' ) {
|
||||
return new WPML_Notice( $id, $text, $group );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $text
|
||||
* @param string $url
|
||||
* @param bool $dismiss
|
||||
* @param bool $hide
|
||||
* @param bool $display_as_button
|
||||
*
|
||||
* @return WPML_Notice_Action
|
||||
*/
|
||||
public function get_new_notice_action( $text, $url = '#', $dismiss = false, $hide = false, $display_as_button = false ) {
|
||||
return new WPML_Notice_Action( $text, $url, $dismiss, $hide, $display_as_button );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WPML_Notice $notice
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function notice_exists( WPML_Notice $notice ) {
|
||||
$notice_id = $notice->get_id();
|
||||
$notice_group = $notice->get_group();
|
||||
|
||||
return $this->group_and_id_exist( $notice_group, $notice_id );
|
||||
}
|
||||
|
||||
private function get_notices_for_group( $group ) {
|
||||
if ( array_key_exists( $group, $this->notices ) ) {
|
||||
return $this->notices[ $group ];
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
private function save_notices() {
|
||||
$this->remove_notices();
|
||||
if ( ! has_action( 'shutdown', array( $this, 'save_to_option' ) ) ) {
|
||||
add_action( 'shutdown', array( $this, 'save_to_option' ), 1000 );
|
||||
}
|
||||
}
|
||||
|
||||
public function save_to_option() {
|
||||
if ( $this->original_notices_md5 !== md5( maybe_serialize( $this->notices ) ) ) {
|
||||
update_option( self::NOTICES_OPTION_KEY, $this->notices, false );
|
||||
}
|
||||
}
|
||||
|
||||
private function save_dismissed() {
|
||||
update_user_meta( get_current_user_id(), self::USER_DISMISSED_KEY, $this->user_dismissed );
|
||||
update_option( self::DISMISSED_OPTION_KEY, $this->dismissed, false );
|
||||
}
|
||||
|
||||
public function remove_notices() {
|
||||
if ( $this->notices_to_remove ) {
|
||||
foreach ( $this->notices_to_remove as $group => &$group_notices ) {
|
||||
foreach ( $group_notices as $id ) {
|
||||
if ( array_key_exists( $group, $this->notices ) && array_key_exists( $id, $this->notices[ $group ] ) ) {
|
||||
unset( $this->notices[ $group ][ $id ] );
|
||||
$group_notices = array_diff( $this->notices_to_remove[ $group ], array( $id ) );
|
||||
}
|
||||
}
|
||||
if ( array_key_exists( $group, $this->notices_to_remove ) && ! $this->notices_to_remove[ $group ] ) {
|
||||
unset( $this->notices_to_remove[ $group ] );
|
||||
}
|
||||
if ( array_key_exists( $group, $this->notices ) && ! $this->notices[ $group ] ) {
|
||||
unset( $this->notices[ $group ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function admin_enqueue_scripts() {
|
||||
if ( WPML_Block_Editor_Helper::is_edit_post() ) {
|
||||
wp_enqueue_script(
|
||||
'block-editor-notices',
|
||||
ICL_PLUGIN_URL . '/dist/js/blockEditorNotices/app.js',
|
||||
array( 'wp-edit-post' ),
|
||||
ICL_SITEPRESS_VERSION,
|
||||
true
|
||||
);
|
||||
}
|
||||
if ( $this->must_display_notices() ) {
|
||||
wp_enqueue_style( 'otgs-notices', ICL_PLUGIN_URL . '/res/css/otgs-notices.css', array( 'sitepress-style' ) );
|
||||
wp_enqueue_script(
|
||||
'otgs-notices',
|
||||
ICL_PLUGIN_URL . '/res/js/otgs-notices.js',
|
||||
array( 'underscore' ),
|
||||
ICL_SITEPRESS_VERSION,
|
||||
true
|
||||
);
|
||||
|
||||
do_action( 'wpml-notices-scripts-enqueued' );
|
||||
}
|
||||
}
|
||||
|
||||
private function must_display_notices() {
|
||||
if ( $this->notices ) {
|
||||
foreach ( $this->notices as $group => $notices ) {
|
||||
foreach ( $notices as $notice ) {
|
||||
if ( $this->notice_render->must_display_notice( $notice ) && ! $this->must_hide_if_notice_exists( $notice ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function must_hide_if_notice_exists( WPML_Notice $notice ) {
|
||||
$hide_if_notice_exists = $notice->get_hide_if_notice_exists();
|
||||
if ( $hide_if_notice_exists ) {
|
||||
$other_notice = $this->get_notice( $hide_if_notice_exists['id'], $hide_if_notice_exists['group'] );
|
||||
|
||||
return $other_notice;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function admin_notices() {
|
||||
if ( $this->notices && $this->must_display_notices() ) {
|
||||
foreach ( $this->notices as $group => $notices ) {
|
||||
foreach ( $notices as $notice ) {
|
||||
if ( $notice instanceof WPML_Notice && ! $this->is_notice_dismissed( $notice ) ) {
|
||||
$this->notice_render->render( $notice );
|
||||
if ( $notice->is_flash() ) {
|
||||
$this->remove_notice( $notice->get_group(), $notice->get_id() );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function wp_ajax_hide_notice() {
|
||||
list( $notice_group, $notice_id ) = $this->parse_group_and_id();
|
||||
|
||||
if ( ! $notice_group ) {
|
||||
$notice_group = self::DEFAULT_GROUP;
|
||||
}
|
||||
|
||||
if ( $this->has_valid_nonce() && $this->group_and_id_exist( $notice_group, $notice_id ) ) {
|
||||
$this->remove_notice( $notice_group, $notice_id );
|
||||
wp_send_json_success( true );
|
||||
}
|
||||
|
||||
wp_send_json_error( __( 'Notice does not exists.', 'sitepress' ) );
|
||||
}
|
||||
|
||||
public function wp_ajax_dismiss_notice() {
|
||||
list( $notice_group, $notice_id ) = $this->parse_group_and_id();
|
||||
|
||||
if ( $this->has_valid_nonce() && $this->dismiss_notice_by_id( $notice_id, $notice_group ) ) {
|
||||
wp_send_json_success( true );
|
||||
}
|
||||
|
||||
wp_send_json_error( __( 'Notice does not exist.', 'sitepress' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $notice_id
|
||||
* @param null|string $notice_group
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function dismiss_notice_by_id( $notice_id, $notice_group = null ) {
|
||||
if ( ! $notice_group ) {
|
||||
$notice_group = self::DEFAULT_GROUP;
|
||||
}
|
||||
|
||||
if ( $this->group_and_id_exist( $notice_group, $notice_id ) ) {
|
||||
$notice = $this->get_notice( $notice_id, $notice_group );
|
||||
|
||||
if ( $notice ) {
|
||||
$this->dismiss_notice( $notice );
|
||||
$this->remove_notice( $notice_group, $notice_id );
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function wp_ajax_dismiss_group() {
|
||||
list( $notice_group ) = $this->parse_group_and_id();
|
||||
|
||||
if ( $notice_group && $this->has_valid_nonce() && $this->dismiss_notice_group( $notice_group ) ) {
|
||||
wp_send_json_success( true );
|
||||
}
|
||||
wp_send_json_error( __( 'Group does not exist.', 'sitepress' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null|string $notice_group
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function dismiss_notice_group( $notice_group ) {
|
||||
if ( $notice_group ) {
|
||||
$notices = $this->get_notices_for_group( $notice_group );
|
||||
|
||||
if ( $notices ) {
|
||||
/** @var WPML_Notice $notice */
|
||||
foreach ( $notices as $notice ) {
|
||||
$this->dismiss_notice( $notice, false );
|
||||
$this->remove_notice( $notice_group, $notice->get_id() );
|
||||
}
|
||||
|
||||
$this->save_dismissed();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private function parse_group_and_id() {
|
||||
$group = isset( $_POST['group'] ) ? sanitize_text_field( $_POST['group'] ) : false;
|
||||
$id = isset( $_POST['id'] ) ? sanitize_text_field( $_POST['id'] ) : false;
|
||||
|
||||
return array( $group, $id );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return false|int
|
||||
*/
|
||||
private function has_valid_nonce() {
|
||||
$nonce = isset( $_POST['nonce'] ) ? $_POST['nonce'] : null;
|
||||
return wp_verify_nonce( $nonce, self::NONCE_NAME );
|
||||
}
|
||||
|
||||
private function group_and_id_exist( $group, $id ) {
|
||||
return array_key_exists( $group, $this->notices ) && array_key_exists( $id, $this->notices[ $group ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $notice_group
|
||||
* @param string|int $notice_id
|
||||
*/
|
||||
public function remove_notice( $notice_group, $notice_id ) {
|
||||
$this->notices_to_remove[ $notice_group ][] = $notice_id;
|
||||
$this->notices_to_remove[ $notice_group ] = array_unique( $this->notices_to_remove[ $notice_group ] );
|
||||
$this->save_notices();
|
||||
|
||||
if ( ! is_array( $this->notices_to_remove ) ) {
|
||||
$this->notices_to_remove = array();
|
||||
}
|
||||
|
||||
if ( isset( $this->notices_to_remove[ $notice_group ] ) ) {
|
||||
foreach ( $this->notices_to_remove[ $notice_group ] as $key => $notice ) {
|
||||
if ( $notice === $notice_id ) {
|
||||
unset( $this->notices_to_remove[ $notice_group ][ $key ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $notice_group
|
||||
*/
|
||||
public function remove_notice_group( $notice_group ) {
|
||||
$notices = $this->get_notices_for_group( $notice_group );
|
||||
$notices_ids = array_keys( $notices );
|
||||
foreach ( $notices_ids as $notices_id ) {
|
||||
$this->remove_notice( $notice_group, $notices_id );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WPML_Notice $notice
|
||||
* @param bool $persist
|
||||
*/
|
||||
public function dismiss_notice( WPML_Notice $notice, $persist = true ) {
|
||||
if ( method_exists( $notice, 'is_user_restricted' ) && $notice->is_user_restricted() ) {
|
||||
$this->init_all_user_dismissed();
|
||||
$this->user_dismissed[ $notice->get_group() ][ $notice->get_id() ] = md5( $notice->get_text() );
|
||||
} else {
|
||||
$this->dismissed[ $notice->get_group() ][ $notice->get_id() ] = md5( $notice->get_text() );
|
||||
}
|
||||
|
||||
if ( $persist ) {
|
||||
$this->save_dismissed();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WPML_Notice $notice
|
||||
* @param bool $persist
|
||||
*/
|
||||
public function undismiss_notice( WPML_Notice $notice, $persist = true ) {
|
||||
if ( method_exists( $notice, 'is_user_restricted' ) && $notice->is_user_restricted() ) {
|
||||
$this->init_all_user_dismissed();
|
||||
unset( $this->user_dismissed[ $notice->get_group() ][ $notice->get_id() ] );
|
||||
} else {
|
||||
unset( $this->dismissed[ $notice->get_group() ][ $notice->get_id() ] );
|
||||
}
|
||||
|
||||
if ( $persist ) {
|
||||
$this->save_dismissed();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WPML_Notice $notice
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_notice_dismissed( WPML_Notice $notice ) {
|
||||
$group = $notice->get_group();
|
||||
$id = $notice->get_id();
|
||||
|
||||
$is_dismissed = isset( $this->dismissed[ $group ][ $id ] ) && $this->dismissed[ $group ][ $id ];
|
||||
|
||||
if ( ! $is_dismissed ) {
|
||||
$this->init_all_user_dismissed();
|
||||
$is_dismissed = isset( $this->user_dismissed[ $group ][ $id ] ) && $this->user_dismissed[ $group ][ $id ];
|
||||
}
|
||||
|
||||
if ( $is_dismissed && method_exists( $notice, 'can_be_dismissed_for_different_text' )
|
||||
&& ! $notice->can_be_dismissed_for_different_text() ) {
|
||||
$is_dismissed = md5( $notice->get_text() ) === $this->dismissed[ $group ][ $id ];
|
||||
}
|
||||
|
||||
return $is_dismissed;
|
||||
}
|
||||
|
||||
public function init_hooks() {
|
||||
add_action( 'admin_notices', array( $this, 'admin_notices' ) );
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ), \WPML_Admin_Scripts_Setup::PRIORITY_ENQUEUE_SCRIPTS + 1 );
|
||||
add_action( 'wp_ajax_otgs-hide-notice', array( $this, 'wp_ajax_hide_notice' ) );
|
||||
add_action( 'wp_ajax_otgs-dismiss-notice', array( $this, 'wp_ajax_dismiss_notice' ) );
|
||||
add_action( 'wp_ajax_otgs-dismiss-group', array( $this, 'wp_ajax_dismiss_group' ) );
|
||||
add_action( 'otgs_add_notice', array( $this, 'add_notice' ), 10, 2 );
|
||||
add_action( 'otgs_remove_notice', array( $this, 'remove_notice' ), 10, 2 );
|
||||
add_action( 'otgs_remove_notice_group', array( $this, 'remove_notice_group' ), 10, 1 );
|
||||
}
|
||||
|
||||
private function filter_invalid_notices( $notices ) {
|
||||
foreach ( $notices as $group => $notices_in_group ) {
|
||||
foreach ( $notices_in_group as $index => $notice ) {
|
||||
if ( ! $notice instanceof WPML_Notice ) {
|
||||
unset( $notices[ $group ][ $index ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
return $notices;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
class WPML_Notice_Show_On_Dashboard_And_WPML_Pages {
|
||||
|
||||
public static function is_on_page() {
|
||||
if ( function_exists( 'get_current_screen' ) ) {
|
||||
$screen = get_current_screen();
|
||||
if ( 'dashboard' === $screen->id ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
$current_page = array_key_exists( 'page', $_GET ) ? $_GET['page'] : null;
|
||||
|
||||
foreach ( array( 'sitepress-multilingual-cms', 'wpml-translation-management' ) as $page ) {
|
||||
if ( strpos( $current_page, $page ) === 0 ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
class WPML_All_Translation_Jobs_Migration_Notice extends WPML_Translation_Jobs_Migration_Notice {
|
||||
|
||||
/**
|
||||
* It gets the definition of the notice's content.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_model() {
|
||||
return array(
|
||||
'strings' => array(
|
||||
'title' => __( 'Problem receiving translation jobs?', 'wpml-translation-management' ),
|
||||
'description' => __( 'WPML needs to update its table of translation jobs, so that your site can continue receiving completed translations. This process will take a few minutes and does not modify content or translations in your site.', 'wpml-translation-management' ),
|
||||
'button' => __( 'Start update', 'wpml-translation-management' ),
|
||||
/* translators: this is shown between two number: processed items and total number of items to process */
|
||||
'of' => __( 'of', 'wpml-translation-management' ),
|
||||
'jobs_migrated' => __( 'jobs fixed', 'wpml-translation-management' ),
|
||||
'communicationError' => __(
|
||||
'The communication error with Translation Proxy has appeared. Please try later.',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
),
|
||||
'nonce' => wp_nonce_field(
|
||||
WPML_Translation_Jobs_Migration_Ajax::ACTION,
|
||||
WPML_Translation_Jobs_Migration_Ajax::ACTION,
|
||||
false,
|
||||
false
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* It gets the ID of the notice.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_notice_id() {
|
||||
return 'all-translation-jobs-migration';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
class WPML_TM_Jobs_Migration_State {
|
||||
const MIGRATION_DONE_KEY = 'wpml-tm-translation-jobs-migration';
|
||||
const FIXING_MIGRATION_STATE_KEY = 'wpml-tm-all-translation-jobs-migration';
|
||||
const MIGRATION_SKIPPED = 'wpml-tm-translation-jobs-migration-skipped';
|
||||
|
||||
/**
|
||||
* The fixing migration has already been run but it contained errors
|
||||
*/
|
||||
const FIRST_MIGRATION_FIX_HAS_RUN = 1;
|
||||
/**
|
||||
* We've already cleaned logs of the first fixing migration and are ready to run another this time flawless version
|
||||
*/
|
||||
const READY_TO_RUN_SECOND_MIGRATION_FIX = 2;
|
||||
/**
|
||||
* The final flawless fixing migration has been run
|
||||
*/
|
||||
const SECOND_MIGRATION_FIX_HAS_RUN = 3;
|
||||
|
||||
/**
|
||||
* Checks if the original migration has been finished
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_migrated() {
|
||||
return (bool) get_option( self::MIGRATION_DONE_KEY );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the fixing migration ( migration which fixes the flaws of the original migration ) has been run
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_fixing_migration_done() {
|
||||
$option = (int) get_option( self::FIXING_MIGRATION_STATE_KEY );
|
||||
if ( $option === self::FIRST_MIGRATION_FIX_HAS_RUN ) { // clear previous log
|
||||
update_option( WPML_Translation_Jobs_Migration::MIGRATION_FIX_LOG_KEY, array(), false );
|
||||
update_option( self::FIXING_MIGRATION_STATE_KEY, self::READY_TO_RUN_SECOND_MIGRATION_FIX, true );
|
||||
}
|
||||
|
||||
return (int) get_option( self::FIXING_MIGRATION_STATE_KEY ) === self::SECOND_MIGRATION_FIX_HAS_RUN;
|
||||
}
|
||||
|
||||
public function mark_migration_as_done() {
|
||||
update_option( self::MIGRATION_DONE_KEY, 1, true );
|
||||
|
||||
// a user has never run the original migration so it does not need the fixing migration
|
||||
$this->mark_fixing_migration_as_done();
|
||||
}
|
||||
|
||||
public function mark_fixing_migration_as_done() {
|
||||
update_option( self::FIXING_MIGRATION_STATE_KEY, self::SECOND_MIGRATION_FIX_HAS_RUN, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $flag
|
||||
*/
|
||||
public function skip_migration( $flag = true ) {
|
||||
update_option( self::MIGRATION_SKIPPED, $flag, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function is_skipped() {
|
||||
return (bool) get_option( self::MIGRATION_SKIPPED );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* The class adds the hook which is triggered in the moment of Translation Service authorization.
|
||||
* It checks if the migration has been skipped due to lack of activated service and if so, it turns on the migration.
|
||||
*/
|
||||
class WPML_TM_Restore_Skipped_Migration implements IWPML_Action {
|
||||
/** @var WPML_TM_Jobs_Migration_State */
|
||||
private $migration_state;
|
||||
|
||||
/**
|
||||
* @param WPML_TM_Jobs_Migration_State $migration_state
|
||||
*/
|
||||
public function __construct( WPML_TM_Jobs_Migration_State $migration_state ) {
|
||||
$this->migration_state = $migration_state;
|
||||
}
|
||||
|
||||
|
||||
public function add_hooks() {
|
||||
add_action( 'wpml_tm_translation_service_authorized', array( $this, 'restore' ) );
|
||||
}
|
||||
|
||||
public function restore() {
|
||||
if ( $this->migration_state->is_skipped() ) {
|
||||
$this->migration_state->skip_migration( false );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
class WPML_TM_Translation_Jobs_Fix_Summary_Factory implements IWPML_Backend_Action_Loader {
|
||||
/**
|
||||
* @return WPML_TM_Translation_Jobs_Fix_Summary
|
||||
*/
|
||||
public function create() {
|
||||
$template_service = new WPML_Twig_Template_Loader( array( WPML_TM_PATH . '/templates/translation-jobs-migration/' ) );
|
||||
|
||||
return new WPML_TM_Translation_Jobs_Fix_Summary(
|
||||
new WPML_TM_Translation_Jobs_Fix_Summary_Notice( wpml_get_admin_notices(), $template_service->get_template() ),
|
||||
new WPML_TM_Jobs_Migration_State()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
class WPML_TM_Translation_Jobs_Fix_Summary_Notice extends WPML_Translation_Jobs_Migration_Notice {
|
||||
|
||||
protected function get_model() {
|
||||
$tm_url = '<a href="' . admin_url( 'admin.php?page=' . WPML_TM_FOLDER . '/menu/main.php' ) . '">' . __( 'Translation Dashboard', 'sitepress' ) . '</a>';
|
||||
$description = sprintf( __( 'WPML completed updating the translations history. Next, please visit the %s and click on the button to "Check status and get translations".', 'wpml-translation-management' ), $tm_url );
|
||||
|
||||
return array(
|
||||
'strings' => array(
|
||||
'title' => __( 'Problem receiving translation jobs?', 'sitepress' ),
|
||||
'description' => $description,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
protected function get_notice_id() {
|
||||
return 'translation-jobs-migration-fix-summary';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
class WPML_TM_Translation_Jobs_Fix_Summary {
|
||||
|
||||
const INVALID_JOBS_SYNCED_KEY = 'wpml_tm_migration_invalid_jobs_already_synced';
|
||||
|
||||
/** @var WPML_TM_Translation_Jobs_Fix_Summary_Notice */
|
||||
private $notice;
|
||||
|
||||
/** @var WPML_TM_Jobs_Migration_State */
|
||||
private $migration_state;
|
||||
|
||||
public function __construct(
|
||||
WPML_TM_Translation_Jobs_Fix_Summary_Notice $notice,
|
||||
WPML_TM_Jobs_Migration_State $migration_state
|
||||
) {
|
||||
$this->notice = $notice;
|
||||
$this->migration_state = $migration_state;
|
||||
}
|
||||
|
||||
public function add_hooks() {
|
||||
add_action( 'init', array( $this, 'display_summary' ) );
|
||||
add_action(
|
||||
'wp_ajax_' . WPML_TP_Sync_Ajax_Handler::AJAX_ACTION,
|
||||
array(
|
||||
$this,
|
||||
'mark_invalid_jobs_as_synced',
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function display_summary() {
|
||||
if ( $this->should_display_summary_notice() ) {
|
||||
$this->notice->add_notice();
|
||||
} elseif ( $this->notice->exists() ) {
|
||||
$this->notice->remove_notice();
|
||||
}
|
||||
}
|
||||
|
||||
private function should_display_summary_notice() {
|
||||
if ( ! $this->migration_state->is_fixing_migration_done() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$jobs_with_new_status = get_option( WPML_Translation_Jobs_Migration::MIGRATION_FIX_LOG_KEY );
|
||||
$jobs_with_new_status = isset( $jobs_with_new_status['status_changed'] ) ? $jobs_with_new_status['status_changed'] : null;
|
||||
$jobs_already_synced = (int) get_option( self::INVALID_JOBS_SYNCED_KEY ) > 1;
|
||||
|
||||
return $jobs_with_new_status && ! $jobs_already_synced;
|
||||
}
|
||||
|
||||
public function mark_invalid_jobs_as_synced() {
|
||||
update_option( self::INVALID_JOBS_SYNCED_KEY, 2 );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
class WPML_TM_Troubleshooting_Fix_Translation_Jobs_TP_ID_Factory implements IWPML_Backend_Action_Loader {
|
||||
|
||||
public function create() {
|
||||
global $wpml_post_translations, $wpml_term_translations, $wpdb;
|
||||
|
||||
$jobs_migration_repository = new WPML_Translation_Jobs_Migration_Repository( wpml_tm_get_jobs_repository(), true );
|
||||
$job_factory = wpml_tm_load_job_factory();
|
||||
$wpml_tm_records = new WPML_TM_Records( $wpdb, $wpml_post_translations, $wpml_term_translations );
|
||||
$cms_id_helper = new WPML_TM_CMS_ID( $wpml_tm_records, $job_factory );
|
||||
$jobs_migration = new WPML_Translation_Jobs_Migration( $jobs_migration_repository, $cms_id_helper, $wpdb, wpml_tm_get_tp_jobs_api() );
|
||||
|
||||
return new WPML_TM_Troubleshooting_Fix_Translation_Jobs_TP_ID( $jobs_migration, wpml_tm_get_jobs_repository() );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
class WPML_TM_Troubleshooting_Fix_Translation_Jobs_TP_ID {
|
||||
|
||||
const AJAX_ACTION = 'wpml-fix-translation-jobs-tp-id';
|
||||
|
||||
private $jobs_migration;
|
||||
private $jobs_repository;
|
||||
|
||||
public function __construct( WPML_Translation_Jobs_Migration $jobs_migration, WPML_TM_Jobs_Repository $jobs_repository ) {
|
||||
$this->jobs_migration = $jobs_migration;
|
||||
$this->jobs_repository = $jobs_repository;
|
||||
}
|
||||
|
||||
public function add_hooks() {
|
||||
add_action(
|
||||
'wpml_troubleshooting_after_fix_element_type_collation',
|
||||
array(
|
||||
$this,
|
||||
'render_troubleshooting_section',
|
||||
)
|
||||
);
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
|
||||
add_action( 'wp_ajax_' . self::AJAX_ACTION, array( $this, 'fix_tp_id_ajax' ) );
|
||||
|
||||
}
|
||||
|
||||
public function fix_tp_id_ajax() {
|
||||
if ( isset( $_POST['nonce'] ) && wp_verify_nonce( $_POST['nonce'], self::AJAX_ACTION ) ) {
|
||||
$job_ids = isset( $_POST['job_ids'] ) ? array_map( 'intval', explode( ',', filter_var( $_POST['job_ids'], FILTER_SANITIZE_FULL_SPECIAL_CHARS ) ) ) : array();
|
||||
$jobs = array();
|
||||
|
||||
foreach ( $job_ids as $job_id ) {
|
||||
if ( ! $job_id ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$params = new WPML_TM_Jobs_Search_Params();
|
||||
$params->set_scope( WPML_TM_Jobs_Search_Params::SCOPE_REMOTE );
|
||||
$params->set_job_types( array( WPML_TM_Job_Entity::POST_TYPE, WPML_TM_Job_Entity::PACKAGE_TYPE ) );
|
||||
$params->set_local_job_id( $job_id );
|
||||
|
||||
$jobs[] = current( $this->jobs_repository->get( $params )->getIterator()->getArrayCopy() );
|
||||
}
|
||||
|
||||
if ( $jobs ) {
|
||||
$this->jobs_migration->migrate_jobs( $jobs, true );
|
||||
}
|
||||
|
||||
wp_send_json_success();
|
||||
} else {
|
||||
wp_send_json_error();
|
||||
}
|
||||
}
|
||||
|
||||
public function enqueue_scripts( $hook ) {
|
||||
if ( WPML_PLUGIN_FOLDER . '/menu/troubleshooting.php' === $hook ) {
|
||||
wp_enqueue_script( 'wpml-fix-tp-id', WPML_TM_URL . '/res/js/fix-tp-id.js', array( 'jquery' ), WPML_TM_VERSION );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function render_troubleshooting_section() {
|
||||
?>
|
||||
<p>
|
||||
<input id="wpml_fix_tp_id_text" type="text" value=""/><input id="wpml_fix_tp_id_btn" type="button" class="button-secondary" value="<?php esc_attr_e( 'Fix WPML Translation Jobs "tp_id" field', 'sitepress' ); ?>"/><br/>
|
||||
<?php wp_nonce_field( self::AJAX_ACTION, 'wpml-fix-tp-id-nonce' ); ?>
|
||||
<small style="margin-left:10px;"><?php esc_attr_e( 'Fixes the "tp_id" field of WPML ranslation jobs and set the status to "in progress" (it requires manual action to re-sync translation status + download translations). It accepts comma separated values of translation job IDs (rid).', 'sitepress' ); ?></small>
|
||||
</p>
|
||||
<?php
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
class WPML_Translation_Jobs_Fixing_Migration_Ajax {
|
||||
|
||||
const ACTION = 'wpml_translation_jobs_migration';
|
||||
const JOBS_MIGRATED_PER_REQUEST = 100;
|
||||
const PAGINATION_OPTION = 'wpml_translation_jobs_migration_processed';
|
||||
|
||||
/** @var WPML_Translation_Jobs_Migration */
|
||||
private $jobs_migration;
|
||||
|
||||
/** @var WPML_Translation_Jobs_Migration_Repository */
|
||||
private $jobs_repository;
|
||||
|
||||
/** @var WPML_TM_Jobs_Migration_State */
|
||||
private $migration_state;
|
||||
|
||||
|
||||
public function __construct(
|
||||
WPML_Translation_Jobs_Migration $jobs_migration,
|
||||
WPML_Translation_Jobs_Migration_Repository $jobs_repository,
|
||||
WPML_TM_Jobs_Migration_State $migration_state
|
||||
) {
|
||||
$this->jobs_migration = $jobs_migration;
|
||||
$this->jobs_repository = $jobs_repository;
|
||||
$this->migration_state = $migration_state;
|
||||
}
|
||||
|
||||
public function run_migration() {
|
||||
if ( ! $this->is_valid_request() ) {
|
||||
wp_send_json_error();
|
||||
}
|
||||
|
||||
$jobs = $this->jobs_repository->get();
|
||||
$total_jobs = count( $jobs );
|
||||
|
||||
$offset = $this->get_already_processed();
|
||||
|
||||
if ( $offset < $total_jobs ) {
|
||||
$jobs_chunk = array_slice( $jobs, $offset, self::JOBS_MIGRATED_PER_REQUEST );
|
||||
|
||||
try {
|
||||
$this->jobs_migration->migrate_jobs( $jobs_chunk, true );
|
||||
} catch ( Exception $e ) {
|
||||
wp_send_json_error( $e->getMessage(), 500 );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$done = $total_jobs <= $offset + self::JOBS_MIGRATED_PER_REQUEST;
|
||||
$jobs_chunk_total = count( $jobs_chunk );
|
||||
|
||||
update_option( self::PAGINATION_OPTION, $offset + self::JOBS_MIGRATED_PER_REQUEST );
|
||||
} else {
|
||||
$done = true;
|
||||
$jobs_chunk_total = 0;
|
||||
}
|
||||
|
||||
$result = array(
|
||||
'totalJobs' => $total_jobs,
|
||||
'jobsMigrated' => $jobs_chunk_total,
|
||||
'done' => $done,
|
||||
);
|
||||
|
||||
if ( $done ) {
|
||||
$this->migration_state->mark_fixing_migration_as_done();
|
||||
delete_option( self::PAGINATION_OPTION );
|
||||
}
|
||||
|
||||
wp_send_json_success( $result );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
private function is_valid_request() {
|
||||
return wp_verify_nonce( $_POST['nonce'], self::ACTION );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
private function get_already_processed() {
|
||||
return (int) get_option( self::PAGINATION_OPTION, 0 );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
class WPML_Translation_Jobs_Migration_Ajax {
|
||||
|
||||
const ACTION = 'wpml_translation_jobs_migration';
|
||||
|
||||
const JOBS_MIGRATED_PER_REQUEST = 100;
|
||||
|
||||
/** @var WPML_Translation_Jobs_Migration */
|
||||
private $jobs_migration;
|
||||
|
||||
/** @var WPML_Translation_Jobs_Migration_Repository */
|
||||
private $jobs_repository;
|
||||
|
||||
/** @var WPML_TM_Jobs_Migration_State */
|
||||
private $migration_state;
|
||||
|
||||
public function __construct(
|
||||
WPML_Translation_Jobs_Migration $jobs_migration,
|
||||
WPML_Translation_Jobs_Migration_Repository $jobs_repository,
|
||||
WPML_TM_Jobs_Migration_State $migration_state
|
||||
) {
|
||||
$this->jobs_migration = $jobs_migration;
|
||||
$this->jobs_repository = $jobs_repository;
|
||||
$this->migration_state = $migration_state;
|
||||
}
|
||||
|
||||
public function run_migration() {
|
||||
if ( ! $this->is_valid_request() ) {
|
||||
wp_send_json_error();
|
||||
return;
|
||||
}
|
||||
|
||||
$jobs = $this->jobs_repository->get();
|
||||
|
||||
$jobs_chunk = array_slice( $jobs, 0, self::JOBS_MIGRATED_PER_REQUEST );
|
||||
try {
|
||||
$this->jobs_migration->migrate_jobs( $jobs_chunk );
|
||||
} catch ( Exception $e ) {
|
||||
wp_send_json_error( $e->getMessage(), 500 );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$done = count( $jobs ) === count( $jobs_chunk );
|
||||
$total_jobs = count( $jobs );
|
||||
$jobs_chunk_total = count( $jobs_chunk );
|
||||
|
||||
$result = array(
|
||||
'totalJobs' => $total_jobs,
|
||||
'jobsMigrated' => $jobs_chunk_total,
|
||||
'done' => $done,
|
||||
);
|
||||
|
||||
if ( $jobs_chunk_total === $total_jobs ) {
|
||||
$this->migration_state->mark_migration_as_done();
|
||||
}
|
||||
|
||||
wp_send_json_success( $result );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
private function is_valid_request() {
|
||||
return wp_verify_nonce( $_POST['nonce'], self::ACTION );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
use WPML\API\Sanitize;
|
||||
|
||||
class WPML_Translation_Jobs_Migration_Hooks_Factory implements IWPML_Backend_Action_Loader, IWPML_AJAX_Action_Loader {
|
||||
|
||||
/**
|
||||
* It creates an instance of WPML_Translation_Jobs_Migration_Notice.
|
||||
*
|
||||
* @return null|WPML_Translation_Jobs_Migration_Hooks|WPML_TM_Restore_Skipped_Migration
|
||||
*/
|
||||
public function create() {
|
||||
$fixing_migration = false;
|
||||
|
||||
$wpml_notices = wpml_get_admin_notices();
|
||||
$wpml_notices->remove_notice( WPML_Translation_Jobs_Migration_Notice::NOTICE_GROUP_ID, 'all-translation-jobs-migration' );
|
||||
$wpml_notices->remove_notice( WPML_Translation_Jobs_Migration_Notice::NOTICE_GROUP_ID, 'translation-jobs-migration' );
|
||||
|
||||
if ( ! $this->should_add_migration_hooks() ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$migration_state = new WPML_TM_Jobs_Migration_State();
|
||||
if ( $migration_state->is_skipped() ) {
|
||||
return new WPML_TM_Restore_Skipped_Migration( $migration_state );
|
||||
}
|
||||
|
||||
if ( $migration_state->is_migrated() ) {
|
||||
if ( $migration_state->is_fixing_migration_done() ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$fixing_migration = true;
|
||||
}
|
||||
|
||||
$template_service = new WPML_Twig_Template_Loader( array( WPML_TM_PATH . '/templates/translation-jobs-migration/' ) );
|
||||
|
||||
if ( $fixing_migration ) {
|
||||
$notice = new WPML_All_Translation_Jobs_Migration_Notice( $wpml_notices, $template_service->get_template() );
|
||||
} else {
|
||||
$notice = new WPML_Translation_Jobs_Missing_TP_ID_Migration_Notice( $wpml_notices, $template_service->get_template() );
|
||||
}
|
||||
|
||||
$jobs_migration_repository = new WPML_Translation_Jobs_Migration_Repository( wpml_tm_get_jobs_repository(), $fixing_migration );
|
||||
|
||||
global $wpml_post_translations, $wpml_term_translations, $wpdb;
|
||||
|
||||
$job_factory = wpml_tm_load_job_factory();
|
||||
$wpml_tm_records = new WPML_TM_Records( $wpdb, $wpml_post_translations, $wpml_term_translations );
|
||||
$cms_id_helper = new WPML_TM_CMS_ID( $wpml_tm_records, $job_factory );
|
||||
$jobs_migration = new WPML_Translation_Jobs_Migration( $jobs_migration_repository, $cms_id_helper, $wpdb, wpml_tm_get_tp_jobs_api() );
|
||||
if ( $fixing_migration ) {
|
||||
$ajax_handler = new WPML_Translation_Jobs_Fixing_Migration_Ajax(
|
||||
$jobs_migration,
|
||||
$jobs_migration_repository,
|
||||
$migration_state
|
||||
);
|
||||
} else {
|
||||
$ajax_handler = new WPML_Translation_Jobs_Migration_Ajax(
|
||||
$jobs_migration,
|
||||
$jobs_migration_repository,
|
||||
$migration_state
|
||||
);
|
||||
}
|
||||
|
||||
return new WPML_Translation_Jobs_Migration_Hooks(
|
||||
$notice,
|
||||
$ajax_handler,
|
||||
$jobs_migration_repository,
|
||||
wpml_get_upgrade_schema(),
|
||||
$migration_state
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if location is allowed to add migration hooks.
|
||||
*/
|
||||
private function should_add_migration_hooks() {
|
||||
$allowed_uris = array(
|
||||
'/.*page=sitepress-multilingual-cms.*/',
|
||||
'/.*page=wpml-string-translation.*/',
|
||||
'/.*page=tm\/menu\/main.*/',
|
||||
);
|
||||
|
||||
if ( wp_doing_ajax() ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$uri = rawurldecode( $this->get_request_uri() );
|
||||
|
||||
foreach ( $allowed_uris as $pattern ) {
|
||||
if ( preg_match( $pattern, $uri ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get request uri.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function get_request_uri() {
|
||||
if ( isset( $_SERVER['REQUEST_URI'] ) ) {
|
||||
return wp_unslash( Sanitize::stringProp( 'REQUEST_URI', $_SERVER ) );
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
class WPML_Translation_Jobs_Migration_Hooks {
|
||||
|
||||
private $notice;
|
||||
private $ajax_handler;
|
||||
|
||||
/** @var WPML_Translation_Jobs_Migration_Repository */
|
||||
private $jobs_migration_repository;
|
||||
|
||||
/** @var WPML_Upgrade_Schema $schema */
|
||||
private $schema;
|
||||
|
||||
/** @var WPML_TM_Jobs_Migration_State */
|
||||
private $migration_state;
|
||||
|
||||
public function __construct(
|
||||
WPML_Translation_Jobs_Migration_Notice $notice,
|
||||
$ajax_handler,
|
||||
WPML_Translation_Jobs_Migration_Repository $jobs_migration_repository,
|
||||
WPML_Upgrade_Schema $schema,
|
||||
WPML_TM_Jobs_Migration_State $migration_state
|
||||
) {
|
||||
$this->notice = $notice;
|
||||
$this->ajax_handler = $ajax_handler;
|
||||
$this->jobs_migration_repository = $jobs_migration_repository;
|
||||
$this->schema = $schema;
|
||||
$this->migration_state = $migration_state;
|
||||
}
|
||||
|
||||
public function add_hooks() {
|
||||
add_action( 'init', array( $this, 'add_hooks_on_init' ), PHP_INT_MAX );
|
||||
}
|
||||
|
||||
public function add_hooks_on_init() {
|
||||
if ( $this->new_columns_are_not_added_yet() ) {
|
||||
add_action( 'wpml_tm_lock_ui', array( $this, 'lock_tm_ui' ) );
|
||||
} elseif ( $this->needs_migration() ) {
|
||||
$this->notice->add_notice();
|
||||
|
||||
add_action( 'wpml_tm_lock_ui', array( $this, 'lock_tm_ui' ) );
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
|
||||
add_action(
|
||||
'wp_ajax_' . WPML_Translation_Jobs_Migration_Ajax::ACTION,
|
||||
array( $this->ajax_handler, 'run_migration' )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see
|
||||
* `WPML_TM_Add_TP_Revision_And_TS_Status_Columns_To_Core_Status`
|
||||
* `WPML_TM_Add_TP_Revision_And_TS_Status_Columns_To_Translation_Status`
|
||||
* `WPML_TM_Add_TP_ID_Column_To_Translation_Status`
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function new_columns_are_not_added_yet() {
|
||||
$has_columns = $this->schema->does_column_exist( 'icl_core_status', 'tp_revision' )
|
||||
&& $this->schema->does_column_exist( 'icl_core_status', 'ts_status' )
|
||||
&& $this->schema->does_column_exist( 'icl_translation_status', 'tp_revision' )
|
||||
&& $this->schema->does_column_exist( 'icl_translation_status', 'ts_status' )
|
||||
&& $this->schema->does_column_exist( 'icl_translation_status', 'tp_id' );
|
||||
|
||||
return ! $has_columns;
|
||||
}
|
||||
|
||||
public function enqueue_scripts() {
|
||||
wp_enqueue_script(
|
||||
'wpml-tm-translation-jobs-migration',
|
||||
WPML_TM_URL . '/dist/js/translationJobsMigration/app.js',
|
||||
array(),
|
||||
WPML_TM_VERSION
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
private function needs_migration() {
|
||||
if ( $this->jobs_migration_repository->get_count() ) {
|
||||
return ! $this->skip_migration_if_service_is_not_active();
|
||||
}
|
||||
|
||||
$this->migration_state->mark_migration_as_done();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
private function skip_migration_if_service_is_not_active() {
|
||||
if ( ! TranslationProxy::is_current_service_active_and_authenticated() ) {
|
||||
$this->migration_state->skip_migration( true );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function lock_tm_ui() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
abstract class WPML_Translation_Jobs_Migration_Notice {
|
||||
|
||||
const NOTICE_GROUP_ID = 'translation-jobs';
|
||||
const TEMPLATE = 'translation-jobs-migration.twig';
|
||||
|
||||
/**
|
||||
* The instance of \WPML_Notices.
|
||||
*
|
||||
* @var \WPML_Notices
|
||||
*/
|
||||
private $admin_notices;
|
||||
|
||||
/**
|
||||
* The instance of \IWPML_Template_Service.
|
||||
*
|
||||
* @var \IWPML_Template_Service
|
||||
*/
|
||||
private $template_service;
|
||||
|
||||
/**
|
||||
* WPML_Translation_Jobs_Migration_Notice constructor.
|
||||
*
|
||||
* @param \WPML_Notices $admin_notices An instance of \WPML_Notices.
|
||||
* @param \IWPML_Template_Service $template_service A class implementing \IWPML_Template_Service.
|
||||
*/
|
||||
public function __construct( WPML_Notices $admin_notices, IWPML_Template_Service $template_service ) {
|
||||
$this->admin_notices = $admin_notices;
|
||||
$this->template_service = $template_service;
|
||||
}
|
||||
|
||||
/**
|
||||
* It adds the notice to be shown when conditions meet.
|
||||
*/
|
||||
public function add_notice() {
|
||||
$notice = $this->admin_notices->create_notice( $this->get_notice_id(), $this->get_notice_content(), self::NOTICE_GROUP_ID );
|
||||
$notice->set_css_class_types( 'notice-error' );
|
||||
$this->admin_notices->add_notice( $notice );
|
||||
}
|
||||
|
||||
/**
|
||||
* It removes the notice.
|
||||
*/
|
||||
public function remove_notice() {
|
||||
$this->admin_notices->remove_notice( self::NOTICE_GROUP_ID, $this->get_notice_id() );
|
||||
}
|
||||
|
||||
/**
|
||||
* It checks is the notice exists.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function exists() {
|
||||
return (bool) $this->admin_notices->get_notice( $this->get_notice_id(), self::NOTICE_GROUP_ID );
|
||||
}
|
||||
|
||||
/**
|
||||
* It gets the notice content.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function get_notice_content() {
|
||||
return $this->template_service->show( $this->get_model(), self::TEMPLATE );
|
||||
}
|
||||
|
||||
/**
|
||||
* It gets the definition of the notice's content.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
abstract protected function get_model();
|
||||
|
||||
/**
|
||||
* It gets the ID of the notice.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract protected function get_notice_id();
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
class WPML_Translation_Jobs_Migration_Repository {
|
||||
|
||||
private $jobs_repository;
|
||||
private $all_jobs = false;
|
||||
|
||||
public function __construct( WPML_TM_Jobs_Repository $jobs_repository, $all_jobs = false ) {
|
||||
$this->jobs_repository = $jobs_repository;
|
||||
$this->all_jobs = $all_jobs;
|
||||
}
|
||||
|
||||
public function get() {
|
||||
return $this->jobs_repository->get( $this->get_params() )->getIterator()->getArrayCopy();
|
||||
}
|
||||
|
||||
public function get_count() {
|
||||
return $this->jobs_repository->get_count( $this->get_params() );
|
||||
}
|
||||
|
||||
private function get_params() {
|
||||
$params = new WPML_TM_Jobs_Search_Params();
|
||||
$params->set_scope( WPML_TM_Jobs_Search_Params::SCOPE_REMOTE );
|
||||
$params->set_job_types( array( WPML_TM_Job_Entity::POST_TYPE, WPML_TM_Job_Entity::PACKAGE_TYPE ) );
|
||||
|
||||
if ( ! $this->all_jobs ) {
|
||||
$params->set_tp_id( null );
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
<?php
|
||||
|
||||
class WPML_Translation_Jobs_Migration {
|
||||
|
||||
const MIGRATION_FIX_LOG_KEY = 'wpml_fixing_migration_log';
|
||||
|
||||
private $jobs_repository;
|
||||
private $cms_id_builder;
|
||||
private $wpdb;
|
||||
private $jobs_api;
|
||||
|
||||
public function __construct(
|
||||
WPML_Translation_Jobs_Migration_Repository $jobs_repository,
|
||||
WPML_TM_CMS_ID $cms_id_builder,
|
||||
wpdb $wpdb,
|
||||
WPML_TP_Jobs_API $jobs_api
|
||||
) {
|
||||
$this->jobs_repository = $jobs_repository;
|
||||
$this->cms_id_builder = $cms_id_builder;
|
||||
$this->wpdb = $wpdb;
|
||||
$this->jobs_api = $jobs_api;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WPML_TM_Post_Job_Entity[] $jobs
|
||||
* @param bool $recover_status
|
||||
*
|
||||
* @throws WPML_TP_API_Exception
|
||||
*/
|
||||
public function migrate_jobs( array $jobs, $recover_status = false ) {
|
||||
$mapped_jobs = $this->map_cms_id_job_id( $jobs );
|
||||
|
||||
if ( $mapped_jobs ) {
|
||||
$tp_jobs = $this->get_tp_jobs( $mapped_jobs );
|
||||
|
||||
foreach ( $jobs as $job ) {
|
||||
$cms_id = array_key_exists( $job->get_id(), $mapped_jobs ) ? $mapped_jobs[ $job->get_id() ] : '';
|
||||
list( $tp_id, $revision_id ) = $this->get_tp_id_revision_id( $cms_id, $tp_jobs );
|
||||
|
||||
if ( $recover_status ) {
|
||||
$this->recovery_mode( $job, $tp_id, $revision_id );
|
||||
} else {
|
||||
$this->first_migration_mode( $job, $tp_id, $revision_id );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $mapped_jobs
|
||||
*
|
||||
* @throws WPML_TP_API_Exception
|
||||
* @return array
|
||||
*/
|
||||
private function get_tp_jobs( array $mapped_jobs ) {
|
||||
return $this->get_latest_jobs_grouped_by_cms_id(
|
||||
$this->jobs_api->get_jobs_per_cms_ids( array_values( $mapped_jobs ), true )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WPML_TM_Post_Job_Entity $job
|
||||
* @param int $tp_id
|
||||
* @param int $revision_id
|
||||
*/
|
||||
private function recovery_mode( WPML_TM_Post_Job_Entity $job, $tp_id, $revision_id ) {
|
||||
if ( $tp_id !== $job->get_tp_id() ) {
|
||||
$new_status = $this->get_new_status( $job, $tp_id );
|
||||
$this->log( $job->get_id(), $job->get_tp_id(), $tp_id, $job->get_status(), $new_status );
|
||||
|
||||
$this->fix_job_fields( $tp_id, $revision_id, $new_status, $job->get_id() );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WPML_TM_Post_Job_Entity $job
|
||||
* @param int $tp_id
|
||||
* @param int $revision_id
|
||||
*/
|
||||
private function first_migration_mode( WPML_TM_Post_Job_Entity $job, $tp_id, $revision_id ) {
|
||||
$this->fix_job_fields( $tp_id, $revision_id, false, $job->get_id() );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $tp_jobs
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_latest_jobs_grouped_by_cms_id( $tp_jobs ) {
|
||||
$result = array();
|
||||
|
||||
foreach ( $tp_jobs as $tp_job ) {
|
||||
if ( ! isset( $result[ $tp_job->cms_id ] ) ) {
|
||||
$result[ $tp_job->cms_id ] = $tp_job;
|
||||
} elseif ( $tp_job->id > $result[ $tp_job->cms_id ]->id ) {
|
||||
$result[ $tp_job->cms_id ] = $tp_job;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WPML_TM_Post_Job_Entity $job
|
||||
* @param int $new_tp_id
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function get_new_status( WPML_TM_Post_Job_Entity $job, $new_tp_id ) {
|
||||
$new_status = false;
|
||||
if ( $job->get_tp_id() !== null && $new_tp_id ) {
|
||||
if ( $job->get_status() === ICL_TM_NOT_TRANSLATED || $this->has_been_completed_after_release( $job ) ) {
|
||||
$new_status = ICL_TM_IN_PROGRESS;
|
||||
}
|
||||
}
|
||||
|
||||
return $new_status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WPML_TM_Post_Job_Entity $job
|
||||
*
|
||||
* @return bool
|
||||
* @throws Exception
|
||||
*/
|
||||
private function has_been_completed_after_release( WPML_TM_Post_Job_Entity $job ) {
|
||||
return $job->get_status() === ICL_TM_COMPLETE && $job->get_completed_date() && $job->get_completed_date() > $this->get_4_2_0_release_date();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $cms_id
|
||||
* @param array $tp_jobs
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_tp_id_revision_id( $cms_id, $tp_jobs ) {
|
||||
$result = array( 0, 0 );
|
||||
if ( isset( $tp_jobs[ $cms_id ] ) ) {
|
||||
$result = array(
|
||||
$tp_jobs[ $cms_id ]->id,
|
||||
$tp_jobs[ $cms_id ]->translation_revision,
|
||||
);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $tp_id
|
||||
* @param int $revision_id
|
||||
* @param int|false $status
|
||||
* @param int $job_id
|
||||
*/
|
||||
private function fix_job_fields( $tp_id, $revision_id, $status, $job_id ) {
|
||||
$new_data = array(
|
||||
'tp_id' => $tp_id,
|
||||
'tp_revision' => $revision_id,
|
||||
);
|
||||
|
||||
if ( $status ) {
|
||||
$new_data['status'] = $status;
|
||||
}
|
||||
|
||||
$this->wpdb->update(
|
||||
$this->wpdb->prefix . 'icl_translation_status',
|
||||
$new_data,
|
||||
array( 'rid' => $job_id )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WPML_TM_Post_Job_Entity[] $jobs
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function map_cms_id_job_id( $jobs ) {
|
||||
$mapped_jobs = array();
|
||||
|
||||
foreach ( $jobs as $job ) {
|
||||
$cms_id = $this->cms_id_builder->cms_id_from_job_id( $job->get_translate_job_id() );
|
||||
$mapped_jobs[ $job->get_id() ] = $cms_id;
|
||||
}
|
||||
|
||||
return $mapped_jobs;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DateTime
|
||||
* @throws Exception
|
||||
*/
|
||||
private function get_4_2_0_release_date() {
|
||||
return new DateTime(
|
||||
defined( 'WPML_4_2_0_RELEASE_DATE' ) ? WPML_4_2_0_RELEASE_DATE : '2019-01-20'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $rid
|
||||
* @param int $old_tp_id
|
||||
* @param int $new_tp_id
|
||||
* @param int $old_status
|
||||
* @param int $new_status
|
||||
*/
|
||||
private function log( $rid, $old_tp_id, $new_tp_id, $old_status, $new_status ) {
|
||||
$log = get_option( self::MIGRATION_FIX_LOG_KEY, array() );
|
||||
$key = $new_status ? 'status_changed' : 'status_not_changed';
|
||||
|
||||
$log[ $key ][ $rid ] = array(
|
||||
'rid' => $rid,
|
||||
'old_tp_id' => $old_tp_id,
|
||||
'new_tp_id' => $new_tp_id,
|
||||
'old_status' => $old_status,
|
||||
'new_status' => $new_status,
|
||||
);
|
||||
|
||||
update_option( self::MIGRATION_FIX_LOG_KEY, $log, false );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
class WPML_Translation_Jobs_Missing_TP_ID_Migration_Notice extends WPML_Translation_Jobs_Migration_Notice {
|
||||
|
||||
/**
|
||||
* It gets the definition of the notice's content.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_model() {
|
||||
return array(
|
||||
'strings' => array(
|
||||
'title' => __( 'WPML Translation Jobs Migration', 'wpml-translation-management' ),
|
||||
/* translators: the placeholder is replaced with the current version of WPML */
|
||||
'description' => sprintf( __( 'WPML found some remote jobs on your site that must be migrated in order to work with WPML %s. You might not be able to access some of the WPML administration pages until this migration is fully completed.', 'wpml-translation-management' ), ICL_SITEPRESS_VERSION ),
|
||||
'button' => __( 'Run now', 'wpml-translation-management' ),
|
||||
/* translators: this is shown between two number: processed items and total number of items to process */
|
||||
'of' => __( 'of', 'wpml-translation-management' ),
|
||||
'jobs_migrated' => __( 'jobs migrated', 'wpml-translation-management' ),
|
||||
'communicationError' => __(
|
||||
'The communication error with Translation Proxy has appeared. Please try later.',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
),
|
||||
'nonce' => wp_nonce_field( WPML_Translation_Jobs_Migration_Ajax::ACTION, WPML_Translation_Jobs_Migration_Ajax::ACTION, false, false ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* It gets the ID of the notice.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_notice_id() {
|
||||
return 'translation-jobs-migration';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
class WPML_TM_TS_Instructions_Hooks_Factory implements IWPML_Backend_Action_Loader, IWPML_AJAX_Action_Loader {
|
||||
/**
|
||||
* @return WPML_TM_TS_Instructions_Hooks
|
||||
*/
|
||||
public function create() {
|
||||
return new WPML_TM_TS_Instructions_Hooks( $this->create_notice() );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return WPML_TM_TS_Instructions_Notice
|
||||
*/
|
||||
private function create_notice() {
|
||||
$template_service = new WPML_Twig_Template_Loader( array( WPML_TM_PATH . '/templates/notices/translation-service-instruction/' ) );
|
||||
|
||||
return new WPML_TM_TS_Instructions_Notice( wpml_get_admin_notices(), $template_service->get_template() );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
class WPML_TM_TS_Instructions_Hooks implements IWPML_Action {
|
||||
/** @var WPML_TM_TS_Instructions_Notice */
|
||||
private $notice;
|
||||
|
||||
/**
|
||||
* WPML_TM_TS_Instructions_Hooks constructor.
|
||||
*
|
||||
* @param WPML_TM_TS_Instructions_Notice $notice
|
||||
*/
|
||||
public function __construct( WPML_TM_TS_Instructions_Notice $notice ) {
|
||||
$this->notice = $notice;
|
||||
}
|
||||
|
||||
|
||||
public function add_hooks() {
|
||||
add_action( 'wpml_tp_project_created', array( $this, 'display_message' ), 10, 3 );
|
||||
add_action( 'init', array( $this, 'add_hooks_on_init' ), 10, 0 );
|
||||
|
||||
add_action( 'wpml_tp_service_de_authorized', array( $this, 'dismiss' ), 10, 0 );
|
||||
add_action( 'wpml_tp_service_dectivated', array( $this, 'dismiss' ), 10, 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param stdClass $service
|
||||
* @param stdClass $project
|
||||
* @param array $icl_translation_projects
|
||||
*/
|
||||
public function display_message( $service, $project, array $icl_translation_projects ) {
|
||||
$is_first_project_ever = empty( $icl_translation_projects );
|
||||
|
||||
if ( $is_first_project_ever || ! $this->has_completed_remote_jobs() ) {
|
||||
$this->notice->add_notice( $service );
|
||||
}
|
||||
}
|
||||
|
||||
public function add_hooks_on_init() {
|
||||
if ( $this->notice->exists() ) {
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ), 10, 0 );
|
||||
add_action( 'wp_ajax_translation_service_instruction_dismiss', array( $this, 'dismiss' ), 10, 0 );
|
||||
}
|
||||
}
|
||||
|
||||
public function enqueue_scripts() {
|
||||
$handle = 'wpml-tm-translation-service-instruction';
|
||||
|
||||
wp_register_script(
|
||||
$handle,
|
||||
WPML_TM_URL . '/dist/js/translationServiceInstruction/app.js',
|
||||
array(),
|
||||
WPML_TM_VERSION
|
||||
);
|
||||
|
||||
$data = array(
|
||||
'restUrl' => untrailingslashit( rest_url() ),
|
||||
'restNonce' => wp_create_nonce( 'wp_rest' ),
|
||||
'ate' => null,
|
||||
'currentUser' => wp_get_current_user(),
|
||||
);
|
||||
|
||||
wp_localize_script( $handle, 'WPML_TM_SETTINGS', $data );
|
||||
|
||||
wp_enqueue_script( $handle );
|
||||
}
|
||||
|
||||
public function dismiss() {
|
||||
$this->notice->remove_notice();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
private function has_completed_remote_jobs() {
|
||||
$search_params = new WPML_TM_Jobs_Search_Params();
|
||||
$search_params->set_status( array( ICL_TM_COMPLETE ) );
|
||||
$search_params->set_scope( WPML_TM_Jobs_Search_Params::SCOPE_REMOTE );
|
||||
|
||||
return wpml_tm_get_jobs_repository()->get_count( $search_params ) > 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
class WPML_TM_TS_Instructions_Notice {
|
||||
const NOTICE_ID = 'translation-service-instructions';
|
||||
const NOTICE_GROUP_ID = 'translation-service-instructions';
|
||||
const TEMPLATE = 'translation-service-instructions.twig';
|
||||
|
||||
/** @var WPML_Notices */
|
||||
private $admin_notices;
|
||||
|
||||
/** @var IWPML_Template_Service */
|
||||
private $template_service;
|
||||
|
||||
public function __construct( WPML_Notices $admin_notices, IWPML_Template_Service $template_service ) {
|
||||
$this->admin_notices = $admin_notices;
|
||||
$this->template_service = $template_service;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param stdClass $service
|
||||
*/
|
||||
public function add_notice( $service ) {
|
||||
$notice = $this->admin_notices->create_notice(
|
||||
self::NOTICE_ID,
|
||||
$this->get_notice_content( $service ),
|
||||
self::NOTICE_GROUP_ID
|
||||
);
|
||||
$notice->add_display_callback( array( 'WPML_TM_Page', 'is_tm_dashboard' ) );
|
||||
$notice->set_text_only( true );
|
||||
$this->admin_notices->add_notice( $notice );
|
||||
}
|
||||
|
||||
public function remove_notice() {
|
||||
$this->admin_notices->remove_notice( self::NOTICE_GROUP_ID, self::NOTICE_ID );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function exists() {
|
||||
return ( WPML_TM_Page::is_dashboard() || wpml_is_ajax() ) &&
|
||||
(bool) $this->admin_notices->get_notice( self::NOTICE_ID, self::NOTICE_GROUP_ID );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param stdClass $service
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function get_notice_content( $service ) {
|
||||
$model = $this->get_model( $service );
|
||||
|
||||
return $this->template_service->show( $model, self::TEMPLATE );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param stdClass $service
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_model( $service ) {
|
||||
return array(
|
||||
'strings' => array(
|
||||
'title' => sprintf(
|
||||
__( 'How to work correctly with %s', 'wpml-translation-management' ),
|
||||
$service->name
|
||||
),
|
||||
'description' => sprintf(
|
||||
__(
|
||||
"Congratulations for choosing %s to translate your site's content. To avoid high costs and wasted time, please watch our short video.",
|
||||
'wpml-translation-management'
|
||||
),
|
||||
$service->name
|
||||
),
|
||||
'need_help' => __( 'Need help? See ', 'wpml-translation-management' ),
|
||||
'help_caption' => __(
|
||||
'how to translate different parts of the site.',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
'this_stuff_is_important' => __( 'This stuff is actually important. Please follow the video to send a test translation. Then, you can dismiss this message. Thank you!', 'wpml-translation-management' ),
|
||||
'my_test_translation_went_fine' => __( 'My test translation went fine.', 'wpml-translation-management' ),
|
||||
'dismiss' => __( 'Dismiss this message.', 'wpml-translation-management' ),
|
||||
),
|
||||
'image_url' => WPML_TM_URL . '/res/img/ts-instruction-video.png',
|
||||
'help_link' => 'https://wpml.org/documentation/translating-your-contents/professional-translation-via-wpml/doing-test-translation/?utm_source=plugin&utm_medium=gui&utm_campaign=wpmltm',
|
||||
'video_link' => 'https://wpml.org/documentation/translating-your-contents/professional-translation-via-wpml/doing-test-translation/?utm_source=plugin&utm_medium=gui&utm_campaign=wpmltm',
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
class WPML_TM_Post_Edit_Notices_Factory {
|
||||
|
||||
const TEMPLATES_PATH = '/templates/notices/post-edit/';
|
||||
|
||||
public function create() {
|
||||
/**
|
||||
* @var SitePress $sitepress
|
||||
* @var WPML_TM_Translation_Status_Display $wpml_tm_status_display_filter
|
||||
*/
|
||||
global $sitepress, $wpml_tm_status_display_filter;
|
||||
|
||||
$status_helper = wpml_get_post_status_helper();
|
||||
|
||||
$paths = array( WPML_TM_PATH . self::TEMPLATES_PATH );
|
||||
$template_service_loader = new WPML_Twig_Template_Loader( $paths );
|
||||
$template_service = $template_service_loader->get_template();
|
||||
|
||||
$super_globals = new WPML_Super_Globals_Validation();
|
||||
|
||||
if ( ! $wpml_tm_status_display_filter ) {
|
||||
wpml_tm_load_status_display_filter();
|
||||
}
|
||||
|
||||
return new WPML_TM_Post_Edit_Notices(
|
||||
$status_helper,
|
||||
$sitepress,
|
||||
$template_service,
|
||||
$super_globals,
|
||||
$wpml_tm_status_display_filter,
|
||||
new WPML_Translation_Element_Factory( $sitepress, new WPML_WP_Cache() ),
|
||||
new WPML_TM_ATE(),
|
||||
new WPML_TM_Rest_Job_Translator_Name(),
|
||||
new WPML_TM_Rest_Jobs_Translation_Service( new WPML_WP_Cache() )
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,530 @@
|
||||
<?php
|
||||
|
||||
use WPML\API\Sanitize;
|
||||
use WPML\TM\API\Jobs;
|
||||
|
||||
class WPML_TM_Post_Edit_Notices {
|
||||
|
||||
const TEMPLATE_TRANSLATION_IN_PROGRESS = 'translation-in-progress.twig';
|
||||
const TEMPLATE_EDIT_ORIGINAL_TRANSLATION_IN_PROGRESS = 'edit-original-translation-in-progress.twig';
|
||||
const TEMPLATE_USE_PREFERABLY_TM_DASHBOARD = 'use-preferably-tm-dashboard.twig';
|
||||
const TEMPLATE_USE_PREFERABLY_TE = 'use-preferably-translation-editor.twig';
|
||||
const DO_NOT_SHOW_AGAIN_EDIT_ORIGINAL_TRANSLATION_IN_PROGRESS_ACTION = 'wpml_dismiss_post_edit_original_te_notice';
|
||||
const DO_NOT_SHOW_AGAIN_USE_PREFERABLY_TE_ACTION = 'wpml_dismiss_post_edit_te_notice';
|
||||
const DISPLAY_LIMIT_TRANSLATIONS_IN_PROGRESS = 5;
|
||||
|
||||
/** @var WPML_Post_Status $post_status */
|
||||
private $post_status;
|
||||
|
||||
/** @var SitePress $sitepress */
|
||||
private $sitepress;
|
||||
|
||||
/** @var IWPML_Template_Service $template_render */
|
||||
private $template_render;
|
||||
|
||||
/** @var WPML_Super_Globals_Validation $super_globals */
|
||||
private $super_globals;
|
||||
|
||||
/** @var WPML_TM_Translation_Status_Display $status_display */
|
||||
private $status_display;
|
||||
|
||||
/** @var WPML_Translation_Element_Factory $element_factory */
|
||||
private $element_factory;
|
||||
|
||||
/** @var WPML_TM_ATE $tm_ate */
|
||||
private $tm_ate;
|
||||
|
||||
/** @var WPML_TM_Rest_Job_Translator_Name $translator_name */
|
||||
private $translator_name;
|
||||
|
||||
/** @var WPML_TM_Rest_Jobs_Translation_Service $translation_service */
|
||||
private $translation_service;
|
||||
|
||||
/**
|
||||
* @param WPML_Post_Status $post_status
|
||||
* @param SitePress $sitepress
|
||||
* @param IWPML_Template_Service $template_render
|
||||
* @param WPML_Super_Globals_Validation $super_globals
|
||||
* @param WPML_TM_Translation_Status_Display $status_display
|
||||
* @param WPML_Translation_Element_Factory $element_factory
|
||||
*/
|
||||
public function __construct(
|
||||
WPML_Post_Status $post_status,
|
||||
SitePress $sitepress,
|
||||
IWPML_Template_Service $template_render,
|
||||
WPML_Super_Globals_Validation $super_globals,
|
||||
WPML_TM_Translation_Status_Display $status_display,
|
||||
WPML_Translation_Element_Factory $element_factory,
|
||||
WPML_TM_ATE $tm_ate,
|
||||
WPML_TM_Rest_Job_Translator_Name $translator_name,
|
||||
WPML_TM_Rest_Jobs_Translation_Service $translation_service
|
||||
) {
|
||||
$this->post_status = $post_status;
|
||||
$this->sitepress = $sitepress;
|
||||
$this->template_render = $template_render;
|
||||
$this->super_globals = $super_globals;
|
||||
$this->status_display = $status_display;
|
||||
$this->element_factory = $element_factory;
|
||||
$this->tm_ate = $tm_ate;
|
||||
$this->translator_name = $translator_name;
|
||||
$this->translation_service = $translation_service;
|
||||
}
|
||||
|
||||
public function add_hooks() {
|
||||
$request_get_trid = isset( $_GET['trid'] ) ?
|
||||
filter_var( $_GET['trid'], FILTER_SANITIZE_NUMBER_INT ) :
|
||||
'';
|
||||
|
||||
$request_get_post = isset( $_GET['post'] ) ?
|
||||
filter_var( $_GET['post'], FILTER_SANITIZE_NUMBER_INT ) :
|
||||
'';
|
||||
|
||||
if ( $request_get_trid || $request_get_post ) {
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) );
|
||||
add_action( 'admin_notices', array( $this, 'display_notices' ) );
|
||||
}
|
||||
|
||||
add_action( 'wp_ajax_' . self::DO_NOT_SHOW_AGAIN_EDIT_ORIGINAL_TRANSLATION_IN_PROGRESS_ACTION, array( $this, 'do_not_display_it_again_to_user' ) );
|
||||
add_action( 'wp_ajax_' . self::DO_NOT_SHOW_AGAIN_USE_PREFERABLY_TE_ACTION, array( $this, 'do_not_display_it_again' ) );
|
||||
}
|
||||
|
||||
public function enqueue_assets() {
|
||||
wp_enqueue_script(
|
||||
'wpml-tm-post-edit-alert',
|
||||
WPML_TM_URL . '/res/js/post-edit-alert.js',
|
||||
array( 'jquery', 'jquery-ui-dialog' ),
|
||||
WPML_TM_VERSION
|
||||
);
|
||||
}
|
||||
|
||||
public function display_notices() {
|
||||
$trid = $this->super_globals->get( 'trid', FILTER_SANITIZE_NUMBER_INT, FILTER_NULL_ON_FAILURE );
|
||||
$post_id = $this->super_globals->get( 'post', FILTER_SANITIZE_NUMBER_INT, FILTER_NULL_ON_FAILURE );
|
||||
$lang = $this->super_globals->get( 'lang', FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_NULL_ON_FAILURE );
|
||||
|
||||
if ( ! $post_id ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$post_element = $this->element_factory->create( $post_id, 'post' );
|
||||
$is_original = ! $post_element->get_source_language_code();
|
||||
|
||||
if ( ! $trid ) {
|
||||
$trid = $post_element->get_trid();
|
||||
}
|
||||
|
||||
if ( $trid ) {
|
||||
$translations_in_progress = $is_original
|
||||
? $this->get_translations_in_progress( $post_element )
|
||||
: [];
|
||||
|
||||
if (
|
||||
! empty( $translations_in_progress ) &&
|
||||
$this->should_display_it_to_user( self::DO_NOT_SHOW_AGAIN_EDIT_ORIGINAL_TRANSLATION_IN_PROGRESS_ACTION )
|
||||
) {
|
||||
$translations_in_progress =
|
||||
$this->prepare_translations_for_gui (
|
||||
$translations_in_progress
|
||||
);
|
||||
|
||||
$msg_stale_job =
|
||||
$this->prepare_stale_jobs_for_gui(
|
||||
$translations_in_progress
|
||||
);
|
||||
|
||||
$model = array(
|
||||
'warning' => sprintf(
|
||||
__( '%sTranslation in progress - wait before editing%s', 'wpml-translation-management' ),
|
||||
'<strong>',
|
||||
'</strong>'
|
||||
),
|
||||
'message' => __( 'This page that you are editing is being translated right now. If you edit now, some or all of the translation for this page may be missing. It\'s best to wait until translation completes, then edit and update the translation.', 'wpml-translation-management' ),
|
||||
'translations_in_progress' => [
|
||||
'display_limit' => self::DISPLAY_LIMIT_TRANSLATIONS_IN_PROGRESS,
|
||||
'translations' => $translations_in_progress,
|
||||
'title' => __( 'Waiting for translators...', 'sitepress' ),
|
||||
/* translators: %d is the number of translations. */
|
||||
'more' => __( '...and %d more translations.', 'sitepress' ),
|
||||
'no_translator' => __( 'First available translator', 'sitepress' ),
|
||||
'msg_stale_job' => $msg_stale_job,
|
||||
],
|
||||
'go_back_button' => __( 'Take me back', 'wpml-translation-management' ),
|
||||
'edit_anyway_button' => __( 'I understand - continue editing', 'wpml-translation-management' ),
|
||||
'do_not_show_again' => __( "Don't show this warning again", 'wpml-translation-management' ),
|
||||
'do_not_show_again_action' => self::DO_NOT_SHOW_AGAIN_EDIT_ORIGINAL_TRANSLATION_IN_PROGRESS_ACTION,
|
||||
'nonce' => wp_nonce_field(
|
||||
self::DO_NOT_SHOW_AGAIN_EDIT_ORIGINAL_TRANSLATION_IN_PROGRESS_ACTION,
|
||||
self::DO_NOT_SHOW_AGAIN_EDIT_ORIGINAL_TRANSLATION_IN_PROGRESS_ACTION,
|
||||
true,
|
||||
false
|
||||
),
|
||||
);
|
||||
|
||||
echo $this->template_render->show( $model, self::TEMPLATE_EDIT_ORIGINAL_TRANSLATION_IN_PROGRESS );
|
||||
}elseif ( $this->is_waiting_for_a_translation( (int) $this->post_status->get_status( $post_id, $trid, $lang ) ) ) {
|
||||
$model = array(
|
||||
'warning' => sprintf(
|
||||
__( '%sWarning:%s You are trying to edit a translation that is currently in the process of being added using WPML.', 'wpml-translation-management' ),
|
||||
'<strong>',
|
||||
'</strong>'
|
||||
),
|
||||
'check_dashboard' => sprintf(
|
||||
__( 'Please refer to the <a href="%s">Translation Management dashboard</a> for the exact status of this translation.', 'wpml-translation-management' ),
|
||||
admin_url( 'admin.php?page=' . WPML_TM_FOLDER . '/menu/main.php&' )
|
||||
),
|
||||
);
|
||||
|
||||
echo $this->template_render->show( $model, self::TEMPLATE_TRANSLATION_IN_PROGRESS );
|
||||
|
||||
} elseif (
|
||||
! $is_original &&
|
||||
WPML_TM_Post_Edit_TM_Editor_Mode::is_using_tm_editor( $this->sitepress, $post_id ) &&
|
||||
apply_filters( 'wpml_tm_show_page_builders_translation_editor_warning', true, $post_id ) &&
|
||||
$this->should_display_it( self::DO_NOT_SHOW_AGAIN_USE_PREFERABLY_TE_ACTION )
|
||||
) {
|
||||
|
||||
$model = array(
|
||||
'warning' => sprintf(
|
||||
__( '%sWarning:%s You are trying to edit a translation using the standard WordPress editor but your site is configured to use the WPML Translation Editor.', 'wpml-translation-management' ),
|
||||
'<strong>',
|
||||
'</strong>'
|
||||
),
|
||||
'go_back_button' => __( 'Go back', 'wpml-translation-management' ),
|
||||
'edit_anyway_button' => __( 'Edit anyway', 'wpml-translation-management' ),
|
||||
'open_in_te_button' => __( 'Open in Translation Editor', 'wpml-translation-management' ),
|
||||
'translation_editor_url' => $this->get_translation_editor_link( $post_element ),
|
||||
'do_not_show_again' => __( "Don't show this warning again", 'wpml-translation-management' ),
|
||||
'do_not_show_again_action' => self::DO_NOT_SHOW_AGAIN_USE_PREFERABLY_TE_ACTION,
|
||||
'nonce' => wp_nonce_field(
|
||||
self::DO_NOT_SHOW_AGAIN_USE_PREFERABLY_TE_ACTION,
|
||||
self::DO_NOT_SHOW_AGAIN_USE_PREFERABLY_TE_ACTION,
|
||||
true,
|
||||
false
|
||||
),
|
||||
);
|
||||
|
||||
echo $this->template_render->show( $model, self::TEMPLATE_USE_PREFERABLY_TE );
|
||||
}
|
||||
|
||||
} elseif ( $post_element->is_translatable()
|
||||
&& WPML_TM_Post_Edit_TM_Editor_Mode::is_using_tm_editor( $this->sitepress, $post_id )
|
||||
){
|
||||
$model = array(
|
||||
'warning' => sprintf(
|
||||
__('%sWarning:%s You are trying to add a translation using the standard WordPress editor but your site is configured to use the WPML Translation Editor.' , 'wpml-translation-management'),
|
||||
'<strong>',
|
||||
'</strong>'
|
||||
),
|
||||
'use_tm_dashboard' => sprintf(
|
||||
__( 'You should use <a href="%s">Translation management dashboard</a> to send the original document to translation.' , 'wpml-translation-management' ),
|
||||
admin_url( 'admin.php?page=' . WPML_TM_FOLDER . '/menu/main.php' )
|
||||
),
|
||||
);
|
||||
|
||||
echo $this->template_render->show( $model, self::TEMPLATE_USE_PREFERABLY_TM_DASHBOARD );
|
||||
}
|
||||
}
|
||||
|
||||
public function do_not_display_it_again_to_user() {
|
||||
$action = Sanitize::stringProp( 'action', $_POST );
|
||||
if( $this->is_valid_request( $action ) ){
|
||||
update_user_option( get_current_user_id(), $action, 1 );
|
||||
}
|
||||
}
|
||||
|
||||
public function do_not_display_it_again() {
|
||||
$action = Sanitize::stringProp( 'action', $_POST );
|
||||
if( $this->is_valid_request( $action ) ){
|
||||
update_option( $action, 1, false );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
private function is_valid_request( $action ) {
|
||||
return isset( $_POST['nonce'] ) && wp_verify_nonce( $_POST['nonce'], $action );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
private function should_display_it_to_user( $action ) {
|
||||
return false === get_user_option( $action );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
private function should_display_it( $action ) {
|
||||
return false === get_option( $action );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WPML_Translation_Element $post_element
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function get_translations_in_progress( $post_element ) {
|
||||
$translations = $this->sitepress->get_element_translations(
|
||||
$post_element->get_trid(),
|
||||
$post_element->get_wpml_element_type()
|
||||
);
|
||||
|
||||
if ( ! is_array( $translations ) || empty( $translations ) ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$wpml_element_translations = wpml_tm_load_element_translations();
|
||||
$translations_in_progress = [];
|
||||
|
||||
foreach ( $translations as $translation ) {
|
||||
if ( ! $translation->original ) {
|
||||
$job = Jobs::getTridJob( $post_element->get_trid(), $translation->language_code );
|
||||
|
||||
// ATE status needs to be checked directly because it can be not updated in DB yet.
|
||||
if (
|
||||
$job
|
||||
&& $this->tm_ate->is_translation_method_ate_enabled()
|
||||
&& 'ate' === $job->editor
|
||||
&& $this->tm_ate->is_translation_ready_for_post(
|
||||
$post_element->get_trid(),
|
||||
$translation->language_code
|
||||
)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
$this->is_waiting_for_a_translation(
|
||||
$wpml_element_translations->get_translation_status(
|
||||
$post_element->get_trid(),
|
||||
$translation->language_code
|
||||
)
|
||||
)
|
||||
) {
|
||||
$translations_in_progress[] = $job;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $translations_in_progress;
|
||||
}
|
||||
|
||||
private function prepare_translations_for_gui( $translations ) {
|
||||
// Prepare data for GUI.
|
||||
$translations = array_map(
|
||||
[ $this, 'prepare_translation_for_gui' ],
|
||||
$translations
|
||||
);
|
||||
|
||||
// Sort languages by language name (as usual).
|
||||
usort(
|
||||
$translations,
|
||||
function( $a, $b ) {
|
||||
return $a['to_language'] > $b['to_language'];
|
||||
}
|
||||
);
|
||||
|
||||
return $translations;
|
||||
}
|
||||
|
||||
private function prepare_translation_for_gui( $job ) {
|
||||
if (
|
||||
! is_object( $job )
|
||||
|| ! property_exists( $job, 'language_code' )
|
||||
|| ! property_exists( $job, 'to_language' )
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
$since = null;
|
||||
if ( property_exists( $job, 'elements' ) && is_array( $job->elements ) ) {
|
||||
foreach ( $job->elements as $element ) {
|
||||
if (
|
||||
! property_exists( $element, 'timestamp' )
|
||||
|| ! property_exists( $element, 'field_finished' )
|
||||
|| 0 !== (int) $element->field_finished
|
||||
) {
|
||||
// No valid element or already finished.
|
||||
continue;
|
||||
}
|
||||
|
||||
$element_since = strtotime( $element->timestamp );
|
||||
|
||||
$since = null === $since || $element_since < $since
|
||||
? $element_since
|
||||
: $since;
|
||||
}
|
||||
}
|
||||
|
||||
$is_automatic = property_exists( $job, 'automatic' )
|
||||
? (bool) $job->automatic
|
||||
: false;
|
||||
|
||||
$editor_job_id = property_exists( $job, 'editor_job_id' )
|
||||
? (int) $job->editor_job_id
|
||||
: null;
|
||||
|
||||
return [
|
||||
'to_language' => $job->to_language,
|
||||
'is_automatic' => $is_automatic,
|
||||
'flag' => $this->sitepress->get_flag_image( $job->language_code ),
|
||||
'translator' => $this->translator_name_by_job( $job ),
|
||||
'since' => $since,
|
||||
'waiting_for' => $this->waiting_for_x_time( $since ),
|
||||
'editor_job_id' => $editor_job_id,
|
||||
];
|
||||
}
|
||||
|
||||
private function translator_name_by_job( $job ) {
|
||||
if (
|
||||
property_exists( $job, 'automatic' )
|
||||
&& 1 === (int) $job->automatic
|
||||
) {
|
||||
// Automatic Translation.
|
||||
return __( 'Automatic translation', 'sitepress' );
|
||||
}
|
||||
|
||||
if (
|
||||
property_exists( $job, 'translation_service' )
|
||||
&& is_numeric( $job->translation_service )
|
||||
) {
|
||||
// Translation Service.
|
||||
return $this->translation_service
|
||||
->get_name( $job->translation_service );
|
||||
}
|
||||
|
||||
// Translator.
|
||||
return property_exists( $job, 'translator_id' )
|
||||
? $this->translator_name->get( $job->translator_id )
|
||||
: null;
|
||||
}
|
||||
|
||||
private function waiting_for_x_time( $since_timestamp ) {
|
||||
if ( empty( $since_timestamp ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$since = new \DateTime();
|
||||
$since->setTimestamp( $since_timestamp );
|
||||
|
||||
$interval = $since->diff( new \DateTime() );
|
||||
|
||||
// Use: x day(s) if translation is longer than 24 hours in progress.
|
||||
if ( $interval->days > 0 ) {
|
||||
return sprintf(
|
||||
/* translators: %d is for the number of day(s). */
|
||||
_n( '%d day', '%d days', $interval->days, 'sitepress' ),
|
||||
$interval->days
|
||||
);
|
||||
}
|
||||
|
||||
// Use: x hour(s) if translation is longer than 1 hour in progress.
|
||||
if ( $interval->h > 0 ) {
|
||||
return sprintf(
|
||||
/* translators: %d is for the number of hour(s). */
|
||||
_n( '%d hour', '%d hours', $interval->h, 'sitepress' ),
|
||||
$interval->h
|
||||
);
|
||||
}
|
||||
|
||||
// Use: x minute(s) if translation is less than a hour in progress.
|
||||
return sprintf(
|
||||
/* translators: %d is for the number of minute(s). */
|
||||
_n( '%d minute', '%d minutes', $interval->i, 'sitepress' ),
|
||||
$interval->i
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stale jobs are automatic translated jobs, which are in progress for
|
||||
* 1 or more days. As there is a limit of jobs being shown, this method
|
||||
* makes sure to move the stale job to the top of the list and returns
|
||||
* an error message, asking the user to contact support with all
|
||||
* stale job ate ids.
|
||||
*
|
||||
* @param array $translations
|
||||
* @return string
|
||||
*/
|
||||
private function prepare_stale_jobs_for_gui( &$translations ) {
|
||||
$stale_ids = [];
|
||||
|
||||
foreach ( $translations as $k => $translation ) {
|
||||
if ( ! $translation['is_automatic'] ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$since = new \DateTime();
|
||||
$since->setTimestamp( $translation['since'] );
|
||||
$interval = $since->diff( new \DateTime() );
|
||||
|
||||
if ( 0 === $interval->days ) {
|
||||
// All good with this translation.
|
||||
continue;
|
||||
}
|
||||
|
||||
$stale_ids[] = $translation['editor_job_id'];
|
||||
|
||||
if (
|
||||
count( $translations ) >
|
||||
self::DISPLAY_LIMIT_TRANSLATIONS_IN_PROGRESS
|
||||
) {
|
||||
// More translations in progress as the dialog shows.
|
||||
// Move this stale automatic translation to top.
|
||||
unset( $translations[ $k ] );
|
||||
array_unshift( $translations, $translation );
|
||||
}
|
||||
}
|
||||
|
||||
if ( empty( $stale_ids ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
/* translators: %1$1s and %2$2s is used for adding html tags to make WPML support a link. %3$s is a list of numeric ids. */
|
||||
_n(
|
||||
'Something went wrong with automatic translation. Please contact %1$1sWPML support%2$2s and report that the following automatic translation is stuck: %3$3s',
|
||||
'Something went wrong with automatic translation. Please contact %1$1sWPML support%2$2s and report that the following automatic translations are stuck: %3$3s',
|
||||
count( $stale_ids ),
|
||||
'sitepress'
|
||||
),
|
||||
'<a href="https://wpml.org/forums/forum/english-support/?utm_source=wpmlplugin&utm_campaign=content-editing&utm_medium=post-editor&utm_term=translation-in-progress/" target="_blank">',
|
||||
'</a>',
|
||||
implode( ', ', $stale_ids )
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param int|null $translation_status
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_waiting_for_a_translation( $translation_status ) {
|
||||
return ! is_null( $translation_status )
|
||||
&& $translation_status > 0
|
||||
&& $translation_status != ICL_TM_DUPLICATE
|
||||
&& $translation_status < ICL_TM_COMPLETE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WPML_Post_Element $post_element
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function get_translation_editor_link( WPML_Post_Element $post_element ) {
|
||||
$post_id = $post_element->get_id();
|
||||
$source_post_element = $post_element->get_source_element();
|
||||
|
||||
if ( $source_post_element ) {
|
||||
$post_id = $source_post_element->get_id();
|
||||
}
|
||||
|
||||
$url = $this->status_display->filter_status_link(
|
||||
'#', $post_id, $post_element->get_language_code(), $post_element->get_trid()
|
||||
);
|
||||
|
||||
return remove_query_arg( 'return_url', $url );
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user