first commit

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

View File

@@ -0,0 +1,135 @@
<?php
use WPML\API\Sanitize;
/**
* Class WPML_TF_Backend_Bulk_Actions
*
* @author OnTheGoSystems
*/
class WPML_TF_Backend_Bulk_Actions {
/** @var WPML_TF_Data_Object_Storage $feedback_storage */
private $feedback_storage;
/** @var WPML_WP_API $wp_api */
private $wp_api;
/** @var WPML_TF_Backend_Notices $backend_notices */
private $backend_notices;
/**
* WPML_TF_Feedback_List_Bulk_Action_Hooks constructor.
*
* @param WPML_TF_Data_Object_Storage $feedback_storage
* @param WPML_WP_API $wp_api
* @param WPML_TF_Backend_Notices $backend_notices
*/
public function __construct(
WPML_TF_Data_Object_Storage $feedback_storage,
WPML_WP_API $wp_api,
WPML_TF_Backend_Notices $backend_notices
) {
$this->feedback_storage = $feedback_storage;
$this->wp_api = $wp_api;
$this->backend_notices = $backend_notices;
}
/**
* Method bulk_action_callback
*/
public function process() {
if ( $this->is_valid_request() && current_user_can( 'manage_options' ) ) {
$feedback_ids = array_map( 'intval', $_GET['feedback_ids'] );
$bulk_action = Sanitize::stringProp( 'bulk_action', $_GET );
$updated_feedback_ids = $feedback_ids;
switch ( $bulk_action ) {
case 'trash':
$this->delete( $feedback_ids );
break;
case 'untrash':
$this->untrash( $feedback_ids );
break;
case 'delete':
$this->delete( $feedback_ids, true );
break;
default:
$updated_feedback_ids = $this->change_status( $feedback_ids, $bulk_action );
break;
}
$this->backend_notices->add_bulk_updated_notice( $updated_feedback_ids, $bulk_action );
$this->redirect();
} else {
$this->backend_notices->remove_bulk_updated_notice_after_display();
}
}
private function change_status( array $feedback_ids, $new_status ) {
$updated_feedback_ids = array();
foreach ( $feedback_ids as $feedback_id ) {
$feedback = $this->feedback_storage->get( $feedback_id );
if ( $feedback ) {
/** @var WPML_TF_Feedback $feedback */
$feedback->set_status( $new_status );
$updated_feedback_ids[] = $this->feedback_storage->persist( $feedback );
}
}
return $updated_feedback_ids;
}
private function delete( array $feedback_ids, $force_delete = false ) {
foreach ( $feedback_ids as $feedback_id ) {
$this->feedback_storage->delete( $feedback_id, $force_delete );
}
}
private function untrash( array $feedback_ids ) {
foreach ( $feedback_ids as $feedback_id ) {
$this->feedback_storage->untrash( $feedback_id );
}
}
/**
* @return bool
*/
private function is_valid_request() {
$is_valid = false;
if ( isset( $_GET['bulk_action'], $_GET['bulk_action2'] ) ) {
if ( '-1' === $_GET['bulk_action'] ) {
$_GET['bulk_action'] = $_GET['bulk_action2'];
}
if ( isset( $_GET['nonce'], $_GET['feedback_ids'] ) ) {
$is_valid = $this->is_valid_action( $_GET['bulk_action'] )
&& wp_verify_nonce( $_GET['nonce'], WPML_TF_Backend_Hooks::PAGE_HOOK );
}
}
return $is_valid;
}
private function is_valid_action( $action ) {
return in_array( $action, array( 'pending', 'fixed', 'trash', 'untrash', 'delete' ), true );
}
/**
* Redirect after processing the bulk action
*/
private function redirect() {
$args_to_remove = array( 'feedback_ids', 'bulk_action', 'bulk_action2' );
$url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
$url = remove_query_arg( $args_to_remove, $url );
$this->wp_api->wp_safe_redirect( $url );
}
}

View File

@@ -0,0 +1,102 @@
<?php
/**
* Class WPML_TF_TP_Ratings_Synchronize
*
* @author OnTheGoSystems
*/
class WPML_TF_TP_Ratings_Synchronize {
const MAX_RATINGS_TO_SYNCHRONIZE = 5;
const PENDING_SYNC_RATING_IDS_OPTION = 'wpml_tf_pending_sync_rating_ids';
const MAX_ATTEMPTS_TO_SYNC = 3;
/** @var WPML_TF_Data_Object_Storage $feedback_storage */
private $feedback_storage;
/** @var WPML_TP_API_TF_Ratings $tp_ratings */
private $tp_ratings;
/** @var array $pending_ids */
private $pending_ids;
/**
* WPML_TF_TP_Ratings_Synchronize constructor.
*
* @param WPML_TF_Data_Object_Storage $feedback_storage
* @param WPML_TP_API_TF_Ratings $tp_ratings
*/
public function __construct( WPML_TF_Data_Object_Storage $feedback_storage, WPML_TP_API_TF_Ratings $tp_ratings ) {
$this->feedback_storage = $feedback_storage;
$this->tp_ratings = $tp_ratings;
}
/** @param bool $clear_all_pending_ratings */
public function run( $clear_all_pending_ratings = false ) {
$this->pending_ids = get_option( self::PENDING_SYNC_RATING_IDS_OPTION, array() );
$filter_args = array(
'pending_tp_ratings' => $clear_all_pending_ratings ? -1 : self::MAX_RATINGS_TO_SYNCHRONIZE,
);
$feedback_filter = new WPML_TF_Feedback_Collection_Filter( $filter_args );
/** @var WPML_TF_Feedback_Collection $feedback_collection */
$feedback_collection = $this->feedback_storage->get_collection( $feedback_filter );
$time_threshold = 5 * MINUTE_IN_SECONDS;
foreach ( $feedback_collection as $feedback ) {
/** @var WPML_TF_Feedback $feedback */
$time_since_creation = time() - strtotime( $feedback->get_date_created() );
if ( ! $clear_all_pending_ratings && $time_since_creation < $time_threshold ) {
continue;
}
$tp_rating_id = $this->tp_ratings->send( $feedback );
if ( $tp_rating_id || $clear_all_pending_ratings ) {
$this->set_tp_rating_id( $feedback, $tp_rating_id );
} else {
$this->handle_pending_rating_sync( $feedback );
}
}
if ( $this->pending_ids ) {
update_option( self::PENDING_SYNC_RATING_IDS_OPTION, $this->pending_ids, false );
} else {
delete_option( self::PENDING_SYNC_RATING_IDS_OPTION );
}
}
private function set_tp_rating_id( WPML_TF_Feedback $feedback, $tp_rating_id ) {
$feedback->get_tp_responses()->set_rating_id( (int) $tp_rating_id );
$this->feedback_storage->persist( $feedback );
}
private function handle_pending_rating_sync( WPML_TF_Feedback $feedback ) {
$this->increment_attempts( $feedback->get_id() );
if ( $this->exceeds_max_attempts( $feedback->get_id() ) ) {
$this->set_tp_rating_id( $feedback, 0 );
unset( $this->pending_ids[ $feedback->get_id() ] );
}
}
/**
* @param int $id
*
* @return bool
*/
private function exceeds_max_attempts( $id ) {
return isset( $this->pending_ids[ $id ] ) && $this->pending_ids[ $id ] >= self::MAX_ATTEMPTS_TO_SYNC;
}
/** @param int $id */
private function increment_attempts( $id ) {
if ( ! isset( $this->pending_ids[ $id ] ) ) {
$this->pending_ids[ $id ] = 1;
} else {
$this->pending_ids[ $id ]++;
}
}
}

View File

@@ -0,0 +1,72 @@
<?php
/**
* Class WPML_TF_WP_Cron_Events
*
* @author OnTheGoSystems
*/
class WPML_TF_WP_Cron_Events implements IWPML_Action {
const SYNCHRONIZE_RATINGS_EVENT = 'wpml_tf_synchronize_ratings_event';
/** @var WPML_TF_Settings_Read $settings_read */
private $settings_read;
/** @var WPML_TF_Settings $settings */
private $settings;
/** @var WPML_TF_TP_Ratings_Synchronize_Factory $ratings_synchronize_factory */
private $ratings_synchronize_factory;
/**
* WPML_TF_WP_Cron_Events constructor.
*
* @param WPML_TF_Settings_Read $settings_read
* @param WPML_TF_TP_Ratings_Synchronize_Factory $ratings_synchronize_factory
*/
public function __construct(
WPML_TF_Settings_Read $settings_read,
WPML_TF_TP_Ratings_Synchronize_Factory $ratings_synchronize_factory
) {
$this->settings_read = $settings_read;
$this->ratings_synchronize_factory = $ratings_synchronize_factory;
}
public function add_hooks() {
add_action( 'init', array( $this, 'init_action' ) );
add_action( self::SYNCHRONIZE_RATINGS_EVENT, array( $this, 'synchronize_ratings' ) );
}
public function init_action() {
if ( $this->get_settings()->is_enabled() ) {
$this->add_synchronize_ratings_event();
} else {
$this->remove_synchronize_ratings_event();
}
}
private function add_synchronize_ratings_event() {
if ( ! wp_next_scheduled( self::SYNCHRONIZE_RATINGS_EVENT ) ) {
wp_schedule_event( time(), 'twicedaily', self::SYNCHRONIZE_RATINGS_EVENT );
}
}
private function remove_synchronize_ratings_event() {
$timestamp = wp_next_scheduled( self::SYNCHRONIZE_RATINGS_EVENT );
wp_unschedule_event( $timestamp, self::SYNCHRONIZE_RATINGS_EVENT );
}
public function synchronize_ratings() {
$ratings_synchronize = $this->ratings_synchronize_factory->create();
$ratings_synchronize->run();
}
/** @return WPML_TF_Settings */
private function get_settings() {
if ( ! $this->settings ) {
$this->settings = $this->settings_read->get( 'WPML_TF_Settings' );
}
return $this->settings;
}
}

View File

@@ -0,0 +1,10 @@
<?php
/**
* Class WPML_TF_AJAX_Exception
*
* @author OnTheGoSystems
*/
class WPML_TF_AJAX_Exception extends Exception {
}

View File

@@ -0,0 +1,10 @@
<?php
/**
* Class WPML_TF_Feedback_Update_Exception
*
* @author OnTheGoSystems
*/
class WPML_TF_Feedback_Update_Exception extends Exception {
}

View File

@@ -0,0 +1,46 @@
<?php
/**
* Class WPML_TF_Backend_AJAX_Feedback_Edit_Hooks_Factory
*
* @author OnTheGoSystems
*/
class WPML_TF_Backend_AJAX_Feedback_Edit_Hooks_Factory extends WPML_AJAX_Base_Factory implements IWPML_Backend_Action_Loader {
const AJAX_ACTION = 'wpml-tf-backend-feedback-edit';
public function create() {
$hooks = null;
if ( $this->is_valid_action( self::AJAX_ACTION ) ) {
$feedback_storage = new WPML_TF_Data_Object_Storage( new WPML_TF_Feedback_Post_Convert() );
$message_storage = new WPML_TF_Data_Object_Storage( new WPML_TF_Message_Post_Convert() );
$feedback_query = new WPML_TF_Feedback_Query(
$feedback_storage,
$message_storage,
new WPML_TF_Collection_Filter_Factory()
);
$feedback_edit = new WPML_TF_Feedback_Edit(
$feedback_query,
$feedback_storage,
$message_storage,
class_exists( 'WPML_TP_Client_Factory' ) ? new WPML_TP_Client_Factory() : null
);
$template_loader = new WPML_Twig_Template_Loader(
array( WPML_PLUGIN_PATH . WPML_TF_Backend_Feedback_Row_View::TEMPLATE_FOLDER )
);
$row_view = new WPML_TF_Backend_Feedback_Row_View( $template_loader->get_template() );
$hooks = new WPML_TF_Backend_AJAX_Feedback_Edit_Hooks(
$feedback_edit,
$row_view,
$_POST
);
}
return $hooks;
}
}

View File

@@ -0,0 +1,25 @@
<?php
/**
* Class WPML_TF_Backend_Hooks_Factory
*
* @author OnTheGoSystems
*/
class WPML_TF_Backend_Hooks_Factory implements IWPML_Backend_Action_Loader {
/**
* @return WPML_TF_Backend_Hooks
*/
public function create() {
/** @var wpdb $wpdb */
global $wpdb;
return new WPML_TF_Backend_Hooks(
new WPML_TF_Backend_Bulk_Actions_Factory(),
new WPML_TF_Backend_Feedback_List_View_Factory(),
new WPML_TF_Backend_Styles(),
new WPML_TF_Backend_Scripts(),
$wpdb
);
}
}

View File

@@ -0,0 +1,35 @@
<?php
/**
* Class WPML_TF_Backend_Options_AJAX_Hooks_Factory
*
* @author OnTheGoSystems
*/
class WPML_TF_Backend_Options_AJAX_Hooks_Factory extends WPML_AJAX_Base_Factory implements IWPML_Backend_Action_Loader {
const AJAX_ACTION = 'wpml-tf-backend-options';
/**
* @return IWPML_Action|null
*/
public function create() {
global $sitepress;
$hooks = null;
if ( $this->is_valid_action( self::AJAX_ACTION ) ) {
$settings_read = new WPML_TF_Settings_Read();
/** @var WPML_TF_Settings $tf_settings */
$tf_settings = $settings_read->get( 'WPML_TF_Settings' );
$hooks = new WPML_TF_Backend_Options_AJAX_Hooks(
$tf_settings,
new WPML_TF_Settings_Write(),
new WPML_TF_Promote_Notices( $sitepress ),
$_POST
);
}
return $hooks;
}
}

View File

@@ -0,0 +1,49 @@
<?php
/**
* Class WPML_TF_Backend_Options_Hooks_Factory
*
* @author OnTheGoSystems
*/
class WPML_TF_Backend_Options_Hooks_Factory extends WPML_Current_Screen_Loader_Factory {
/** @return string */
protected function get_screen_regex() {
return '#' . WPML_PLUGIN_FOLDER . '/menu/languages#';
}
/** @return null|WPML_TF_Backend_Options_Hooks */
protected function create_hooks() {
/** @var SitePress $sitepress */
global $sitepress;
if ( $sitepress->is_setup_complete() ) {
$template_loader = new WPML_Twig_Template_Loader(
array( WPML_PLUGIN_PATH . WPML_TF_Backend_Options_View::TEMPLATE_FOLDER )
);
$settings_read = new WPML_TF_Settings_Read();
/** @var WPML_TF_Settings $tf_settings */
$tf_settings = $settings_read->get( 'WPML_TF_Settings' );
$options_view = new WPML_TF_Backend_Options_View(
$template_loader->get_template(),
$tf_settings,
$sitepress
);
$translation_service = new WPML_TF_Translation_Service(
class_exists( 'WPML_TP_Client_Factory' ) ? new WPML_TP_Client_Factory() : null
);
return new WPML_TF_Backend_Options_Hooks(
$options_view,
new WPML_TF_Backend_Options_Scripts(),
new WPML_TF_Backend_Options_Styles(),
$translation_service
);
}
return null;
}
}

View File

@@ -0,0 +1,25 @@
<?php
/**
* Class WPML_TF_Backend_Post_List_Hooks_Factory
*
* @author OnTheGoSystems
*/
class WPML_TF_Backend_Post_List_Hooks_Factory extends WPML_Current_Screen_Loader_Factory {
/** @return string */
protected function get_screen_regex() {
return '/^edit$/';
}
/** @return WPML_TF_Backend_Post_List_Hooks */
protected function create_hooks() {
global $wpdb, $sitepress;
$post_rating_metrics = new WPML_TF_Post_Rating_Metrics( $wpdb );
$document_information = new WPML_TF_Document_Information( $sitepress );
$backend_styles = new WPML_TF_Backend_Styles();
return new WPML_TF_Backend_Post_List_Hooks( $post_rating_metrics, $document_information, $backend_styles );
}
}

View File

@@ -0,0 +1,41 @@
<?php
/**
* Class WPML_TF_Backend_Promote_Hooks_Factory
*
* @author OnTheGoSystems
*/
class WPML_TF_Backend_Promote_Hooks_Factory implements IWPML_Backend_Action_Loader, IWPML_Deferred_Action_Loader {
/** @return string */
public function get_load_action() {
return 'wpml_after_tm_loaded';
}
public function create() {
/** @var SitePress $sitepress */
global $sitepress;
$hooks = null;
$settings_read = new WPML_TF_Settings_Read();
/** @var WPML_TF_Settings $tf_settings */
$tf_settings = $settings_read->get( 'WPML_TF_Settings' );
if ( ! $tf_settings->is_enabled() ) {
$setup_complete = $sitepress->is_setup_complete();
$translation_service = new WPML_TF_Translation_Service(
class_exists( 'WPML_TP_Client_Factory' ) ? new WPML_TP_Client_Factory() : null
);
$hooks = new WPML_TF_Backend_Promote_Hooks(
new WPML_TF_Promote_Notices( $sitepress ),
$setup_complete,
$translation_service
);
}
return $hooks;
}
}

View File

@@ -0,0 +1,17 @@
<?php
/**
* Class WPML_TF_Common_Hooks_Factory
* @author OnTheGoSystems
*/
class WPML_TF_Common_Hooks_Factory implements IWPML_Backend_Action_Loader, IWPML_Frontend_Action_Loader {
/**
* @return WPML_TF_Common_Hooks
*/
public function create() {
$feedback_storage = new WPML_TF_Data_Object_Storage( new WPML_TF_Feedback_Post_Convert() );
return new WPML_TF_Common_Hooks( $feedback_storage );
}
}

View File

@@ -0,0 +1,32 @@
<?php
/**
* Class WPML_TF_Frontend_AJAX_Hooks_Factory
*
* @author OnTheGoSystems
*/
class WPML_TF_Frontend_AJAX_Hooks_Factory extends WPML_AJAX_Base_Factory {
const AJAX_ACTION = 'wpml-tf-frontend-feedback';
/**
* @return IWPML_Action|null
*/
public function create() {
/** @var SitePress $sitepress */
/** @var wpdb $wpdb */
global $sitepress, $wpdb;
if ( $this->is_valid_action( self::AJAX_ACTION ) ) {
return new WPML_TF_Frontend_AJAX_Hooks(
new WPML_TF_Data_Object_Storage( new WPML_TF_Feedback_Post_Convert() ),
new WPML_TF_Document_Information( $sitepress ),
new WPML_TF_Post_Rating_Metrics( $wpdb ),
class_exists( 'WPML_TP_Client_Factory' ) ? new WPML_TP_Client_Factory() : null,
$_POST
);
}
return null;
}
}

View File

@@ -0,0 +1,102 @@
<?php
/**
* Class WPML_TF_Frontend_Hooks_Factory
*
* @author OnTheGoSystems
*/
class WPML_TF_Frontend_Hooks_Factory implements IWPML_Frontend_Action_Loader, IWPML_Deferred_Action_Loader {
/** @var WPML_Queried_Object $queried_object */
private $queried_object;
/** @var WPML_TF_Frontend_Display_Requirements $display_requirements */
private $display_requirements;
/**
* WPML_TF_Frontend_Hooks_Factory constructor.
*
* @param WPML_Queried_Object $queried_object
* @param WPML_TF_Frontend_Display_Requirements $display_requirements
*/
public function __construct(
WPML_Queried_Object $queried_object = null,
WPML_TF_Frontend_Display_Requirements $display_requirements = null
) {
$this->queried_object = $queried_object;
$this->display_requirements = $display_requirements;
}
/**
* The frontend hooks must be loaded when the request has been parsed (in "wp")
* to avoid unnecessary instantiation if the current page is not a translation
*
* @return string
*/
public function get_load_action() {
return 'wp';
}
/**
* @return null|WPML_TF_Frontend_Hooks
*/
public function create() {
global $sitepress;
$hooks = null;
if ( $this->get_display_requirements()->verify() ) {
$template_loader = new WPML_Twig_Template_Loader(
array( WPML_PLUGIN_PATH . WPML_TF_Frontend_Feedback_View::TEMPLATE_FOLDER )
);
$template_service = $template_loader->get_template();
$settings_reader = new WPML_TF_Settings_Read();
/** @var WPML_TF_Settings $settings */
$settings = $settings_reader->get( 'WPML_TF_Settings' );
$frontend_feedback_form = new WPML_TF_Frontend_Feedback_View(
$template_service,
$sitepress,
$this->get_queried_object(),
$settings
);
$hooks = new WPML_TF_Frontend_Hooks(
$frontend_feedback_form,
new WPML_TF_Frontend_Scripts(),
new WPML_TF_Frontend_Styles()
);
}
return $hooks;
}
/**
* @return WPML_Queried_Object
*/
private function get_queried_object() {
global $sitepress;
if ( ! $this->queried_object ) {
$this->queried_object = new WPML_Queried_Object( $sitepress );
}
return $this->queried_object;
}
/**
* @return WPML_TF_Frontend_Display_Requirements
*/
private function get_display_requirements() {
if ( ! $this->display_requirements ) {
$settings_read = new WPML_TF_Settings_Read();
$settings = $settings_read->get( 'WPML_TF_Settings' );
/** @var WPML_TF_Settings $settings */
$this->display_requirements = new WPML_TF_Frontend_Display_Requirements( $this->get_queried_object(), $settings );
}
return $this->display_requirements;
}
}

View File

@@ -0,0 +1,20 @@
<?php
/**
* Class WPML_TF_Translation_Queue_Hooks_Factory
*
* @author OnTheGoSystems
*/
class WPML_TF_Translation_Queue_Hooks_Factory extends WPML_Current_Screen_Loader_Factory {
/** @return string */
protected function get_screen_regex() {
return '/translations-queue/';
}
/** @return WPML_TF_Translation_Queue_Hooks */
protected function create_hooks() {
$feedback_storage = new WPML_TF_Data_Object_Storage( new WPML_TF_Feedback_Post_Convert() );
return new WPML_TF_Translation_Queue_Hooks( $feedback_storage );
}
}

View File

@@ -0,0 +1,18 @@
<?php
/**
* Class WPML_TF_Translation_Service_Change_Hooks_Factory
*
* @author OnTheGoSystems
*/
class WPML_TF_Translation_Service_Change_Hooks_Factory implements IWPML_Backend_Action_Loader {
/** @return WPML_TF_Translation_Service_Change_Hooks */
public function create() {
return new WPML_TF_Translation_Service_Change_Hooks(
new WPML_TF_Settings_Read(),
new WPML_TF_Settings_Write(),
new WPML_TF_TP_Ratings_Synchronize_Factory()
);
}
}

View File

@@ -0,0 +1,17 @@
<?php
/**
* Class WPML_TF_Common_Hooks_Factory
*
* @author OnTheGoSystems
*/
class WPML_TF_WP_Cron_Events_Factory implements IWPML_Backend_Action_Loader, IWPML_Frontend_Action_Loader {
/** @return WPML_TF_WP_Cron_Events */
public function create() {
return new WPML_TF_WP_Cron_Events(
new WPML_TF_Settings_Read(),
new WPML_TF_TP_Ratings_Synchronize_Factory()
);
}
}

View File

@@ -0,0 +1,17 @@
<?php
/**
* Class WPML_TF_XML_RPC_Hooks_Factory
*
* @author OnTheGoSystems
*/
class WPML_TF_XML_RPC_Hooks_Factory implements IWPML_Frontend_Action_Loader {
/** @return WPML_TF_XML_RPC_Hooks */
public function create() {
return new WPML_TF_XML_RPC_Hooks(
new WPML_TF_XML_RPC_Feedback_Update_Factory(),
new WPML_WP_API()
);
}
}

View File

@@ -0,0 +1,14 @@
<?php
/**
* Class WPML_TM_TF_AJAX_Feedback_List_Hooks_Factory
*
* @author OnTheGoSystems
*/
class WPML_TM_TF_AJAX_Feedback_List_Hooks_Factory implements IWPML_AJAX_Action_Loader {
/** @return WPML_TM_TF_Feedback_List_Hooks */
public function create() {
return new WPML_TM_TF_Feedback_List_Hooks();
}
}

View File

@@ -0,0 +1,19 @@
<?php
/**
* Class WPML_TM_TF_Feedback_List_Hooks_Factory
*
* @author OnTheGoSystems
*/
class WPML_TM_TF_Feedback_List_Hooks_Factory extends WPML_Current_Screen_Loader_Factory {
/** @return string */
public function get_screen_regex() {
return '/wpml-translation-feedback-list/';
}
/** @return WPML_TM_TF_Feedback_List_Hooks */
public function create_hooks() {
return new WPML_TM_TF_Feedback_List_Hooks();
}
}

View File

@@ -0,0 +1,20 @@
<?php
/**
* Class WPML_TF_Backend_Bulk_Actions_Factory
*
* @author OnTheGoSystems
*/
class WPML_TF_Backend_Bulk_Actions_Factory {
/**
* @return WPML_TF_Backend_Bulk_Actions
*/
public function create() {
return new WPML_TF_Backend_Bulk_Actions(
new WPML_TF_Data_Object_Storage( new WPML_TF_Feedback_Post_Convert() ),
new WPML_WP_API(),
new WPML_TF_Backend_Notices()
);
}
}

View File

@@ -0,0 +1,33 @@
<?php
/**
* Class WPML_TF_Backend_Feedback_List_View_Factory
*
* @author OnTheGoSystems
*/
class WPML_TF_Backend_Feedback_List_View_Factory {
public function create() {
/** @var SitePress $sitepress*/
global $sitepress;
$template_loader = new WPML_Twig_Template_Loader(
array( WPML_PLUGIN_PATH . WPML_TF_Backend_Feedback_List_View::TEMPLATE_FOLDER )
);
$feedback_query = new WPML_TF_Feedback_Query(
new WPML_TF_Data_Object_Storage( new WPML_TF_Feedback_Post_Convert() ),
new WPML_TF_Data_Object_Storage( new WPML_TF_Message_Post_Convert() ),
new WPML_TF_Collection_Filter_Factory()
);
return new WPML_TF_Backend_Feedback_List_View(
$template_loader->get_template(),
$feedback_query,
new WPML_Admin_Pagination(),
new WPML_Admin_Table_Sort(),
new WPML_TF_Feedback_Page_Filter( $sitepress, $feedback_query )
);
}
}

View File

@@ -0,0 +1,22 @@
<?php
/**
* Class WPML_TF_TP_Ratings_Synchronize
*
* @author OnTheGoSystems
*/
class WPML_TF_TP_Ratings_Synchronize_Factory {
/**
* @return WPML_TF_TP_Ratings_Synchronize
*/
public function create() {
$tp_client_factory = new WPML_TP_Client_Factory();
$tp_client = $tp_client_factory->create();
return new WPML_TF_TP_Ratings_Synchronize(
new WPML_TF_Data_Object_Storage( new WPML_TF_Feedback_Post_Convert() ),
$tp_client->ratings()
);
}
}

View File

@@ -0,0 +1,22 @@
<?php
/**
* Class WPML_TF_XML_RPC_Feedback_Update_Factory
*
* @author OnTheGoSystems
*/
class WPML_TF_XML_RPC_Feedback_Update_Factory {
/** @return WPML_TF_XML_RPC_Feedback_Update */
public function create() {
global $sitepress;
$translation_service = $sitepress->get_setting( 'translation_service' );
$translation_projects = $sitepress->get_setting( 'icl_translation_projects' );
$tp_project = new WPML_TP_Project( $translation_service, $translation_projects );
$feedback_storage = new WPML_TF_Data_Object_Storage( new WPML_TF_Feedback_Post_Convert() );
return new WPML_TF_XML_RPC_Feedback_Update( $feedback_storage, $tp_project );
}
}

View File

@@ -0,0 +1,82 @@
<?php
/**
* Class WPML_TF_Backend_AJAX_Feedback_Edit_Hooks
*
* @author OnTheGoSystems
*/
class WPML_TF_Backend_AJAX_Feedback_Edit_Hooks implements IWPML_Action {
/** @var WPML_TF_Feedback_Edit $feedback_edit */
private $feedback_edit;
/** @var WPML_TF_Backend_Feedback_Row_View $row_view */
private $row_view;
/** @var array $post_data */
private $post_data;
/**
* WPML_TF_Backend_AJAX_Feedback_Edit_Hooks constructor.
*
* @param WPML_TF_Feedback_Edit $feedback_edit
* @param WPML_TF_Backend_Feedback_Row_View $row_view
* @param array $post_data
*/
public function __construct(
WPML_TF_Feedback_Edit $feedback_edit,
WPML_TF_Backend_Feedback_Row_View $row_view,
array $post_data
) {
$this->feedback_edit = $feedback_edit;
$this->row_view = $row_view;
$this->post_data = $post_data;
}
public function add_hooks() {
add_action(
'wp_ajax_' . WPML_TF_Backend_AJAX_Feedback_Edit_Hooks_Factory::AJAX_ACTION,
array( $this, 'edit_feedback_callback' )
);
}
public function edit_feedback_callback() {
try {
$this->check_post_data_key( 'feedback_id' );
$feedback_id = filter_var( $this->post_data['feedback_id'], FILTER_SANITIZE_NUMBER_INT );
$feedback = $this->feedback_edit->update( $feedback_id, $this->post_data );
if ( ! $feedback ) {
throw new WPML_TF_AJAX_Exception( esc_html__( 'Failed to update the feedback.', 'sitepress' ) );
}
$response = array(
'summary_row' => $this->row_view->render_summary_row( $feedback ),
'details_row' => $this->row_view->render_details_row( $feedback ),
);
wp_send_json_success( $response );
} catch ( WPML_TF_AJAX_Exception $e ) {
wp_send_json_error( $e->getMessage() );
} catch ( WPML_TF_Feedback_Update_Exception $e ) {
wp_send_json_error( $e->getMessage() );
}
}
/**
* @param string $key
*
* @throws WPML_TF_AJAX_Exception
*/
private function check_post_data_key( $key ) {
if ( ! isset( $this->post_data[ $key ] ) ) {
$message = sprintf(
esc_html__( 'Missing key "%s".', 'sitepress' ),
$key
);
throw new WPML_TF_AJAX_Exception( $message );
}
}
}

View File

@@ -0,0 +1,141 @@
<?php
/**
* Class WPML_TF_Backend_Hooks
*
* @author OnTheGoSystems
*/
class WPML_TF_Backend_Hooks implements IWPML_Action {
const PAGE_HOOK = 'wpml-translation-feedback-list';
/** @var WPML_TF_Backend_Bulk_Actions_Factory */
private $bulk_actions_factory;
/** @var WPML_TF_Backend_Feedback_List_View_Factory feedback_list_view_factory */
private $feedback_list_view_factory;
/** @var WPML_TF_Backend_Styles $backend_styles */
private $backend_styles;
/** @var WPML_TF_Backend_Scripts */
private $backend_scripts;
/** @var wpdb $wpdb */
private $wpdb;
/**
* WPML_TF_Backend_Hooks constructor.
*
* @param WPML_TF_Backend_Bulk_Actions_Factory $bulk_actions_factory
* @param WPML_TF_Backend_Feedback_List_View_Factory $feedback_list_view_factory
* @param WPML_TF_Backend_Styles $backend_styles
* @param WPML_TF_Backend_Scripts $backend_scripts
* @param wpdb $wpdb
*/
public function __construct(
WPML_TF_Backend_Bulk_Actions_Factory $bulk_actions_factory,
WPML_TF_Backend_Feedback_List_View_Factory $feedback_list_view_factory,
WPML_TF_Backend_Styles $backend_styles,
WPML_TF_Backend_Scripts $backend_scripts,
wpdb $wpdb
) {
$this->bulk_actions_factory = $bulk_actions_factory;
$this->feedback_list_view_factory = $feedback_list_view_factory;
$this->backend_styles = $backend_styles;
$this->backend_scripts = $backend_scripts;
$this->wpdb = $wpdb;
}
/**
* method add_hooks
*/
public function add_hooks() {
add_action( 'wpml_admin_menu_configure', array( $this, 'add_translation_feedback_list_menu' ) );
add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts_action' ) );
add_action( 'current_screen', array( $this, 'bulk_actions_callback' ) );
add_filter( 'posts_where', array( $this, 'maybe_exclude_rating_only_status' ), 10, 2 );
}
/**
* Define translation feedback list menu and callback
*
* @param string $menu_id
*/
public function add_translation_feedback_list_menu( $menu_id ) {
if ( 'WPML' !== $menu_id ) {
return;
}
if ( current_user_can( 'manage_options' ) ) {
$menu = array();
$menu['order'] = 1200;
$menu['page_title'] = __( 'Translation Feedback', 'sitepress' );
$menu['menu_title'] = __( 'Translation Feedback', 'sitepress' );
$menu['capability'] = 'manage_options';
$menu['menu_slug'] = self::PAGE_HOOK;
$menu['function'] = array( $this, 'translation_feedback_list_display' );
do_action( 'wpml_admin_menu_register_item', $menu );
} else {
add_menu_page(
__( 'Translation Feedback', 'sitepress' ),
__( 'Translation Feedback', 'sitepress' ),
'read',
self::PAGE_HOOK,
array( $this, 'translation_feedback_list_display' )
);
}
}
/**
* Callback to display the feedback list page
*/
public function translation_feedback_list_display() {
$view = $this->feedback_list_view_factory->create();
echo $view->render_page();
}
/**
* @param string $hook
*/
public function admin_enqueue_scripts_action( $hook ) {
if ( $this->is_page_hook( $hook ) ) {
$this->backend_styles->enqueue();
$this->backend_scripts->enqueue();
}
}
public function bulk_actions_callback( $current_screen ) {
$hook = isset( $current_screen->id ) ? $current_screen->id : null;
if ( $this->is_page_hook( $hook ) ) {
$bulk_actions = $this->bulk_actions_factory->create();
$bulk_actions->process();
}
}
/**
* @param string $hook
*
* @return bool
*/
private function is_page_hook( $hook ) {
return strpos( $hook, self::PAGE_HOOK ) !== false;
}
/**
* @param string $where
* @param WP_Query $query
*
* @return string
*/
public function maybe_exclude_rating_only_status( $where, $query ) {
if ( isset( $query->query_vars['exclude_tf_rating_only'] ) && $query->query_vars['exclude_tf_rating_only'] ) {
$where .= " AND {$this->wpdb->posts}.post_status <> 'rating_only'";
}
return $where;
}
}

View File

@@ -0,0 +1,94 @@
<?php
/**
* Class WPML_TF_Backend_Options_AJAX_Hooks
*
* @author OnTheGoSystems
*/
class WPML_TF_Backend_Options_AJAX_Hooks implements IWPML_Action {
/** @var WPML_TF_Settings $settings */
private $settings;
/** @var WPML_TF_Settings_Write $settings_write */
private $settings_write;
/** @var WPML_TF_Promote_Notices $promote_notices */
private $promote_notices;
/** @var array $post_data */
private $post_data;
/**
* WPML_TF_Frontend_AJAX_Hooks constructor.
*
* @param WPML_TF_Settings $settings
* @param WPML_TF_Settings_Write $settings_write
* @param WPML_TF_Promote_Notices $promote_notices
* @param array $post_data
*/
public function __construct(
WPML_TF_Settings $settings,
WPML_TF_Settings_Write $settings_write,
WPML_TF_Promote_Notices $promote_notices,
array $post_data
) {
$this->settings = $settings;
$this->settings_write = $settings_write;
$this->promote_notices = $promote_notices;
$this->post_data = $post_data;
}
public function add_hooks() {
add_action( 'wp_ajax_' . WPML_TF_Backend_Options_AJAX_Hooks_Factory::AJAX_ACTION, array( $this, 'save_settings_callback' ) );
}
public function save_settings_callback() {
$new_settings = array();
if ( isset( $this->post_data['settings'] ) ) {
parse_str( $this->post_data['settings'], $new_settings );
}
if ( isset( $new_settings['enabled'] ) ) {
$this->settings->set_enabled( true );
$this->promote_notices->remove();
} else {
$this->settings->set_enabled( false );
}
if ( isset( $new_settings['button_mode'] ) ) {
$this->settings->set_button_mode( $new_settings['button_mode'] );
}
if ( isset( $new_settings['icon_style'] ) ) {
$this->settings->set_icon_style( $new_settings['icon_style'] );
}
if ( isset( $new_settings['languages_to'] ) ) {
$this->settings->set_languages_to( $new_settings['languages_to'] );
} else {
$this->settings->set_languages_to( array() );
}
if ( isset( $new_settings['display_mode'] ) ) {
$this->settings->set_display_mode( $new_settings['display_mode'] );
}
if ( isset( $new_settings['expiration_mode'] ) ) {
$this->settings->set_expiration_mode( $new_settings['expiration_mode'] );
}
if ( isset( $new_settings['expiration_delay_quantity'] ) ) {
$this->settings->set_expiration_delay_quantity( $new_settings['expiration_delay_quantity'] );
}
if ( isset( $new_settings['expiration_delay_unit'] ) ) {
$this->settings->set_expiration_delay_unit( $new_settings['expiration_delay_unit'] );
}
$this->settings_write->save( $this->settings );
wp_send_json_success( __( 'Settings saved', 'sitepress' ) );
}
}

View File

@@ -0,0 +1,82 @@
<?php
/**
* Class WPML_TF_Backend_Options_Hooks
*
* @author OnTheGoSystems
*/
class WPML_TF_Backend_Options_Hooks implements IWPML_Action {
const WPML_LOVE_ID = '#lang-sec-10';
/** @var WPML_TF_Backend_Options_View $options_view */
private $options_view;
/** @var WPML_TF_Backend_Options_Scripts $scripts */
private $scripts;
/** @var WPML_TF_Backend_Options_Styles $styles */
private $styles;
/** @var WPML_TF_Translation_Service $translation_service */
private $translation_service;
/**
* WPML_TF_Backend_Options_Hooks constructor.
*
* @param WPML_TF_Backend_Options_View $options_view
* @param WPML_TF_Backend_Options_Scripts $scripts
* @param WPML_TF_Backend_Options_Styles $styles
* @param WPML_TF_Translation_Service $translation_service
*/
public function __construct(
WPML_TF_Backend_Options_View $options_view,
WPML_TF_Backend_Options_Scripts $scripts,
WPML_TF_Backend_Options_Styles $styles,
WPML_TF_Translation_Service $translation_service
) {
$this->options_view = $options_view;
$this->scripts = $scripts;
$this->styles = $styles;
$this->translation_service = $translation_service;
}
/**
* Method add_hooks
*/
public function add_hooks() {
if ( $this->translation_service->allows_translation_feedback() ) {
add_action( 'wpml_after_settings', array( $this, 'display_options_ui' ) );
add_filter( 'wpml_admin_languages_navigation_items', array( $this, 'insert_navigation_item' ) );
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts_action' ) );
}
}
/**
* Method to render the options UI
*/
public function display_options_ui() {
echo $this->options_view->render();
}
/**
* @param array $items
*
* @return mixed
*/
public function insert_navigation_item( $items ) {
$insert_key_index = array_search( self::WPML_LOVE_ID, array_keys( $items ) );
$insert_position = false === $insert_key_index ? count( $items ) : $insert_key_index + 1;
return array_merge(
array_slice( $items, 0, $insert_position ),
array( '#wpml-translation-feedback-options' => esc_html__( 'Translation Feedback', 'sitepress' ) ),
array_slice( $items, $insert_position )
);
}
public function enqueue_scripts_action() {
$this->scripts->enqueue();
$this->styles->enqueue();
}
}

View File

@@ -0,0 +1,116 @@
<?php
/**
* Class WPML_TF_Backend_Post_List_Hooks
*
* @author OnTheGoSystems
*/
class WPML_TF_Backend_Post_List_Hooks implements IWPML_Action {
const RATING_COLUMN_ID = 'translation_rating';
/** @var WPML_TF_Post_Rating_Metrics $post_rating_metrics*/
private $post_rating_metrics;
/** @var WPML_TF_Document_Information $document_information */
private $document_information;
/** @var WPML_TF_Backend_Styles $styles */
private $styles;
public function __construct(
WPML_TF_Post_Rating_Metrics $post_rating_metrics,
WPML_TF_Document_Information $document_information,
WPML_TF_Backend_Styles $styles
) {
$this->post_rating_metrics = $post_rating_metrics;
$this->document_information = $document_information;
$this->styles = $styles;
}
/** @return array */
private function get_post_types() {
return array(
'edit-post' => 'posts',
'edit-page' => 'pages',
);
}
public function add_hooks() {
foreach ( $this->get_post_types() as $screen_id => $post_type ) {
add_filter( 'manage_' . $post_type . '_columns' , array( $this, 'add_rating_column_header' ) );
add_action( 'manage_' . $post_type . '_custom_column', array( $this, 'add_rating_column_content' ), 10, 2 );
add_filter( 'manage_' . $screen_id .'_sortable_columns', array( $this, 'add_rating_sortable_column' ) );
}
add_action( 'pre_get_posts', array( $this, 'order_posts_by_rating' ) );
add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts_action' ) );
}
/**
* @param array $columns
*
* @return array
*/
public function add_rating_column_header( array $columns ) {
$key_to_insert_before = 'date';
$column_name = __( 'Translation rating', 'sitepress' );
if ( array_key_exists( $key_to_insert_before, $columns ) ) {
$insert_position = array_search( $key_to_insert_before, array_keys( $columns ) );
$columns_before = array_slice( $columns, 0, $insert_position, true );
$columns_to_insert = array( self::RATING_COLUMN_ID => $column_name );
$columns_after = array_slice( $columns, $insert_position, null, true );
$columns = array_merge( $columns_before, $columns_to_insert, $columns_after );
} else {
$columns[ self::RATING_COLUMN_ID ] = $column_name;
}
return $columns;
}
/**
* @param string $column_name
* @param int $post_id
*/
public function add_rating_column_content( $column_name, $post_id ) {
if ( self::RATING_COLUMN_ID !== $column_name ) {
return;
}
$post_type = get_post_type( $post_id );
$this->document_information->init( $post_id, 'post_' . $post_type );
if ( $this->document_information->get_source_language() ) {
echo $this->post_rating_metrics->get_display( $post_id );
}
}
/**
* @param array $columns
*
* @return array
*/
public function add_rating_sortable_column( array $columns ) {
$columns[ self::RATING_COLUMN_ID ] = self::RATING_COLUMN_ID;
return $columns;
}
public function order_posts_by_rating( WP_Query $query ) {
$orderby = $query->get( 'orderby');
if( self::RATING_COLUMN_ID === $orderby ) {
$query->set( 'meta_key', WPML_TF_Post_Rating_Metrics::AVERAGE_KEY );
$query->set( 'orderby', 'meta_value_num' );
$query->set( 'meta_type', 'NUMERIC' );
}
}
public function admin_enqueue_scripts_action() {
$current_screen = get_current_screen();
if ( isset( $current_screen->id ) && array_key_exists( $current_screen->id, $this->get_post_types() ) ) {
$this->styles->enqueue();
}
}
}

View File

@@ -0,0 +1,59 @@
<?php
use WPML\FP\Obj;
/**
* Class WPML_TF_Backend_Promote_Hooks
*
* @author OnTheGoSystems
*/
class WPML_TF_Backend_Promote_Hooks implements IWPML_Action {
/** @var WPML_TF_Promote_Notices $promote_notices */
private $promote_notices;
/** @var WPML_TF_Translation_Service $translation_service */
private $translation_service;
/** @var bool $is_setup_complete */
private $is_setup_complete;
/**
* WPML_TF_Backend_Promote_Hooks constructor.
*
* @param WPML_TF_Promote_Notices $promote_notices
* @param bool $is_setup_complete
* @param WPML_TF_Translation_Service $translation_service
*/
public function __construct(
WPML_TF_Promote_Notices $promote_notices,
$is_setup_complete,
WPML_TF_Translation_Service $translation_service
) {
$this->promote_notices = $promote_notices;
$this->is_setup_complete = $is_setup_complete;
$this->translation_service = $translation_service;
}
public function add_hooks() {
if ( $this->translation_service->allows_translation_feedback() ) {
if ( $this->is_setup_complete && get_option( WPML_Installation::WPML_START_VERSION_KEY ) ) {
add_action(
'wpml_pro_translation_completed',
[ $this, 'add_notice_for_manager_on_job_completed' ],
10, 3
);
}
}
}
/**
* @param int $new_post_id
* @param array $fields
* @param stdClass $job
*/
public function add_notice_for_manager_on_job_completed( $new_post_id, $fields, $job ) {
if ( Obj::prop( 'translation_service', $job ) !== 'local' ) {
$this->promote_notices->show_notice_for_new_site( (int) $job->manager_id );
}
}
}

View File

@@ -0,0 +1,49 @@
<?php
/**
* Class WPML_TF_Common_Hooks
* @author OnTheGoSystems
*/
class WPML_TF_Common_Hooks implements IWPML_Action {
/** @var WPML_TF_Data_Object_Storage $feedback_storage */
private $feedback_storage;
public function __construct( WPML_TF_Data_Object_Storage $feedback_storage ) {
$this->feedback_storage = $feedback_storage;
}
/**
* method init
*/
public function add_hooks() {
add_action( 'init', array( $this, 'init_action' ) );
add_action( 'deleted_post', array( $this, 'cleanup_post_feedback_data' ) );
}
/**
* method init_action
*/
public function init_action() {
register_post_type( WPML_TF_Feedback_Post_Convert::POST_TYPE );
register_post_type( WPML_TF_Message_Post_Convert::POST_TYPE );
}
/** @param int $post_id */
public function cleanup_post_feedback_data( $post_id ) {
if ( WPML_TF_Feedback_Post_Convert::POST_TYPE === get_post_type( $post_id ) ) {
return;
}
$args = array(
'post_id' => $post_id,
);
$collection_filter = new WPML_TF_Feedback_Collection_Filter( $args );
$feedback_collection = $this->feedback_storage->get_collection( $collection_filter );
foreach ( $feedback_collection as $feedback ) {
$this->feedback_storage->delete( $feedback->get_id(), true );
}
}
}

View File

@@ -0,0 +1,154 @@
<?php
/**
* Class WPML_TF_Frontend_AJAX_Hooks
*
* @author OnTheGoSystems
*/
class WPML_TF_Frontend_AJAX_Hooks implements IWPML_Action {
/** @var WPML_TF_Data_Object_Storage $feedback_storage */
private $feedback_storage;
/** @var WPML_TF_Document_Information $document_information */
private $document_information;
/** @var WPML_TF_Post_Rating_Metrics $post_rating_metrics */
private $post_rating_metrics;
/** @var WPML_TP_Client_Factory $tp_client_factory */
private $tp_client_factory;
/** @var WPML_TP_Client $tp_client */
private $tp_client;
private $post_data;
/**
* WPML_TF_Frontend_AJAX_Hooks constructor.
*
* @param WPML_TF_Data_Object_Storage $feedback_storage
* @param WPML_TF_Document_Information $document_information
* @param WPML_TF_Post_Rating_Metrics $post_rating_metrics
* @param WPML_TP_Client_Factory|null $tp_client_factory
* @param mixed[]|null $post_data
*/
public function __construct(
WPML_TF_Data_Object_Storage $feedback_storage,
WPML_TF_Document_Information $document_information,
WPML_TF_Post_Rating_Metrics $post_rating_metrics,
WPML_TP_Client_Factory $tp_client_factory = null,
array $post_data = null
) {
$this->feedback_storage = $feedback_storage;
$this->document_information = $document_information;
$this->post_rating_metrics = $post_rating_metrics;
$this->tp_client_factory = $tp_client_factory;
$this->post_data = $post_data;
}
/**
* method init
*/
public function add_hooks() {
add_action( 'wp_ajax_nopriv_' . WPML_TF_Frontend_AJAX_Hooks_Factory::AJAX_ACTION, array( $this, 'save_feedback_callback' ) );
add_action( 'wp_ajax_' . WPML_TF_Frontend_AJAX_Hooks_Factory::AJAX_ACTION, array( $this, 'save_feedback_callback' ) );
}
/**
* Method callback
*/
public function save_feedback_callback() {
$feedback = null;
if ( isset( $this->post_data['feedback_id'] ) ) {
$feedback = $this->update_feedback( $this->post_data['feedback_id'] );
} elseif ( isset( $this->post_data['rating'], $this->post_data['document_id'], $this->post_data['document_type'] ) ) {
$feedback = $this->create_feedback();
}
if ( $feedback ) {
$feedback_id = $this->feedback_storage->persist( $feedback );
$this->post_rating_metrics->refresh( $feedback->get_document_id() );
wp_send_json_success( array( 'feedback_id' => $feedback_id ) );
} else {
wp_send_json_error( esc_html__( 'Invalid request!', 'sitepress' ) );
}
}
/**
* @param int $feedback_id
*
* @return WPML_TF_Feedback|null|false
*/
private function update_feedback( $feedback_id ) {
$feedback = $this->feedback_storage->get( $feedback_id );
if ( ! $feedback ) {
return false;
}
/** @var WPML_TF_Feedback $feedback */
if ( isset( $this->post_data['content'] ) ) {
$feedback->set_content( $this->post_data['content'] );
}
if ( isset( $this->post_data['rating'] ) ) {
$feedback->set_rating( $this->post_data['rating'] );
}
$feedback->set_status( $this->get_filtered_status() );
return $feedback;
}
/**
* @return WPML_TF_Feedback
*/
private function create_feedback() {
$this->document_information->init( $this->post_data['document_id'], $this->post_data['document_type'] );
$args = array(
'rating' => $this->post_data['rating'],
'status' => $this->get_filtered_status(),
'document_id' => $this->post_data['document_id'],
'document_type' => $this->post_data['document_type'],
'language_from' => $this->document_information->get_source_language(),
'language_to' => $this->document_information->get_language(),
'job_id' => $this->document_information->get_job_id(),
);
if ( $this->document_information->is_local_translation( $args['job_id'] ) ) {
$args['tp_rating_id'] = 0;
} elseif ( $this->get_tp_client() ) {
$active_service = $this->get_tp_client()->services()->get_active();
if ( isset( $active_service->feedback_forward_method ) ) {
$args['feedback_forward_method'] = $active_service->feedback_forward_method;
}
}
return new WPML_TF_Feedback( $args );
}
/** @return string */
private function get_filtered_status() {
$rating = isset( $this->post_data['rating'] ) ? $this->post_data['rating'] : null;
$status = 'pending';
if ( 3 < (int) $rating || empty( $this->post_data['content'] ) ) {
$status = 'rating_only';
}
return $status;
}
/** @return null|WPML_TP_Client */
private function get_tp_client() {
if ( $this->tp_client_factory && ! $this->tp_client ) {
$this->tp_client = $this->tp_client_factory->create();
}
return $this->tp_client;
}
}

View File

@@ -0,0 +1,65 @@
<?php
/**
* Class WPML_TF_Frontend_Hooks
*
* @author OnTheGoSystems
*/
class WPML_TF_Frontend_Hooks implements IWPML_Action {
/** @var WPML_TF_Frontend_Feedback_View $feedback_view */
private $feedback_view;
/** @var WPML_TF_Frontend_Scripts $scripts */
private $scripts;
/** @var WPML_TF_Frontend_Styles $styles */
private $styles;
/**
* WPML_TF_Frontend_Hooks constructor.
*
* @param WPML_TF_Frontend_Feedback_View $feedback_view
* @param WPML_TF_Frontend_Scripts $scripts
* @param WPML_TF_Frontend_Styles $styles
*/
public function __construct(
WPML_TF_Frontend_Feedback_View $feedback_view,
WPML_TF_Frontend_Scripts $scripts,
WPML_TF_Frontend_Styles $styles
) {
$this->feedback_view = $feedback_view;
$this->scripts = $scripts;
$this->styles = $styles;
}
/**
* method init
*/
public function add_hooks() {
add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts_action' ) );
add_action( 'wp_footer', array( $this, 'render_feedback_form' ) );
add_action( 'wpml_tf_feedback_open_link', array( $this, 'render_custom_form_open_link' ) );
}
/**
* method enqueue_scripts_action
*/
public function enqueue_scripts_action() {
$this->scripts->enqueue();
$this->styles->enqueue();
}
/**
* method render_feedback_form
*/
public function render_feedback_form() {
echo $this->feedback_view->render_open_button();
echo $this->feedback_view->render_form();
}
/** @param string|array $args */
public function render_custom_form_open_link( $args ) {
echo $this->feedback_view->render_custom_open_link( $args );
}
}

View File

@@ -0,0 +1,49 @@
<?php
/**
* Class WPML_TF_Translation_Queue_Hooks
*
* @author OnTheGoSystems
*/
class WPML_TF_Translation_Queue_Hooks implements IWPML_Action {
/** @var WPML_TF_Data_Object_Storage $feedback_storage */
private $feedback_storage;
public function __construct( WPML_TF_Data_Object_Storage $feedback_storage ) {
$this->feedback_storage = $feedback_storage;
}
public function add_hooks() {
add_filter( 'wpml_tm_allowed_translators_for_job', array( $this, 'add_reviewer_to_allowed_translators' ), 10, 2 );
}
public function add_reviewer_to_allowed_translators(
array $allowed_translators,
WPML_Translation_Job $translation_job
) {
$current_user_id = get_current_user_id();
if ( $translation_job->get_translator_id() !== $current_user_id ) {
$filter_args = array(
'reviewer_id' => $current_user_id,
);
$collection_filter = new WPML_TF_Feedback_Collection_Filter( $filter_args );
$feedback_collection = $this->feedback_storage->get_collection( $collection_filter );
foreach ( $feedback_collection as $feedback ) {
/** @var WPML_TF_Feedback $feedback */
if ( $feedback->get_job_id() === (int) $translation_job->get_id()
&& 'fixed' !== $feedback->get_status()
) {
$allowed_translators[] = $current_user_id;
break;
}
}
}
return $allowed_translators;
}
}

View File

@@ -0,0 +1,54 @@
<?php
/**
* Class WPML_TF_Translation_Service_Change_Hooks
*
* @author OnTheGoSystems
*/
class WPML_TF_Translation_Service_Change_Hooks implements IWPML_Action {
/** @var WPML_TF_Settings_Read $settings_read */
private $settings_read;
/** @var WPML_TF_Settings_Write $settings_write */
private $settings_write;
/** @var WPML_TF_TP_Ratings_Synchronize_Factory $tp_ratings_synchronize_factory */
private $tp_ratings_synchronize_factory;
public function __construct(
WPML_TF_Settings_Read $settings_read,
WPML_TF_Settings_Write $settings_write,
WPML_TF_TP_Ratings_Synchronize_Factory $tp_ratings_synchronize_factory
) {
$this->settings_read = $settings_read;
$this->settings_write = $settings_write;
$this->tp_ratings_synchronize_factory = $tp_ratings_synchronize_factory;
}
public function add_hooks() {
add_action(
'wpml_tm_before_set_translation_service',
array( $this, 'before_set_translation_service_callback' )
);
}
public function before_set_translation_service_callback( stdClass $service ) {
$this->cleanup_pending_ratings_queue();
$this->disable_tf_if_not_allowed_by_ts( $service );
}
private function cleanup_pending_ratings_queue() {
$tp_ratings_sync = $this->tp_ratings_synchronize_factory->create();
$tp_ratings_sync->run( true );
}
private function disable_tf_if_not_allowed_by_ts( stdClass $service ) {
if ( isset( $service->translation_feedback ) && ! $service->translation_feedback ) {
/** @var WPML_TF_Settings $settings */
$settings = $this->settings_read->get( 'WPML_TF_Settings' );
$settings->set_enabled( false );
$this->settings_write->save( $settings );
}
}
}

View File

@@ -0,0 +1,45 @@
<?php
/**
* Class WPML_TF_XML_RPC_Hooks
*
* @author OnTheGoSystems
*/
class WPML_TF_XML_RPC_Hooks implements IWPML_Action {
/** @var WPML_TF_XML_RPC_Feedback_Update_Factory $xml_rpc_feedback_update_factory */
private $xml_rpc_feedback_update_factory;
/** @var WPML_WP_API $wp_api */
private $wp_api;
public function __construct(
WPML_TF_XML_RPC_Feedback_Update_Factory $xml_rpc_feedback_update_factory,
WPML_WP_API $wp_api
) {
$this->xml_rpc_feedback_update_factory = $xml_rpc_feedback_update_factory;
$this->wp_api = $wp_api;
}
public function add_hooks() {
if ( $this->wp_api->constant( 'XMLRPC_REQUEST' ) ) {
add_filter( 'xmlrpc_methods', array( $this, 'add_tf_xmlrpc_methods' ) );
}
}
/**
* @param array $methods
*
* @return array
*/
public function add_tf_xmlrpc_methods( $methods ) {
$methods['translationproxy.update_feedback_status'] = array( $this, 'update_feedback_status' );
return $methods;
}
/** @param array $args */
public function update_feedback_status( array $args ) {
$xml_rpc_feedback_update = $this->xml_rpc_feedback_update_factory->create();
$xml_rpc_feedback_update->set_status( $args );
}
}

View File

@@ -0,0 +1,26 @@
<?php
/**
* Class WPML_TM_TF_Feedback_List_Hooks
*
* @author OnTheGoSystems
*/
class WPML_TM_TF_Feedback_List_Hooks implements IWPML_Action {
public function add_hooks() {
add_filter( 'wpml_use_tm_editor', array( $this, 'maybe_force_to_use_translation_editor' ) );
}
/**
* @param int|bool $use_translation_editor
*
* @return int|bool
*/
public function maybe_force_to_use_translation_editor( $use_translation_editor ) {
if ( ! current_user_can( 'edit_others_posts' ) ) {
$use_translation_editor = true;
}
return $use_translation_editor;
}
}

View File

@@ -0,0 +1,183 @@
<?php
/**
* Class WPML_TF_Feedback_Collection_Filter
*
* @author OnTheGoSystems
*/
class WPML_TF_Feedback_Collection_Filter implements IWPML_TF_Collection_Filter {
/** @var bool $exclude_rating_only */
private $exclude_rating_only;
/** @var array $language_pairs */
private $language_pairs;
/** @var int $pending_tp_ratings */
private $pending_tp_ratings;
/** @var int tp_feedback_id */
private $tp_feedback_id;
/** @var int $post_id */
private $post_id;
/** @var int $reviewer_id */
private $reviewer_id;
/**
* WPML_TF_Feedback_Collection_Filter constructor.
*
* @param array $args
*/
public function __construct( array $args ) {
if ( isset( $args['exclude_rating_only'] ) ) {
$this->exclude_rating_only = $args['exclude_rating_only'];
}
if ( isset( $args['language_pairs'] ) ) {
$this->language_pairs = $args['language_pairs'];
}
if ( isset( $args['pending_tp_ratings'] ) ) {
$this->pending_tp_ratings = (int) $args['pending_tp_ratings'];
}
if ( isset( $args['tp_feedback_id'] ) ) {
$this->tp_feedback_id = (int) $args['tp_feedback_id'];
}
if ( isset( $args['post_id'] ) ) {
$this->post_id = (int) $args['post_id'];
}
if ( isset( $args['reviewer_id'] ) ) {
$this->reviewer_id = (int) $args['reviewer_id'];
}
}
/** @return null|bool */
private function get_exclude_rating_only() {
return $this->exclude_rating_only;
}
/** @return null|array */
private function get_language_pairs() {
return $this->language_pairs;
}
/** @return null|int */
private function get_pending_tp_ratings() {
return $this->pending_tp_ratings;
}
/** @return null|int */
private function get_tp_feedback_id() {
return $this->tp_feedback_id;
}
/** @return null|int */
private function get_reviewer_id() {
return $this->reviewer_id;
}
/** @return null|int */
private function get_post_id() {
return $this->post_id;
}
/** @return array */
public function get_posts_args() {
$args = array(
'posts_per_page' => -1,
'post_type' => WPML_TF_Feedback_Post_Convert::POST_TYPE,
'suppress_filters' => false,
'post_status' => array( 'any', 'trash' ),
);
if ( $this->get_exclude_rating_only() ) {
$args['exclude_tf_rating_only'] = true;
}
if ( is_array( $this->get_language_pairs() ) ) {
$meta_query = array(
'relation' => 'OR',
);
foreach ( $this->get_language_pairs() as $from => $targets ) {
foreach ( $targets as $to => $value ) {
$meta_query[] = array(
'relation' => 'AND',
array(
'key' => WPML_TF_Data_Object_Storage::META_PREFIX . 'language_from',
'value' => $from,
'compare' => '=',
),
array(
'key' => WPML_TF_Data_Object_Storage::META_PREFIX . 'language_to',
'value' => $to,
'compare' => '=',
),
);
}
}
$args['meta_query'] = $meta_query;
} elseif ( $this->get_pending_tp_ratings() ) {
$args['posts_per_page'] = $this->get_pending_tp_ratings();
$args['orderby'] = 'ID';
$args['order'] = 'ASC';
$args['meta_query'] = array(
array(
'key' => WPML_TF_Data_Object_Storage::META_PREFIX . 'tp_rating_id',
'value' => '',
'compare' => '=',
'type' => 'CHAR',
),
);
} elseif ( $this->get_tp_feedback_id() ) {
$args['posts_per_page'] = 1;
$args['meta_query'] = array(
array(
'key' => WPML_TF_Data_Object_Storage::META_PREFIX . 'tp_feedback_id',
'value' => $this->get_tp_feedback_id(),
'compare' => '=',
),
);
} elseif ( $this->get_post_id() ) {
$args['meta_query'] = array(
'relation' => 'AND',
array(
'key' => WPML_TF_Data_Object_Storage::META_PREFIX . 'document_id',
'value' => $this->get_post_id(),
'compare' => '=',
),
array(
'key' => WPML_TF_Data_Object_Storage::META_PREFIX . 'document_type',
'value' => 'post_' . get_post_type( $this->get_post_id() ),
'compare' => '=',
),
);
} elseif ( $this->get_reviewer_id() ) {
$args['meta_query'] = array(
array(
'key' => WPML_TF_Data_Object_Storage::META_PREFIX . 'reviewer_id',
'value' => $this->get_reviewer_id(),
'compare' => '=',
),
);
}
return $args;
}
/** @return WPML_TF_Feedback_Collection */
public function get_new_collection() {
return new WPML_TF_Feedback_Collection();
}
}

View File

@@ -0,0 +1,240 @@
<?php
/**
* Class WPML_TF_Feedback_Collection
*
* @author OnTheGoSystems
*/
class WPML_TF_Feedback_Collection extends WPML_TF_Collection {
private $order;
private $filter_value;
/**
* @param int $offset
* @param int $length
*/
public function reduce_collection( $offset, $length ) {
$this->collection = array_slice( $this->collection, $offset, $length, true );
}
/**
* @param string $property
* @param string $order
*/
public function sort_collection( $property, $order ) {
$this->order = $order;
$method = 'compare_by_' . $property;
if ( method_exists( $this, $method ) ) {
// Use @ to avoid warnings in unit tests => see bug https://bugs.php.net/bug.php?id=50688
@uasort( $this->collection, array( $this, $method ) );
}
}
/**
* @param WPML_TF_Feedback $a
* @param WPML_TF_Feedback $b
*
* @return mixed
*/
private function compare_by_pending( WPML_TF_Feedback $a, WPML_TF_Feedback $b ) {
if ( $a->is_pending() && ! $b->is_pending() ) {
$compare = -1;
} elseif ( ! $a->is_pending() && $b->is_pending() ) {
$compare = 1;
} else {
$compare = $this->compare_by_id( $a, $b );
}
return $compare;
}
/**
* @param WPML_TF_Feedback $a
* @param WPML_TF_Feedback $b
*
* @return mixed
*/
private function compare_by_feedback( WPML_TF_Feedback $a, WPML_TF_Feedback $b ) {
if ( 'asc' === $this->order ) {
$compare = strcasecmp( $a->get_content(), $b->get_content() );
} else {
$compare = strcasecmp( $b->get_content(), $a->get_content() );
}
if ( 0 === $compare ) {
$compare = $this->compare_by_id( $a, $b );
}
return $compare;
}
/**
* @param WPML_TF_Feedback $a
* @param WPML_TF_Feedback $b
*
* @return mixed
*/
private function compare_by_rating( WPML_TF_Feedback $a, WPML_TF_Feedback $b ) {
if ( 'asc' === $this->order ) {
$compare = $a->get_rating() - $b->get_rating();
} else {
$compare = $b->get_rating() - $a->get_rating();
}
if ( 0 === $compare ) {
$compare = $this->compare_by_id( $a, $b );
}
return $compare;
}
/**
* @param WPML_TF_Feedback $a
* @param WPML_TF_Feedback $b
*
* @return mixed
*/
private function compare_by_status( WPML_TF_Feedback $a, WPML_TF_Feedback $b ) {
if ( 'asc' === $this->order ) {
$compare = strcmp( $a->get_text_status(), $b->get_text_status() );
} else {
$compare = strcmp( $b->get_text_status(), $a->get_text_status() );
}
if ( 0 === $compare ) {
$compare = $this->compare_by_id( $a, $b );
}
return $compare;
}
/**
* @param WPML_TF_Feedback $a
* @param WPML_TF_Feedback $b
*
* @return mixed
*/
private function compare_by_document_title( WPML_TF_Feedback $a, WPML_TF_Feedback $b ) {
if ( 'asc' === $this->order ) {
$compare = strcasecmp( $a->get_document_information()->get_title(), $b->get_document_information()->get_title() );
} else {
$compare = strcasecmp( $b->get_document_information()->get_title(), $a->get_document_information()->get_title() );
}
if ( 0 === $compare ) {
$compare = $this->compare_by_id( $a, $b );
}
return $compare;
}
/**
* @param WPML_TF_Feedback $a
* @param WPML_TF_Feedback $b
*
* @return mixed
*/
private function compare_by_date( WPML_TF_Feedback $a, WPML_TF_Feedback $b ) {
if ( 'asc' === $this->order ) {
$compare = strtotime( $a->get_date_created() ) - strtotime( $b->get_date_created() );
} else {
$compare = strtotime( $b->get_date_created() ) - strtotime( $a->get_date_created() );
}
if ( 0 === $compare ) {
$compare = $this->compare_by_id( $a, $b );
}
return $compare;
}
/**
* @param WPML_TF_Feedback $a
* @param WPML_TF_Feedback $b
*
* @return int
*/
private function compare_by_id( WPML_TF_Feedback $a, WPML_TF_Feedback $b ) {
if ( 'asc' === $this->order ) {
return $a->get_id() - $b->get_id();
} else {
return $b->get_id() - $a->get_id();
}
}
/**
* @param WPML_TF_Message_Collection $message_collection
*/
public function link_messages_to_feedback( WPML_TF_Message_Collection $message_collection ) {
foreach ( $message_collection as $message ) {
if ( array_key_exists( $message->get_feedback_id(), $this->collection ) ) {
$this->collection[ $message->get_feedback_id() ]->add_message( $message );
}
}
}
/**
* @param string $property
* @param string $value
*/
public function filter_by( $property, $value ) {
$this->filter_value = $value;
$method = 'filter_by_' . $property;
if ( method_exists( $this, $method ) ) {
$this->collection = array_filter( $this->collection, array( $this, $method ) );
}
}
/**
* @param WPML_TF_Feedback $feedback
*
* @return bool
*/
private function filter_by_status( WPML_TF_Feedback $feedback ) {
if ( $feedback->get_status() === $this->filter_value ) {
return true;
}
return false;
}
/**
* @param WPML_TF_Feedback $feedback
*
* @return bool
*/
private function filter_by_language( WPML_TF_Feedback $feedback ) {
if ( $feedback->get_language_to() === $this->filter_value ) {
return true;
}
return false;
}
/**
* @param WPML_TF_Feedback $feedback
*
* @return bool
*/
private function filter_by_post_id( WPML_TF_Feedback $feedback ) {
if ( 0 === strpos( $feedback->get_document_type(), 'post_' )
&& $feedback->get_document_id() === (int) $this->filter_value
) {
return true;
}
return false;
}
public function remove_trashed() {
foreach ( $this->collection as $id => $feedback ) {
/** @var WPML_TF_Feedback $feedback */
if ( 'trash' === $feedback->get_status() ) {
unset( $this->collection[ $id ] );
}
}
}
}

View File

@@ -0,0 +1,25 @@
<?php
/**
* Class WPML_TF_Feedback_Factory
*
* @author OnTheGoSystems
*/
class WPML_TF_Feedback_Factory {
/**
* @param array $feedback_data
*
* @return WPML_TF_Feedback
*/
public function create( array $feedback_data ) {
global $sitepress;
$document_information = new WPML_TF_Backend_Document_Information(
$sitepress,
class_exists( 'WPML_TP_Client_Factory' ) ? new WPML_TP_Client_Factory() : null
);
return new WPML_TF_Feedback( $feedback_data, $document_information );
}
}

View File

@@ -0,0 +1,88 @@
<?php
/**
* Class WPML_TF_Feedback_Post_Convert
*
* @author OnTheGoSystems
*/
class WPML_TF_Feedback_Post_Convert extends WPML_TF_Data_Object_Post_Convert {
const POST_TYPE = 'wpml_tf_feedback';
/**
* @return array
*/
public function get_post_fields() {
return array(
'id' => 'ID',
'date_created' => 'post_date',
'content' => 'post_content',
'status' => 'post_status',
);
}
/**
* @return array
*/
public function get_meta_fields() {
return array(
'rating',
'document_id',
'document_type',
'language_from',
'language_to',
'job_id',
'reviewer_id',
'tp_rating_id',
'tp_feedback_id',
'feedback_forward_method'
);
}
/**
* @param IWPML_TF_Data_Object $feedback
*
* @return array
* @throws Exception
*/
public function to_post_data( IWPML_TF_Data_Object $feedback ) {
if( ! $feedback instanceof WPML_TF_Feedback ) {
throw new Exception( 'The $feedback argument must be an instance of WPML_TF_Feedback' );
}
/** @var WPML_TF_Feedback $feedback */
$post = new stdClass();
$post->ID = $feedback->get_id();
$post->post_date = $feedback->get_date_created();
$post->post_content = $feedback->get_content();
$post->post_status = $feedback->get_status();
$post->post_type = self::POST_TYPE;
return array(
'post' => $post,
'metadata' => array(
'rating' => $feedback->get_rating(),
'document_id' => $feedback->get_document_id(),
'document_type' => $feedback->get_document_type(),
'language_from' => $feedback->get_language_from(),
'language_to' => $feedback->get_language_to(),
'job_id' => $feedback->get_job_id(),
'reviewer_id' => $feedback->get_reviewer()->get_id(),
'tp_rating_id' => $feedback->get_tp_responses()->get_rating_id(),
'tp_feedback_id' => $feedback->get_tp_responses()->get_feedback_id(),
'feedback_forward_method' => $feedback->get_tp_responses()->get_feedback_forward_method(),
),
);
}
/**
* @param array $post_data
*
* @return WPML_TF_Feedback
*/
public function to_object( array $post_data ) {
$feedback_data = $this->build_object_data_for_constructor( $post_data );
$feedback_factory = new WPML_TF_Feedback_Factory();
return $feedback_factory->create( $feedback_data );
}
}

View File

@@ -0,0 +1,41 @@
<?php
/**
* @author OnTheGoSystems
*/
class WPML_TF_Feedback_Reviewer {
/** @var int $id WP_User ID */
private $id;
/**
* WPML_TF_Feedback_Reviewer constructor.
*
* @param int $id
*/
public function __construct( $id ) {
$this->id = (int) $id;
}
/**
* @return int|null
*/
public function get_id() {
return $this->id;
}
/**
* @return string
*/
public function get_reviewer_display_name() {
$display_name = __( 'Unknown reviewer', 'sitepress' );
$reviewer = get_user_by( 'id', $this->get_id() );
if ( isset( $reviewer->display_name ) ) {
$display_name = $reviewer->display_name;
}
return $display_name;
}
}

View File

@@ -0,0 +1,138 @@
<?php
/**
* Class WPML_TF_Feedback_Status
*
* @author OnTheGoSystems
*/
class WPML_TF_Feedback_Status {
/** @var string $status */
private $status = 'pending';
/**
* WPML_TF_Feedback_Status constructor.
*
* @param string $status
*/
public function __construct( $status = null ) {
if ( $status ) {
$this->set_value( $status );
}
}
/** @param string $status*/
public function set_value( $status ) {
$this->status = sanitize_text_field( $status );
}
/** @return string */
public function get_value() {
return $this->status;
}
/** @return null|string */
public function get_display_text() {
switch ( $this->get_value() ) {
case 'pending':
return __( 'New', 'sitepress' );
case 'sent_to_translator':
if ( $this->is_admin_user() ) {
return __( 'Sent to translator', 'sitepress' );
}
return __( 'New', 'sitepress' );
case 'translator_replied':
if ( $this->is_admin_user() ) {
return __( 'Translator replied', 'sitepress' );
}
return __( 'Replied', 'sitepress' );
case 'admin_replied':
if ( $this->is_admin_user() ) {
return __( 'Sent to translator', 'sitepress' );
}
return __( 'Admin replied', 'sitepress' );
case 'sent_to_ts_api':
case 'sent_to_ts_manual':
return __( 'Sent to translation service', 'sitepress' );
case 'sent_to_ts_email':
return __( 'E-mail sent to translation service', 'sitepress' );
case 'fixed':
return __( 'Translation fixed', 'sitepress' );
case 'publish':
return __( 'Approved', 'sitepress' );
}
return null;
}
/** @return bool */
private function is_admin_user() {
return current_user_can( 'manage_options' );
}
/**
* This is used by the blue button on the feedback list
*
* @return array|null
*/
public function get_next_status() {
if ( $this->is_admin_user() ) {
switch ( $this->get_value() ) {
case 'pending':
case 'sent_to_translator':
return array(
'value' => 'sent_to_translator',
'label' => __( 'Send to translator', 'sitepress' ),
);
case 'translator_replied':
return array(
'value' => 'admin_replied',
'label' => __( 'Reply to translator', 'sitepress' ),
);
case 'admin_replied':
return array(
'value' => 'admin_replied',
'label' => __( 'Send to translator', 'sitepress' ),
);
}
} else {
switch ( $this->get_value() ) {
case 'sent_to_translator':
case 'translator_replied':
case 'admin_replied':
return array(
'value' => 'translator_replied',
'label' => __( 'Reply to admin', 'sitepress' ),
);
}
}
return null;
}
/** @return bool */
public function is_pending() {
$pending_statuses = array( 'pending' );
if ( current_user_can( 'manage_options' ) ) {
$pending_statuses[] = 'translator_replied';
} else {
$pending_statuses[] = 'admin_replied';
$pending_statuses[] = 'sent_to_translator';
}
return in_array( $this->status, $pending_statuses, true );
}
}

View File

@@ -0,0 +1,297 @@
<?php
/**
* Class WPML_TF_Feedback
*
* @author OnTheGoSystems
*/
class WPML_TF_Feedback implements IWPML_TF_Data_Object {
/** @var int */
private $id;
/** @var string */
private $date_created;
/** @var WPML_TF_Feedback_Status */
private $status;
/** @var int */
private $rating;
/** @var string */
private $content;
/** @var int */
private $document_id;
/** @var string */
private $document_type;
/** @var string */
private $language_from;
/** @var string */
private $language_to;
/** @var int|null */
private $job_id;
/** @var WPML_TF_Feedback_Reviewer */
private $reviewer;
/** @var WPML_TF_Collection $messages */
private $messages;
/** @var WPML_TF_Backend_Document_Information $document_information */
private $document_information;
/** @var WPML_TF_TP_Responses $tp_rating_responses */
private $tp_responses;
/**
* WPML_Translation_Feedback constructor.
*
* @param array $data
* @param WPML_TF_Backend_Document_Information $document_information
*/
public function __construct(
$data = array(),
WPML_TF_Backend_Document_Information $document_information = null
) {
$this->id = array_key_exists( 'id', $data ) ? (int) $data['id'] : null;
$this->date_created = array_key_exists( 'date_created', $data )
? sanitize_text_field( $data['date_created'] ) : null;
$this->rating = array_key_exists( 'rating', $data ) ? (int) $data['rating'] : null;
$this->content = array_key_exists( 'content', $data )
? sanitize_text_field( $data['content'] ) : '';
$this->document_id = array_key_exists( 'document_id', $data ) ? (int) $data['document_id'] : null;
$this->document_type = array_key_exists( 'document_type', $data )
? sanitize_text_field( $data['document_type'] ) : null;
$this->language_from = array_key_exists( 'language_from', $data )
? sanitize_text_field( $data['language_from'] ) : null;
$this->language_to = array_key_exists( 'language_to', $data )
? sanitize_text_field( $data['language_to'] ) : null;
$this->job_id = array_key_exists( 'job_id', $data ) ? (int) $data['job_id'] : null;
$this->messages = array_key_exists( 'messages', $data ) && $data['messages'] instanceof WPML_TF_Collection
? $data['messages'] : new WPML_TF_Message_Collection();
if ( array_key_exists( 'reviewer_id', $data ) ) {
$this->set_reviewer( $data['reviewer_id'] );
}
$this->status = array_key_exists( 'status', $data )
? new WPML_TF_Feedback_Status( $data['status'] ) : new WPML_TF_Feedback_Status( 'pending' );
$this->set_tp_responses( new WPML_TF_TP_Responses( $data ) );
if ( $document_information ) {
$this->set_document_information( $document_information );
}
}
/**
* @return int|mixed|null
*/
public function get_id() {
return $this->id;
}
/**
* @return int|null
*/
public function get_feedback_id() {
return $this->id;
}
/**
* @param \WPML_TF_Message $message
*/
public function add_message( WPML_TF_Message $message ) {
$this->messages->add( $message );
}
/**
* @return mixed|null|string
*/
public function get_date_created() {
return $this->date_created;
}
/**
* @return string
*/
public function get_status() {
return $this->status->get_value();
}
/**
* @param string $status
*/
public function set_status( $status ) {
$this->status->set_value( $status );
}
/**
* @return int
*/
public function get_rating() {
return $this->rating;
}
/**
* @param int $rating
*/
public function set_rating( $rating ) {
$this->rating = (int) $rating;
}
/**
* @return mixed|null|string
*/
public function get_content() {
return $this->content;
}
/**
* @param string $content
*/
public function set_content( $content ) {
$this->content = sanitize_text_field( $content );
}
/**
* @return int|null
*/
public function get_document_id() {
return $this->document_id;
}
/**
* @return null|string
*/
public function get_document_type() {
return $this->document_type;
}
/**
* @return null|string
*/
public function get_language_from() {
return $this->language_from;
}
/**
* @return null|string
*/
public function get_language_to() {
return $this->language_to;
}
/**
* @return int|null
*/
public function get_job_id() {
return $this->job_id;
}
/**
* @return WPML_TF_Feedback_Reviewer
*/
public function get_reviewer() {
if ( ! isset( $this->reviewer ) ) {
$this->set_reviewer( 0 );
}
return $this->reviewer;
}
/**
* @param int $reviewer_id
*/
public function set_reviewer( $reviewer_id ) {
$this->reviewer = new WPML_TF_Feedback_Reviewer( $reviewer_id );
}
/**
* @return WPML_TF_Collection
*/
public function get_messages() {
return $this->messages;
}
/** @param WPML_TF_TP_Responses $tp_responses */
public function set_tp_responses( WPML_TF_TP_Responses $tp_responses ) {
$this->tp_responses = $tp_responses;
}
/** @return WPML_TF_TP_Responses */
public function get_tp_responses() {
return $this->tp_responses;
}
/**
* @return string|null
*/
public function get_text_status() {
return $this->status->get_display_text();
}
/** @return array */
public function get_next_status() {
return $this->status->get_next_status();
}
/** @return bool */
public function is_pending() {
return $this->status->is_pending();
}
/**
* @return string
*/
public function get_document_flag_url() {
return $this->document_information->get_flag_url( $this->get_language_to() );
}
/**
* @return string
*/
public function get_source_document_flag_url() {
return $this->document_information->get_flag_url( $this->get_language_from() );
}
/**
* @return bool
*/
public function is_local_translation() {
return $this->document_information->is_local_translation( $this->get_job_id() );
}
/**
* @return string
*/
public function get_translator_name() {
return $this->document_information->get_translator_name( $this->get_job_id() );
}
/**
* @return array
*/
public function get_available_translators() {
return $this->document_information->get_available_translators( $this->language_from, $this->language_to );
}
/**
* @param WPML_TF_Backend_Document_Information $document_information
*/
public function set_document_information( WPML_TF_Backend_Document_Information $document_information ) {
$this->document_information = $document_information;
$this->document_information->init( $this->get_document_id(), $this->get_document_type() );
}
/** @return WPML_TF_Backend_Document_Information */
public function get_document_information() {
return $this->document_information;
}
}

View File

@@ -0,0 +1,117 @@
<?php
use WPML\API\Sanitize;
/**
* Class WPML_TF_TP_Responses
*
* @author OnTheGoSystems
*/
class WPML_TF_TP_Responses {
const FEEDBACK_FORWARD_MANUAL = 'manual';
const FEEDBACK_FORWARD_EMAIL = 'email';
const FEEDBACK_FORWARD_API = 'api';
const FEEDBACK_TP_URL_ENDPOINT = '/feedbacks/{feedback_id}/external';
/**
* @var string|int $tp_rating_id
*
* - empty string for rating never sent (or problems occurred during TP transmission)
* - 0 for local jobs (which does not need to be sent)
* - positive integer for ratings already sent
*
* This will allow to have a shorter DB query to select feedback to be sent
*/
private $rating_id = '';
/** @var null|int $feedback_id */
private $feedback_id;
/** @var null|string $feedback_forward_method */
private $feedback_forward_method;
public function __construct( array $args = array() ) {
if( isset( $args['tp_rating_id'] ) ) {
$this->set_rating_id( $args['tp_rating_id'] );
}
if( isset( $args['tp_feedback_id'] ) ) {
$this->set_feedback_id( $args['tp_feedback_id'] );
}
if( isset( $args['feedback_forward_method'] ) ) {
$this->set_feedback_forward_method( $args['feedback_forward_method'] );
}
}
/** @param string|int $rating_id */
public function set_rating_id( $rating_id ) {
if ( is_numeric( $rating_id ) ) {
$rating_id = (int) $rating_id;
}
$this->rating_id = $rating_id;
}
/** @return string|int */
public function get_rating_id() {
return $this->rating_id;
}
/** @param int $feedback_id */
public function set_feedback_id( $feedback_id ) {
$this->feedback_id = (int) $feedback_id;
}
/** @return null|int */
public function get_feedback_id() {
return $this->feedback_id;
}
/** @param string $method */
public function set_feedback_forward_method( $method ) {
$this->feedback_forward_method = Sanitize::string( $method );
}
/** @return null|string */
public function get_feedback_forward_method() {
return $this->feedback_forward_method;
}
/** @return bool */
public function is_manual_feedback() {
return self::FEEDBACK_FORWARD_MANUAL === $this->feedback_forward_method;
}
/** @return bool */
public function is_email_feedback() {
return self::FEEDBACK_FORWARD_EMAIL === $this->feedback_forward_method;
}
/** @return bool */
public function is_api_feedback() {
return self::FEEDBACK_FORWARD_API === $this->feedback_forward_method;
}
/** @return null|string */
public function get_feedback_tp_url() {
$url = null;
if ( $this->is_api_feedback() && defined( 'OTG_TRANSLATION_PROXY_URL' ) ) {
$url = OTG_TRANSLATION_PROXY_URL . self::FEEDBACK_TP_URL_ENDPOINT;
$url = preg_replace( '/{feedback_id}/', $this->get_feedback_id(), $url );
}
return $url;
}
/** @return array */
public function get_strings() {
return array(
'display_for_manual' => __( '%1s cannot receive feedback about the translation automatically. Please log-in to %1s website and report these issues manually.', 'sitepress' ),
'display_for_email' => __( 'An email has been sent to %s to report the issue. Please check your email for a feedback from their part.', 'sitepress' ),
'display_for_api' => __( 'Issue tracking in %s', 'sitepress' ),
);
}
}

View File

@@ -0,0 +1,19 @@
<?php
/**
* Interface IWPML_TF_Collection_Filter
*
* @author OnTheGoSystems
*/
interface IWPML_TF_Collection_Filter {
/**
* @return array
*/
public function get_posts_args();
/**
* @return WPML_TF_Collection
*/
public function get_new_collection();
}

View File

@@ -0,0 +1,24 @@
<?php
/**
* Interface IWPML_TF_Data_Object
*
* @author OnTheGoSystems
*/
interface IWPML_TF_Data_Object {
/**
* @return int
*/
public function get_id();
/**
* @return int|null
*/
public function get_feedback_id();
/**
* @param \WPML_TF_Message $message
*/
public function add_message( WPML_TF_Message $message );
}

View File

@@ -0,0 +1,74 @@
<?php
/**
* Class WPML_TF_Message_Collection_Filter
*
* @author OnTheGoSystems
*/
class WPML_TF_Message_Collection_Filter implements IWPML_TF_Collection_Filter {
/** @var int $feedback_id */
private $feedback_id;
/** @var array|null */
private $feedback_ids;
/**
* WPML_TF_Feedback_Collection_Filter constructor.
*
* @param array $args
*/
public function __construct( array $args = array() ) {
if ( isset( $args['feedback_id'] ) ) {
$this->feedback_id = (int) $args['feedback_id'];
}
if ( isset( $args['feedback_ids'] ) && is_array( $args['feedback_ids'] ) ) {
$this->feedback_ids = $args['feedback_ids'];
}
}
/**
* @return int|null
*/
private function get_feedback_id() {
return $this->feedback_id;
}
/**
* @return array|null
*/
private function get_feedback_ids() {
return $this->feedback_ids;
}
/**
* @return array
*/
public function get_posts_args() {
$posts_args = array(
'posts_per_page' => -1,
'post_type' => WPML_TF_Message_Post_Convert::POST_TYPE,
'suppress_filters' => false,
'post_status' => 'any',
);
if ( $this->get_feedback_id() ) {
$posts_args['post_parent'] = $this->get_feedback_id();
}
if ( $this->get_feedback_ids() ) {
$posts_args['post_parent__in'] = $this->get_feedback_ids();
}
return $posts_args;
}
/**
* @return WPML_TF_Message_Collection
*/
public function get_new_collection() {
return new WPML_TF_Message_Collection();
}
}

View File

@@ -0,0 +1,10 @@
<?php
/**
* Class WPML_TF_Message_Collection
*
* @author OnTheGoSystems
*/
class WPML_TF_Message_Collection extends WPML_TF_Collection {
}

View File

@@ -0,0 +1,69 @@
<?php
/**
* Class WPML_TF_Message_Post_Convert
*
* @author OnTheGoSystems
*/
class WPML_TF_Message_Post_Convert extends WPML_TF_Data_Object_Post_Convert {
const POST_TYPE = 'wpml_tf_message';
/**
* @return array
*/
public function get_post_fields() {
return array(
'id' => 'ID',
'date_created' => 'post_date',
'content' => 'post_content',
'feedback_id' => 'post_parent',
);
}
/**
* @return array
*/
public function get_meta_fields() {
return array(
'author_id',
);
}
/**
* @param IWPML_TF_Data_Object $message
*
* @return array
* @throws Exception
*/
public function to_post_data( IWPML_TF_Data_Object $message ) {
if( ! $message instanceof WPML_TF_Message ) {
throw new Exception( 'The $message argument must be an instance of WPML_TF_Message' );
}
/** @var WPML_TF_Message $message */
$post = new stdClass();
$post->ID = $message->get_id();
$post->post_date = $message->get_date_created();
$post->post_content = $message->get_content();
$post->post_parent = $message->get_feedback_id();
$post->post_type = self::POST_TYPE;
return array(
'post' => $post,
'metadata' => array(
'author_id' => $message->get_author_id(),
),
);
}
/**
* @param array $post_data
*
* @return WPML_TF_Message
*/
public function to_object( array $post_data ) {
$message_data = $this->build_object_data_for_constructor( $post_data );
return new WPML_TF_Message( $message_data );
}
}

View File

@@ -0,0 +1,99 @@
<?php
/**
* Class WPML_TF_Message
*
* @author OnTheGoSystems
*/
class WPML_TF_Message implements IWPML_TF_Data_Object {
/** @var int $id */
private $id;
/** @var int $feedback_id */
private $feedback_id;
/** @var string $date_created */
private $date_created;
/** @var string $content */
private $content;
/** @var string $author_id */
private $author_id;
/**
* WPML_Translation_Feedback constructor.
*
* @param array $data
*/
public function __construct( $data = array() ) {
$this->id = array_key_exists( 'id', $data ) ? (int) $data['id'] : null;
$this->feedback_id = array_key_exists( 'feedback_id', $data ) ? (int) $data['feedback_id'] : null;
$this->date_created = array_key_exists( 'date_created', $data )
? sanitize_text_field( $data['date_created'] ) : null;
$this->content = array_key_exists( 'content', $data )
? sanitize_text_field( $data['content'] ) : null;
$this->author_id = array_key_exists( 'author_id', $data )
? (int) $data['author_id'] : null;
}
/**
* @return int|mixed|null
*/
public function get_id() {
return $this->id;
}
/**
* @return int|null
*/
public function get_feedback_id() {
return $this->feedback_id;
}
/**
* @param \WPML_TF_Message $message
*/
public function add_message( WPML_TF_Message $message ) {
return;
}
/**
* @return mixed|null|string
*/
public function get_date_created() {
return $this->date_created;
}
/**
* @return mixed|null|string
*/
public function get_content() {
return $this->content;
}
/**
* @return int|null
*/
public function get_author_id() {
return $this->author_id;
}
/** @return string */
public function get_author_display_label() {
$label = __( 'Translator', 'sitepress' );
if ( user_can( $this->get_author_id(), 'manage_options' ) ) {
$label = __( 'Admin', 'sitepress' );
}
return $label;
}
/** @return bool */
public function author_is_current_user() {
$current_user = wp_get_current_user();
return $current_user->ID === $this->author_id;
}
}

View File

@@ -0,0 +1,26 @@
<?php
class WPML_TF_Collection_Filter_Factory {
/**
* @param string $type
* @param array $args
*
* @return null|IWPML_TF_Collection_Filter
*/
public function create( $type, array $args = array() ) {
$collection_filter = null;
switch ( $type ) {
case 'feedback':
$collection_filter = new WPML_TF_Feedback_Collection_Filter( $args );
break;
case 'message':
$collection_filter = new WPML_TF_Message_Collection_Filter( $args );
break;
}
return $collection_filter;
}
}

View File

@@ -0,0 +1,71 @@
<?php
/**
* Class WPML_TF_Collection
*
* @author OnTheGoSystems
*/
class WPML_TF_Collection implements Iterator, Countable {
/** @var array<\IWPML_TF_Data_Object> */
protected $collection = array();
/**
* @param \IWPML_TF_Data_Object $data_object
*/
public function add( IWPML_TF_Data_Object $data_object ) {
$this->collection[ $data_object->get_id() ] = $data_object;
}
/**
* @return array
*/
public function get_ids() {
return array_keys( $this->collection );
}
/**
* @param int $id
*
* @return IWPML_TF_Data_Object|null
*/
public function get( $id ) {
return array_key_exists( $id, $this->collection ) ? $this->collection[ $id ] : null;
}
/**
* @return int
*/
public function count() {
return count( $this->collection );
}
public function rewind() {
reset( $this->collection );
}
/**
* @return mixed
*/
public function current() {
return current( $this->collection );
}
/**
* @return mixed
*/
public function key() {
return key( $this->collection );
}
public function next() {
next( $this->collection );
}
/**
* @return bool
*/
public function valid() {
return key( $this->collection ) !== null;
}
}

View File

@@ -0,0 +1,54 @@
<?php
/**
* Class WPML_TF_Data_Object_Post_Convert
*
* @author OnTheGoSystems
*/
abstract class WPML_TF_Data_Object_Post_Convert {
/**
* @return array
*/
abstract public function get_post_fields();
/**
* @return array
*/
abstract public function get_meta_fields();
/**
* @param IWPML_TF_Data_Object $data_object
*
* @return array
*/
abstract public function to_post_data( IWPML_TF_Data_Object $data_object );
/**
* @param array $post_data
*
* @return object
*/
abstract public function to_object( array $post_data );
/**
* @param array $post_data
*
* @return array
*/
protected function build_object_data_for_constructor( array $post_data ) {
$object_data = array();
foreach ( $this->get_post_fields() as $feedback_field => $post_field ) {
$object_data[ $feedback_field ] = isset( $post_data['post']->{$post_field} )
? $post_data['post']->{$post_field} : null;
}
foreach ( $this->get_meta_fields() as $meta_field ) {
$object_data[ $meta_field ] = isset( $post_data['metadata'][ $meta_field ] )
? $post_data['metadata'][ $meta_field ] : null;
}
return $object_data;
}
}

View File

@@ -0,0 +1,124 @@
<?php
/**
* Class WPML_TF_Data_Object_Storage
*
* @author OnTheGoSystems
*/
class WPML_TF_Data_Object_Storage {
const META_PREFIX = 'wpml_tf_';
/** @var WPML_TF_Data_Object_Post_Convert */
private $post_convert;
/**
* WPML_TF_Data_Object_Storage constructor.
*
* @param WPML_TF_Data_Object_Post_Convert $post_convert
*/
public function __construct( WPML_TF_Data_Object_Post_Convert $post_convert ) {
$this->post_convert = $post_convert;
}
/**
* @param int $id
*
* @return IWPML_TF_Data_Object|null
*/
public function get( $id ) {
$result = null;
$post_data = array();
$post_data['post'] = get_post( $id );
if ( $post_data['post'] ) {
foreach ( $this->post_convert->get_meta_fields() as $meta_field ) {
$post_data['metadata'][ $meta_field ] = get_post_meta( $id, self::META_PREFIX . $meta_field, true );
}
$result = $this->post_convert->to_object( $post_data );
}
return $result;
}
/**
* @param IWPML_TF_Data_Object $data_object
*
* @return int|WP_Error
*/
public function persist( IWPML_TF_Data_Object $data_object ) {
$post_data = $this->post_convert->to_post_data( $data_object );
$updated_id = wp_insert_post( $post_data['post'] );
if ( $updated_id && ! is_wp_error( $updated_id ) ) {
foreach ( $post_data['metadata'] as $key => $value ) {
update_post_meta( $updated_id, self::META_PREFIX . $key, $value );
}
}
return $updated_id;
}
/**
* @param int $id
* @param bool $force_delete
*/
public function delete( $id, $force_delete = false ) {
if ( $force_delete ) {
wp_delete_post( $id );
} else {
wp_trash_post( $id );
}
}
/** @param int $id */
public function untrash( $id ) {
wp_untrash_post( $id );
}
/**
* @param IWPML_TF_Collection_Filter $collection_filter
*
* @return WPML_TF_Collection
*/
public function get_collection( IWPML_TF_Collection_Filter $collection_filter ) {
$collection = $collection_filter->get_new_collection();
$posts_args = $collection_filter->get_posts_args();
if ( isset( $posts_args['meta_query']['relation'] ) && 'OR' === $posts_args['meta_query']['relation'] ) {
$object_posts = $this->get_posts_from_split_queries( $posts_args );
} else {
$object_posts = get_posts( $posts_args );
}
foreach ( $object_posts as $object_post ) {
$collection->add( $this->get( $object_post->ID ) );
}
return $collection;
}
/**
* For more than 2 meta queries with "OR" relation, the standard WP query has a very bad performance.
* It's much more efficient to make one query for each meta query.
*
* @param array $posts_args
*
* @return array
*/
private function get_posts_from_split_queries( array $posts_args ) {
$object_posts = array();
$meta_query_parts = $posts_args['meta_query'];
unset( $meta_query_parts['relation'] );
foreach ( $meta_query_parts as $meta_query ) {
$posts_args['meta_query'] = $meta_query;
$object_posts = $object_posts + get_posts( $posts_args );
}
return $object_posts;
}
}

View File

@@ -0,0 +1,255 @@
<?php
/**
* Class WPML_TF_Feedback_Edit
*
* @author OnTheGoSystems
*/
class WPML_TF_Feedback_Edit {
/** @var WPML_TF_Feedback_Query */
private $feedback_query;
/** @var WPML_TF_Data_Object_Storage $feedback_storage */
private $feedback_storage;
/** @var WPML_TF_Data_Object_Storage $message_storage */
private $message_storage;
/** @var null|WPML_TP_Client_Factory $tp_client_factory */
private $tp_client_factory;
/** @var null|WPML_TP_Client $tp_client */
private $tp_client;
/**
* WPML_TF_Feedback_Edit constructor.
*
* @param \WPML_TF_Feedback_Query $feedback_query
* @param \WPML_TF_Data_Object_Storage $feedback_storage
* @param \WPML_TF_Data_Object_Storage $message_storage
* @param \WPML_TP_Client_Factory|null $tp_client_factory
*/
public function __construct(
WPML_TF_Feedback_Query $feedback_query,
WPML_TF_Data_Object_Storage $feedback_storage,
WPML_TF_Data_Object_Storage $message_storage,
WPML_TP_Client_Factory $tp_client_factory = null
) {
$this->feedback_query = $feedback_query;
$this->feedback_storage = $feedback_storage;
$this->message_storage = $message_storage;
$this->tp_client_factory = $tp_client_factory;
}
/**
* @param int $feedback_id
* @param array $args
*
* @return null|WPML_TF_Feedback
* @throws \WPML_TF_Feedback_Update_Exception
*/
public function update( $feedback_id, array $args ) {
$feedback = $this->feedback_query->get_one( $feedback_id );
if ( $feedback ) {
$this->update_feedback_content( $feedback, $args );
$this->add_message_to_feedback( $feedback, $args );
$this->assign_feedback_to_reviewer( $feedback, $args );
$this->update_feedback_status( $feedback, $args );
$this->feedback_storage->persist( $feedback );
$feedback = $this->feedback_query->get_one( $feedback_id, true );
}
return $feedback;
}
/**
* @param WPML_TF_Feedback $feedback
* @param array $args
*/
private function update_feedback_content( WPML_TF_Feedback $feedback, array $args ) {
if ( isset( $args['feedback_content'] ) && $this->is_admin_user() ) {
$feedback->set_content( $args['feedback_content'] );
}
}
/**
* @param WPML_TF_Feedback $feedback
* @param array $args
*/
private function add_message_to_feedback( WPML_TF_Feedback $feedback, array $args ) {
if ( isset( $args['message_content'] ) ) {
$message_args = array(
'feedback_id' => $feedback->get_id(),
'content' => $args['message_content'],
'author_id' => get_current_user_id(),
);
$message = new WPML_TF_Message( $message_args );
$feedback->add_message( $message );
$this->message_storage->persist( $message );
}
}
/**
* @param WPML_TF_Feedback $feedback
* @param array $args
*/
private function assign_feedback_to_reviewer( WPML_TF_Feedback $feedback, array $args ) {
if ( isset( $args['feedback_reviewer_id'] ) && $this->is_admin_user() ) {
$feedback->set_reviewer( $args['feedback_reviewer_id'] );
}
}
/**
* @param WPML_TF_Feedback $feedback
* @param array $args
*
* @throws \WPML_TF_Feedback_Update_Exception
*/
private function update_feedback_status( WPML_TF_Feedback $feedback, array $args ) {
if ( isset( $args['feedback_status'] )
&& in_array( $args['feedback_status'], $this->get_feedback_statuses(), true )
) {
if ( 'sent_to_translator' === $args['feedback_status'] && ! $feedback->is_local_translation() ) {
$this->send_feedback_to_tp( $feedback );
} elseif ( 'sent_to_ts_api' === $args['feedback_status'] ) {
$this->update_feedback_status_from_tp( $feedback );
} else {
$feedback->set_status( $args['feedback_status'] );
}
}
}
/**
* @param int $feedback_id
*
* @return bool
*/
public function delete( $feedback_id ) {
if ( $this->is_admin_user() ) {
$this->feedback_storage->delete( $feedback_id );
return true;
}
return false;
}
/** @return bool */
private function is_admin_user() {
return current_user_can( 'manage_options' );
}
/** @return array */
private function get_feedback_statuses() {
return array(
'pending',
'sent_to_translator',
'translator_replied',
'admin_replied',
'fixed',
'sent_to_ts_api',
);
}
/**
* @param WPML_TF_Feedback $feedback
*
* @throws WPML_TF_Feedback_Update_Exception
*/
private function send_feedback_to_tp( WPML_TF_Feedback $feedback ) {
$current_user = wp_get_current_user();
$args = array(
'email' => $current_user->user_email,
);
$tp_feedback_id = $this->get_tp_client()->feedback()->send( $feedback, $args );
if ( ! $tp_feedback_id ) {
throw new WPML_TF_Feedback_Update_Exception( $this->get_communication_error_message( 'send' ) );
}
$feedback->get_tp_responses()->set_feedback_id( $tp_feedback_id );
$active_service = $this->get_tp_client()->services()->get_active();
$feedback->get_tp_responses()->set_feedback_forward_method( $active_service->get_feedback_forward_method() );
$new_status = 'sent_to_ts_' . $active_service->get_feedback_forward_method();
$feedback->set_status( $new_status );
}
/**
* @param WPML_TF_Feedback $feedback
*
* @throws WPML_TF_Feedback_Update_Exception
*/
private function update_feedback_status_from_tp( WPML_TF_Feedback $feedback ) {
$tp_feedback_status = $this->get_tp_client()->feedback()->status( $feedback );
if ( ! $tp_feedback_status ) {
throw new WPML_TF_Feedback_Update_Exception( $this->get_communication_error_message( 'status' ) );
} elseif ( 'closed' === $tp_feedback_status ) {
$feedback->set_status( 'fixed' );
}
}
/**
* @param string $endpoint
*
* @return string
* @throws \WPML_TF_Feedback_Update_Exception
*/
private function get_communication_error_message( $endpoint ) {
$active_service = $this->get_tp_client()->services()->get_active();
$service_name = isset( $active_service->name ) ? $active_service->name : esc_html__( 'Translation Service', 'sitepress' );
if ( 'send' === $endpoint ) {
$error_message = sprintf(
esc_html__( 'Could not send the report to %s.', 'sitepress' ),
$service_name
);
$error_message .= ' ' . sprintf(
esc_html__( "This means that %s isn't yet aware of the problem in the translation and cannot fix it.", 'sitepress' ),
$service_name
);
} else {
$error_message = sprintf(
esc_html__( 'Could not fetch the status from %s.', 'sitepress' ),
$service_name
);
}
$error_message .= ' ' . sprintf(
esc_html__( "Let's get it working for you. Please contact %1sWPML support%2s and give them the following error details:", 'sitepress' ),
'<a href="https://wpml.org/forums/forum/english-support/" target="_blank">',
'</a>'
);
$error_message .= '<br><div class="js-wpml-tf-error-details"><a href="#">' .
esc_html__( 'Show details', 'sitepress' ) . '</a>' .
'<pre style="display:none;">' . esc_html( $this->get_tp_client()->feedback()->get_error_message() ) .
'</pre></div>';
return $error_message;
}
/**
* @return null|WPML_TP_Client
*
* @throws WPML_TF_Feedback_Update_Exception
*/
private function get_tp_client() {
if ( ! $this->tp_client && $this->tp_client_factory ) {
$this->tp_client = $this->tp_client_factory->create();
if ( ! $this->tp_client ) {
throw new WPML_TF_Feedback_Update_Exception(
esc_html__( 'WPML cannot communicate with the remote translation service. Please make sure WPML Translation Management is active.', 'sitepress' )
);
}
}
return $this->tp_client;
}
}

View File

@@ -0,0 +1,225 @@
<?php
/**
* Class WPML_TF_Feedback_Query
*
* @author OnTheGoSystems
*/
class WPML_TF_Feedback_Query {
/** @var WPML_TF_Data_Object_Storage $feedback_storage */
private $feedback_storage;
/** @var WPML_TF_Data_Object_Storage $message_storage */
private $message_storage;
/** @var WPML_TF_Collection_Filter_Factory $collection_filter_factory */
private $collection_filter_factory;
/** @var WPML_TF_Feedback_Collection $unfiltered_feedback_collection */
private $unfiltered_feedback_collection;
/** @var int $unfiltered_items_count */
private $unfiltered_items_count;
/** @var int $trashed_items_count */
private $trashed_items_count;
/** @var int $total_items_count */
private $total_items_count;
/** @var int $filtered_items_count */
private $filtered_items_count;
/** @var bool $is_in_trash */
private $is_in_trash = false;
/**
* WPML_TF_Feedback_Collection_Factory constructor.
*
* @param WPML_TF_Data_Object_Storage $feedback_storage
* @param WPML_TF_Data_Object_Storage $message_storage
* @param WPML_TF_Collection_Filter_Factory $collection_filter_factory
*/
public function __construct(
WPML_TF_Data_Object_Storage $feedback_storage,
WPML_TF_Data_Object_Storage $message_storage,
WPML_TF_Collection_Filter_Factory $collection_filter_factory
) {
$this->feedback_storage = $feedback_storage;
$this->message_storage = $message_storage;
$this->collection_filter_factory = $collection_filter_factory;
}
/**
* @return WPML_TF_Feedback_Collection
*/
public function get_unfiltered_collection() {
if ( ! $this->unfiltered_feedback_collection ) {
$storage_filters = array(
'exclude_rating_only' => true,
);
if ( ! current_user_can( 'manage_options' ) ) {
$storage_filters['reviewer_id'] = get_current_user_id();
}
$feedback_collection_filter = $this->collection_filter_factory->create( 'feedback', $storage_filters );
$this->unfiltered_feedback_collection = $this->feedback_storage->get_collection( $feedback_collection_filter );
$this->unfiltered_items_count = count( $this->unfiltered_feedback_collection );
}
return $this->unfiltered_feedback_collection;
}
/**
* @param array $args
*
* @return WPML_TF_Feedback_Collection
*/
public function get( array $args ) {
$feedback_collection = clone $this->get_unfiltered_collection();
$feedback_collection = $this->trash_filter_collection( $feedback_collection, $args );
$feedback_collection = $this->filter_collection( $feedback_collection, $args );
$feedback_collection = $this->sort_collection( $feedback_collection, $args );
$feedback_collection = $this->apply_pagination( $feedback_collection, $args );
$message_filter_args = array( 'feedback_ids' => $feedback_collection->get_ids() );
$message_collection_filter = $this->collection_filter_factory->create( 'message', $message_filter_args );
/** @var WPML_TF_Message_Collection $message_collection */
$message_collection = $this->message_storage->get_collection( $message_collection_filter );
$feedback_collection->link_messages_to_feedback( $message_collection );
return $feedback_collection;
}
/**
* @param WPML_TF_Feedback_Collection $feedback_collection
* @param array $args
*
* @return WPML_TF_Feedback_Collection
*/
public function trash_filter_collection( WPML_TF_Feedback_Collection $feedback_collection, array $args ) {
if ( isset( $args['status'] ) && 'trash' === $args['status'] ) {
$this->is_in_trash = true;
$feedback_collection->filter_by( 'status', 'trash' );
$this->trashed_items_count = count( $feedback_collection );
$this->total_items_count = $this->unfiltered_items_count - $this->trashed_items_count;
} else {
$feedback_collection->remove_trashed();
$this->total_items_count = count( $feedback_collection );
$this->trashed_items_count = $this->unfiltered_items_count - $this->total_items_count;
}
return $feedback_collection;
}
/**
* @param WPML_TF_Feedback_Collection $feedback_collection
* @param array $args
*
* @return WPML_TF_Feedback_Collection
*/
private function filter_collection( WPML_TF_Feedback_Collection $feedback_collection, array $args ) {
if ( isset( $args['status'] ) && 'trash' !== $args['status'] ) {
$feedback_collection->filter_by( 'status', $args['status'] );
} elseif ( isset( $args['language'] ) ) {
$feedback_collection->filter_by( 'language', $args['language'] );
} elseif ( isset( $args['post_id'] ) ) {
$feedback_collection->filter_by( 'post_id', $args['post_id'] );
}
$this->filtered_items_count = count( $feedback_collection );
return $feedback_collection;
}
/**
* @param WPML_TF_Feedback_Collection $feedback_collection
* @param array $args
*
* @return WPML_TF_Feedback_Collection
*/
private function sort_collection( WPML_TF_Feedback_Collection $feedback_collection, array $args ) {
$order_by = 'pending';
$order = 'desc';
if ( isset( $args['orderby'] ) ) {
$order_by = $args['orderby'];
}
if ( isset( $args['order'] ) ) {
$order = $args['order'];
}
$feedback_collection->sort_collection( $order_by, $order );
return $feedback_collection;
}
/**
* @param WPML_TF_Feedback_Collection $feedback_collection
* @param array $args
*
* @return WPML_TF_Feedback_Collection
*/
private function apply_pagination( WPML_TF_Feedback_Collection $feedback_collection, array $args ) {
if ( isset( $args['paged'], $args['items_per_page'] ) ) {
$offset = $args['items_per_page'] * max( 0, $args['paged'] - 1 );
$feedback_collection->reduce_collection( $offset, $args['items_per_page'] );
}
return $feedback_collection;
}
/**
* @return int
*/
public function get_total_items_count() {
return $this->total_items_count;
}
/** @return int */
public function get_total_trashed_items_count() {
return $this->trashed_items_count;
}
/** @return int */
public function get_filtered_items_count() {
return $this->filtered_items_count;
}
/** @return bool */
public function is_in_trash() {
return $this->is_in_trash;
}
/**
* @param int $feedback_id
* @param bool $with_messages
*
* @return null|\IWPML_TF_Data_Object
*/
public function get_one( $feedback_id, $with_messages = true ) {
$feedback = $this->feedback_storage->get( $feedback_id );
if ( $feedback && $with_messages ) {
$filter_args = array(
'feedback_id' => $feedback_id,
);
$filter = new WPML_TF_Message_Collection_Filter( $filter_args );
$messages = $this->message_storage->get_collection( $filter );
foreach ( $messages as $message ) {
$feedback->add_message( $message );
}
}
return $feedback;
}
}

View File

@@ -0,0 +1,71 @@
<?php
/**
* Class WPML_TF_Backend_Notices
*
* @author OnTheGoSystems
*/
class WPML_TF_Backend_Notices {
const GROUP = 'wpml_tf_backend_notices';
const BULK_UPDATED = 'bulk_updated';
/** @var WPML_Notices $admin_notices */
private $admin_notices;
/**
* @param array $updated_feedback_ids
* @parem string
*/
public function add_bulk_updated_notice( array $updated_feedback_ids, $action ) {
$count_feedback = count( $updated_feedback_ids );
$message = _n( '%d feedback was updated.', '%d feedback were updated.', $count_feedback, 'sitepress' );
if ( 'trash' === $action ) {
$permanent_trash_delay = defined( 'EMPTY_TRASH_DAYS' ) ? EMPTY_TRASH_DAYS : 30;
$message = _n( '%d feedback was trashed.', '%d feedback were trashed.', $count_feedback, 'sitepress' );
$message .= ' ' . sprintf(
__( 'The trashed feedback will be permanently deleted after %d days.', 'sitepress' ),
$permanent_trash_delay
);
} elseif ( 'untrash' === $action ) {
$message = _n( '%d feedback was restored.', '%d feedback were restored.', $count_feedback, 'sitepress' );
} elseif ( 'delete' === $action ) {
$message = _n( '%d feedback was permanently deleted.', '%d feedback were permanently deleted.', $count_feedback, 'sitepress' );
}
$text = sprintf( $message, $count_feedback );
$new_notice = $this->get_admin_notices()->get_new_notice( self::BULK_UPDATED, $text, self::GROUP );
$new_notice->set_hideable( true );
$new_notice->set_css_class_types( 'notice-success' );
$this->get_admin_notices()->add_notice( $new_notice );
}
/**
* Add action to remove updated notice after display
*/
public function remove_bulk_updated_notice_after_display() {
add_action( 'admin_notices', array( $this, 'remove_bulk_updated_notice' ), PHP_INT_MAX );
}
/**
* Remove bulk_updated notice
*/
public function remove_bulk_updated_notice() {
$this->get_admin_notices()->remove_notice( self::GROUP, self::BULK_UPDATED );
}
/**
* @return WPML_Notices
*/
private function get_admin_notices() {
if ( ! $this->admin_notices ) {
$this->admin_notices = wpml_get_admin_notices();
}
return $this->admin_notices;
}
}

View File

@@ -0,0 +1,56 @@
<?php
/**
* Class WPML_TF_Promote_Notices
*
* @author OnTheGoSystems
*/
class WPML_TF_Promote_Notices {
const NOTICE_GROUP = 'wpml-tf-promote';
const NOTICE_NEW_SITE = 'notice-new-site';
const DOC_URL = 'https://wpml.org/documentation/getting-started-guide/getting-visitor-feedback-about-your-sites-translations/?utm_source=plugin&utm_medium=gui&utm_campaign=wpmlcore';
/** @var SitePress $sitepress */
private $sitepress;
public function __construct( SitePress $sitepress ) {
$this->sitepress = $sitepress;
}
/**
* @param int $user_id
*/
public function show_notice_for_new_site( $user_id ) {
$notices = wpml_get_admin_notices();
$settings_url = admin_url( '?page=' . WPML_PLUGIN_FOLDER . '/menu/languages.php#wpml-translation-feedback-options' );
$user_lang = $this->sitepress->get_user_admin_language( $user_id );
$this->sitepress->switch_lang( $user_lang );
$text = '<h2>' . __( 'Want to know if recent translations you received have problems?', 'sitepress' ) . '</h2>';
$text .= '<p>';
$text .= __( 'You got back several jobs from translation and they now appear on your site.', 'sitepress' );
$text .= ' ' . __( 'WPML lets you open these pages for feedback, so that visitors can tell you if they notice anything wrong.', 'sitepress' );
$text .= '<br><br>';
$text .= '<a href="' . $settings_url . '" class="button-secondary">' . __( 'Enable Translation Feedback', 'sitepress' ) . '</a>';
$text .= ' <a href="' . self::DOC_URL . '" target="_blank">' . __( 'Learn more about translation feedback', 'sitepress' ) . '</a>';
$text .= '</p>';
$notice = $notices->get_new_notice( self::NOTICE_NEW_SITE, $text, self::NOTICE_GROUP );
$notice->set_dismissible( true );
$notice->set_css_class_types( 'notice-info' );
$notice->add_user_restriction( $user_id );
if ( ! $notices->is_notice_dismissed( $notice ) ) {
$notices->add_notice( $notice );
}
$this->sitepress->switch_lang( null );
}
public function remove() {
$notices = wpml_get_admin_notices();
$notices->remove_notice( self::NOTICE_GROUP, self::NOTICE_NEW_SITE );
}
}

View File

@@ -0,0 +1,20 @@
<?php
/**
* Class WPML_TF_Backend_Options_Scripts
*
* @author OnTheGoSystems
*/
class WPML_TF_Backend_Options_Scripts {
const HANDLE = 'wpml-tf-backend-options';
/**
* method enqueue
*/
public function enqueue() {
$script = ICL_PLUGIN_URL . '/res/js/translation-feedback/wpml-tf-backend-options-script.js';
wp_register_script( self::HANDLE, $script, array( 'jquery', 'wp-util' ), ICL_SITEPRESS_VERSION );
wp_enqueue_script( self::HANDLE );
}
}

View File

@@ -0,0 +1,20 @@
<?php
/**
* Class WPML_TF_Backend_Scripts
*
* @author OnTheGoSystems
*/
class WPML_TF_Backend_Scripts {
const HANDLE = 'wpml-tf-backend';
/**
* method enqueue
*/
public function enqueue() {
$script = ICL_PLUGIN_URL . '/res/js/translation-feedback/wpml-tf-backend-script.js';
wp_register_script( self::HANDLE, $script, array( 'jquery', 'wp-util' ), ICL_SITEPRESS_VERSION );
wp_enqueue_script( self::HANDLE );
}
}

View File

@@ -0,0 +1,20 @@
<?php
/**
* Class WPML_TF_Frontend_Scripts
*
* @author OnTheGoSystems
*/
class WPML_TF_Frontend_Scripts {
const HANDLE = 'wpml-tf-frontend';
/**
* method enqueue
*/
public function enqueue() {
$script = ICL_PLUGIN_URL . '/res/js/translation-feedback/wpml-tf-frontend-script.js';
wp_register_script( self::HANDLE, $script, array( 'jquery', 'jquery-ui-dialog', 'wp-util' ), ICL_SITEPRESS_VERSION );
wp_enqueue_script( self::HANDLE );
}
}

View File

@@ -0,0 +1,16 @@
<?php
/**
* Interface WPML_Settings_Interface
*
* @author OnTheGoSystems
*/
interface IWPML_TF_Settings {
/**
* @return array of name/value pairs
*
* Each property should have its own setter "set_{$property_name}"
*/
public function get_properties();
}

View File

@@ -0,0 +1,18 @@
<?php
/**
* Class WPML_TF_Settings_Handler
*
* @author OnTheGoSystems
*/
abstract class WPML_TF_Settings_Handler {
/**
* @param string $class_name
*
* @return string
*/
protected function get_option_name( $class_name ) {
return sanitize_title( $class_name );
}
}

View File

@@ -0,0 +1,57 @@
<?php
/**
* Class WPML_TF_Settings_Read
*
* @author OnTheGoSystems
*/
class WPML_TF_Settings_Read extends WPML_TF_Settings_Handler {
/**
* @param string $settings_class
*
* @return IWPML_TF_Settings
*
* @throws InvalidArgumentException
*/
public function get( $settings_class ) {
if ( ! class_exists( $settings_class ) ) {
throw new InvalidArgumentException( $settings_class . ' does not exist.' );
}
if ( ! in_array( 'IWPML_TF_Settings', class_implements( $settings_class ) ) ) {
throw new InvalidArgumentException( $settings_class . ' should implement IWPML_TF_Settings.' );
}
$settings_properties = get_option( $this->get_option_name( $settings_class ) );
/** @var IWPML_TF_Settings $settings */
$settings = new $settings_class();
if ( is_array( $settings_properties ) ) {
$this->set_properties( $settings, $settings_properties );
}
return $settings;
}
/**
* @param IWPML_TF_Settings $settings
* @param array $settings_properties
*
* @throws BadMethodCallException
*/
private function set_properties( IWPML_TF_Settings $settings, array $settings_properties ) {
foreach ( $settings->get_properties() as $property_name => $property_value ) {
if ( method_exists( $settings, 'set_' . $property_name ) ) {
if ( isset( $settings_properties[ $property_name ] ) ) {
call_user_func( array( $settings, 'set_' . $property_name ), $settings_properties[ $property_name ] );
}
} else {
throw new BadMethodCallException( 'The method set_' . $property_name . ' is required in ' . get_class( $settings ) . '.' );
}
}
}
}

View File

@@ -0,0 +1,18 @@
<?php
/**
* Class WPML_TF_Settings_Write
*
* @author OnTheGoSystems
*/
class WPML_TF_Settings_Write extends WPML_TF_Settings_Handler {
/**
* @param IWPML_TF_Settings $settings
*
* @return bool
*/
public function save( IWPML_TF_Settings $settings ) {
return update_option( $this->get_option_name( get_class( $settings ) ), $settings->get_properties() );
}
}

View File

@@ -0,0 +1,180 @@
<?php
use WPML\API\Sanitize;
/**
* Class WPML_TF_Settings
*
* @author OnTheGoSystems
*/
class WPML_TF_Settings implements IWPML_TF_Settings {
const BUTTON_MODE_DISABLED = 'disabled';
const BUTTON_MODE_LEFT = 'left';
const BUTTON_MODE_RIGHT = 'right';
const BUTTON_MODE_CUSTOM = 'custom';
const ICON_STYLE_LEGACY = 'translation';
const ICON_STYLE_STAR = 'star';
const ICON_STYLE_THUMBSUP = 'thumbsup';
const ICON_STYLE_BULLHORN = 'bullhorn';
const ICON_STYLE_COMMENT = 'comment';
const ICON_STYLE_QUOTE = 'quote';
const DISPLAY_ALWAYS = 'always';
const DISPLAY_CUSTOM = 'custom';
const EXPIRATION_ON_PUBLISH_OR_UPDATE = 'publish_or_update';
const EXPIRATION_ON_PUBLISH_ONLY = 'publish_only';
const EXPIRATION_ON_UPDATE_ONLY = 'update_only';
const DELAY_DAY = 1;
const DELAY_WEEK = 7;
const DELAY_MONTH = 30;
/** @var bool $enabled */
private $enabled = false;
/** @var string $button_mode */
private $button_mode = self::BUTTON_MODE_LEFT;
/** @var string $icon_style */
private $icon_style = self::ICON_STYLE_LEGACY;
/** @var null|array $languages_to */
private $languages_to = null;
/** @var string $display_mode */
private $display_mode = self::DISPLAY_CUSTOM;
/** @var string $expiration_mode */
private $expiration_mode = self::EXPIRATION_ON_PUBLISH_OR_UPDATE;
/** @var int $expiration_delay_quantity */
private $expiration_delay_quantity = 1;
/** @var int $expiration_delay_unit */
private $expiration_delay_unit = self::DELAY_MONTH;
/**
* @param bool $enabled
*/
public function set_enabled( $enabled ) {
$this->enabled = (bool) $enabled;
}
/**
* @return bool
*/
public function is_enabled() {
return $this->enabled;
}
/**
* @param string $button_mode
*/
public function set_button_mode( $button_mode ) {
$this->button_mode = Sanitize::string( $button_mode );
}
/**
* @return string
*/
public function get_button_mode() {
return $this->button_mode;
}
/** @param string $style */
public function set_icon_style( $style ) {
$this->icon_style = Sanitize::string( $style );
}
/** @return string */
public function get_icon_style() {
return $this->icon_style;
}
/**
* @param array $languages_to
*/
public function set_languages_to( array $languages_to ) {
$this->languages_to = array_map( 'sanitize_title', $languages_to );
}
/**
* @return null|array
*/
public function get_languages_to() {
return $this->languages_to;
}
/**
* @param string $display_mode
*/
public function set_display_mode( $display_mode ) {
$this->display_mode = Sanitize::string( $display_mode );
}
/**
* @return string
*/
public function get_display_mode() {
return $this->display_mode;
}
/**
* @param string $expiration_mode
*/
public function set_expiration_mode( $expiration_mode ) {
$this->expiration_mode = Sanitize::string( $expiration_mode );
}
/**
* @return string
*/
public function get_expiration_mode() {
return $this->expiration_mode;
}
/**
* @param int $expiration_delay_quantity
*/
public function set_expiration_delay_quantity( $expiration_delay_quantity ) {
$this->expiration_delay_quantity = (int) $expiration_delay_quantity;
}
/**
* @return int
*/
public function get_expiration_delay_quantity() {
return $this->expiration_delay_quantity;
}
/**
* @param int $expiration_delay_unit
*/
public function set_expiration_delay_unit( $expiration_delay_unit ) {
$this->expiration_delay_unit = (int) $expiration_delay_unit;
}
/**
* @return int
*/
public function get_expiration_delay_unit() {
return $this->expiration_delay_unit;
}
/**
* @return int delay in days before expiration
*/
public function get_expiration_delay_in_days() {
return $this->expiration_delay_quantity * $this->expiration_delay_unit;
}
/**
* @return array
*/
public function get_properties() {
return get_object_vars( $this );
}
}

View File

@@ -0,0 +1,20 @@
<?php
/**
* Class WPML_TF_Backend_Options_Styles
*
* @author OnTheGoSystems
*/
class WPML_TF_Backend_Options_Styles {
const HANDLE = 'wpml-tf-backend-options';
/**
* method enqueue
*/
public function enqueue() {
$style = ICL_PLUGIN_URL . '/res/css/translation-feedback/backend-options.css';
wp_register_style( self::HANDLE, $style, array(), ICL_SITEPRESS_VERSION );
wp_enqueue_style( self::HANDLE );
}
}

View File

@@ -0,0 +1,20 @@
<?php
/**
* Class WPML_TF_Backend_Styles
*
* @author OnTheGoSystems
*/
class WPML_TF_Backend_Styles {
const HANDLE = 'wpml-tf-backend';
/**
* method enqueue
*/
public function enqueue() {
$style = ICL_PLUGIN_URL . '/res/css/translation-feedback/backend-feedback-list.css';
wp_register_style( self::HANDLE, $style, array( 'otgs-ico' ), ICL_SITEPRESS_VERSION );
wp_enqueue_style( self::HANDLE );
}
}

View File

@@ -0,0 +1,20 @@
<?php
/**
* Class WPML_TF_Frontend_Styles
*
* @author OnTheGoSystems
*/
class WPML_TF_Frontend_Styles {
const HANDLE = 'wpml-tf-frontend';
/**
* method enqueue
*/
public function enqueue() {
$style = ICL_PLUGIN_URL . '/res/css/translation-feedback/front-style.css';
wp_register_style( self::HANDLE, $style, array( 'otgs-dialogs', 'otgs-icons' ), ICL_SITEPRESS_VERSION );
wp_enqueue_style( self::HANDLE );
}
}

View File

@@ -0,0 +1,215 @@
<?php
/**
* Class WPML_TF_Backend_Document_Information
*
* @author OnTheGoSystems
*/
class WPML_TF_Backend_Document_Information extends WPML_TF_Document_Information {
/** @var WPML_TP_Client_Factory|null $tp_client_factory */
private $tp_client_factory;
/** @var WPML_TP_Client|null $tp_client */
private $tp_client;
/**
* WPML_TF_Backend_Document_Information constructor.
*
* @param SitePress $sitepress
* @param WPML_TP_Client_Factory $tp_client_factory
*/
public function __construct( SitePress $sitepress, WPML_TP_Client_Factory $tp_client_factory = null ) {
parent::__construct( $sitepress );
$this->tp_client_factory = $tp_client_factory;
}
/**
* @return false|null|string
*/
public function get_url() {
$url = null;
if ( $this->is_post_document() ) {
$url = get_permalink( $this->id );
}
return $url;
}
/**
* @return null|string
*/
public function get_title() {
$title = null;
if ( $this->is_post_document() ) {
$title = $this->get_post_title( $this->id );
}
return $title;
}
/**
* @return bool
*/
private function is_post_document() {
return 0 === strpos( $this->type, 'post_' );
}
/**
* @param string $language_code
*
* @return string
*/
public function get_flag_url( $language_code ) {
return $this->sitepress->get_flag_url( $language_code );
}
/**
* @return string
*/
public function get_edit_url() {
$this->load_link_to_translation_tm_filters();
$url = 'post.php?' . http_build_query(
array(
'lang' => $this->get_language(),
'action' => 'edit',
'post_type' => $this->type,
'post' => $this->id,
)
);
return apply_filters( 'wpml_link_to_translation', $url, $this->get_source_id(), $this->get_language(), $this->get_trid() );
}
private function load_link_to_translation_tm_filters() {
if ( ! did_action( 'wpml_pre_status_icon_display' ) ) {
do_action( 'wpml_pre_status_icon_display' );
}
}
/**
* @return null|int
*/
public function get_source_id() {
$source_id = null;
$translations = $this->get_translations();
if ( isset( $translations[ $this->get_source_language() ]->element_id ) ) {
$source_id = (int) $translations[ $this->get_source_language() ]->element_id;
}
return $source_id;
}
/**
* @return null|string
*/
public function get_source_url() {
$url = null;
if ( $this->get_source_id() ) {
$url = get_the_permalink( $this->get_source_id() );
}
return $url;
}
/**
* @return null|string
*/
public function get_source_title() {
$title = null;
if ( $this->get_source_id() ) {
$title = $this->get_post_title( $this->get_source_id() );
}
return $title;
}
/**
* @return array|bool|mixed
*/
private function get_translations() {
return $this->sitepress->get_element_translations( $this->get_trid(), $this->type );
}
/**
* @param int $job_id
*
* @return string
*/
public function get_translator_name( $job_id ) {
$translation_job = $this->get_translation_job( $job_id );
if ( isset( $translation_job->translation_service ) ) {
if ( 'local' === $translation_job->translation_service && isset( $translation_job->translator_id ) ) {
$translator_name = get_the_author_meta( 'display_name', $translation_job->translator_id );
$translator_name .= ' (' . __( 'local translator', 'sitepress' ) . ')';
} else {
$translator_name = __( 'Unknown remote translation service', 'sitepress' );
$tp_client = $this->get_tp_client();
if ( $tp_client ) {
$translator_name = $tp_client->services()->get_name( $translation_job->translation_service );
}
}
} else {
$post_author_id = get_post_field( 'post_author', $this->id );
$translator_name = get_the_author_meta( 'display_name', $post_author_id );
}
return $translator_name;
}
/**
* @param string $from
* @param string $to
*
* @return array
*/
public function get_available_translators( $from, $to ) {
$translators = array();
if ( function_exists( 'wpml_tm_load_blog_translators' ) ) {
$args = array(
'from' => $from,
'to' => $to,
);
$users = wpml_tm_load_blog_translators()->get_blog_translators( $args );
$translators = wp_list_pluck( $users, 'display_name', 'ID' );
}
return $translators;
}
/**
* @param int $post_id
*
* @return null|string
*/
private function get_post_title( $post_id ) {
$title = null;
$post = get_post( $post_id );
if ( isset( $post->post_title ) ) {
$title = $post->post_title;
}
return $title;
}
/** @return null|WPML_TP_Client */
private function get_tp_client() {
if ( ! $this->tp_client && $this->tp_client_factory ) {
$this->tp_client = $this->tp_client_factory->create();
}
return $this->tp_client;
}
}

View File

@@ -0,0 +1,117 @@
<?php
/**
* Class WPML_TF_Document_Information
*
* @author OnTheGoSystems
*/
class WPML_TF_Document_Information {
/** @var SitePress $sitepress */
protected $sitepress;
/** @var int $id */
protected $id;
/** @var string $type */
protected $type;
/** @var stdClass $language_details */
protected $language_details;
/** @var null|int|stdClass */
private $translation_job;
/**
* WPML_TF_Document_Information constructor.
*
* @param SitePress $sitepress
*/
public function __construct( SitePress $sitepress ) {
$this->sitepress = $sitepress;
}
/**
* @param int $id
* @param string $type
*/
public function init( $id, $type ) {
$this->id = $id;
$this->type = $type;
$this->language_details = $this->sitepress->get_element_language_details( $this->id, $this->type );
}
/**
* @return string|null
*/
public function get_source_language() {
return isset( $this->language_details->source_language_code )
? $this->language_details->source_language_code
: null;
}
/**
* @return string
*/
public function get_language() {
return isset( $this->language_details->language_code )
? $this->language_details->language_code
: null;
}
/**
* @return null|int
*/
public function get_job_id() {
$args = array(
'trid' => $this->get_trid(),
'language_code' => $this->get_language(),
);
$job_id = apply_filters( 'wpml_translation_job_id', false, $args );
return $job_id ? $job_id : null;
}
/**
* @return null|int
*/
protected function get_trid() {
$trid = null;
if ( isset( $this->language_details->trid ) ) {
$trid = $this->language_details->trid;
}
return $trid;
}
/**
* @param int $job_id
*
* @return bool
*/
public function is_local_translation( $job_id ) {
$is_local = true;
$translation_job = $this->get_translation_job( $job_id );
if ( isset( $translation_job->translation_service ) && 'local' !== $translation_job->translation_service ) {
$is_local = false;
}
return $is_local;
}
/**
* @param int $job_id
*
* @return int|stdClass
*/
protected function get_translation_job( $job_id ) {
if ( ! $this->translation_job ) {
$this->translation_job = apply_filters( 'wpml_get_translation_job', $job_id );
}
return $this->translation_job;
}
}

View File

@@ -0,0 +1,239 @@
<?php
/**
* Class WPML_TF_Feedback_Page_Filter
*
* @author OnTheGoSystems
*/
class WPML_TF_Feedback_Page_Filter {
/** @var SitePress $sitepress */
private $sitepress;
/** @var WPML_TF_Feedback_Query $feedback_query */
private $feedback_query;
/** @var array $statuses */
private $statuses = array();
/** @var array $languages */
private $languages = array();
/** @var array $url_args */
private $url_args;
/** @var string $current_url */
private $current_url;
/**
* WPML_TF_Feedback_Page_Filter constructor.
*
* @param SitePress $sitepress
* @param WPML_TF_Feedback_Query $feedback_query
*/
public function __construct( SitePress $sitepress, WPML_TF_Feedback_Query $feedback_query ) {
$this->sitepress = $sitepress;
$this->feedback_query = $feedback_query;
}
/**
* @return array
*/
public static function get_filter_keys() {
return array(
'status',
'language',
'post_id',
);
}
/**
* Will not create filters inside the trash
* And will not include the "trash" status in the status row
*/
public function populate_counters_and_labels() {
if ( $this->feedback_query->is_in_trash() ) {
return;
}
foreach ( $this->feedback_query->get_unfiltered_collection() as $feedback ) {
/** @var WPML_TF_Feedback $feedback */
if ( 'trash' === $feedback->get_status() ) {
continue;
}
if ( ! array_key_exists( $feedback->get_status(), $this->statuses ) ) {
$this->statuses[ $feedback->get_status() ] = array(
'count' => 1,
'label' => $feedback->get_text_status(),
);
} else {
$this->statuses[ $feedback->get_status() ]['count']++;
}
if ( ! array_key_exists( $feedback->get_language_to(), $this->languages ) ) {
$lang_details = $this->sitepress->get_language_details( $feedback->get_language_to() );
$this->languages[ $feedback->get_language_to() ] = array(
'count' => 1,
'label' => isset( $lang_details['display_name'] ) ? $lang_details['display_name'] : null,
);
} else {
$this->languages[ $feedback->get_language_to() ]['count']++;
}
}
ksort( $this->statuses );
ksort( $this->languages );
}
/**
* @return array
*/
public function get_all_and_trash_data() {
$main_data = array(
'all' => array(
'count' => $this->feedback_query->get_total_items_count(),
'label' => __( 'All', 'sitepress' ),
'url' => $this->get_reset_filters_url(),
'current' => $this->get_current_url() === $this->get_reset_filters_url(),
),
);
if ( $this->feedback_query->get_total_trashed_items_count() ) {
$main_data['trashed'] = array(
'count' => $this->feedback_query->get_total_trashed_items_count(),
'label' => __( 'Trash', 'sitepress' ),
'url' => $this->get_filter_url( 'status', 'trash' ),
'current' => $this->get_current_url() === $this->get_filter_url( 'status', 'trash' ),
);
}
if ( $this->is_current_filter( 'post_id' ) ) {
$filters = $this->get_current_filters();
$post = get_post( $filters['post_id'] );
if ( isset( $post->post_title ) ) {
$main_data['post_id'] = array(
'label' => sprintf(
__( 'For "%s"', 'sitepress' ),
mb_strimwidth( $post->post_title, 0, 40, '...' )
),
'current' => true,
);
}
}
return $main_data;
}
/**
* @return array
*/
public function get_statuses_data() {
foreach ( $this->statuses as $status => $data ) {
$this->statuses[ $status ]['url'] = $this->get_filter_url( 'status', $status );
$this->statuses[ $status ]['current'] = false;
if ( $this->is_current_filter( 'status', $status ) ) {
$this->statuses[ $status ]['current'] = true;
}
}
return $this->statuses;
}
/**
* @return array
*/
public function get_languages_data() {
foreach ( $this->languages as $language_code => $data ) {
$this->languages[ $language_code ]['url'] = $this->get_filter_url( 'language', $language_code );
$this->languages[ $language_code ]['current'] = false;
if ( $this->is_current_filter( 'language', $language_code ) ) {
$this->languages[ $language_code ]['current'] = true;
}
}
return $this->languages;
}
/**
* @param string $filter_name
* @param string $filter_value
*
* @return string
*/
private function get_filter_url( $filter_name, $filter_value ) {
return add_query_arg( $filter_name, $filter_value, $this->get_reset_filters_url() );
}
/**
* @param string $filter_key
* @param string $filter_value
*
* @return bool
*/
private function is_current_filter( $filter_key, $filter_value = null ) {
$is_current_filter = false;
$query_args = $this->get_url_args();
if ( array_key_exists( $filter_key, $query_args )
&& ( ! $filter_value || $query_args[ $filter_key ] === $filter_value )
) {
$is_current_filter = true;
}
return $is_current_filter;
}
/**
* @return array
*/
public function get_current_filters() {
$filters = array();
$query_args = $this->get_url_args();
foreach ( self::get_filter_keys() as $filter_key ) {
if ( array_key_exists( $filter_key, $query_args ) ) {
$filters[ $filter_key ] = $query_args[ $filter_key ];
}
}
return $filters;
}
/**
* @return array
*/
private function get_url_args() {
if ( ! $this->url_args ) {
$this->url_args = array();
$url_query = wpml_parse_url( $this->get_current_url(), PHP_URL_QUERY );
parse_str( $url_query, $this->url_args );
}
return $this->url_args;
}
/**
* @return string
*/
private function get_current_url() {
if ( ! $this->current_url ) {
$this->current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
$this->current_url = remove_query_arg( 'paged', $this->current_url );
}
return $this->current_url;
}
/**
* @return string
*/
private function get_reset_filters_url() {
return remove_query_arg( self::get_filter_keys(), $this->get_current_url() );
}
}

View File

@@ -0,0 +1,87 @@
<?php
/**
* Class WPML_TF_Frontend_Display_Requirements
*
* @author OnTheGoSystems
*/
class WPML_TF_Frontend_Display_Requirements {
/** @var WPML_Queried_Object $queried_object */
private $queried_object;
/** @var WPML_TF_Settings $settings */
private $settings;
/**
* WPML_TF_Frontend_Display_Requirements constructor.
*
* @param WPML_Queried_Object $queried_object
* @param WPML_TF_Settings $settings
*/
public function __construct( WPML_Queried_Object $queried_object, WPML_TF_Settings $settings ) {
$this->queried_object = $queried_object;
$this->settings = $settings;
}
/**
* @return bool
*/
public function verify() {
return $this->is_enabled_on_frontend()
&& $this->is_translation()
&& $this->is_allowed_language()
&& $this->is_not_expired();
}
/**
* @return bool
*/
private function is_enabled_on_frontend() {
return $this->settings->is_enabled()
&& $this->settings->get_button_mode() !== WPML_TF_Settings::BUTTON_MODE_DISABLED;
}
/**
* @return bool
*/
private function is_translation() {
return (bool) $this->queried_object->get_source_language_code();
}
/**
* @return bool
*/
private function is_allowed_language() {
return is_array( $this->settings->get_languages_to() )
&& in_array( $this->queried_object->get_language_code(), $this->settings->get_languages_to(), true );
}
/**
* @return bool
*/
private function is_not_expired() {
$is_expired = false;
if ( $this->settings->get_display_mode() === WPML_TF_Settings::DISPLAY_CUSTOM
&& $this->queried_object->is_post()
) {
$post = get_post( $this->queried_object->get_id() );
$now = strtotime( 'now' );
if ( $this->settings->get_expiration_mode() === WPML_TF_Settings::EXPIRATION_ON_PUBLISH_ONLY ) {
$post_date = strtotime( $post->post_date );
} else {
$post_date = max( strtotime( $post->post_date ), strtotime( $post->post_modified ) );
}
$post_age_in_days = ( $now - $post_date ) / DAY_IN_SECONDS;
if ( $post_age_in_days > $this->settings->get_expiration_delay_in_days() ) {
$is_expired = true;
}
}
return ! $is_expired;
}
}

View File

@@ -0,0 +1,92 @@
<?php
/**
* Class WPML_TF_Rating_Average
*
* @author OnTheGoSystems
*/
class WPML_TF_Post_Rating_Metrics {
const QUANTITY_KEY = 'wpml_tf_post_rating_quantity';
const AVERAGE_KEY = 'wpml_tf_post_rating_average';
/** @var wpdb $wpdb */
private $wpdb;
public function __construct( wpdb $wpdb ) {
$this->wpdb = $wpdb;
}
/**
* @param int $post_id
*
* @return string
*/
public function get_display( $post_id ) {
$quantity = (int) get_post_meta( $post_id, self::QUANTITY_KEY, true );
if ( 0 === $quantity ) {
return '';
}
$average = get_post_meta( $post_id, self::AVERAGE_KEY, true );
$title = sprintf( __( 'Average rating - %s', 'sitepress' ), $average );
$stars = round( $average );
$url = admin_url( 'admin.php?page=' . WPML_TF_Backend_Hooks::PAGE_HOOK . '&post_id=' . $post_id );
$output = '<div class="wpml-tf-rating" title="' . esc_attr( $title ) . '"><a href="'. esc_url( $url ) . '">';
for ( $i = 1; $i < 6; $i++ ) {
$output .= '<span class="otgs-ico-star';
if ( $i <= $stars ) {
$output .= ' full-star';
}
$output .= '"></span>';
}
$output .= '</a> <span class="rating-quantity">(' . esc_html( $quantity ) . ')</span></div>';
return $output;
}
/** @param int $post_id */
public function refresh( $post_id ) {
$document_id_key = WPML_TF_Data_Object_Storage::META_PREFIX . 'document_id';
$rating_key = WPML_TF_Data_Object_Storage::META_PREFIX . 'rating';
$subquery = $this->wpdb->prepare(
"SELECT post_id FROM {$this->wpdb->postmeta}
WHERE meta_key = %s AND meta_value = %d",
$document_id_key,
$post_id
);
$query = $this->wpdb->prepare(
"SELECT meta_value FROM {$this->wpdb->postmeta}
WHERE meta_key = %s AND post_id IN( {$subquery} )",
$rating_key
);
$ratings = $this->wpdb->get_results( $query );
if ( $ratings ) {
$total = 0;
foreach ( $ratings as $rating ) {
$total += (int) $rating->meta_value;
}
$quantity = count( $ratings );
$average = round( $total / $quantity, 1 );
update_post_meta( $post_id, self::QUANTITY_KEY, $quantity );
update_post_meta( $post_id, self::AVERAGE_KEY, $average );
} else {
delete_post_meta( $post_id, self::QUANTITY_KEY );
delete_post_meta( $post_id, self::AVERAGE_KEY );
}
}
}

View File

@@ -0,0 +1,35 @@
<?php
/**
* Class WPML_TF_Translation_Service
*
* @author OnTheGoSystems
*/
class WPML_TF_Translation_Service {
/** @var WPML_TP_Client_Factory $tp_client_factory */
private $tp_client_factory;
/**
* WPML_TF_Translation_Service constructor.
*
* @param WPML_TP_Client_Factory $tp_client_factory
*/
public function __construct( WPML_TP_Client_Factory $tp_client_factory = null ) {
$this->tp_client_factory = $tp_client_factory;
}
/** @return bool */
public function allows_translation_feedback() {
if ( ! $this->tp_client_factory ) {
return true;
}
$translation_service = $this->tp_client_factory->create()->services()->get_active();
if ( isset( $translation_service->translation_feedback ) && ! $translation_service->translation_feedback ) {
return false;
}
return true;
}
}

View File

@@ -0,0 +1,222 @@
<?php
use WPML\API\Sanitize;
/**
* Class WPML_TF_Backend_Feedback_List_View
*
* @author OnTheGoSystems
*/
class WPML_TF_Backend_Feedback_List_View {
const TEMPLATE_FOLDER = '/templates/translation-feedback/backend/';
const TEMPLATE_NAME = 'feedback-list-page.twig';
const ITEMS_PER_PAGE = 20;
/** @var IWPML_Template_Service $template_service */
private $template_service;
/** @var WPML_TF_Feedback_Query $feedback_query */
private $feedback_query;
/** @var WPML_Admin_Pagination $pagination */
private $pagination;
/** @var WPML_Admin_Table_Sort $table_sort */
private $table_sort;
/** @var WPML_TF_Feedback_Page_Filter $page_filter */
private $page_filter;
/**
* WPML_TF_Backend_Feedback_List_View constructor.
*
* @param IWPML_Template_Service $template_service
* @param WPML_TF_Feedback_Query $feedback_query
* @param WPML_Admin_Pagination $pagination
* @param WPML_Admin_Table_Sort $table_sort
* @param WPML_TF_Feedback_Page_Filter $page_filter
*/
public function __construct(
IWPML_Template_Service $template_service,
WPML_TF_Feedback_Query $feedback_query,
WPML_Admin_Pagination $pagination,
WPML_Admin_Table_Sort $table_sort,
WPML_TF_Feedback_Page_Filter $page_filter
) {
$this->template_service = $template_service;
$this->feedback_query = $feedback_query;
$this->pagination = $pagination;
$this->table_sort = $table_sort;
$this->page_filter = $page_filter;
}
/** @return string */
public function render_page() {
$args = $this->parse_request_args();
$feedback_collection = $this->feedback_query->get( $args );
$model = array(
'strings' => $this->get_strings(),
'columns' => $this->get_columns(),
'pagination' => $this->get_pagination( $args ),
'page_filters' => $this->get_page_filters(),
'admin_page_hook' => WPML_TF_Backend_Hooks::PAGE_HOOK,
'current_query' => $this->get_current_query(),
'nonce' => wp_create_nonce( WPML_TF_Backend_Hooks::PAGE_HOOK ),
'ajax_action' => WPML_TF_Backend_AJAX_Feedback_Edit_Hooks_Factory::AJAX_ACTION,
'ajax_nonce' => wp_create_nonce( WPML_TF_Backend_AJAX_Feedback_Edit_Hooks_Factory::AJAX_ACTION ),
'has_admin_capabilities' => current_user_can( 'manage_options' ),
'is_in_trash' => $this->feedback_query->is_in_trash(),
'feedback_collection' => $feedback_collection,
);
return $this->template_service->show( $model, self::TEMPLATE_NAME );
}
/** @return array */
private function parse_request_args() {
$args = array(
'paged' => 1,
'items_per_page' => self::ITEMS_PER_PAGE,
);
if ( isset( $_GET['paged'] ) ) {
$args['paged'] = filter_var( $_GET['paged'], FILTER_SANITIZE_NUMBER_INT );
}
if ( isset( $_GET['order'], $_GET['orderby'] ) ) {
$args['order'] = Sanitize::stringProp( 'order', $_GET );
$args['orderby'] = Sanitize::stringProp( 'orderby', $_GET );
}
if ( isset( $_GET['status'] ) ) {
$args['status'] = Sanitize::stringProp( 'status', $_GET );
} elseif ( isset( $_GET['language'] ) ) {
$args['language'] = Sanitize::stringProp( 'language', $_GET );
}
if ( isset( $_GET['post_id'] ) ) {
$args['post_id'] = filter_var( $_GET['post_id'], FILTER_SANITIZE_NUMBER_INT );
}
return $args;
}
/** @return array */
private function get_strings() {
$strings = array(
'page_title' => __( 'Translation Feedback', 'sitepress' ),
'page_subtitle' => __( 'Issues with translations', 'sitepress' ),
'table_header' => WPML_TF_Backend_Feedback_Row_View::get_columns_strings(),
'screen_reader' => array(
'select_all' => __( 'Select All', 'sitepress' ),
),
'pagination' => array(
'list_navigation' => __( 'Feedback list navigation', 'sitepress' ),
'first_page' => __( 'First page', 'sitepress' ),
'previous_page' => __( 'Previous page', 'sitepress' ),
'next_page' => __( 'Next page', 'sitepress' ),
'last_page' => __( 'Last page', 'sitepress' ),
'current_page' => __( 'Current page', 'sitepress' ),
'of' => __( 'of', 'sitepress' ),
),
'bulk_actions' => array(
'select' => __( 'Select bulk action', 'sitepress' ),
'apply_button' => __( 'Apply', 'sitepress' ),
'options' => array(
'-1' => __( 'Bulk Actions', 'sitepress' ),
'fixed' => __( 'Mark as fixed', 'sitepress' ),
'pending' => __( 'Mark as new', 'sitepress' ),
'trash' => __( 'Move to trash', 'sitepress' ),
),
),
'row_summary' => WPML_TF_Backend_Feedback_Row_View::get_summary_strings(),
'row_details' => WPML_TF_Backend_Feedback_Row_View::get_details_strings(),
'no_feedback' => __( 'No feedback found.', 'sitepress' ),
);
if ( $this->feedback_query->is_in_trash() ) {
$strings['bulk_actions']['options'] = array(
'-1' => __( 'Bulk Actions', 'sitepress' ),
'untrash' => __( 'Restore', 'sitepress' ),
'delete' => __( 'Delete permanently', 'sitepress' ),
);
}
return $strings;
}
/** @return array */
private function get_columns() {
$this->table_sort->set_primary_column( 'feedback' );
return array(
'feedback' => array(
'url' => $this->table_sort->get_column_url( 'feedback' ),
'classes' => $this->table_sort->get_column_classes( 'feedback' ),
),
'rating' => array(
'url' => $this->table_sort->get_column_url( 'rating' ),
'classes' => $this->table_sort->get_column_classes( 'rating' ),
),
'status' => array(
'url' => $this->table_sort->get_column_url( 'status' ),
'classes' => $this->table_sort->get_column_classes( 'status' ),
),
'document_url' => array(
'url' => $this->table_sort->get_column_url( 'document_title' ),
'classes' => $this->table_sort->get_column_classes( 'document_title' ),
),
'date' => array(
'url' => $this->table_sort->get_column_url( 'date' ),
'classes' => $this->table_sort->get_column_classes( 'date' ),
),
);
}
/**
* @param array $args
*
* @return array
*/
private function get_pagination( array $args ) {
$this->pagination->set_total_items( $this->feedback_query->get_filtered_items_count() );
$this->pagination->set_items_per_page( $args['items_per_page'] );
$this->pagination->set_current_page( $args['paged'] );
return array(
'first_page' => $this->pagination->get_first_page_url(),
'previous_page' => $this->pagination->get_previous_page_url(),
'next_page' => $this->pagination->get_next_page_url(),
'last_page' => $this->pagination->get_last_page_url(),
'current_page' => $this->pagination->get_current_page(),
'total_pages' => $this->pagination->get_total_pages(),
'total_items' => sprintf(
_n( '%s item', '%s items', $this->pagination->get_total_items() ),
$this->pagination->get_total_items()
),
);
}
/** @return array */
private function get_page_filters() {
$this->page_filter->populate_counters_and_labels();
return array(
'all_and_trash' => $this->page_filter->get_all_and_trash_data(),
'statuses' => $this->page_filter->get_statuses_data(),
'languages' => $this->page_filter->get_languages_data(),
);
}
/** @return array */
private function get_current_query() {
return array(
'filters' => $this->page_filter->get_current_filters(),
'sorters' => $this->table_sort->get_current_sorters(),
);
}
}

View File

@@ -0,0 +1,98 @@
<?php
/**
* Class WPML_TF_Backend_Feedback_Row_View
*/
class WPML_TF_Backend_Feedback_Row_View {
const TEMPLATE_FOLDER = '/templates/translation-feedback/backend/';
const SUMMARY_TEMPLATE = 'feedback-list-page-table-row.twig';
const DETAILS_TEMPLATE = 'feedback-list-page-table-row-details.twig';
/** @var IWPML_Template_Service $template_service */
private $template_service;
/**
* WPML_TF_Backend_Feedback_Row_View constructor.
*
* @param IWPML_Template_Service $template_service
*/
public function __construct( IWPML_Template_Service $template_service ) {
$this->template_service = $template_service;
}
/** @param WPML_TF_Feedback $feedback */
public function render_summary_row( WPML_TF_Feedback $feedback ) {
$model = array(
'strings' => self::get_summary_strings(),
'has_admin_capabilities' => current_user_can( 'manage_options' ),
'feedback' => $feedback,
);
return $this->template_service->show( $model, self::SUMMARY_TEMPLATE );
}
/** @param WPML_TF_Feedback $feedback */
public function render_details_row( WPML_TF_Feedback $feedback ) {
$model = array(
'strings' => self::get_details_strings(),
'has_admin_capabilities' => current_user_can( 'manage_options' ),
'feedback' => $feedback,
);
return $this->template_service->show( $model, self::DETAILS_TEMPLATE );
}
/** @return array */
public static function get_columns_strings() {
return array(
'feedback' => __( 'Feedback', 'sitepress' ),
'rating' => __( 'Rating', 'sitepress' ),
'status' => __( 'Status', 'sitepress' ),
'document' => __( 'Translated post', 'sitepress' ),
'date' => __( 'Date', 'sitepress' ),
);
}
/** @return array */
public static function get_summary_strings() {
return array(
'select_validation' => __( 'Select Validation', 'sitepress' ),
'inline_actions' => array(
'review' => __( 'Review', 'sitepress' ),
'trash' => __( 'Trash', 'sitepress' ),
'view_post' => __( 'View post', 'sitepress' ),
'untrash' => __( 'Restore', 'sitepress' ),
'delete' => __( 'Delete permanently', 'sitepress' ),
),
'columns' => self::get_columns_strings(),
);
}
/** @return array */
public static function get_details_strings() {
return array(
'title' => __( 'Translation Feedback', 'sitepress' ),
'translation' => __( 'Translation:', 'sitepress' ),
'edit_translation' => __( 'Edit translation', 'sitepress' ),
'original_post' => __( 'Original post:', 'sitepress' ),
'rating' => __( 'Rating:', 'sitepress' ),
'feedback' => __( 'Feedback:', 'sitepress' ),
'edit_feedback' => __( 'Edit feedback', 'sitepress' ),
'status' => __( 'Status:', 'sitepress' ),
'refresh_status' => __( 'Check for updates', 'sitepress' ),
'translated_by' => __( 'Translated by:', 'sitepress' ),
'corrected_by' => __( 'Corrected by:', 'sitepress' ),
'reviewed_by' => __( 'Reviewed by:', 'sitepress' ),
'add_note_to_translator' => __( 'Add a note to the translator', 'sitepress' ),
'add_note_to_admin' => __( 'Add a note to the administrator', 'sitepress' ),
'communication' => __( 'Communication:', 'sitepress' ),
'reply_to_translator_label' => __( 'Reply to translator:', 'sitepress' ),
'reply_to_admin_label' => __( 'Reply to admin:', 'sitepress' ),
'cancel_button' => __( 'Cancel', 'sitepress' ),
'trash_button' => __( 'Trash', 'sitepress' ),
'translation_fixed_button' => __( 'Mark as fixed', 'sitepress' ),
'no_translator_available' => __( 'No translator available for this language pair.', 'sitepress' ),
);
}
}

View File

@@ -0,0 +1,292 @@
<?php
/**
* Class WPML_TF_Backend_Options_View
*
* @author OnTheGoSystems
*/
class WPML_TF_Backend_Options_View {
const TEMPLATE_FOLDER = '/templates/translation-feedback/backend/';
const TEMPLATE = 'options-ui.twig';
const MAX_EXPIRATION_QUANTITY = 10;
/** @var IWPML_Template_Service $template_service */
private $template_service;
/** @var WPML_TF_Settings $settings */
private $settings;
/** @var SitePress $sitepress */
private $sitepress;
/**
* WPML_TF_Frontend_Hooks constructor.
*
* @param IWPML_Template_Service $template_service
* @param WPML_TF_Settings $settings
* @param SitePress $sitepress
*/
public function __construct(
IWPML_Template_Service $template_service,
WPML_TF_Settings $settings,
SitePress $sitepress
) {
$this->template_service = $template_service;
$this->settings = $settings;
$this->sitepress = $sitepress;
}
/**
* @return string
*/
public function render() {
$model = array(
'strings' => self::get_strings(),
'action' => WPML_TF_Backend_Options_AJAX_Hooks_Factory::AJAX_ACTION,
'nonce' => wp_create_nonce( WPML_TF_Backend_Options_AJAX_Hooks_Factory::AJAX_ACTION ),
'module_toggle' => $this->get_module_toggle(),
'button_modes' => $this->get_button_modes(),
'icon_styles' => $this->get_icon_styles(),
'languages_to' => $this->get_languages_to(),
'display_modes' => $this->get_display_modes(),
'expiration_modes' => $this->get_expiration_modes(),
'expiration_quantities' => $this->get_expiration_quantities(),
'expiration_units' => $this->get_expiration_units(),
);
return $this->template_service->show( $model, self::TEMPLATE );
}
/**
* @return array
*/
public static function get_strings() {
return array(
'section_title' => __( 'Translation Feedback', 'sitepress' ),
'button_mode_section_title' => __( 'Translation Feedback button on front-end:', 'sitepress' ),
'icon_style_section_title' => __( 'Icon style:', 'sitepress' ),
'languages_to_section_title' => __( 'Show Translation Feedback module for these languages:', 'sitepress' ),
'expiration_section_title' => __( 'Expiration date for Translation Feedback module:', 'sitepress' ),
);
}
/**
* @return array
*/
private function get_module_toggle() {
return array(
'value' => 1,
'label' => __( 'Enable Translation Feedback module', 'sitepress' ),
'selected' => $this->settings->is_enabled(),
);
}
/**
* @return array
*/
private function get_button_modes() {
$modes = array(
WPML_TF_Settings::BUTTON_MODE_LEFT => array(
'value' => WPML_TF_Settings::BUTTON_MODE_LEFT,
'label' => __( 'Show on the left side of the screen', 'sitepress' ),
'selected' => false,
),
WPML_TF_Settings::BUTTON_MODE_RIGHT => array(
'value' => WPML_TF_Settings::BUTTON_MODE_RIGHT,
'label' => __( 'Show on the right side of the screen', 'sitepress' ),
'selected' => false,
),
WPML_TF_Settings::BUTTON_MODE_CUSTOM => array(
'value' => WPML_TF_Settings::BUTTON_MODE_CUSTOM,
'label' => __( 'I will add it manually (%1$sinstructions%2$s)', 'sitepress' ),
'link' => 'https://wpml.org/wpml-hook/wpml_tf_feedback_open_link/?utm_source=plugin&utm_medium=gui&utm_campaign=wpmlcore',
'selected' => false,
),
WPML_TF_Settings::BUTTON_MODE_DISABLED => array(
'value' => WPML_TF_Settings::BUTTON_MODE_DISABLED,
'label' => __( 'Do not show it', 'sitepress' ),
'selected' => false,
),
);
if ( isset( $modes[ $this->settings->get_button_mode() ] ) ) {
$modes[ $this->settings->get_button_mode() ]['selected'] = true;
}
return $modes;
}
private function get_icon_styles() {
$css_classes = WPML_TF_Frontend_Feedback_View::get_icon_css_classes();
$styles = array(
WPML_TF_Settings::ICON_STYLE_LEGACY => array(
'value' => WPML_TF_Settings::ICON_STYLE_LEGACY,
'image_class' => $css_classes[ WPML_TF_Settings::ICON_STYLE_LEGACY ],
'selected' => false,
),
WPML_TF_Settings::ICON_STYLE_STAR => array(
'value' => WPML_TF_Settings::ICON_STYLE_STAR,
'image_class' => $css_classes[ WPML_TF_Settings::ICON_STYLE_STAR ],
'selected' => false,
),
WPML_TF_Settings::ICON_STYLE_THUMBSUP => array(
'value' => WPML_TF_Settings::ICON_STYLE_THUMBSUP,
'image_class' => $css_classes[ WPML_TF_Settings::ICON_STYLE_THUMBSUP ],
'selected' => false,
),
WPML_TF_Settings::ICON_STYLE_BULLHORN => array(
'value' => WPML_TF_Settings::ICON_STYLE_BULLHORN,
'image_class' => $css_classes[ WPML_TF_Settings::ICON_STYLE_BULLHORN ],
'selected' => false,
),
WPML_TF_Settings::ICON_STYLE_COMMENT => array(
'value' => WPML_TF_Settings::ICON_STYLE_COMMENT,
'image_class' => $css_classes[ WPML_TF_Settings::ICON_STYLE_COMMENT ],
'selected' => false,
),
WPML_TF_Settings::ICON_STYLE_QUOTE => array(
'value' => WPML_TF_Settings::ICON_STYLE_QUOTE,
'image_class' => $css_classes[ WPML_TF_Settings::ICON_STYLE_QUOTE ],
'selected' => false,
),
);
if ( isset( $styles[ $this->settings->get_icon_style() ] ) ) {
$styles[ $this->settings->get_icon_style() ]['selected'] = true;
}
return $styles;
}
/**
* @return array
*/
private function get_languages_to() {
$languages_to = array();
$active_languages = $this->sitepress->get_active_languages();
$allowed_languages = $this->settings->get_languages_to();
if ( null === $allowed_languages ) {
$allowed_languages = array_keys( $active_languages );
}
foreach ( $active_languages as $code => $language ) {
$languages_to[ $code ] = array(
'value' => $code,
'label' => $language['display_name'],
'flag_url' => $this->sitepress->get_flag_url( $code ),
'selected' => false,
);
if ( in_array( $code, $allowed_languages ) ) {
$languages_to[ $code ]['selected'] = true;
}
}
return $languages_to;
}
/**
* @return array
*/
private function get_display_modes() {
$modes = array(
WPML_TF_Settings::DISPLAY_CUSTOM => array(
'value' => WPML_TF_Settings::DISPLAY_CUSTOM,
'label' => esc_html__( 'Ask for feedback about translated content that was %1$s in the last %2$s %3$s', 'sitepress' ),
'selected' => false,
),
WPML_TF_Settings::DISPLAY_ALWAYS => array(
'value' => WPML_TF_Settings::DISPLAY_ALWAYS,
'label' => __( 'Always ask for feedback (no time limit for feedback)', 'sitepress' ),
'selected' => false,
),
);
if ( isset( $modes[ $this->settings->get_display_mode() ] ) ) {
$modes[ $this->settings->get_display_mode() ]['selected'] = true;
}
return $modes;
}
/**
* @return array
*/
private function get_expiration_modes() {
$modes = array(
WPML_TF_Settings::EXPIRATION_ON_PUBLISH_OR_UPDATE => array(
'value' => WPML_TF_Settings::EXPIRATION_ON_PUBLISH_OR_UPDATE,
'label' => __( 'published or updated', 'sitepress' ),
'selected' => false,
),
WPML_TF_Settings::EXPIRATION_ON_PUBLISH_ONLY => array(
'value' => WPML_TF_Settings::EXPIRATION_ON_PUBLISH_ONLY,
'label' => __( 'published', 'sitepress' ),
'selected' => false,
),
WPML_TF_Settings::EXPIRATION_ON_UPDATE_ONLY => array(
'value' => WPML_TF_Settings::EXPIRATION_ON_UPDATE_ONLY,
'label' => __( 'updated', 'sitepress' ),
'selected' => false,
),
);
if ( isset( $modes[ $this->settings->get_expiration_mode() ] ) ) {
$modes[ $this->settings->get_expiration_mode() ]['selected'] = true;
}
return $modes;
}
/**
* @return array
*/
private function get_expiration_quantities() {
$quantities = array();
for ( $i = 1; $i < self::MAX_EXPIRATION_QUANTITY + 1; $i++ ) {
$quantities[ $i ] = array(
'value' => $i,
'selected' => false,
);
}
if ( isset( $quantities[ $this->settings->get_expiration_delay_quantity() ] ) ) {
$quantities[ $this->settings->get_expiration_delay_quantity() ]['selected'] = true;
}
return $quantities;
}
/**
* @return array
*/
private function get_expiration_units() {
$units = array(
WPML_TF_Settings::DELAY_DAY => array(
'value' => WPML_TF_Settings::DELAY_DAY,
'label' => __( 'day(s)', 'sitepress' ),
'selected' => false,
),
WPML_TF_Settings::DELAY_WEEK => array(
'value' => WPML_TF_Settings::DELAY_WEEK,
'label' => __( 'week(s)', 'sitepress' ),
'selected' => false,
),
WPML_TF_Settings::DELAY_MONTH => array(
'value' => WPML_TF_Settings::DELAY_MONTH,
'label' => __( 'month(s)', 'sitepress' ),
'selected' => false,
),
);
if ( isset( $units[ $this->settings->get_expiration_delay_unit() ] ) ) {
$units[ $this->settings->get_expiration_delay_unit() ]['selected'] = true;
}
return $units;
}
}

View File

@@ -0,0 +1,159 @@
<?php
/**
* Class WPML_TF_Frontend_Feedback_View
*
* @author OnTheGoSystems
*/
class WPML_TF_Frontend_Feedback_View {
const TEMPLATE_FOLDER = '/templates/translation-feedback/frontend/';
const FORM_TEMPLATE = 'feedback-form.twig';
const OPEN_TEMPLATE = 'feedback-open-button.twig';
const CUSTOM_OPEN_LINK_TEMPLATE = 'feedback-custom-open-link.twig';
const JS_OPEN_NODE_CLASS = 'js-wpml-tf-feedback-icon';
/** @var IWPML_Template_Service $template_service */
private $template_service;
/** @var SitePress $sitepress */
private $sitepress;
/** @var WPML_Queried_Object $queried_object */
private $queried_object;
/** @var WPML_TF_Settings $settings */
private $settings;
/**
* WPML_TF_Frontend_Hooks constructor.
*
* @param IWPML_Template_Service $template_service
* @param SitePress $sitepress
* @param WPML_Queried_Object $queried_object
* @param WPML_TF_Settings $settings
*/
public function __construct(
IWPML_Template_Service $template_service,
SitePress $sitepress,
WPML_Queried_Object $queried_object,
WPML_TF_Settings $settings
) {
$this->template_service = $template_service;
$this->sitepress = $sitepress;
$this->queried_object = $queried_object;
$this->settings = $settings;
}
/**
* @return string
*/
public function render_form() {
$model = array(
'strings' => array(
'dialog_title' => __( 'Rate translation', 'sitepress' ),
'thank_you_rating' => __( 'Thank you for your rating!', 'sitepress' ),
'thank_you_comment' => __( 'Thank you for your rating and comment!', 'sitepress' ),
'translated_from' => __( 'This page was translated from:', 'sitepress' ),
'please_rate' => __( 'Please rate this translation:', 'sitepress' ),
'your_rating' => __( 'Your rating:', 'sitepress' ),
'star5_title' => __( 'It is perfect!', 'sitepress' ),
'star4_title' => __( 'It is OK', 'sitepress' ),
'star3_title' => __( 'It could be improved', 'sitepress' ),
'star2_title' => __( 'I can see a lot of language errors', 'sitepress' ),
'star1_title' => __( "I can't understand anything", 'sitepress' ),
'change_rating' => __( 'Change', 'sitepress' ),
'error_examples' => __( 'Please give some examples of errors and how would you improve them:', 'sitepress' ),
'send_button' => __( 'Send', 'sitepress' ),
'honeypot_label' => __( 'If you are a human, do not fill in this field.', 'sitepress' ),
),
'flag_url' => $this->sitepress->get_flag_url( $this->queried_object->get_source_language_code() ),
'language_name' => $this->sitepress->get_display_language_name( $this->queried_object->get_source_language_code() ),
'document_id' => $this->queried_object->get_id(),
'document_type' => $this->queried_object->get_element_type(),
'action' => WPML_TF_Frontend_AJAX_Hooks_Factory::AJAX_ACTION,
'nonce' => wp_create_nonce( WPML_TF_Frontend_AJAX_Hooks_Factory::AJAX_ACTION ),
'source_url' => $this->queried_object->get_source_url(),
);
return $this->template_service->show( $model, self::FORM_TEMPLATE );
}
/**
* @return string
*/
public function render_open_button() {
$rendering = '';
if ( WPML_TF_Settings::BUTTON_MODE_CUSTOM !== $this->settings->get_button_mode() ) {
$model = array(
'strings' => array(
'form_open_title' => __( 'Rate translation of this page', 'sitepress' ),
),
'wrapper_css_classes' => $this->get_wrapper_css_classes(),
'icon_css_class' => $this->get_icon_css_class(),
);
$rendering = $this->template_service->show( $model, self::OPEN_TEMPLATE );
}
return $rendering;
}
/** @return string */
private function get_wrapper_css_classes() {
$base_classes = self::JS_OPEN_NODE_CLASS . ' wpml-tf-feedback-icon ';
$class = $base_classes . 'wpml-tf-feedback-icon-left';
if ( WPML_TF_Settings::BUTTON_MODE_RIGHT === $this->settings->get_button_mode() ) {
$class = $base_classes . 'wpml-tf-feedback-icon-right';
}
return $class;
}
/** @return string */
private function get_icon_css_class() {
$icon_style = $this->settings->get_icon_style();
$css_classes = self::get_icon_css_classes();
if ( array_key_exists( $icon_style, $css_classes ) ) {
return $css_classes[ $icon_style ];
}
return $css_classes[ WPML_TF_Settings::ICON_STYLE_LEGACY ];
}
/**
* @param string|array $args
*
* @return string
*/
public function render_custom_open_link( $args ) {
$model = wp_parse_args( $args, self::get_default_arguments_for_open_link() );
$model['classes'] = self::JS_OPEN_NODE_CLASS . ' ' . $model['classes'];
return $this->template_service->show( $model, self::CUSTOM_OPEN_LINK_TEMPLATE );
}
/** @return array */
public static function get_default_arguments_for_open_link() {
return array(
'title' => __( 'Rate translation of this page', 'sitepress' ),
'classes' => 'wpml-tf-feedback-custom-link',
);
}
/** @return array */
public static function get_icon_css_classes() {
return array(
WPML_TF_Settings::ICON_STYLE_LEGACY => 'otgs-ico-translation',
WPML_TF_Settings::ICON_STYLE_STAR => 'otgs-ico-star',
WPML_TF_Settings::ICON_STYLE_THUMBSUP => 'otgs-ico-thumbsup',
WPML_TF_Settings::ICON_STYLE_BULLHORN => 'otgs-ico-bullhorn',
WPML_TF_Settings::ICON_STYLE_COMMENT => 'otgs-ico-comment',
WPML_TF_Settings::ICON_STYLE_QUOTE => 'otgs-ico-quote',
);
}
}

View File

@@ -0,0 +1,59 @@
<?php
/**
* Class WPML_TF_Module
*
* @author OnTheGoSystems
*/
class WPML_TF_Module {
/** @var WPML_Action_Filter_Loader $action_filter_loader */
private $action_filter_loader;
/** @var WPML_TF_Settings $settings */
private $settings;
/**
* WPML_TF_Module constructor.
*
* @param WPML_Action_Filter_Loader $action_filter_loader
* @param IWPML_TF_Settings $settings
*/
public function __construct( WPML_Action_Filter_Loader $action_filter_loader, IWPML_TF_Settings $settings ) {
$this->action_filter_loader = $action_filter_loader;
$this->settings = $settings;
}
public function run() {
$this->action_filter_loader->load( $this->get_actions_to_load_always() );
if ( $this->settings->is_enabled() ) {
$this->action_filter_loader->load( $this->get_actions_to_load_when_module_enabled() );
}
}
/**
* @return array
*/
private function get_actions_to_load_always() {
return array(
'WPML_TF_Backend_Options_Hooks_Factory',
'WPML_TF_Backend_Options_AJAX_Hooks_Factory',
'WPML_TF_Backend_Promote_Hooks_Factory',
);
}
/**
* @return array
*/
private function get_actions_to_load_when_module_enabled() {
return array(
'WPML_TF_Common_Hooks_Factory',
'WPML_TF_Backend_Hooks_Factory',
'WPML_TF_Frontend_Hooks_Factory',
'WPML_TF_Frontend_AJAX_Hooks_Factory',
'WPML_TF_Backend_AJAX_Feedback_Edit_Hooks_Factory',
'WPML_TF_Backend_Post_List_Hooks_Factory',
);
}
}

View File

@@ -0,0 +1,56 @@
<?php
/**
* Class WPML_TM_TF_Module
*
* @author OnTheGoSystems
*/
class WPML_TM_TF_Module {
/** @var WPML_Action_Filter_Loader $action_filter_loader */
private $action_filter_loader;
/** @var WPML_TF_Settings $settings */
private $settings;
/**
* WPML_TF_Module constructor.
*
* @param WPML_Action_Filter_Loader $action_filter_loader
* @param WPML_TF_Settings $settings
*/
public function __construct( WPML_Action_Filter_Loader $action_filter_loader, WPML_TF_Settings $settings ) {
$this->action_filter_loader = $action_filter_loader;
$this->settings = $settings;
}
public function run() {
$this->action_filter_loader->load( $this->get_actions_to_load_always() );
if ( $this->settings->is_enabled() ) {
$this->action_filter_loader->load( $this->get_actions_to_load_when_module_enabled() );
}
}
/**
* @return array
*/
private function get_actions_to_load_always() {
return array(
'WPML_TF_WP_Cron_Events_Factory',
);
}
/**
* @return array
*/
private function get_actions_to_load_when_module_enabled() {
return array(
'WPML_TF_XML_RPC_Hooks_Factory',
'WPML_TF_Translation_Service_Change_Hooks_Factory',
'WPML_TF_Translation_Queue_Hooks_Factory',
'WPML_TM_TF_Feedback_List_Hooks_Factory',
'WPML_TM_TF_AJAX_Feedback_List_Hooks_Factory',
);
}
}

View File

@@ -0,0 +1,72 @@
<?php
/**
* Class WPML_TF_XML_RPC_Feedback_Update
*
* @author OnTheGoSystems
*/
class WPML_TF_XML_RPC_Feedback_Update {
/** @var WPML_TF_Data_Object_Storage $feedback_storage */
private $feedback_storage;
/** @var WPML_TP_Project $tp_project */
private $tp_project;
public function __construct( WPML_TF_Data_Object_Storage $feedback_storage, WPML_TP_Project $tp_project ) {
$this->feedback_storage = $feedback_storage;
$this->tp_project = $tp_project;
}
public function set_status( array $args ) {
if ( $this->valid_arguments( $args ) ) {
$feedback = $this->get_feedback( $args['feedback']['id'] );
if ( $feedback ) {
/** @var WPML_TF_Feedback $feedback */
$feedback->set_status( $args['feedback']['status'] );
$this->feedback_storage->persist( $feedback );
}
}
}
/**
* @param array $args
*
* @return bool
*/
private function valid_arguments( array $args ) {
$valid = false;
if ( isset( $args['feedback']['id'], $args['feedback']['status'], $args['authorization_hash'] ) ) {
$expected_hash = sha1( $this->tp_project->get_id() . $this->tp_project->get_access_key() . $args['feedback']['id'] );
if ( $expected_hash === $args['authorization_hash'] ) {
$valid = true;
}
}
return $valid;
}
/**
* @param int $tp_feedback_id
*
* @return null|WPML_TF_Feedback
*/
private function get_feedback( $tp_feedback_id ) {
$feedback = null;
$filter_args = array(
'tp_feedback_id' => $tp_feedback_id,
);
$collection_filter = new WPML_TF_Feedback_Collection_Filter( $filter_args );
$collection = $this->feedback_storage->get_collection( $collection_filter );
if ( $collection->count() ) {
$feedback = $collection->current();
}
return $feedback;
}
}