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,39 @@
<?php
class WPML_TM_ATE_Request_Activation_Email {
const REQUEST_ACTIVATION_TEMPLATE = 'notification/request-ate-activation.twig';
/** @var WPML_TM_Email_Notification_View */
private $email_view;
public function __construct( WPML_TM_Email_Notification_View $email_view ) {
$this->email_view = $email_view;
}
public function send_email( $to_manager, $from_user ) {
$site_name = get_option( 'blogname' );
$translators_tab_url = WPML_TM_Page::get_translators_url();
$model = array(
'setup_url' => esc_url( $translators_tab_url ),
'casual_name' => $to_manager->user_firstname,
'username' => $to_manager->display_name,
'intro_message_1' => sprintf( __( 'The translator %1$s is requesting to use the Advanced Translation Editor on site %2$s.', 'wpml-translation-management' ), $from_user->display_name, $site_name ),
'setup' => __( 'Advanced Translation Editor settings', 'wpml-translation-management' ),
'reminder' => sprintf( __( '* Remember, your login name for %1$s is %2$s. If you need help with your password, use the password reset in the login page.', 'wpml-translation-management' ), $site_name, $to_manager->user_login ),
);
$to = $to_manager->display_name . ' <' . $to_manager->user_email . '>';
$message = $this->email_view->render_model( $model, self::REQUEST_ACTIVATION_TEMPLATE );
$subject = sprintf( __( "Request to use WPML's Advanced Translation Editor by %s", 'wpml-translation-management' ), $site_name );
$headers = array(
'MIME-Version: 1.0',
'Content-type: text/html; charset=UTF-8',
);
return wp_mail( $to, $subject, $message, $headers );
}
}

View File

@@ -0,0 +1,18 @@
<?php
class WPML_TM_Jobs_Daily_Summary_Report_Model implements WPML_TM_Jobs_Summary_Report_Model {
/**
* @return string
*/
public function get_subject() {
return __( 'Translation updates for %1$s for %2$s', 'wpml-translation-management' );
}
/**
* @return string
*/
public function get_summary_text() {
return __( 'Today %1$s had the following %2$s translation updates', 'wpml-translation-management' );
}
}

View File

@@ -0,0 +1,81 @@
<?php
class WPML_TM_Jobs_Summary_Report_Process_Factory {
/** @var WPML_TM_Jobs_Summary_Report_View $template */
private $template;
/** @var WPML_TM_Jobs_Summary_Report_Process $weekly_report */
private $weekly_report;
/** @var WPML_TM_Jobs_Summary_Report_Process $daily_report */
private $daily_report;
/**
* @return WPML_TM_Jobs_Summary_Report_Process
*/
public function create_weekly_report() {
if ( ! $this->weekly_report ) {
$summary_report = $this->get_summary_report( WPML_TM_Jobs_Summary::WEEKLY_REPORT );
$this->weekly_report = new WPML_TM_Jobs_Summary_Report_Process(
$this->get_template(),
new WPML_TM_Jobs_Weekly_Summary_Report_Model(),
$summary_report->get_jobs()
);
}
return $this->weekly_report;
}
/**
* @return WPML_TM_Jobs_Summary_Report_Process
*/
public function create_daily_report() {
if ( ! $this->daily_report ) {
$summary_report = $this->get_summary_report( WPML_TM_Jobs_Summary::DAILY_REPORT );
$this->daily_report = new WPML_TM_Jobs_Summary_Report_Process(
$this->get_template(),
new WPML_TM_Jobs_Daily_Summary_Report_Model(),
$summary_report->get_jobs()
);
}
return $this->daily_report;
}
/**
* @param string $frequency
*
* @return WPML_TM_Jobs_Summary_Report
*/
private function get_summary_report( $frequency ) {
global $sitepress, $wpdb;
$word_count_records_factory = new WPML_TM_Word_Count_Records_Factory();
$word_count_records = $word_count_records_factory->create();
$single_process_factory = new WPML_TM_Word_Count_Single_Process_Factory();
$single_process = $single_process_factory->create();
return new WPML_TM_Jobs_Summary_Report(
new WPML_Translation_Jobs_Collection( $wpdb, array() ),
new WPML_TM_String( false, $word_count_records, $single_process ),
new WPML_TM_Post( false, $word_count_records, $single_process ),
$frequency,
new WPML_Translation_Element_Factory( $sitepress )
);
}
/**
* @return WPML_TM_Jobs_Summary_Report_View
*/
private function get_template() {
if ( ! $this->template ) {
$template_service_factory = new WPML_TM_Email_Twig_Template_Factory();
$this->template = new WPML_TM_Jobs_Summary_Report_View( $template_service_factory->create() );
}
return $this->template;
}
}

View File

@@ -0,0 +1,57 @@
<?php
class WPML_TM_Jobs_Summary_Report_Process {
/**
* @var WPML_TM_Jobs_Summary_Report_View
*/
private $view;
/**
* @var WPML_TM_Jobs_Summary_Report_Model
*/
private $report_model;
/**
* @var array
*/
private $jobs;
public function __construct(
WPML_TM_Jobs_Summary_Report_View $view,
WPML_TM_Jobs_Summary_Report_Model $report_model,
array $jobs
) {
$this->view = $view;
$this->report_model = $report_model;
$this->jobs = $jobs;
}
public function send() {
foreach ( $this->jobs as $manager_id => $jobs ) {
if ( array_key_exists( WPML_TM_Jobs_Summary::JOBS_COMPLETED_KEY, $jobs ) ) {
$this->view
->set_jobs( $jobs )
->set_manager_id( $manager_id )
->set_summary_text( $this->report_model->get_summary_text() );
$this->send_email( $manager_id );
}
}
}
/**
* @param int $manager_id
*/
private function send_email( $manager_id ) {
wp_mail(
get_userdata( $manager_id )->user_email,
sprintf( $this->report_model->get_subject(), get_bloginfo( 'name' ), date( 'd/F/Y', time() ) ),
$this->view->get_report_content(),
array(
'MIME-Version: 1.0',
'Content-type: text/html; charset=UTF-8',
)
);
}
}

View File

@@ -0,0 +1,108 @@
<?php
class WPML_TM_Jobs_Summary_Report_View extends WPML_TM_Email_View {
const WEEKLY_SUMMARY_TEMPLATE = 'notification/summary/summary.twig';
/**
* @var array
*/
private $jobs;
/**
* @var int
*/
private $manager_id;
/**
* @var string
*/
private $summary_text;
/**
* @return string
*/
public function get_report_content() {
$model = $this->get_model();
$content = $this->render_header( $model['username'] );
$content .= $this->template_service->show( $model, self::WEEKLY_SUMMARY_TEMPLATE );
$content .= $this->render_email_footer();
return $content;
}
/**
* @return array
*/
private function get_model() {
return array(
'username' => get_userdata( $this->manager_id )->display_name,
'jobs' => $this->jobs,
'text' => $this->summary_text,
'site_name' => get_bloginfo( 'name' ),
'number_of_updates' => isset( $this->jobs['completed'] ) ? count( $this->jobs['completed'] ) : 0,
'strings' => array(
'jobs_waiting' => __( 'Jobs that are waiting for translation', 'wpml-translation-management' ),
'original_page' => __( 'Original Page', 'wpml-translation-management' ),
'translation' => __( 'Translation', 'wpml-translation-management' ),
'translator' => __( 'Translator', 'wpml-translation-management' ),
'updated' => __( 'Updated / Translated', 'wpml-translation-management' ),
'date' => __( 'Date', 'wpml-translation-management' ),
'your_deadline' => __( 'Your deadline', 'wpml-translation-management' ),
'translation_languages' => __( 'Translation languages', 'wpml-translation-management' ),
'number_of_pages' => __( 'Number of pages', 'wpml-translation-management' ),
'number_of_strings' => __( 'Number of strings', 'wpml-translation-management' ),
'number_of_words' => __( 'Number of words', 'wpml-translation-management' ),
'undefined' => __( 'Undefined', 'wpml-translation-management' ),
),
'improve_quality' => array(
'title' => __( 'Want to improve the quality of your sites translation?', 'wpml-translation-management' ),
'options' => array(
array(
'link_url' => admin_url( 'admin.php?page=' . WPML_PLUGIN_FOLDER . '/menu/languages.php#wpml-translation-feedback-options' ),
'link_text' => __( 'Translation Feedback', 'wpml-translation-management' ),
'text' => __( 'Allow visitors to tell you about translation issues by enabling %s', 'wpml-translation-management' ),
),
array(
'link_url' => admin_url( 'admin.php?page=' . WPML_TM_FOLDER . '/menu/main.php&sm=translators' ),
'link_text' => __( 'translation services that are integrated with WPML', 'wpml-translation-management' ),
'text' => __( 'Try one of the %s', 'wpml-translation-management' ),
),
)
),
);
}
/**
* @param array $jobs
*
* @return $this
*/
public function set_jobs( $jobs ) {
$this->jobs = $jobs;
return $this;
}
/**
* @param int $manager_id
*
* @return $this
*/
public function set_manager_id( $manager_id ) {
$this->manager_id = $manager_id;
return $this;
}
/**
* @param string $summary_text
*
* @return $this
*/
public function set_summary_text( $summary_text ) {
$this->summary_text = $summary_text;
return $this;
}
}

View File

@@ -0,0 +1,158 @@
<?php
class WPML_TM_Jobs_Summary_Report {
/**
* @var WPML_Translation_Jobs_Collection
*/
private $jobs_collection;
/**
* @var array
*/
private $jobs = array();
/**
* @var WPML_TM_String
*/
private $string_counter;
/**
* @var WPML_TM_Post
*/
private $post_counter;
/**
* @var string
*/
private $type;
/**
* @var WPML_Translation_Element_Factory
*/
private $element_factory;
public function __construct(
WPML_Translation_Jobs_Collection $jobs_collection,
WPML_TM_String $string_counter,
WPML_TM_Post $post_counter,
$type,
WPML_Translation_Element_Factory $element_factory
) {
$this->jobs_collection = $jobs_collection;
$this->string_counter = $string_counter;
$this->post_counter = $post_counter;
$this->type = $type;
$this->element_factory = $element_factory;
$this->build_completed_jobs();
$this->build_waiting_jobs();
}
private function build_completed_jobs() {
$jobs = $this->jobs_collection->get_jobs( array(
'any_translation_service' => true,
'status' => ICL_TM_COMPLETE
)
);
foreach ( $jobs as $job ) {
$completed_date = $job instanceof WPML_Post_Translation_Job || $job instanceof WPML_Element_Translation_Job ? $job->get_completed_date() : '';
$out_of_period = strtotime( $completed_date ) < strtotime( '-' . WPML_TM_Jobs_Summary::WEEKLY_SCHEDULE );
if ( WPML_TM_Jobs_Summary::DAILY_REPORT === $this->type ) {
$out_of_period = strtotime( $completed_date ) < strtotime( '-' . WPML_TM_Jobs_Summary::DAILY_SCHEDULE );
}
if ( ! $completed_date || $out_of_period ) {
continue;
}
$original_element = $this->element_factory->create( $job->get_original_element_id(), $job->get_type() );
$translation_element = $original_element->get_translation( $job->get_language_code() );
$this->jobs[ $job->get_basic_data()->manager_id ][ WPML_TM_Jobs_Summary::JOBS_COMPLETED_KEY ][] = array(
'completed_date' => date_i18n( get_option( 'date_format', 'F d, Y' ), strtotime( $job->get_completed_date() ) ),
'original_page' => array(
'title' => $job->get_title(),
'url' => $job->get_url( true ),
),
'translated_page' => array(
'title' => get_the_title( $translation_element->get_element_id() ) . ' (' . $job->get_language_code() . ')',
'url' => get_the_permalink( $translation_element->get_element_id() ),
),
'translator' => $this->get_translator_name( $job ),
'deadline' => $job->get_deadline_date() ?
date_i18n( get_option( 'date_format', 'F d, Y' ), strtotime( $job->get_deadline_date() ) ) :
'',
'status' => $job->get_status(),
'overdue' => $job->get_deadline_date() &&
strtotime( $job->get_deadline_date() ) < strtotime( $job->get_completed_date() )
);
}
}
private function build_waiting_jobs() {
$jobs = $this->jobs_collection->get_jobs( array(
'any_translation_service' => true,
'status' => ICL_TM_WAITING_FOR_TRANSLATOR
)
);
$counters = array();
$number_of_strings = 0;
$number_of_words_in_strings = 0;
foreach ( $jobs as $job ) {
$manager_id = isset( $job->get_basic_data()->manager_id ) ? $job->get_basic_data()->manager_id : 0;
$lang_pair = $job->get_source_language_code() . '|' . $job->get_language_code();
if ( ! isset( $counters[ $manager_id ][ $lang_pair ]['number_of_strings'], $counters[ $manager_id ][ $lang_pair ]['number_of_words'], $counters[ $manager_id ][ $lang_pair ]['number_of_pages'] ) ) {
$counters[ $manager_id ][ $lang_pair ]['number_of_strings'] = 0;
$counters[ $manager_id ][ $lang_pair ]['number_of_words'] = 0;
$counters[ $manager_id ][ $lang_pair ]['number_of_pages'] = 0;
}
if ( 'String' === $job->get_type() ) {
$this->string_counter->set_id( $job->get_original_element_id() );
$number_of_strings ++;
$number_of_words_in_strings += $this->string_counter->get_words_count();
} else {
$this->post_counter->set_id( $job->get_original_element_id() );
$counters[ $manager_id ][ $lang_pair ]['number_of_pages'] += 1;
$counters[ $manager_id ][ $lang_pair ]['number_of_words'] += $this->post_counter->get_words_count();
$this->jobs[ $manager_id ][ WPML_TM_Jobs_Summary::JOBS_WAITING_KEY ][ $lang_pair ] = array(
'lang_pair' => $job->get_source_language_code( true ) . ' ' . __( 'to', 'wpml-translation-management' ) . ' ' . $job->get_language_code( true ),
'number_of_strings' => $number_of_strings,
'number_of_words' => $counters[ $manager_id ][ $lang_pair ]['number_of_words'] + $number_of_words_in_strings,
'number_of_pages' => $counters[ $manager_id ][ $lang_pair ]['number_of_pages'],
);
}
}
}
/**
* @param WPML_Element_Translation_Job $job
*
* @return string
*/
private function get_translator_name( WPML_Element_Translation_Job $job ) {
$translator_name = $job->get_translation_service() ?
TranslationProxy::get_service_name( $job->get_translation_service() ) :
$job->get_translator_name();
if ( 'local' === $job->get_translation_service() ) {
$user = get_userdata( $job->get_translator_id() );
$translator_name = $user->display_name . ' (' . $user->user_login . ')';
}
return $translator_name;
}
/**
* @return array
*/
public function get_jobs() {
return $this->jobs;
}
}

View File

@@ -0,0 +1,11 @@
<?php
class WPML_TM_Jobs_Summary {
const WEEKLY_REPORT = 'weekly';
const DAILY_REPORT = 'daily';
const DAILY_SCHEDULE = '1 day';
const WEEKLY_SCHEDULE = '1 week';
const JOBS_COMPLETED_KEY = 'completed';
const JOBS_WAITING_KEY = 'waiting';
}

View File

@@ -0,0 +1,18 @@
<?php
class WPML_TM_Jobs_Weekly_Summary_Report_Model implements WPML_TM_Jobs_Summary_Report_Model {
/**
* @return string
*/
public function get_subject() {
return __( 'Translation updates for %1$s until %2$s', 'wpml-translation-management' );
}
/**
* @return string
*/
public function get_summary_text() {
return __( 'This week %1$s had the following %2$s translation updates', 'wpml-translation-management' );
}
}

View File

@@ -0,0 +1,14 @@
<?php
interface WPML_TM_Jobs_Summary_Report_Model {
/**
* @return string
*/
public function get_subject();
/**
* @return string
*/
public function get_summary_text();
}

View File

@@ -0,0 +1,16 @@
<?php
class WPML_TM_Jobs_Summary_Report_Hooks_Factory implements IWPML_Frontend_Action_Loader, IWPML_Backend_Action_Loader {
/**
* @return WPML_TM_Jobs_Summary_Report_Hooks
*/
public function create() {
global $iclTranslationManagement;
return new WPML_TM_Jobs_Summary_Report_Hooks(
new WPML_TM_Jobs_Summary_Report_Process_Factory(),
$iclTranslationManagement
);
}
}

View File

@@ -0,0 +1,78 @@
<?php
class WPML_TM_Jobs_Summary_Report_Hooks {
const EVENT_HOOK = 'wpml_tm_send_summary_report';
const EVENT_CALLBACK = 'send_summary_report';
/**
* @var WPML_TM_Jobs_Summary_Report_Process_Factory
*/
private $process_factory;
/**
* @var TranslationManagement
*/
private $tm;
public function __construct( WPML_TM_Jobs_Summary_Report_Process_Factory $process_factory, TranslationManagement $tm ) {
$this->process_factory = $process_factory;
$this->tm = $tm;
}
public function add_hooks() {
if ( $this->notification_setting_allow_scheduling() ) {
add_action( self::EVENT_HOOK, array( $this, self::EVENT_CALLBACK ) );
add_action( 'init', array( $this, 'schedule_email' ) );
}
}
/**
* @return bool
*/
private function notification_setting_allow_scheduling() {
$schedulable_settings = array(
WPML_TM_Emails_Settings::NOTIFY_DAILY,
WPML_TM_Emails_Settings::NOTIFY_WEEKLY
);
return isset( $this->tm->settings['notification'][ WPML_TM_Emails_Settings::COMPLETED_JOB_FREQUENCY ] ) &&
in_array(
(int) $this->tm->settings['notification'][ WPML_TM_Emails_Settings::COMPLETED_JOB_FREQUENCY ],
$schedulable_settings,
true
);
}
public function send_summary_report() {
if ( WPML_TM_Emails_Settings::NOTIFY_DAILY === (int) $this->tm->settings['notification'][ WPML_TM_Emails_Settings::COMPLETED_JOB_FREQUENCY ] ) {
$summary_report_process = $this->process_factory->create_daily_report();
} else {
$summary_report_process = $this->process_factory->create_weekly_report();
}
if ( $summary_report_process ) {
$summary_report_process->send();
}
}
public function schedule_email() {
if ( ! wp_next_scheduled( self::EVENT_HOOK ) ) {
wp_schedule_single_event( $this->get_schedule_time(), self::EVENT_HOOK );
}
}
/**
* @return int
*/
private function get_schedule_time() {
$schedule_time = strtotime( '+ ' . WPML_TM_Jobs_Summary::DAILY_SCHEDULE );
if ( WPML_TM_Emails_Settings::NOTIFY_WEEKLY === (int) $this->tm->settings['notification'][ WPML_TM_Emails_Settings::COMPLETED_JOB_FREQUENCY ] ) {
$schedule_time = strtotime( '+ ' . WPML_TM_Jobs_Summary::WEEKLY_SCHEDULE );
}
return $schedule_time;
}
}

View File

@@ -0,0 +1,66 @@
<?php
class WPML_TM_Email_Notification_View extends WPML_TM_Email_View {
const PROMOTE_TRANSLATION_SERVICES_TEMPLATE = 'notification/promote-translation-services.twig';
/**
* @param array $model
* @param string $template
*
* @return string
*/
public function render_model( array $model, $template ) {
if ( isset( $model['casual_name'] ) && $model['casual_name'] ) {
$content = $this->render_casual_header( $model['casual_name'] );
} else {
$content = $this->render_header( $model['username'] );
}
$content .= $this->template_service->show( $model, $template );
$content .= $this->render_promote_translation_services( $model );
$content .= $this->render_footer();
return $content;
}
/**
* @param array $model
*
* @return string
*/
private function render_promote_translation_services( array $model ) {
$content = '';
if ( isset( $model['promote_translation_services'] ) && $model['promote_translation_services'] ) {
$translation_services_url = esc_url( admin_url( 'admin.php?page=' . WPML_TM_FOLDER . '/menu/main.php&sm=translators' ) );
/* translators: Promote translation services: %s replaced by "professional translation services integrated with WPML" */
$promoteA = esc_html_x( 'Need faster translation work? Try one of the %s.', 'Promote translation services: %s replaced by "professional translation services integrated with WPML"', 'wpml-translation-management' );
/* translators: Promote translation services: Promote translation services: used to build a link to the translation services page */
$promoteB = esc_html_x( 'professional translation services integrated with WPML', 'Promote translation services: used to build a link to the translation services page', 'wpml-translation-management' );
$promote_model['message'] = sprintf( $promoteA, '<a href="' . $translation_services_url . '">' . $promoteB . '</a>' );
$content = $this->template_service->show( $promote_model, self::PROMOTE_TRANSLATION_SERVICES_TEMPLATE );
}
return $content;
}
/** @return string */
private function render_footer() {
$notifications_url = esc_url( admin_url( 'admin.php?page=' . WPML_TM_FOLDER . WPML_Translation_Management::PAGE_SLUG_SETTINGS . '&sm=notifications' ) );
$notifications_text = esc_html__( 'WPML Notification Settings', 'wpml-translation-management' );
$notifications_link = '<a href="' . $notifications_url . '" style="color: #ffffff;">' . $notifications_text . '</a>';
$bottom_text = sprintf(
esc_html__(
'To stop receiving notifications, log-in to %s and change your preferences.',
'wpml-translation-management'
),
$notifications_link
);
return $this->render_email_footer( $bottom_text );
}
}

View File

@@ -0,0 +1,25 @@
<?php
class WPML_TM_Overdue_Jobs_Report_Factory {
public function create() {
global $wpdb, $iclTranslationManagement, $sitepress;
$jobs_collection = new WPML_Translation_Jobs_Collection( $wpdb, array() );
$email_template_service_factory = wpml_tm_get_email_twig_template_factory();
$report_email_view = new WPML_TM_Email_Notification_View( $email_template_service_factory->create() );
$has_active_remote_service = TranslationProxy::is_current_service_active_and_authenticated();
$notification_settings = $iclTranslationManagement->settings['notification'];
$tp_jobs_factory = new WPML_TP_Jobs_Collection_Factory();
$tp_jobs = $tp_jobs_factory->create();
return new WPML_TM_Overdue_Jobs_Report(
$jobs_collection,
$report_email_view,
$has_active_remote_service,
$notification_settings,
$sitepress,
$tp_jobs
);
}
}

View File

@@ -0,0 +1,131 @@
<?php
class WPML_TM_Overdue_Jobs_Report {
const OVERDUE_JOBS_REPORT_TEMPLATE = 'notification/overdue-jobs-report.twig';
/** @var WPML_Translation_Jobs_Collection $jobs_collection */
private $jobs_collection;
/** @var WPML_TM_Email_Notification_View $email_view */
private $email_view;
/** @var bool $has_active_remote_service */
private $has_active_remote_service;
/** @var array $notification_settings */
private $notification_settings;
private $sitepress;
private $tp_jobs;
/**
* @param WPML_Translation_Jobs_Collection $jobs_collection
* @param WPML_TM_Email_Notification_View $email_view
* @param bool $has_active_remote_service
* @param array $notification_settings
* @param SitePress $sitepress
* @param WPML_TP_Jobs_Collection|null $tp_jobs
*/
public function __construct(
WPML_Translation_Jobs_Collection $jobs_collection,
WPML_TM_Email_Notification_View $email_view,
$has_active_remote_service,
array $notification_settings,
SitePress $sitepress,
WPML_TP_Jobs_Collection $tp_jobs = null
) {
$this->jobs_collection = $jobs_collection;
$this->email_view = $email_view;
$this->has_active_remote_service = $has_active_remote_service;
$this->notification_settings = $notification_settings;
$this->sitepress = $sitepress;
$this->tp_jobs = $tp_jobs;
}
public function send() {
$jobs_by_manager_id = $this->get_overdue_jobs_by_manager_id();
if ( $jobs_by_manager_id ) {
$current_language = $this->sitepress->get_current_language();
$this->sitepress->switch_lang( $this->sitepress->get_default_language() );
foreach ( $jobs_by_manager_id as $manager_id => $jobs ) {
$this->send_email( $manager_id, $jobs );
}
$this->sitepress->switch_lang( $current_language );
}
}
/** @return array */
private function get_overdue_jobs_by_manager_id() {
$args = array(
'overdue' => true,
'translator_id' => '',
);
$jobs = $this->jobs_collection->get_jobs( $args );
$jobs_by_manager_id = array();
foreach ( $jobs as $key => $job ) {
if ( $this->tp_jobs && $this->tp_jobs->is_job_canceled( $job ) ) {
continue;
}
if ( ! $job instanceof WPML_Element_Translation_Job ) {
continue;
}
if ( $job->get_number_of_days_overdue() < $this->notification_settings['overdue_offset'] ) {
continue;
}
$job->get_basic_data();
$jobs_by_manager_id[ $job->get_manager_id() ][] = $job;
}
return $jobs_by_manager_id;
}
/**
* @param string $manager_id
* @param array $jobs
*/
private function send_email( $manager_id, array $jobs ) {
$manager = get_user_by( 'id', $manager_id );
$translation_jobs_url = admin_url( 'admin.php?page=' . WPML_TM_FOLDER . '/menu/main.php&sm=jobs' );
/* translators: List of translation jobs: %s replaced by "list of translation jobs" */
$message_to_translation_jobsA = esc_html_x( 'You can see all the jobs that you sent and their deadlines in the %s.', 'List of translation jobs: %s replaced by "list of translation jobs"', 'wpml-translation-management' );
/* translators: List of translation jobs: used to build a link to the translation jobs page */
$message_to_translation_jobsB = esc_html_x( 'list of translation jobs', 'List of translation jobs: used to build a link to the translation jobs page', 'wpml-translation-management' );
$message_to_translation_jobs = sprintf(
$message_to_translation_jobsA,
'<a href="' . esc_url( $translation_jobs_url ) . '">' . $message_to_translation_jobsB . '</a>'
);
$model = array(
'username' => $manager->display_name,
'intro_message_1' => __( 'This is a quick reminder about translation jobs that you sent and are behind schedule.', 'wpml-translation-management' ),
'intro_message_2' => __( 'The deadline that you set for the following jobs has passed:', 'wpml-translation-management' ),
'jobs' => $jobs,
'job_deadline_details' => __( 'deadline: %1$s, late by %2$d days', 'wpml-translation-management' ),
'message_to_translation_jobs' => $message_to_translation_jobs,
'promote_translation_services' => ! $this->has_active_remote_service,
);
$to = $manager->display_name . ' <' . $manager->user_email . '>';
$subject = esc_html__( 'Overdue translation jobs report', 'wpml-translation-management' );
$message = $this->email_view->render_model( $model, self::OVERDUE_JOBS_REPORT_TEMPLATE );
$headers = array(
'MIME-Version: 1.0',
'Content-type: text/html; charset=UTF-8',
);
wp_mail( $to, $subject, $message, $headers );
}
}

View File

@@ -0,0 +1,162 @@
<?php
/**
* Class WPML_TM_Batch_Report_Email
*/
class WPML_TM_Batch_Report_Email_Builder {
/**
* @var WPML_TM_Batch_Report
*/
private $batch_report;
/**
* @var array
*/
private $emails;
/**
* @var WPML_TM_Email_Jobs_Summary_View
*/
private $email_template;
/**
* WPML_TM_Notification_Batch_Email constructor.
*
* @param WPML_TM_Batch_Report $batch_report
* @param WPML_TM_Email_Jobs_Summary_View $email_template
*/
public function __construct( WPML_TM_Batch_Report $batch_report, WPML_TM_Email_Jobs_Summary_View $email_template ) {
$this->batch_report = $batch_report;
$this->email_template = $email_template;
$this->emails = array();
}
/**
* @param array $batch_jobs
*/
public function prepare_assigned_jobs_emails( $batch_jobs ) {
foreach ( $batch_jobs as $translator_id => $language_pairs ) {
if ( 0 !== $translator_id ) {
$translator = get_userdata( $translator_id );
$title = __( 'You have been assigned to new translation job(s):', 'wpml-translation-management' );
$render_jobs_list = $this->email_template->render_jobs_list( $language_pairs, $translator_id, $title );
if ( null === $render_jobs_list ) {
continue;
}
$body = $this->email_template->render_header( $translator->display_name );
$body .= $render_jobs_list;
$assigned_jobs = $this->email_template->get_assigned_jobs();
$title_singular = __( 'There is 1 job, which you can take (not specifically assigned to you):', 'wpml-translation-management' );
$title_plural = __( 'There are %s jobs, which you can take (not specifically assigned to you):', 'wpml-translation-management' );
$unassigned_jobs_body = $this->email_template->render_jobs_list(
$this->batch_report->get_unassigned_jobs(),
$translator_id,
$title_singular,
$title_plural
);
if ( null !== $unassigned_jobs_body ) {
$body .= $unassigned_jobs_body;
}
$body .= $this->email_template->render_footer();
$email['body'] = $body;
$email = $this->add_attachments( $email, $assigned_jobs );
$this->emails[] = array(
'translator_id' => $translator->ID,
'email' => $translator->user_email,
'subject' => $this->get_subject_assigned_job(),
'body' => $body,
'attachment' => array_key_exists( 'attachment', $email ) ? $email['attachment'] : array(),
);
}
}
}
/**
* @param array $batch_jobs
*/
public function prepare_unassigned_jobs_emails( $batch_jobs ) {
if ( array_key_exists( 0, $batch_jobs ) ) {
$unassigned_jobs = $batch_jobs[0];
$translators = $this->batch_report->get_unassigned_translators();
$title_singular = __( 'There is 1 job waiting for a translator:', 'wpml-translation-management' );
$title_plural = __( 'There are %s jobs waiting for a translator:', 'wpml-translation-management' );
foreach ( $translators as $translator ) {
$translator_user = get_userdata( $translator );
$render_jobs_list = $this->email_template->render_jobs_list( $unassigned_jobs, $translator_user->ID, $title_singular, $title_plural );
if ( null !== $render_jobs_list ) {
$body = $this->email_template->render_header( $translator_user->display_name );
$body .= $render_jobs_list;
$body .= $this->email_template->render_footer();
$this->emails[] = array(
'translator_id' => $translator_user->ID,
'email' => $translator_user->user_email,
'subject' => $this->get_subject_unassigned_job(),
'body' => $body,
);
}
}
}
}
/**
* @param array $email
* @param array $jobs
*
* @return array
*/
private function add_attachments( $email, $jobs ) {
$attachments = array();
foreach ( $jobs as $job ) {
if ( 'post' === $job['type'] ) {
$email = apply_filters( 'wpml_new_job_notification', $email, $job['job_id'] );
if ( array_key_exists( 'attachment', $email ) ) {
$attachments[] = $email['attachment'];
}
}
}
if ( $attachments ) {
$attachments = apply_filters( 'wpml_new_job_notification_attachments', $attachments );
if ( count( $attachments ) > 0 ) {
$attachment_values = array_values( $attachments );
$email['attachment'] = $attachment_values[0];
}
}
return $email;
}
/**
* @return string
*/
private function get_subject_assigned_job() {
return sprintf( __( 'New translation job from %s', 'wpml-translation-management' ), get_bloginfo( 'name' ) );
}
/**
* @return string
*/
private function get_subject_unassigned_job() {
return sprintf( __( 'Job waiting for a translator in %s', 'wpml-translation-management' ), get_bloginfo( 'name' ) );
}
/**
* @return array
*/
public function get_emails() {
return $this->emails;
}
}

View File

@@ -0,0 +1,53 @@
<?php
/**
* Class WPML_TM_Batch_Report_Email_Process
*/
class WPML_TM_Batch_Report_Email_Process {
/**
* @var WPML_TM_Batch_Report
*/
private $batch_report;
/**
* @var WPML_TM_Batch_Report_Email_Builder
*/
private $email_builder;
/**
* WPML_TM_Batch_Report_Email_Process constructor.
*
* @param WPML_TM_Batch_Report $batch_report
* @param WPML_TM_Batch_Report_Email_Builder $email_builder
*/
public function __construct( WPML_TM_Batch_Report $batch_report, WPML_TM_Batch_Report_Email_Builder $email_builder ) {
$this->batch_report = $batch_report;
$this->email_builder = $email_builder;
}
public function process_emails() {
$batch_jobs = $this->batch_report->get_jobs();
$this->email_builder->prepare_assigned_jobs_emails( $batch_jobs );
$this->email_builder->prepare_unassigned_jobs_emails( $batch_jobs );
$this->send_emails();
}
private function send_emails() {
$headers = array();
$headers[] = 'Content-type: text/html; charset=UTF-8';
foreach ( $this->email_builder->get_emails() as $email ) {
$email['attachment'] = isset( $email['attachment'] ) ? $email['attachment'] : array();
$email_sent = wp_mail( $email['email'], $email['subject'], $email['body'], $headers, $email['attachment'] );
if ( $email_sent ) {
$this->batch_report->reset_batch_report( $email['translator_id'] );
}
}
$this->batch_report->reset_batch_report( 0 );
}
}

View File

@@ -0,0 +1,44 @@
<?php
/**
* Class WPML_TM_Notification_Batch_Hooks
*/
class WPML_TM_Batch_Report_Hooks {
/**
* @var WPML_TM_Batch_Report
*/
private $batch_report;
/**
* @var WPML_TM_Batch_Report_Email_Process
*/
private $email_process;
/**
* WPML_TM_Batch_Report_Hooks constructor.
*
* @param WPML_TM_Batch_Report $batch_report
* @param WPML_TM_Batch_Report_Email_Process $email_process
*/
public function __construct(
WPML_TM_Batch_Report $batch_report,
WPML_TM_Batch_Report_Email_Process $email_process
) {
$this->batch_report = $batch_report;
$this->email_process = $email_process;
}
public function add_hooks() {
add_action( 'wpml_tm_assign_job_notification', array( $this, 'set_job' ) );
add_action( 'wpml_tm_new_job_notification', array( $this, 'set_job' ), 10, 2 );
add_action( 'wpml_tm_local_string_sent', array( $this, 'set_job' ) );
add_action( 'wpml_tm_basket_committed', array( $this->email_process, 'process_emails' ) );
}
public function set_job( $job ) {
if ( $job instanceof WPML_Translation_Job ) {
$this->batch_report->set_job( $job );
}
}
}

View File

@@ -0,0 +1,85 @@
<?php
/**
* Class WPML_TM_Batch_Report
*/
class WPML_TM_Batch_Report {
const BATCH_REPORT_OPTION = '_wpml_batch_report';
/**
* @var WPML_TM_Blog_Translators
*/
private $blog_translators;
/**
* WPML_TM_Batch_Report constructor.
*
* @param WPML_TM_Blog_Translators $blog_translators
*/
public function __construct( WPML_TM_Blog_Translators $blog_translators) {
$this->blog_translators = $blog_translators;
}
/**
* @param WPML_Translation_Job $job
*/
public function set_job( WPML_Translation_Job $job ) {
$batch_jobs = $batch_jobs_raw = $this->get_jobs();
$job_fields = $job->get_basic_data();
if ( WPML_User_Jobs_Notification_Settings::is_new_job_notification_enabled( $job_fields->translator_id ) ) {
$lang_pair = $job_fields->source_language_code . '|' . $job_fields->language_code;
$batch_jobs[ (int) $job_fields->translator_id ][$lang_pair][] = array(
'element_id' => isset( $job_fields->original_doc_id ) ? $job_fields->original_doc_id : null,
'type' => strtolower( $job->get_type() ),
'job_id' => $job->get_id(),
);
}
if ( $batch_jobs_raw !== $batch_jobs ) {
update_option( self::BATCH_REPORT_OPTION, $batch_jobs, 'no' );
}
}
/**
* @return array
*/
public function get_unassigned_jobs() {
$batch_jobs = $this->get_jobs();
$unassigned_jobs = array();
if( array_key_exists( 0, $batch_jobs ) ) {
$unassigned_jobs = $batch_jobs[0];
}
return $unassigned_jobs;
}
/**
* @return array
*/
public function get_unassigned_translators() {
$assigned_translators = array_keys( $this->get_jobs() );
$blog_translators = wp_list_pluck( $this->blog_translators->get_blog_translators() , 'ID');
return array_diff( $blog_translators, $assigned_translators );
}
/**
* @return array
*/
public function get_jobs() {
return get_option( self::BATCH_REPORT_OPTION ) ? get_option( self::BATCH_REPORT_OPTION ) : array();
}
public function reset_batch_report( $translator_id ) {
$batch_jobs = $this->get_jobs();
if ( array_key_exists( $translator_id, $batch_jobs ) ) {
unset( $batch_jobs[$translator_id] );
}
update_option( self::BATCH_REPORT_OPTION, $batch_jobs, 'no' );
}
}

View File

@@ -0,0 +1,179 @@
<?php
/**
* Class WPML_TM_Email_Jobs_Summary_View
*/
class WPML_TM_Email_Jobs_Summary_View extends WPML_TM_Email_View {
const JOBS_TEMPLATE = 'batch-report/email-job-pairs.twig';
/**
* @var WPML_TM_Blog_Translators
*/
private $blog_translators;
/**
* @var SitePress
*/
private $sitepress;
/**
* @var array
*/
private $assigned_jobs;
/**
* WPML_TM_Batch_Report_Email_Template constructor.
*
* @param WPML_Twig_Template $template_service
* @param WPML_TM_Blog_Translators $blog_translators
* @param SitePress $sitepress
*/
public function __construct(
WPML_Twig_Template $template_service,
WPML_TM_Blog_Translators $blog_translators,
SitePress $sitepress
) {
parent::__construct( $template_service );
$this->blog_translators = $blog_translators;
$this->sitepress = $sitepress;
}
/**
* @param array $language_pairs
* @param int $translator_id
* @param string $title_singular
* @param string $title_plural
*
* @return null|string
*/
public function render_jobs_list( $language_pairs, $translator_id, $title_singular, $title_plural = '' ) {
$this->empty_assigned_jobs();
$model = array(
'strings' => array(
'strings_text' => __( 'Strings', 'wpml-translation-management' ),
'start_translating_text' => __( 'start translating', 'wpml-translation-management' ),
'take' => _x( 'take it', 'Take a translation job waiting for a translator', 'wpml-translation-management' ),
'strings_link' => admin_url(
'admin.php?page=wpml-string-translation%2Fmenu%2Fstring-translation.php'
),
'closing_sentence' => $this->get_closing_sentence(),
),
);
foreach ( $language_pairs as $lang_pair => $elements ) {
$languages = explode( '|', $lang_pair );
$args = array(
'lang_from' => $languages[0],
'lang_to' => $languages[1]
);
if ( $this->blog_translators->is_translator( $translator_id, $args ) &&
WPML_User_Jobs_Notification_Settings::is_new_job_notification_enabled( $translator_id ) ) {
$model_elements = array();
$string_added = false;
foreach ( $elements as $element ) {
if ( ! $string_added || 'string' !== $element['type'] ) {
$model_elements[] = array(
'original_link' => get_permalink( $element['element_id'] ),
'original_text' => sprintf( __( 'Link to original document %d', 'wpml-translation-management' ), $element['element_id'] ),
'start_translating_link' => admin_url(
'admin.php?page=' . WPML_TM_FOLDER . '%2Fmenu%2Ftranslations-queue.php&job_id=' . $element['job_id']
),
'type' => $element['type'],
);
if ( 'string' === $element['type'] ) {
$string_added = true;
}
}
$this->add_assigned_job( $element['job_id'], $element['type'] );
}
$source_lang = $this->sitepress->get_language_details( $languages[0] );
$target_lang = $this->sitepress->get_language_details( $languages[1] );
$model['lang_pairs'][$lang_pair] = array(
'title' => sprintf( __( 'From %1$s to %2$s:', 'wpml-translation-management' ), $source_lang['english_name'], $target_lang['english_name'] ),
'elements' => $model_elements,
);
}
}
$model['strings']['title'] = $title_singular;
if ( 1 < count( $this->get_assigned_jobs() ) ) {
$model['strings']['title'] = sprintf( $title_plural, count( $this->get_assigned_jobs() ) );
}
return count( $this->get_assigned_jobs() ) ? $this->template_service->show( $model, self::JOBS_TEMPLATE ) : null;
}
/** @return string */
public function render_footer() {
$site_url = get_bloginfo( 'url' );
$profile_link = '<a href="' . admin_url( 'profile.php' ) . '" style="color: #ffffff;">' . esc_html__( 'Your Profile', '' ) .'</a>';
$bottom_text = sprintf(
__(
'You are receiving this email because you have a translator
account in %1$s. To stop receiving notifications,
log-in to %2$s and unselect "Send me a notification email
when there is something new to translate". Please note that
this will take you out of the translators pool.', 'wpml-translation-management'
),
$site_url,
$profile_link
);
return $this->render_email_footer( $bottom_text );
}
/**
* @param int $job_id
* @param string $type
*/
private function add_assigned_job( $job_id, $type ) {
$this->assigned_jobs[] = array(
'job_id' => $job_id,
'type' => $type,
);
}
/**
* @return array
*/
public function get_assigned_jobs() {
$string_counted = false;
foreach ( $this->assigned_jobs as $key => $assigned_job ) {
if ( 'string' === $assigned_job['type'] ) {
if ( $string_counted ) {
unset( $this->assigned_jobs[$key] );
}
$string_counted = true;
}
}
return $this->assigned_jobs;
}
private function empty_assigned_jobs() {
$this->assigned_jobs = array();
}
private function get_closing_sentence() {
$sentence = null;
if ( WPML_TM_ATE_Status::is_enabled_and_activated() ) {
$link = '<a href="https://wpml.org/documentation/translating-your-contents/advanced-translation-editor/?utm_source=plugin&utm_medium=gui&utm_campaign=wpmltm">' . __( "WPML's Advanced Translation Editor", 'wpml-translation-management' ) . '</a>';
$sentence = sprintf( __( "Need help translating? Read how to use %s.", 'wpml-translation-management' ), $link );
}
return $sentence;
}
}

View File

@@ -0,0 +1,10 @@
<?php
class WPML_TM_Email_Twig_Template_Factory {
/** @return WPML_Twig_Template */
public function create() {
$loader = new WPML_Twig_Template_Loader( array( WPML_TM_PATH . '/templates/emails/' ) );
return $loader->get_template();
}
}

View File

@@ -0,0 +1,59 @@
<?php
abstract class WPML_TM_Email_View {
const FOOTER_TEMPLATE = 'email-footer.twig';
const HEADER_TEMPLATE = 'email-header.twig';
/** @var WPML_Twig_Template $template_service */
protected $template_service;
public function __construct( WPML_Twig_Template $template_service ) {
$this->template_service = $template_service;
}
/**
* @param string $username
*
* @return string
*/
public function render_header( $username = '' ) {
$model = array(
'greetings' => sprintf( __( 'Dear %s,', 'wpml-translation-management' ), $username ),
);
return $this->template_service->show( $model, self::HEADER_TEMPLATE );
}
/**
* @param string $username
*
* @return string
*/
public function render_casual_header( $first_name = '' ) {
$model = array(
'greetings' => sprintf( __( 'Hi %s,', 'wpml-translation-management' ), $first_name ),
);
return $this->template_service->show( $model, self::HEADER_TEMPLATE );
}
/**
* @param string $bottom_text
*
* @return string
*/
protected function render_email_footer( $bottom_text = '' ) {
$site_url = get_bloginfo( 'url' );
$model = array(
'bottom_text' => $bottom_text,
'wpml_footer' => sprintf(
esc_html__( 'Generated by WPML plugin, running on %s.', 'wpml-translation-management' ),
'<a href="' . $site_url . '" style="color: #ffffff;">' . $site_url . '</a>'
),
);
return $this->template_service->show( $model, self::FOOTER_TEMPLATE );
}
}

View File

@@ -0,0 +1,507 @@
<?php
/**
* Class WPML_TM_Mail_Notification
*/
class WPML_TM_Mail_Notification {
const JOB_COMPLETE_TEMPLATE = 'notification/job-completed.twig';
const JOB_REVISED_TEMPLATE = 'notification/job-revised.twig';
const JOB_CANCELED_TEMPLATE = 'notification/job-canceled.twig';
private $mail_cache = array();
private $process_mail_queue;
/** @var wpdb $wpdb */
private $wpdb;
/** @var SitePress $sitepress */
private $sitepress;
/** @var WPML_Translation_Job_Factory $job_factory */
private $job_factory;
/** @var WPML_TM_Email_Notification_View $email_view */
private $email_view;
/** @var array $notification_settings */
private $notification_settings;
/** @var bool $has_active_remote_service */
private $has_active_remote_service;
public function __construct(
SitePress $sitepress,
wpdb $wpdb,
WPML_Translation_Job_Factory $job_factory,
WPML_TM_Email_Notification_View $email_view,
array $notification_settings,
$has_active_remote_service
) {
$this->wpdb = $wpdb;
$this->sitepress = $sitepress;
$this->job_factory = $job_factory;
$this->email_view = $email_view;
$this->notification_settings = array_merge(
array(
'resigned' => 0,
'completed' => 0,
),
$notification_settings
);
$this->has_active_remote_service = $has_active_remote_service;
}
public function init() {
add_action( 'wpml_tm_empty_mail_queue', array( $this, 'send_queued_mails' ), 10, 0 );
if ( $this->should_send_email_on_update() ) {
add_action( 'wpml_tm_complete_job_notification', array( $this, 'wpml_tm_job_complete_mail' ), 10, 2 );
add_action( 'wpml_tm_revised_job_notification', array( $this, 'revised_job_email' ), 10 );
add_action( 'wpml_tm_canceled_job_notification', array( $this, 'canceled_job_email' ), 10 );
}
add_action( 'wpml_tm_remove_job_notification', array( $this, 'translator_removed_mail' ), 10, 2 );
add_action( 'wpml_tm_resign_job_notification', array( $this, 'translator_resign_mail' ), 10, 2 );
add_action( 'icl_ajx_custom_call', array( $this, 'send_queued_mails' ), 10, 0 );
add_action( 'icl_pro_translation_completed', array( $this, 'send_queued_mails' ), 10, 0 );
}
/**
* @return bool
*/
private function should_send_email_on_update() {
return ! isset( $this->notification_settings[ WPML_TM_Emails_Settings::COMPLETED_JOB_FREQUENCY ] ) ||
( isset( $this->notification_settings[ WPML_TM_Emails_Settings::COMPLETED_JOB_FREQUENCY ] ) &&
WPML_TM_Emails_Settings::NOTIFY_IMMEDIATELY === (int) $this->notification_settings[ WPML_TM_Emails_Settings::COMPLETED_JOB_FREQUENCY ] );
}
public function send_queued_mails() {
$tj_url = admin_url( 'admin.php?page=' . WPML_TM_FOLDER . '/menu/translations-queue.php' );
foreach ( $this->mail_cache as $type => $mail_to_send ) {
foreach ( $mail_to_send as $to => $subjects ) {
$headers = '';
$body_to_send = '';
foreach ( $subjects as $subject => $content ) {
$body = $content['body'];
$home_url = get_home_url();
if ( 'completed' === $type ) {
$headers = array(
'Content-type: text/html; charset=UTF-8',
);
$body_to_send .= $body[0];
} else {
$body_to_send .= $body_to_send . "\n\n" . implode( "\n\n\n\n", $body ) . "\n\n\n\n";
if ( $type === 'translator' ) {
$footer = sprintf(
__( 'You can view your other translation jobs here: %s', 'wpml-translation-management' ),
$tj_url
) . "\n\n--\n";
$footer .= sprintf(
__(
"This message was automatically sent by Translation Management running on %1\$s. To stop receiving these notifications contact the system administrator at %2\$s.\n\nThis email is not monitored for replies.",
'wpml-translation-management'
),
get_bloginfo( 'name' ),
$home_url
);
} else {
$footer = "\n--\n" . sprintf(
__(
"This message was automatically sent by Translation Management running on %1\$s. To stop receiving these notifications, go to Notification Settings, or contact the system administrator at %2\$s.\n\nThis email is not monitored for replies.",
'wpml-translation-management'
),
get_bloginfo( 'name' ),
$home_url
);
}
$body_to_send .= $footer;
}
$attachments = isset( $content['attachment'] ) ? $content['attachment'] : array();
$attachments = apply_filters( 'wpml_new_job_notification_attachments', $attachments );
/**
* @deprecated Use 'wpml_new_job_notification_attachments' instead
*/
$attachments = apply_filters( 'WPML_new_job_notification_attachments', $attachments );
$this->sitepress->get_wp_api()->wp_mail( $to, $subject, $body_to_send, $headers, $attachments );
}
}
}
$this->mail_cache = array();
$this->process_mail_queue = false;
}
/**
* @param int $job_id
*
* @return array|null
*/
private function get_basic_mail_data( $job_id ) {
$manager_id = false;
/** @var WPML_Translation_Job|false $job */
if ( is_object( $job_id ) ) {
$job = $job_id;
} else {
$job = $this->job_factory->get_translation_job( $job_id, false, 0, true );
}
if ( is_object( $job ) ) {
$data = $job->get_basic_data();
$manager_id = isset( $data->manager_id ) ? $data->manager_id : -1;
}
if ( ! $job || ( $manager_id && (int) $manager_id === (int) $job->get_translator_id() ) ) {
return null;
}
$manager = new WP_User( $manager_id );
$translator = new WP_User( $job->get_translator_id() );
$user_language = $this->sitepress->get_user_admin_language( $manager->ID );
$mail = array(
'to' => $manager->display_name . ' <' . $manager->user_email . '>',
);
$this->sitepress->switch_locale( $user_language );
list( $lang_from, $lang_to ) = $this->get_lang_to_from( $job, $user_language );
$model = array(
'view_jobs_text' => __( 'View translation jobs', 'wpml-translation-management' ),
'username' => $manager->display_name,
'lang_from' => $lang_from,
'lang_to' => $lang_to,
);
$document_title = $job->get_title();
if ( 'string' !== strtolower( $job->get_type() ) ) {
/** @var WPML_Post_Translation_Job $job */
$model['translation_jobs_url'] = admin_url( 'admin.php?page=' . WPML_TM_FOLDER . '/menu/main.php&sm=jobs' );
$document_title = '<a href="' . $job->get_url( true ) . '">' . $document_title . '</a>';
$model = $this->update_model_for_deadline( $model, $job );
}
return array(
'mail' => $mail,
'document_title' => $document_title,
'job' => $job,
'manager' => $manager,
'translator' => $translator,
'model' => $model,
);
}
/**
* @param WPML_Translation_Job|int $job_id
* @param bool|false $update
*
* @return false|array representation of the email to be sent
*/
public function wpml_tm_job_complete_mail( $job_id, $update = false ) {
$basic_mail_data = $this->get_basic_mail_data( $job_id );
if ( null === $basic_mail_data ) {
return null;
}
$mail = $basic_mail_data['mail'];
$document_title = $basic_mail_data['document_title'];
$translator = $basic_mail_data['translator'];
$job = $basic_mail_data['job'];
$model = $basic_mail_data['model'];
$lang_from = $model['lang_from'];
$lang_to = $model['lang_to'];
if ( $update ) {
$mail['subject'] = sprintf(
__( 'Translator has updated translation job for %s', 'wpml-translation-management' ),
get_bloginfo( 'name' )
);
$body_placeholder = esc_html__(
'The translator %1$shas updated the translation job for "%2$s" from %3$s to %4$s.',
'wpml-translation-management'
);
} else {
$mail['subject'] = sprintf(
__( 'Translator has completed translation job for %s', 'wpml-translation-management' ),
get_bloginfo( 'name' )
);
$body_placeholder = esc_html__(
'The translator %1$shas completed the translation job for "%2$s" from %3$s to %4$s.',
'wpml-translation-management'
);
}
$translator_name = ! empty( $translator->display_name ) ? '(' . $translator->display_name . ') ' : '';
$model['message'] = sprintf( $body_placeholder, $translator_name, $document_title, $lang_from, $lang_to );
$model['needs_help'] = array(
'title' => __( 'Need help with translation?', 'wpml-translation-management' ),
'options_or' => __( 'or', 'wpml-translation-management' ),
'translators_link' => admin_url( 'admin.php?page=' . WPML_TM_FOLDER . '/menu/main.php&sm=translators' ),
'translators_text' => __( 'Manage your translators', 'wpml-translation-management' ),
'translation_services_link' => admin_url( 'admin.php?page=' . WPML_TM_FOLDER . '/menu/main.php&sm=translators' ),
'translation_services_text' => __( 'try a translation service', 'wpml-translation-management' ),
);
$model['overdue_job'] = ! $job->is_completed_on_time();
$mail['body'] = $this->email_view->render_model( $model, self::JOB_COMPLETE_TEMPLATE );
$mail['type'] = 'completed';
$this->enqueue_mail( $mail );
$this->sitepress->switch_locale();
return $mail;
}
/**
* @param $job_id
*
* @return array|bool
*/
public function revised_job_email( $job_id ) {
if ( ! $this->should_send_immediate_notification( 'completed' ) ) {
return false;
}
$subject = sprintf( __( 'Translator job updated for %s', 'wpml-translation-management' ), get_bloginfo( 'name' ) );
$placeholder = esc_html__(
'A new translation for %1$s from %2$s to %3$s was created on the Translation Service. It&#8217;s ready to download and will be applied next time the Translation Service delivers completed translations to your site or when you manually fetch them.',
'wpml-translation-management'
);
return $this->generic_update_notification_email( $job_id, $subject, $placeholder, self::JOB_REVISED_TEMPLATE );
}
/**
* @param WPML_TM_Job_Entity $job
*
* @return array|bool
*/
public function canceled_job_email( WPML_TM_Job_Entity $job ) {
if ( ! $job instanceof WPML_TM_Post_Job_Entity ) {
return false;
}
if ( ! $this->should_send_immediate_notification( 'completed' ) ) {
return false;
}
$subject = sprintf( __( 'Translator job canceled for %s', 'wpml-translation-management' ), get_bloginfo( 'name' ) );
$placeholder = esc_html__(
'The translation for %1$s from %2$s to %3$s was canceled on the Translation Service. You can send this document to translation again from the Translation Dashboard.',
'wpml-translation-management'
);
return $this->generic_update_notification_email(
$job->get_translate_job_id(),
$subject,
$placeholder,
self::JOB_CANCELED_TEMPLATE
);
}
private function generic_update_notification_email( $job_id, $mail_subject, $body_placeholder, $template ) {
$basic_mail_data = $this->get_basic_mail_data( $job_id );
if ( null === $basic_mail_data ) {
return null;
}
$mail = $basic_mail_data['mail'];
$document_title = $basic_mail_data['document_title'];
$model = $basic_mail_data['model'];
$lang_from = $model['lang_from'];
$lang_to = $model['lang_to'];
$mail['subject'] = $mail_subject;
$model['message'] = sprintf( $body_placeholder, $document_title, $lang_from, $lang_to );
$mail['body'] = $this->email_view->render_model( $model, $template );
$mail['type'] = 'completed';
$this->enqueue_mail( $mail );
$this->sitepress->switch_locale();
return $mail;
}
private function should_send_immediate_notification( $type ) {
return isset( $this->notification_settings[ $type ] ) &&
(int) $this->notification_settings[ $type ] === WPML_TM_Emails_Settings::NOTIFY_IMMEDIATELY;
}
/**
* @param array $model
* @param WPML_Element_Translation_Job $job
*
* @return array
*/
private function update_model_for_deadline( array $model, WPML_Element_Translation_Job $job ) {
if ( $job->is_completed_on_time() ) {
$model['deadline_status'] = __( 'The translation job was completed on time.', 'wpml-translation-management' );
} else {
$overdue_days = $job->get_number_of_days_overdue();
$model['deadline_status'] = sprintf(
_n(
'This translation job is overdue by %s day.',
'This translation job is overdue by %s days.',
$overdue_days,
'wpml-translation-management'
),
$overdue_days
);
if ( $overdue_days >= 7 ) {
$model['promote_translation_services'] = ! $this->has_active_remote_service;
}
}
return $model;
}
/**
* @param int $translator_id
* @param WPML_Translation_Job|int $job
*
* @return bool
*/
public function translator_removed_mail( $translator_id, $job ) {
/** @var WPML_Translation_Job $job */
list( $manager_id, $job ) = $this->get_mail_elements( $job );
if ( ! $job || $manager_id == $translator_id ) {
return false;
}
$translator = new WP_User( $translator_id );
$manager = new WP_User( $manager_id );
$user_language = $this->sitepress->get_user_admin_language( $manager->ID );
$doc_title = $job->get_title();
$this->sitepress->switch_locale( $user_language );
list( $lang_from, $lang_to ) = $this->get_lang_to_from( $job, $user_language );
$mail['to'] = $translator->display_name . ' <' . $translator->user_email . '>';
$mail['subject'] = sprintf( __( 'Removed from translation job on %s', 'wpml-translation-management' ), get_bloginfo( 'name' ) );
$mail['body'] = sprintf(
__( 'You have been removed from the translation job "%1$s" for %2$s to %3$s.', 'wpml-translation-management' ),
$doc_title,
$lang_from,
$lang_to
);
$mail['type'] = 'translator';
$this->enqueue_mail( $mail );
$this->sitepress->switch_locale();
return $mail;
}
/**
* @param int $translator_id
* @param int|WPML_Translation_Job $job_id
*
* @return array|bool
*/
public function translator_resign_mail( $translator_id, $job_id ) {
/** @var WPML_Translation_Job $job */
list( $manager_id, $job ) = $this->get_mail_elements( $job_id );
if ( ! $job || $manager_id == $translator_id ) {
return false;
}
$translator = new WP_User( $translator_id );
$manager = new WP_User( $manager_id );
$tj_url = admin_url( 'admin.php?page=' . WPML_TM_FOLDER . '/menu/main.php&sm=jobs' );
$doc_title = $job->get_title();
$user_language = $this->sitepress->get_user_admin_language( $manager->ID );
$this->sitepress->switch_locale( $user_language );
list( $lang_from, $lang_to ) = $this->get_lang_to_from( $job, $user_language );
$mail = array();
if ( $this->notification_settings['resigned'] == ICL_TM_NOTIFICATION_IMMEDIATELY ) {
$mail['to'] = $manager->display_name . ' <' . $manager->user_email . '>';
$mail['subject'] = sprintf(
__( 'Translator has resigned from job on %s', 'wpml-translation-management' ),
get_bloginfo( 'name' )
);
$original_doc_title = $doc_title ? $doc_title : __( 'Deleted', 'wpml-translation-management' );
$mail['body'] = sprintf(
__(
'Translator %1$s has resigned from the translation job "%2$s" for %3$s to %4$s.%5$sView translation jobs: %6$s',
'wpml-translation-management'
),
$translator->display_name,
$original_doc_title,
$lang_from,
$lang_to,
"\n",
$tj_url
);
$mail['type'] = 'admin';
$this->enqueue_mail( $mail );
}
// restore locale
$this->sitepress->switch_locale();
return $mail;
}
private function enqueue_mail( $mail ) {
if ( $mail !== 'empty_queue' ) {
$this->mail_cache[ $mail['type'] ][ $mail['to'] ][ $mail['subject'] ]['body'][] = $mail['body'];
if ( isset( $mail['attachment'] ) ) {
$this->mail_cache[ $mail['type'] ][ $mail['to'] ][ $mail['subject'] ]['attachment'][] = $mail['attachment'];
}
$this->process_mail_queue = true;
}
}
/**
* @param int|WPML_Translation_Job $job_id
*
* @return array
*/
private function get_mail_elements( $job_id ) {
$job = is_object( $job_id ) ? $job_id : $this->job_factory->get_translation_job(
$job_id,
false,
0,
true
);
if ( is_object( $job ) ) {
$data = $job->get_basic_data();
$manager_id = isset( $data->manager_id ) ? $data->manager_id : - 1;
} else {
$job = false;
$manager_id = false;
}
return array( $manager_id, $job );
}
/**
* @param WPML_Translation_Job $job
* @param string $user_language
*
* @return array
*/
private function get_lang_to_from( $job, $user_language ) {
$sql = "SELECT name FROM {$this->wpdb->prefix}icl_languages_translations WHERE language_code=%s AND display_language_code=%s LIMIT 1";
$lang_from = $this->wpdb->get_var(
$this->wpdb->prepare(
$sql,
$job->get_source_language_code(),
$user_language
)
);
$lang_to = $this->wpdb->get_var( $this->wpdb->prepare( $sql, $job->get_language_code(), $user_language ) );
return array( $lang_from, $lang_to );
}
}