d8e970ab by Jeff Balicki

stuff

Signed-off-by: Jeff <jeff@gotenzing.com>
1 parent bdf10794
Showing 143 changed files with 4879 additions and 0 deletions
1 <?php if ( ! defined( 'ABSPATH' ) ) {
2 exit;
3 } ?>
4 <div class="wrap dpp_page_settings">
5 <h1><?php _e( 'Plugin Settings', 'duplicate-wp-page-post' ) ?></h1>
6 <?php
7 $dpp_options = array();
8 $opt = get_option( 'dpp_wpp_page_options' );
9 $getinstruct = sanitize_text_field($_GET['instruct']);
10 $instruct = isset( $getinstruct ) ? $getinstruct : '';
11 if ( isset( $_POST['submit_dpp_wpp_page'] ) && wp_verify_nonce( $_POST['dpp_nonce_field'], 'dpp_page_action' ) ):
12 _e( "<strong>changes saving..</strong>", 'duplicate-wp-page-post' );
13 $dpp_nosave = array( 'submit_dpp_wpp_page' );
14 foreach ( $dpp_nosave as $noneed ):
15 unset( $_POST[ $noneed ] );
16 endforeach;
17 foreach ( $_POST as $key => $val ):
18 $dpp_options[ $key ] = sanitize_text_field( $val );
19 endforeach;
20 $dpp_settings_save = update_option( 'dpp_wpp_page_options', $dpp_options );
21 if ( $dpp_settings_save ) {
22 dpp_wpp_page::dp_redirect( 'options-general.php?page=dpp_page_settings&instruct=1' );
23 } else {
24 dpp_wpp_page::dp_redirect( 'options-general.php?page=dpp_page_settings&instruct=2' );
25 }endif;
26 if ( ! empty( $instruct ) && $instruct == 1 ):
27 _e( '<div id="message" class="updated notice notice-success is-dismissible">
28 <p>Changes Saved!</p>
29 <button type="button" class="notice-dismiss">
30 <span class="screen-reader-text">Ignore this notice.</span>
31 </button>
32 </div>', 'duplicate-wp-page-post' );
33 elseif ( ! empty( $instruct ) && $instruct == 2 ):
34 _e( '<div id="message" class="error notice notice-error is-dismissible">
35 <p>Changes not saved!</p>
36 <button type="button" class="notice-dismiss">
37 <span class="screen-reader-text">Ignore this notice.</span>
38 </button>
39 </div>', 'duplicate-wp-page-post' );
40 endif;
41 ?>
42 <div id="dpp-stuff">
43 <div id="dpp-post-body" class="metabox-holder columns-2">
44 <div id="dpp-post-body-content" style="position: relative;">
45 <form style="padding: 10px; border: 1px solid #333;" action="" method="post" name="dpp_wpp_page_form">
46 <?php wp_nonce_field( 'dpp_page_action', 'dpp_nonce_field' ); ?>
47 <table class="form-table">
48 <tbody>
49 <tr>
50 <th scope="row"><label
51 for="dpp_posteditor">Select Editor<br><em>Default: Classic Editor</em></label>
52 </th>
53 <td>
54 <select id="dpp_posteditor" name="dpp_posteditor">
55 <option value="classic" <?php echo ( isset( $opt['dpp_posteditor'] ) && $opt['dpp_posteditor'] == 'classic' ) ? "selected = 'selected'" : ''; ?>><?php _e( 'Classic Editor', 'duplicate-wp-page-post' ); ?></option>
56 <option value="gutenberg" <?php echo ( isset( $opt['dpp_posteditor'] ) && $opt['dpp_posteditor'] == 'gutenberg' ) ? "selected = 'selected'" : ''; ?>><?php _e( 'Gutenberg Editor', 'duplicate-wp-page-post' ); ?></option>
57 </select>
58 <p>Please select which editor you are using.<br> If you are using Gutenberg, select
59 gutenberg editor otherwise it will not show Duplicate button on edit screen.</p>
60 </td>
61 </tr>
62 <tr>
63 <th scope="row"><label
64 for="dpp_post_status">Post Status<br><em>Default: Draft</em></label>
65 </th>
66 <td>
67 <select id="dpp_post_status" name="dpp_post_status">
68 <option value="draft" <?php echo ( $opt['dpp_post_status'] == 'draft' ) ? "selected = 'selected'" : ''; ?>><?php _e( 'Draft', 'duplicate-wp-page-post' ); ?></option>
69 <option value="publish" <?php echo ( $opt['dpp_post_status'] == 'publish' ) ? "selected = 'selected'" : ''; ?>><?php _e( 'Publish', 'duplicate-wp-page-post' ); ?></option>
70 <option value="private" <?php echo ( $opt['dpp_post_status'] == 'private' ) ? "selected = 'selected'" : ''; ?>><?php _e( 'Private', 'duplicate-wp-page-post' ); ?></option>
71 <option value="pending" <?php echo ( $opt['dpp_post_status'] == 'pending' ) ? "selected = 'selected'" : ''; ?>><?php _e( 'Pending', 'duplicate-wp-page-post' ); ?></option>
72 </select>
73 <p>Please select any post status you want to assign for duplicate post.</p>
74 </td>
75 </tr>
76 <tr>
77 <th scope="row"><label
78 for="dpp_post_redirect">Redirect<br><em>Default: To current list.</em><br>(After
79 click on <strong>Duplicate</strong></label>
80 </th>
81 <td>
82 <select id="dpp_post_redirect" name="dpp_post_redirect">
83 <option value="to_list" <?php echo ( $opt['dpp_post_redirect'] == 'to_list' ) ? "selected = 'selected'" : ''; ?>><?php _e( 'All Post List', 'duplicate-wp-page-post' ); ?></option>
84 <option value="to_page" <?php echo ( $opt['dpp_post_redirect'] == 'to_page' ) ? "selected = 'selected'" : ''; ?>><?php _e( 'Direct Edit', 'duplicate-wp-page-post' ); ?></option>
85 </select>
86 <p>Please select any post redirection, redirect you to selected after click on
87 duplicate.</p>
88 </td>
89 </tr>
90 <tr>
91 <th scope="row"><label
92 for="dpp_post_suffix">Duplicate Post Suffix<br><em>Default: Empty</em></label>
93 </th>
94
95 <td>
96 <input type="text" class="regular-text"
97 value="<?php echo ! empty( $opt['dpp_post_suffix'] ) ? esc_attr( $opt['dpp_post_suffix'] ) : '' ?>"
98 id="dpp_post_suffix" name="dpp_post_suffix">
99 <p>Add a suffix for duplicate page and post. It will show after title.</p>
100 </td>
101 </tr>
102 <tr>
103 <th scope="row"><label
104 for="dpp_post_link_title">Duplicate Link Text<br><em>Default:
105 Duplicate</em></label>
106 </th>
107 <td>
108 <input type="text" class="regular-text"
109 value="<?php echo ! empty( $opt['dpp_post_link_title'] ) ? esc_attr( $opt['dpp_post_link_title'] ) : '' ?>"
110 id="dpp_post_link_title" name="dpp_post_link_title">
111 <p>It will show above text on duplicate page/post link button instead of default
112 (Duplicate)</p>
113 </td>
114 </tr>
115 </tbody>
116 </table>
117 <p class="submit"><input type="submit" value="Save Settings" class="button button-primary"
118 id="submit" name="submit_dpp_wpp_page"></p>
119 </form>
120 </div>
121 </div>
122 <div>
123 <h3><a href="https://wordpress.org/support/plugin/duplicate-wp-page-post/reviews/?filter=5#new-post">Please
124 review us</a> if you like the plugin.</h3>
125 </div>
126 </div>
127 </div>
...\ No newline at end of file ...\ No newline at end of file
1 === Duplicate Page and Post ===
2 Contributors: arjunthakur, efficientninja
3 Tags: duplicate post, duplicate page, clone page, clone post, duplicate custom posts, clone custom post, wordpress page duplicator, wordpress post duplicator, page duplicate, clone page and post, wp post clone.
4 Requires at least: 3.5
5 Tested up to: 6.2
6 Requires PHP: 5.2.4
7 Stable tag: 2.9.3
8 Version: 2.9.3
9 License: GPLv2 or later
10 License URI: http://www.gnu.org/licenses/gpl-2.0.html
11
12 Duplicate post, Duplicate page and Duplicate custom post or clone page and clone post.
13
14 == Description ==
15
16 Duplicate page and post plugin provides functionality to create a clone of page or posts. You can duplicate pages, posts and custom post by single click and it will be saved as draft.
17 Duplicate page and post doesn't have a lot of features that other plugins have, but it also is lightning fast by comparison.
18
19 = Major features of this plugin include =
20
21 * Create a clone of particular page.
22 * Create a clone of particular post.
23 * Create a clone of particular custom post(CPT).
24 * Option to select editor (Classic and Gutenberg)
25 * Option to add Post Suffix.
26 * Option to add custom text for duplicate link button.
27 * Option to select Duplicate Posts Status.
28 * Option to Redirect after click on Duplicate.
29
30 = Like the plugin? =
31 <a href="https://wordpress.org/support/plugin/duplicate-wp-page-post/reviews/?rate=5#new-post">Please Vote</a>, Your votes really make a difference! Thanks.
32
33 == Installation ==
34
35 The plugin is simple to install:
36
37 * Download duplicate-wp-page-post.zip
38 * Unzip
39 * Upload duplicate-wp-page-post directory to your /wp-content/plugins directory
40 * Go to the plugin menu page and activate the plugin
41
42 == Frequently asked questions ==
43
44 = How to create the duplicate of a page or a post? =
45
46 1. Activate the plugin through the 'Plugins' menu in WordPress.
47 2. Then Create New Post/Page Or you can use old one.
48 3. Now go to all pages or all posts page on your dashboard.
49 4. Hover your cursor over any page or any post on dashboard section, you'll see a "Click here to clone" Button.
50 5. After you click on "Duplicate" link, then duplicate post/page will be created and saved as draft, make the changes you wish and hit publish.
51
52 = What is the benefit of using this plugin? =
53
54 User can easily duplicate the posts, pages or the custom posts with single click. It saves user's time to re-create the same post or page again.
55
56 == Screenshots ==
57
58 1. screenshot1.png
59 2. screenshot2.png
60 3. screenshot3.png
61 4. screenshot4.png
...\ No newline at end of file ...\ No newline at end of file
1 <?php
2
3 namespace WPML\Media\Widgets\Block;
4
5 use WPML\Element\API\Languages;
6 use WPML\LIB\WP\Hooks;
7 use function WPML\FP\spreadArgs;
8
9 class DisplayTranslation implements \IWPML_Frontend_Action, \IWPML_DIC_Action {
10
11 /**
12 * @var \WPML_Media_Translated_Images_Update $translatedImageUpdate
13 */
14 private $translatedImageUpdate;
15
16 public function __construct( \WPML_Media_Translated_Images_Update $translatedImageUpdate ) {
17 $this->translatedImageUpdate = $translatedImageUpdate;
18 }
19
20 public function add_hooks() {
21 Hooks::onFilter( 'widget_block_content' )
22 ->then( spreadArgs( function ( $content ) {
23 return $this->translatedImageUpdate->replace_images_with_translations( $content, Languages::getCurrentCode() );
24 } ) );
25 }
26 }
1 <?php
2
3 /**
4 * Class WPML_Media_Batch_Url_Translation
5 */
6 abstract class WPML_Media_Batch_Url_Translation {
7
8 const BATCH_SIZE = 10;
9
10 const BATCH_SIZE_FACTOR_ALL_MEDIA = 1;
11 const BATCH_SIZE_FACTOR_SPECIFIC_MEDIA = 10;
12
13 /**
14 * @var wpdb
15 */
16 protected $wpdb;
17
18 /**
19 * WPML_Media_Batch_Url_Translation constructor.
20 *
21 * @param wpdb $wpdb
22 */
23 public function __construct( wpdb $wpdb ) {
24 $this->wpdb = $wpdb;
25 }
26
27 public function add_hooks() {
28 add_action( 'wp_ajax_' . $this->get_ajax_action(), array( $this, 'run_batch' ) );
29 }
30
31 public function run_batch() {
32 $offset = isset( $_POST['offset'] ) ? (int) $_POST['offset'] : 0;
33 $attachment_id = isset( $_POST['attachment_id'] ) ? (int) $_POST['attachment_id'] : 0;
34 $all_media = ! empty( $_POST['global'] );
35
36 if ( $all_media ) {
37 $number_of_elements_left = $this->process_batch( $offset );
38 } else {
39 $number_of_elements_left = $this->process_batch_for_selected_media( $offset, $attachment_id );
40 }
41 $batch_size_factor = $all_media ? self::BATCH_SIZE_FACTOR_ALL_MEDIA : self::BATCH_SIZE_FACTOR_SPECIFIC_MEDIA;
42 $response = array(
43 'offset' => $offset + $this->get_batch_size( $batch_size_factor ),
44 'continue' => (int) ( $number_of_elements_left > 0 ),
45 'message' => $this->get_response_message( $number_of_elements_left ),
46 );
47
48 wp_send_json_success( $response );
49 }
50
51 /**
52 * @param int $number_of_elements_left
53 *
54 * @return string
55 */
56 abstract protected function get_response_message( $number_of_elements_left );
57
58 /**
59 * @param int $offset
60 *
61 * @return int
62 */
63 abstract protected function process_batch( $offset );
64
65 /**
66 * @param int $offset
67 * @param int $attachment_id
68 *
69 * @return int
70 */
71 abstract protected function process_batch_for_selected_media( $offset, $attachment_id );
72
73 /**
74 * @return array
75 */
76 abstract protected function get_ajax_error_message();
77
78 /**
79 * @param int $batch_size_factor
80 *
81 * @return int
82 */
83 protected function get_batch_size( $batch_size_factor = self::BATCH_SIZE_FACTOR_ALL_MEDIA ) {
84 return $batch_size_factor * self::BATCH_SIZE;
85 }
86
87 /**
88 * @return string
89 */
90 abstract protected function get_ajax_action();
91
92 }
1 <?php
2
3 /**
4 * Class WPML_Media_Custom_Field_Batch_Url_Translation_Factory
5 */
6 class WPML_Media_Custom_Field_Batch_Url_Translation_Factory implements IWPML_Backend_Action_Loader {
7
8 public function create() {
9 global $wpdb, $sitepress;
10
11 if ( WPML_Media_Custom_Field_Batch_Url_Translation::is_ajax_request() ) {
12
13 $translatable_custom_fields = $sitepress->get_custom_fields_translation_settings(
14 $sitepress->get_wp_api()->constant( 'WPML_TRANSLATE_CUSTOM_FIELD' )
15 );
16
17 $custom_field_images_translation_factory = new WPML_Media_Custom_Field_Images_Translation_Factory();
18
19 return new WPML_Media_Custom_Field_Batch_Url_Translation(
20 $custom_field_images_translation_factory->create(),
21 $wpdb,
22 $translatable_custom_fields
23 );
24
25 }
26
27 return null;
28 }
29
30 }
1 <?php
2
3 /**
4 * Class WPML_Media_Custom_Field_Batch_Url_Translation
5 */
6 class WPML_Media_Custom_Field_Batch_Url_Translation extends WPML_Media_Batch_Url_Translation implements IWPML_Action {
7
8 const AJAX_ACTION = 'wpml_media_translate_media_url_in_custom_fields';
9
10 /**
11 * @var WPML_Media_Custom_Field_Images_Translation
12 */
13 private $custom_field_translation;
14 /**
15 * @var array
16 */
17 private $translatable_custom_fields;
18
19 /**
20 * WPML_Media_Custom_Field_Batch_Url_Translation constructor.
21 *
22 * @param WPML_Media_Custom_Field_Images_Translation $custom_field_translation
23 * @param wpdb $wpdb
24 * @param array $translatable_custom_fields
25 */
26 public function __construct(
27 WPML_Media_Custom_Field_Images_Translation $custom_field_translation,
28 wpdb $wpdb,
29 array $translatable_custom_fields
30 ) {
31 parent::__construct( $wpdb );
32 $this->custom_field_translation = $custom_field_translation;
33 $this->translatable_custom_fields = $translatable_custom_fields;
34 }
35
36 /**
37 * @return string
38 */
39 protected function get_ajax_action() {
40 return self::AJAX_ACTION;
41 }
42
43 public static function is_ajax_request() {
44 return isset( $_POST['action'] ) && self::AJAX_ACTION === $_POST['action'];
45 }
46
47 /**
48 * @param int $number_of_custom_fields_left
49 *
50 * @return string
51 */
52 protected function get_response_message( $number_of_custom_fields_left ) {
53 return sprintf(
54 __( 'Translating media urls in custom field translations: %s', 'wpml-media' ),
55 $number_of_custom_fields_left > 0 ?
56 sprintf( __( '%d left', 'wpml-media' ), $number_of_custom_fields_left ) :
57 __( 'done!', 'wpml-media' )
58 );
59 }
60
61 protected function get_ajax_error_message() {
62 return array(
63 'key' => 'wpml_media_batch_urls_update_error_custom_fields',
64 'value' => esc_js( __( 'Translating media urls in custom fields translations failed: Please try again (%s)', 'wpml-media' ) ),
65 );
66 }
67
68 protected function process_batch( $offset ) {
69
70 if ( $this->translatable_custom_fields ) {
71 $translatable_custom_fields_where_in = wpml_prepare_in( $this->translatable_custom_fields );
72 $custom_fields = $this->wpdb->get_results(
73 "
74 SELECT SQL_CALC_FOUND_ROWS t.element_id AS post_id, p.meta_id, p.meta_key, p.meta_value
75 FROM {$this->wpdb->prefix}icl_translations t
76 JOIN {$this->wpdb->prefix}postmeta p ON t.element_id = p.post_id
77 WHERE t.element_type LIKE 'post_%'
78 AND t.element_type <> 'post_attachment'
79 AND t.source_language_code IS NULL
80 AND p.meta_key IN ({$translatable_custom_fields_where_in})
81 ORDER BY t.element_id ASC
82 LIMIT {$offset}, " . self::BATCH_SIZE
83 );
84
85 $number_of_all_custom_fields = (int) $this->wpdb->get_var( 'SELECT FOUND_ROWS()' );
86
87 foreach ( $custom_fields as $custom_field ) {
88 $this->custom_field_translation->translate_images(
89 $custom_field->meta_id,
90 $custom_field->post_id,
91 $custom_field->meta_key,
92 $custom_field->meta_value
93 );
94 }
95 } else {
96 $number_of_all_custom_fields = 0;
97 }
98
99 return $number_of_all_custom_fields - $offset - self::BATCH_SIZE;
100 }
101
102 protected function process_batch_for_selected_media( $offset, $attachment_id ) {
103 $media_url = wpml_like_escape( wp_get_attachment_url( $attachment_id ) );
104 if ( ! $media_url ) {
105 return 0;
106 }
107 preg_match( '/(.+)\.([a-z]+)$/', $media_url, $match );
108 $media_url_no_extension = wpml_like_escape( $match[1] );
109 $extension = wpml_like_escape( $match[2] );
110
111 $batch_size = $this->get_batch_size( parent::BATCH_SIZE_FACTOR_SPECIFIC_MEDIA );
112 if ( $this->translatable_custom_fields ) {
113 $translatable_custom_fields_where_in = wpml_prepare_in( $this->translatable_custom_fields );
114 $custom_fields = $this->wpdb->get_results(
115 "
116 SELECT SQL_CALC_FOUND_ROWS t.element_id AS post_id, p.meta_id, p.meta_key, p.meta_value
117 FROM {$this->wpdb->prefix}icl_translations t
118 JOIN {$this->wpdb->prefix}postmeta p ON t.element_id = p.post_id
119 WHERE t.element_type LIKE 'post_%'
120 AND t.element_type <> 'post_attachment'
121 AND t.source_language_code IS NULL
122 AND p.meta_key IN ({$translatable_custom_fields_where_in})
123 AND (
124 p.meta_value LIKE '%{$media_url}%' OR
125 p.meta_value LIKE '%{$media_url_no_extension}-%x%.{$extension}%'
126 )
127 ORDER BY t.element_id ASC
128 LIMIT {$offset}, " . $batch_size
129 );
130
131 $number_of_all_custom_fields = (int) $this->wpdb->get_var( 'SELECT FOUND_ROWS()' );
132
133 foreach ( $custom_fields as $custom_field ) {
134 $this->custom_field_translation->translate_images(
135 $custom_field->meta_id,
136 $custom_field->post_id,
137 $custom_field->meta_key,
138 $custom_field->meta_value
139 );
140 }
141 } else {
142 $number_of_all_custom_fields = 0;
143 }
144
145 return $number_of_all_custom_fields - $offset - $batch_size;
146
147 }
148
149 }
1 <?php
2
3 /**
4 * Class WPML_Media_Post_Batch_Url_Translation_Factory
5 */
6 class WPML_Media_Post_Batch_Url_Translation_Factory implements IWPML_Backend_Action_Loader {
7
8 public function create() {
9 global $wpdb;
10
11 if ( WPML_Media_Post_Batch_Url_Translation::is_ajax_request() ) {
12 $post_images_translation_factory = new WPML_Media_Post_Images_Translation_Factory();
13
14 return new WPML_Media_Post_Batch_Url_Translation( $post_images_translation_factory->create(), $wpdb );
15 }
16
17 return null;
18 }
19
20 }
...\ No newline at end of file ...\ No newline at end of file
1 <?php
2
3 /**
4 * Class WPML_Media_Post_Batch_Url_Translation
5 */
6 class WPML_Media_Post_Batch_Url_Translation extends WPML_Media_Batch_Url_Translation implements IWPML_Action {
7
8 const AJAX_ACTION = 'wpml_media_translate_media_url_in_posts';
9
10 /**
11 * @var WPML_Media_Post_Images_Translation
12 */
13 private $post_image_translation;
14
15 /**
16 * WPML_Media_Post_Batch_Url_Translation constructor.
17 *
18 * @param WPML_Media_Post_Images_Translation $post_image_translation
19 * @param wpdb $wpdb
20 */
21 public function __construct( WPML_Media_Post_Images_Translation $post_image_translation, wpdb $wpdb ) {
22 parent::__construct( $wpdb );
23 $this->post_image_translation = $post_image_translation;
24 $this->wpdb = $wpdb;
25 }
26
27 /**
28 * @return string
29 */
30 protected function get_ajax_action() {
31 return self::AJAX_ACTION;
32 }
33
34 public static function is_ajax_request() {
35 return isset( $_POST['action'] ) && self::AJAX_ACTION === $_POST['action'];
36 }
37
38 /**
39 * @param int $number_of_posts_left
40 *
41 * @return string
42 */
43 protected function get_response_message( $number_of_posts_left ) {
44 return sprintf(
45 __( 'Translating media urls in post translations: %s', 'wpml-media' ),
46 $number_of_posts_left > 0 ?
47 sprintf( __( '%d left', 'wpml-media' ), $number_of_posts_left ) :
48 __( 'done!', 'wpml-media' )
49 );
50 }
51
52 protected function get_ajax_error_message() {
53 return array(
54 'key' => 'wpml_media_batch_urls_update_errors_posts',
55 'value' => esc_js( __( 'Translating media urls in posts translations failed: Please try again (%s)', 'wpml-media' ) )
56 );
57 }
58
59 /**
60 * @param int $offset
61 *
62 * @return int
63 */
64 protected function process_batch( $offset ) {
65
66 $posts = $this->wpdb->get_col( "
67 SELECT SQL_CALC_FOUND_ROWS element_id AS id
68 FROM {$this->wpdb->prefix}icl_translations
69 WHERE element_type LIKE 'post_%'
70 AND element_type <> 'post_attachment'
71 AND source_language_code IS NULL
72 ORDER BY element_id ASC
73 LIMIT {$offset}, " . self::BATCH_SIZE );
74
75 $number_of_all_posts = (int) $this->wpdb->get_var( "SELECT FOUND_ROWS()" );
76
77 foreach ( $posts as $post_id ) {
78 $this->post_image_translation->translate_images( $post_id );
79 }
80
81 return $number_of_all_posts - $offset - self::BATCH_SIZE;
82 }
83
84 protected function process_batch_for_selected_media( $offset, $attachment_id ) {
85 $media_url = wpml_like_escape( wp_get_attachment_url( $attachment_id ) );
86 if ( ! $media_url ) {
87 return 0;
88 }
89 preg_match( "/(.+)\.([a-z]+)$/", $media_url, $match );
90 $media_url_no_extension = wpml_like_escape( $match[1] );
91 $extension = wpml_like_escape( $match[2] );
92
93 $batch_size = $this->get_batch_size( parent::BATCH_SIZE_FACTOR_SPECIFIC_MEDIA );
94
95 $posts = $this->wpdb->get_col( "
96 SELECT SQL_CALC_FOUND_ROWS element_id AS id
97 FROM {$this->wpdb->prefix}icl_translations t
98 JOIN {$this->wpdb->posts} p ON t.element_id = p.ID
99 WHERE element_type LIKE 'post_%'
100 AND element_type <> 'post_attachment'
101 AND source_language_code IS NULL
102 AND (
103 post_content LIKE '%{$media_url}%' OR
104 post_content LIKE '%{$media_url_no_extension}-%x%.{$extension}%'
105 )
106 ORDER BY element_id ASC
107 LIMIT {$offset}, " . $batch_size );
108
109 $number_of_all_posts = (int) $this->wpdb->get_var( "SELECT FOUND_ROWS()" );
110
111 foreach ( $posts as $post_id ) {
112 $this->post_image_translation->translate_images( $post_id );
113 }
114
115 return $number_of_all_posts - $offset - $batch_size;
116 }
117
118 }
...\ No newline at end of file ...\ No newline at end of file
1 <?php
2
3 /**
4 * Class WPML_Media_String_Batch_Url_Translation
5 */
6 class WPML_Media_String_Batch_Url_Translation_Factory implements IWPML_Backend_Action_Loader {
7
8 public function create() {
9 global $wpdb;
10
11 if ( WPML_Media_String_Batch_Url_Translation::is_ajax_request() ) {
12 $string_factory = new WPML_ST_String_Factory( $wpdb );
13
14 return new WPML_Media_String_Batch_Url_Translation( $wpdb, $string_factory );
15 }
16
17 return null;
18 }
19
20 }
1 <?php
2
3 /**
4 * Class WPML_Media_String_Batch_Url_Translation
5 */
6 class WPML_Media_String_Batch_Url_Translation extends WPML_Media_Batch_Url_Translation implements IWPML_Action {
7
8 const BATCH_SIZE = 500;
9 const AJAX_ACTION = 'wpml_media_translate_media_url_in_strings';
10
11 /**
12 * @var WPML_ST_String_Factory
13 */
14 private $string_factory;
15
16 /**
17 * WPML_Media_String_Batch_Url_Translation constructor.
18 *
19 * @param wpdb $wpdb
20 * @param WPML_ST_String_Factory $string_factory
21 */
22 public function __construct(
23 wpdb $wpdb,
24 WPML_ST_String_Factory $string_factory
25 ) {
26 parent::__construct( $wpdb );
27 $this->string_factory = $string_factory;
28 }
29
30 /**
31 * @param int $batch_size_factor
32 *
33 * @return int
34 */
35 protected function get_batch_size( $batch_size_factor = self::BATCH_SIZE_FACTOR_ALL_MEDIA ) {
36 return $batch_size_factor * self::BATCH_SIZE;
37 }
38
39 /**
40 * @return string
41 */
42 protected function get_ajax_action() {
43 return self::AJAX_ACTION;
44 }
45
46 public static function is_ajax_request() {
47 return isset( $_POST['action'] ) && self::AJAX_ACTION === $_POST['action'];
48 }
49
50 /**
51 * @param int $number_of_strings_left
52 *
53 * @return string
54 */
55 protected function get_response_message( $number_of_strings_left ) {
56 return sprintf(
57 __( 'Translating media urls in string translations: %s', 'wpml-media' ),
58 $number_of_strings_left > 0 ?
59 sprintf( __( '%d left', 'wpml-media' ), $number_of_strings_left ) :
60 __( 'done!', 'wpml-media' )
61 );
62 }
63
64 protected function get_ajax_error_message() {
65 return array(
66 'key' => 'wpml_media_batch_urls_update_error_strings',
67 'value' => esc_js( __( 'Translating media urls in string translations failed: Please try again (%s)', 'wpml-media' ) ),
68 );
69 }
70
71 /**
72 * @param int $offset
73 *
74 * @return int
75 */
76 protected function process_batch( $offset ) {
77
78 $original_strings = $this->wpdb->get_col(
79 "
80 SELECT SQL_CALC_FOUND_ROWS id, language
81 FROM {$this->wpdb->prefix}icl_strings
82 ORDER BY id ASC
83 LIMIT {$offset}, " . self::BATCH_SIZE
84 );
85
86 $number_of_all_strings = (int) $this->wpdb->get_var( 'SELECT FOUND_ROWS()' );
87
88 foreach ( $original_strings as $string_id ) {
89 $string = $this->string_factory->find_by_id( $string_id );
90 $string_translations = $string->get_translations();
91 foreach ( $string_translations as $string_translation ) {
92 if ( $string_translation->value ) {
93 $string->set_translation( $string_translation->language, $string_translation->value );
94 }
95 }
96 }
97
98 return $number_of_all_strings - $offset - self::BATCH_SIZE;
99 }
100
101 protected function process_batch_for_selected_media( $offset, $attachment_id ) {
102
103 $media_url = wpml_like_escape( wp_get_attachment_url( $attachment_id ) );
104 if ( ! $media_url ) {
105 return 0;
106 }
107 preg_match( '/(.+)\.([a-z]+)$/', $media_url, $match );
108 $media_url_no_extension = wpml_like_escape( $match[1] );
109 $extension = wpml_like_escape( $match[2] );
110
111 $batch_size = $this->get_batch_size( parent::BATCH_SIZE_FACTOR_SPECIFIC_MEDIA );
112
113 $original_strings = $this->wpdb->get_col(
114 "
115 SELECT SQL_CALC_FOUND_ROWS id, language
116 FROM {$this->wpdb->prefix}icl_strings
117 WHERE (
118 value LIKE '%{$media_url}%' OR
119 value LIKE '%{$media_url_no_extension}-%x%.{$extension}%'
120 )
121 ORDER BY id ASC
122 LIMIT {$offset}, " . $batch_size
123 );
124
125 $number_of_all_strings = (int) $this->wpdb->get_var( 'SELECT FOUND_ROWS()' );
126
127 foreach ( $original_strings as $string_id ) {
128 $string = $this->string_factory->find_by_id( $string_id );
129 $string_translations = $string->get_translations();
130 foreach ( $string_translations as $string_translation ) {
131 if ( $string_translation->value ) {
132 $string->set_translation( $string_translation->language, $string_translation->value );
133 }
134 }
135 }
136
137 return $number_of_all_strings - $offset - $batch_size;
138 }
139
140 }
1 <?php
2
3 class WPML_Media_Attachment_By_URL_Factory {
4
5 public function create( $url, $language, \WPML\Media\Classes\WPML_Media_Attachment_By_URL_Query $media_attachment_by_url_query ) {
6 global $wpdb;
7
8 return new WPML_Media_Attachment_By_URL( $wpdb, $url, $language, $media_attachment_by_url_query );
9 }
10
11 }
1 <?php
2
3 namespace WPML\Media\Factories;
4
5 class WPML_Media_Attachment_By_URL_Query_Factory {
6 public function create() {
7 global $wpdb;
8
9 return new \WPML\Media\Classes\WPML_Media_Attachment_By_URL_Query( $wpdb );
10 }
11 }
1 <?php
2
3 namespace WPML\Media\Classes;
4
5 use WPML\FP\Obj;
6
7 class WPML_Media_Attachment_By_URL_Query {
8 /**
9 * @var wpdb
10 */
11 private $wpdb;
12
13 /**
14 * @var array
15 */
16 private $id_from_guid_cache = [];
17
18 /**
19 * @var array
20 */
21 private $id_from_meta_cache = [];
22
23 /**
24 * @var boolean Used in tests
25 */
26 private $was_last_fetch_from_cache = false;
27
28 /**
29 * \WPML\Media\Classes\WPML_Media_Attachment_By_URL_Query constructor.
30 *
31 * @param \wpdb $wpdb
32 */
33 public function __construct( \wpdb $wpdb ) {
34 $this->wpdb = $wpdb;
35 }
36
37 /**
38 * @return boolean
39 */
40 public function getWasLastFetchFromCache() {
41 return $this->was_last_fetch_from_cache;
42 }
43
44 /**
45 * Sometimes multiple rows are returned for one language(language_code field)/url(guid field) or language(language_code field)/relativePath(meta_value field) pair.
46 * We should set only first result in such cases same as with original get_var call, otherwise code with cache will not work in the same way as the original code.
47 * Example: [[post_id = 1, lang = en, url = otgs.com], [post_id = 2, lang = en, url = otgs.com]] => only first entry should be set to cache, second should be ignored.
48 *
49 * @param array $item
50 * @param string $cache_prop
51 * @param string $item_index_in_cache
52 */
53 private function setItemToCache( $item, $cache_prop, $item_index_in_cache ) {
54 if ( array_key_exists( $item_index_in_cache, $this->$cache_prop ) ) {
55 return;
56 }
57
58 $this->{$cache_prop}[$item_index_in_cache] = $item;
59 }
60
61 /**
62 * @param array $source_items
63 */
64 private function filterItems( $source_items ) {
65 return array_values( array_filter( array_unique( $source_items ) ) );
66 }
67
68 /**
69 * @param string $language
70 * @param array $urls
71 */
72 public function prefetchAllIdsFromGuids( $language, $urls ) {
73 $urls = $this->filterItems( $urls );
74 $urls = array_filter( $urls, function( $url ) use ( $language ) {
75 $index = md5( $language . $url );
76 return ! array_key_exists( $index, $this->id_from_guid_cache );
77 } );
78
79 if ( 0 === count( $urls ) ) {
80 return;
81 }
82
83 $sql = '';
84 $sql .= "SELECT p.ID AS post_id, p.guid, t.language_code FROM {$this->wpdb->posts} p ";
85 $sql .= "JOIN {$this->wpdb->prefix}icl_translations t ON t.element_id = p.ID ";
86 $sql .= "WHERE t.element_type='post_attachment' AND t.language_code=%s ";
87 $sql .= 'AND p.guid IN (' . wpml_prepare_in( $urls ) . ')';
88
89 // phpcs:disable WordPress.WP.PreparedSQL.NotPrepared
90 $results = $this->wpdb->get_results( $this->wpdb->prepare( $sql, $language ), ARRAY_A );
91 foreach ( $results as $result ) {
92 $index = md5( $result['language_code'] . $result['guid'] );
93 $this->setItemToCache( $result, 'id_from_guid_cache', $index );
94 }
95
96 // We should put not found values into the cache too, otherwise they will be still queried later.
97 $urls_count = count( $urls );
98 for ( $i = 0; $i < $urls_count; $i++ ) {
99 $index = md5( $language . $urls[ $i ] );
100 $this->setItemToCache( null, 'id_from_guid_cache', $index );
101 }
102 }
103
104 /**
105 * @param string $language
106 * @param string $url
107 */
108 public function getIdFromGuid( $language, $url ) {
109 $this->was_last_fetch_from_cache = false;
110 $index = md5( $language . $url );
111 if ( array_key_exists( $index, $this->id_from_guid_cache ) ) {
112 $this->was_last_fetch_from_cache = true;
113 return ( $this->id_from_guid_cache[ $index ] ) ? $this->id_from_guid_cache[ $index ]['post_id'] : null;
114 }
115
116 $attachment_id = $this->wpdb->get_var(
117 // phpcs:disable WordPress.WP.PreparedSQL.NotPrepared
118 $this->wpdb->prepare(
119 "
120 SELECT ID FROM {$this->wpdb->posts} p
121 JOIN {$this->wpdb->prefix}icl_translations t ON t.element_id = p.ID
122 WHERE t.element_type='post_attachment' AND t.language_code=%s AND p.guid=%s
123 ",
124 $language,
125 $url
126 )
127 );
128
129 return $attachment_id;
130 }
131
132 /**
133 * @param string $language
134 * @param array $pathes
135 */
136 public function prefetchAllIdsFromMetas( $language, $pathes ) {
137 $pathes = $this->filterItems( $pathes );
138 $pathes = array_filter( $pathes, function( $path ) use ( $language ) {
139 $index = md5( $language . $path );
140 return ! array_key_exists( $index, $this->id_from_meta_cache );
141 } );
142
143 if ( 0 === count( $pathes ) ) {
144 return;
145 }
146
147 $sql = '';
148 $sql .= "SELECT p.post_id, t.language_code, p.meta_value FROM {$this->wpdb->postmeta} p ";
149 $sql .= "JOIN {$this->wpdb->prefix}icl_translations t ON t.element_id = p.post_id ";
150 $sql .= "WHERE p.meta_key='_wp_attached_file' AND t.element_type='post_attachment' AND t.language_code=%s ";
151 $sql .= 'AND p.meta_value IN (' . wpml_prepare_in( $pathes ) . ')';
152
153 // phpcs:disable WordPress.WP.PreparedSQL.NotPrepared
154 $results = $this->wpdb->get_results( $this->wpdb->prepare( $sql, $language ), ARRAY_A );
155 foreach ( $results as $result ) {
156 $index = md5( $result['language_code'] . $result['meta_value'] );
157 $this->setItemToCache( $result, 'id_from_meta_cache', $index );
158 }
159
160 // We should put not found values into the cache too, otherwise they will be still queried later.
161 $pathes_count = count( $pathes );
162 for ( $i = 0; $i < $pathes_count; $i++ ) {
163 $index = md5( $language . $pathes[ $i ] );
164 $this->setItemToCache( null, 'id_from_meta_cache', $index );
165 }
166 }
167
168 /**
169 * @param string $relative_path
170 * @param string $language
171 */
172 public function getIdFromMeta( $relative_path, $language ) {
173 $this->was_last_fetch_from_cache = false;
174 $index = md5( $language . $relative_path );
175 if ( array_key_exists( $index, $this->id_from_meta_cache ) ) {
176 $this->was_last_fetch_from_cache = true;
177 return ( $this->id_from_meta_cache[ $index ] ) ? $this->id_from_meta_cache[ $index ]['post_id'] : null;
178 }
179
180 $attachment_id = $this->wpdb->get_var(
181 // phpcs:disable WordPress.WP.PreparedSQL.NotPrepared
182 $this->wpdb->prepare(
183 "
184 SELECT post_id
185 FROM {$this->wpdb->postmeta} p
186 JOIN {$this->wpdb->prefix}icl_translations t ON t.element_id = p.post_id
187 WHERE p.meta_key='_wp_attached_file' AND p.meta_value=%s
188 AND t.element_type='post_attachment' AND t.language_code=%s
189 ",
190 $relative_path,
191 $language
192 )
193 );
194
195 return $attachment_id;
196 }
197 }
1 <?php
2
3 class WPML_Media_Attachment_By_URL {
4
5 /**
6 * @var wpdb
7 */
8 private $wpdb;
9
10 /**
11 * @var string
12 */
13 private $url;
14
15 /**
16 * @var string
17 */
18 private $language;
19
20 /**
21 * @var \WPML\Media\Classes\WPML_Media_Attachment_By_URL_Query
22 */
23 private $media_attachment_by_url_query;
24
25 const SIZE_SUFFIX_REGEXP = '/-([0-9]+)x([0-9]+)\.([a-z]{3,4})$/';
26
27 const CACHE_KEY_PREFIX = 'attachment-id-from-guid-';
28 const CACHE_GROUP = 'wpml-media-setup';
29 const CACHE_EXPIRATION = 1800;
30
31 /** @var null|boolean */
32 public $cache_hit_flag = null;
33
34 /**
35 * WPML_Media_Attachment_By_URL constructor.
36 *
37 * @param wpdb $wpdb
38 * @param string $url
39 * @param string $language
40 * @param \WPML\Media\Classes\WPML_Media_Attachment_By_URL_Query $media_attachment_by_url_query
41 */
42 public function __construct(
43 wpdb $wpdb,
44 $url,
45 $language,
46 \WPML\Media\Classes\WPML_Media_Attachment_By_URL_Query $media_attachment_by_url_query
47 ) {
48 $this->url = $url;
49 $this->language = $language;
50 $this->wpdb = $wpdb;
51 $this->media_attachment_by_url_query = $media_attachment_by_url_query;
52 }
53
54 /**
55 * @param string $url
56 *
57 * @return string
58 */
59 public static function getUrl( $url ) {
60 $url = preg_replace( self::SIZE_SUFFIX_REGEXP, '.$3', $url );
61
62 return $url;
63 }
64
65 /**
66 * @param string $url
67 *
68 * @return string
69 */
70 public static function getUrlNotScaled( $url ) {
71 $url = preg_replace( self::SIZE_SUFFIX_REGEXP, '.$3', $url );
72 $url = str_replace( '-scaled', '', $url );
73
74 return $url;
75 }
76
77 /**
78 * @param string $url
79 *
80 * @return string
81 */
82 public static function getUrlRelativePath( $url ) {
83 $uploads_dir = wp_get_upload_dir();
84 $relative_path = ltrim( preg_replace( '@^' . $uploads_dir['baseurl'] . '@', '', $url ), '/' );
85
86 return $relative_path;
87 }
88
89 /**
90 * @param string $relative_path
91 *
92 * @return string
93 */
94 public static function getUrlRelativePathOriginal( $relative_path ) {
95 return preg_replace( self::SIZE_SUFFIX_REGEXP, '.$3', $relative_path );
96 }
97
98 /**
99 * @param string $url
100 *
101 * @return string
102 */
103 public static function getUrlRelativePathScaled( $url ) {
104 $relative_path = self::getUrlRelativePath( $url );
105 $relative_path = str_replace( '-scaled', '', $relative_path );
106 $relative_path = preg_replace( '/(\.[^.]+)$/', '-scaled$1', $relative_path );
107
108 return $relative_path;
109 }
110
111 public function get_id() {
112 if ( ! $this->url ) {
113 return 0;
114 }
115
116 $cache_key = self::CACHE_KEY_PREFIX . md5( $this->language . '#' . $this->url );
117
118 $attachment_id = wp_cache_get( $cache_key, self::CACHE_GROUP, false, $this->cache_hit_flag );
119 if ( ! $this->cache_hit_flag ) {
120 $attachment_id = $this->get_id_from_guid();
121 if ( ! $attachment_id ) {
122 $attachment_id = $this->get_id_from_meta();
123 }
124
125 wp_cache_add( $cache_key, $attachment_id, self::CACHE_GROUP, self::CACHE_EXPIRATION );
126 }
127
128 return $attachment_id;
129 }
130
131 private function get_id_from_guid() {
132 $attachment_id = $this->media_attachment_by_url_query->getIdFromGuid( $this->language, $this->url );
133 if ( ! $attachment_id ) {
134 $attachment_id = $this->media_attachment_by_url_query->getIdFromGuid( $this->language, self::getUrlNotScaled( $this->url ) );
135 }
136
137 return $attachment_id;
138 }
139
140 private function get_id_from_meta() {
141 $relative_path = self::getUrlRelativePath( $this->url );
142 $relative_path_scaled = self::getUrlRelativePathScaled( $this->url );
143
144 // Using _wp_attached_file.
145 $attachment_id = $this->media_attachment_by_url_query->getIdFromMeta( $relative_path, $this->language );
146 if ( ! $attachment_id ) {
147 $attachment_id = $this->media_attachment_by_url_query->getIdFromMeta( $relative_path_scaled, $this->language );
148 }
149
150 // Using attachment meta (fallback).
151 if ( ! $attachment_id && preg_match( self::SIZE_SUFFIX_REGEXP, $relative_path ) ) {
152 $attachment_id = $this->get_attachment_image_from_meta_fallback( $relative_path );
153 }
154
155 return $attachment_id;
156 }
157
158 private function get_attachment_image_from_meta_fallback( $relative_path ) {
159 $attachment_id = null;
160
161 $relative_path_original = self::getUrlRelativePathOriginal( $relative_path );
162 $attachment_id_original = $this->media_attachment_by_url_query->getIdFromMeta( $relative_path_original, $this->language );
163
164 // Validate size.
165 if ( $attachment_id_original ) {
166 $attachment_meta_data = wp_get_attachment_metadata( $attachment_id_original );
167 if ( $this->validate_image_size( $relative_path, $attachment_meta_data ) ) {
168 $attachment_id = $attachment_id_original;
169 }
170 }
171
172 return $attachment_id;
173 }
174
175 private function validate_image_size( $path, $attachment_meta_data ) {
176 $valid = false;
177 $file_name = basename( $path );
178
179 foreach ( $attachment_meta_data['sizes'] as $size ) {
180 if ( $file_name === $size['file'] ) {
181 $valid = true;
182 break;
183 }
184 }
185
186 return $valid;
187 }
188
189 }
1 <?php
2
3 /**
4 * Class WPML_Media_Attachments_Query_Factory
5 */
6 class WPML_Media_Attachments_Query_Factory implements IWPML_Frontend_Action_Loader, IWPML_Backend_Action_Loader {
7
8 /**
9 * @return IWPML_Action|WPML_Media_Attachments_Query
10 */
11 public function create() {
12 return new WPML_Media_Attachments_Query();
13 }
14
15 }
1 <?php
2
3 /**
4 * Class WPML_Media_Attachments_Query
5 */
6 class WPML_Media_Attachments_Query implements IWPML_Action {
7
8
9 public function add_hooks() {
10 add_action( 'pre_get_posts', array( $this, 'adjust_attachment_query_action' ), 10 );
11 }
12
13 public function adjust_attachment_query_action( $query ) {
14 return $this->adjust_attachment_query( $query );
15 }
16
17 /**
18 * Set `suppress_filters` to false if attachment is displayed.
19 *
20 * @param WP_Query $query
21 *
22 * @return WP_Query
23 */
24 public function adjust_attachment_query( $query ) {
25 if ( isset( $query->query['post_type'] ) && 'attachment' === $query->query['post_type'] ) {
26 $query->set( 'suppress_filters', false );
27 }
28 return $query;
29 }
30 }
1 <?php
2
3 /**
4 * Class WPML_Media_Factory
5 */
6 class WPML_Media_Factory implements IWPML_Frontend_Action_Loader, IWPML_Backend_Action_Loader {
7
8 public function create() {
9 global $sitepress, $wpdb;
10
11 $wpml_wp_api = $sitepress->get_wp_api();
12
13 $template_service_loader = new WPML_Twig_Template_Loader(
14 array( $wpml_wp_api->constant( 'WPML_MEDIA_PATH' ) . '/templates/menus/' )
15 );
16 $wpml_media_menus_factory = new WPML_Media_Menus_Factory();
17
18 return new WPML_Media( $sitepress, $wpdb, $wpml_media_menus_factory );
19 }
20
21 }
1 <?php
2
3 /**
4 * Class WPML_Media_File_Factory
5 */
6 class WPML_Media_File_Factory {
7
8 /**
9 * @param $attachment_id
10 *
11 * @return WPML_Media_File
12 */
13 public function create( $attachment_id ) {
14 global $wpdb;
15
16 return new WPML_Media_File( $attachment_id, $this->get_wp_filesystem(), $wpdb );
17 }
18
19 private function get_wp_filesystem() {
20 global $wp_filesystem;
21 if ( null === $wp_filesystem ) {
22 WP_Filesystem();
23 }
24
25 return $wp_filesystem;
26 }
27
28
29 }
1 <?php
2
3 class WPML_Media_File {
4
5 /**
6 * @var int
7 */
8 private $attachment_id;
9 /**
10 * @var WP_Filesystem_Base
11 */
12 private $wp_filesystem;
13 /**
14 * @var wpdb
15 */
16 private $wpdb;
17
18 public function __construct( $attachment_id, WP_Filesystem_Base $wp_filesystem, wpdb $wpdb ) {
19 $this->wp_filesystem = $wp_filesystem;
20 $this->attachment_id = $attachment_id;
21 $this->wpdb = $wpdb;
22 }
23
24 public function delete() {
25 $relative_file_path = get_post_meta( $this->attachment_id, '_wp_attached_file', true );
26
27 if ( $relative_file_path && ! $this->file_is_shared( $relative_file_path, $this->attachment_id ) ) {
28
29 $file_path = $this->get_full_file_upload_path( $relative_file_path );
30
31 $this->wp_filesystem->delete( $file_path, false, 'f' );
32
33 $attachment_meta_data = wp_get_attachment_metadata( $this->attachment_id );
34 if ( $attachment_meta_data && isset( $attachment_meta_data['file'] ) ) {
35 $subdir = dirname( $attachment_meta_data['file'] );
36 foreach ( $attachment_meta_data['sizes'] as $key => $size ) {
37 $file_path = $this->get_full_file_upload_path( $subdir . '/' . $size['file'] );
38 $this->wp_filesystem->delete( $file_path, false, 'f' );
39 }
40 }
41 }
42
43 }
44
45 private function get_full_file_upload_path( $relative_file_path ) {
46 $upload_dir = wp_upload_dir();
47 $relative_file_path = trim( $relative_file_path, ' /' );
48 $file_path = $upload_dir['basedir'] . '/' . $relative_file_path;
49
50 return $file_path;
51 }
52
53 private function file_is_shared( $relative_file_path, $attachment_id ) {
54
55 $sql = "SELECT post_id FROM {$this->wpdb->postmeta}
56 WHERE post_id <> %d AND meta_key='_wp_attached_file' AND meta_value=%s";
57
58 return $this->wpdb->get_var( $this->wpdb->prepare( $sql, $attachment_id, $relative_file_path ) );
59 }
60
61 }
1 <?php
2
3 class WPML_Media_Add_To_Basket_Factory implements IWPML_Backend_Action_Loader {
4
5 public function create() {
6 global $sitepress;
7 if ( isset( $_POST['icl_tm_action'] ) && 'add_jobs' === $_POST['icl_tm_action'] ) {
8 return new WPML_Media_Add_To_Basket( $sitepress );
9 }
10
11 return null;
12 }
13
14 }
1 <?php
2
3 class WPML_Media_Add_To_Basket implements IWPML_Action {
4
5 /**
6 * @var SitePress
7 */
8 private $sitepress;
9
10 /**
11 * WPML_Media_Add_To_Basket constructor.
12 */
13 public function __construct( SitePress $sitepress ) {
14 $this->sitepress = $sitepress;
15 }
16
17 public function add_hooks() {
18 add_filter(
19 'pre_update_option_' . $this->sitepress->get_wp_api()->constant( 'TranslationProxy_Basket::ICL_TRANSLATION_JOBS_BASKET' ),
20 array( $this, 'add_media' )
21 );
22 }
23
24 public function add_media( $data ) {
25
26 if ( ! empty( $data['post'] ) ) {
27 foreach ( $data['post'] as $post_id => $post ) {
28 if ( $media = $this->get_post_media( $post_id ) ) {
29 $data['post'][ $post_id ]['media-translation'] = $media;
30 }
31 }
32 }
33
34 return $data;
35 }
36
37 private function get_post_media( $post_id ) {
38 return isset( $_POST['post'][ $post_id ]['media-translation'] ) ?
39 array_map( 'intval', $_POST['post'][ $post_id ]['media-translation'] ) :
40 array();
41 }
42 }
1 <?php
2
3 class WPML_Media_Selector_Factory implements IWPML_Backend_Action_Loader {
4
5 public function create() {
6 global $sitepress;
7
8 $wpml_wp_api = $sitepress->get_wp_api();
9 $wpml_media_path = $wpml_wp_api->constant( 'WPML_MEDIA_PATH' );
10
11 return new WPML_Media_Selector(
12 $sitepress,
13 new WPML_Twig_Template_Loader( array( $wpml_media_path . '/templates/media-selector/' ) ),
14 new WPML_Media_Post_With_Media_Files_Factory(),
15 new WPML_Translation_Element_Factory( $sitepress )
16 );
17 }
18
19
20 }
1 <?php
2
3 use WPML\UIPage;
4
5 class WPML_Media_Selector implements IWPML_Action {
6
7 /**
8 * @var SitePress
9 */
10 private $sitepress;
11 /**
12 * @var WPML_Twig_Template_Loader
13 */
14 private $template_loader;
15 /**
16 * @var WPML_Media_Post_With_Media_Files_Factory
17 */
18 private $post_with_media_files_factory;
19 /**
20 * @var WPML_Media_Post_With_Media_Files_Factory
21 */
22 private $translation_element_factory;
23
24
25 const USER_META_HIDE_POST_MEDIA_SELECTOR = '_wpml_media_hide_post_media_selector';
26
27 public function __construct(
28 SitePress $sitepress,
29 WPML_Twig_Template_Loader $template_loader,
30 WPML_Media_Post_With_Media_Files_Factory $post_with_media_files_factory,
31 WPML_Translation_Element_Factory $translation_element_factory
32 ) {
33 $this->sitepress = $sitepress;
34 $this->template_loader = $template_loader;
35 $this->post_with_media_files_factory = $post_with_media_files_factory;
36 $this->translation_element_factory = $translation_element_factory;
37 }
38
39 public function add_hooks() {
40 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_res' ) );
41 add_action( 'wp_ajax_wpml_media_load_image_selector', array( $this, 'load_images_selector' ) );
42 add_action( 'wp_ajax_wpml_media_toogle_show_media_selector', array( $this, 'toggle_show_media_selector' ) );
43
44 add_filter( 'wpml_translation_dashboard_row_data', array( $this, 'add_media_data_to_dashboard_row' ), 10, 2 );
45 add_action( 'wpml_tm_after_translation_dashboard_documents', array( $this, 'add_media_selector_preloader' ) );
46 }
47
48 public function enqueue_res() {
49 if ( UIPage::isTMDashboard( $_GET ) ) {
50 $wpml_media_url = $this->sitepress->get_wp_api()->constant( 'WPML_MEDIA_URL' );
51 wp_enqueue_script( 'wpml-media-selector', $wpml_media_url . '/res/js/media-selector.js', array( 'jquery' ), false, true );
52 wp_enqueue_style( 'wpml-media-selector', $wpml_media_url . '/res/css/media-selector.css', array() );
53 }
54 }
55
56 public function load_images_selector() {
57 $post_id = (int) $_POST['post_id'];
58 if ( isset( $_POST['languages'] ) && is_array( $_POST['languages'] ) ) {
59 $languages = array_map( 'sanitize_text_field', $_POST['languages'] );
60 } else {
61 $languages = array();
62 }
63
64 $media_files_list = $this->get_media_files_list( $post_id, $languages );
65 $media_files_count = count( $media_files_list );
66
67 $model = array(
68 'files' => $media_files_list,
69 'post_id' => $post_id,
70 );
71
72 $html = $this->template_loader->get_template()->show( $model, 'media-selector.twig' );
73
74 wp_send_json_success(
75 array(
76 'html' => $html,
77 'media_files_count' => $media_files_count,
78 )
79 );
80 }
81
82 /**
83 * @param int $post_id
84 * @param array $languages
85 *
86 * @return array
87 */
88 private function get_media_files_list( $post_id, $languages ) {
89
90 $media_files_list = array();
91
92 $post_with_media = $this->post_with_media_files_factory->create( $post_id );
93
94 $media_ids = $post_with_media->get_media_ids();
95
96 foreach ( $media_ids as $attachment_id ) {
97
98 $media_files_list[ $attachment_id ] = array(
99 'thumbnail' => wp_get_attachment_thumb_url( $attachment_id ),
100 'name' => get_post_field( 'post_title', $attachment_id ),
101 'translated' => $this->media_file_is_translated( $attachment_id, $languages ),
102 );
103 }
104
105 return $media_files_list;
106 }
107
108 private function media_file_is_translated( $attachment_id, $languages ) {
109 $post_element = $this->translation_element_factory->create( $attachment_id, 'post' );
110 foreach ( $languages as $language ) {
111 $translation = $post_element->get_translation( $language );
112 if ( null === $translation || get_post_meta( $attachment_id, '_wp_attached_file', true )
113 === get_post_meta( $translation->get_id(), '_wp_attached_file', true ) ) {
114 return false;
115 }
116 }
117
118 return true;
119 }
120
121 public function toggle_show_media_selector() {
122 $current_value = get_user_meta( get_current_user_id(), self::USER_META_HIDE_POST_MEDIA_SELECTOR, true );
123 update_user_meta( get_current_user_id(), self::USER_META_HIDE_POST_MEDIA_SELECTOR, ! $current_value );
124 wp_send_json_success();
125 }
126
127 /**
128 * @param array $row_data
129 * @param stdClass $doc_data
130 *
131 * @return array
132 */
133 public function add_media_data_to_dashboard_row( $row_data, $doc_data ) {
134 if ( 0 !== strpos( $doc_data->translation_element_type, 'post_' ) ) {
135 return $row_data;
136 }
137
138 $row_data = $this->add_post_has_media_flag( $row_data, $doc_data->ID );
139 $row_data = $this->add_post_type_attribute_data( $row_data, $doc_data->ID );
140
141 return $row_data;
142 }
143
144 /**
145 * @param array $data
146 * @param int $post_id
147 *
148 * @return array
149 */
150 private function add_post_has_media_flag( array $data, $post_id ) {
151 $data['has-media'] = get_post_meta( $post_id, WPML_Media_Set_Posts_Media_Flag::HAS_MEDIA_POST_FLAG, true );
152
153 return $data;
154 }
155
156 /**
157 * @param array $data
158 * @param int $post_id
159 *
160 * @return array
161 */
162 private function add_post_type_attribute_data( $data, $post_id ) {
163 $post_type = get_post_type( $post_id );
164 $post_type_object = get_post_type_object( $post_type );
165
166 $data['post-type'] = strtolower( $post_type_object->labels->singular_name );
167
168 return $data;
169 }
170
171 public function add_media_selector_preloader() {
172 $model = array(
173 'strings' => array(
174 'has_posts' => sprintf(
175 __(
176 'Choose which media to translate with this %s',
177 'wpml-media'
178 ),
179 '%POST_TYPE%'
180 ),
181 'loading' => __( 'Loading...', 'wpml-media' ),
182 ),
183 'hide_selector' => get_user_meta( get_current_user_id(), self::USER_META_HIDE_POST_MEDIA_SELECTOR, true ),
184 );
185 echo $this->template_loader->get_template()->show( $model, 'media-selector-preloader.twig' );
186 }
187 }
1 <?php
2
3 class WPML_Media_Submitted_Basket_Notice_Factory implements IWPML_Backend_Action_Loader {
4
5 public function create() {
6 global $sitepress;
7 if ( $sitepress->get_wp_api()->is_tm_page( 'basket' ) && $this->basket_has_media() ) {
8 $template_loader = new WPML_Twig_Template_Loader( array( WPML_MEDIA_PATH . '/templates/media-selector/' ) );
9
10 return new WPML_Media_Submitted_Basket_Notice( $template_loader );
11 }
12
13 return null;
14
15 }
16
17 private function basket_has_media() {
18 $basket = TranslationProxy_Basket::get_basket( true );
19 $item_types = TranslationProxy_Basket::get_basket_items_types();
20
21 foreach ( $item_types as $item_type => $type_type ) {
22 if ( isset( $basket[ $item_type ] ) ) {
23 foreach ( $basket[ $item_type ] as $item ) {
24 if ( ! empty( $item['media-translation'] ) ) {
25 return true;
26 }
27 }
28 }
29 }
30
31 return false;
32 }
33
34 }
1 <?php
2
3 class WPML_Media_Submitted_Basket_Notice implements IWPML_Action {
4 /**
5 * @var WPML_Twig_Template_Loader
6 */
7 private $template_loader;
8
9 public function __construct( WPML_Twig_Template_Loader $template_loader ) {
10 $this->template_loader = $template_loader;
11 }
12
13 public function add_hooks() {
14 add_action( 'wpml_tm_scripts_enqueued', array( $this, 'load_js' ) );
15 add_action( 'wpml_translation_basket_page_after', array( $this, 'load_dialog_template' ) );
16 }
17
18 public function load_js() {
19 $script_handle = 'submitted-basket-notice';
20 wp_enqueue_script(
21 $script_handle,
22 WPML_MEDIA_URL . '/res/js/submitted-basket-notice.js',
23 array( 'jquery-ui-dialog' ),
24 WPML_MEDIA_VERSION,
25 false
26 );
27
28 $wpml_media_basket_notice_data = array(
29 'button_label' => __( 'Continue', 'wpml_media' ),
30 );
31 wp_localize_script( $script_handle, 'wpml_media_basket_notice_data', $wpml_media_basket_notice_data );
32 }
33
34 public function load_dialog_template() {
35
36 /* translators: WPML plugin name */
37 $wpml_plugin_name = __( 'WPML', 'wpml-media' );
38 /* translators: WPML Media Translation saddon/section name */
39 $media_translation_name = __( 'Media Translation', 'wpml-media' );
40
41 $media_translation_url = admin_url( 'admin.php?page=wpml-media' );
42 $media_translation_link = sprintf(
43 '<a href="%s" target="_blank" rel="noopener" class="wpml-external-link">%s &raquo; %s</a>',
44 $media_translation_url,
45 $wpml_plugin_name,
46 $media_translation_name
47 );
48
49 /* translators: media file string used in "if you want to use a different media file for each language..." */
50 $media_file_string = __( 'media file', 'wpml-media' );
51 $redirect_url = add_query_arg( 'page', \WPML\UIPage::TM_PAGE, admin_url( 'admin.php' ) );
52
53 $model = array(
54 'strings' => array(
55 'dialog_title' => __( 'Media sent to translation', 'wpml-media' ),
56 'content_with_media_sent' => __( 'You have sent content which contains media attachments for translation.', 'wpml-media' ),
57 'media_texts_translated' => sprintf( __( 'Translators will translate all your %1$smedia texts%2$s.', 'wpml-media' ), '<strong>', '</strong>' ),
58 'use_different_media' => sprintf(
59 __( 'If you want to use a different %1$s for each language, you can set them in: %2$s.', 'wpml-media' ),
60 '<strong>' . $media_file_string . '</strong>',
61 $media_translation_link
62 ),
63 'learn_more' => __( 'Learn more about Media Translation', 'wpml-media' ),
64 'wpml' => _x( 'WPML', 'plugin name', 'wpml-media' ),
65 'media_translation' => _x( 'Media Translation', 'wpml addon name', 'wpml-media' ),
66 ),
67
68 'learn_more_url' => 'https://wpml.org/documentation/getting-started-guide/media-translation/?utm_source=plugin&utm_medium=gui&utm_campaign=wpmlmedia',
69 'redirect_url' => $redirect_url,
70 );
71
72 echo $this->template_loader->get_template()->show( $model, 'submitted-basket-notice.twig' );
73
74 }
75
76 }
1 <?php
2
3 namespace WPML\Media\Classes;
4
5 use WPML\FP\Obj;
6 use WPML\FP\Str;
7
8 /**
9 * Media file block parser
10 */
11 class WPML_Non_Embedded_Pdf_Parser extends WPML_Media_Element_Parser {
12
13 /**
14 * @var string
15 */
16 private static $non_embedded_pdf_expression = '/wp:file.*class="wp-block-file".*(href=".*\.pdf")>.*\/wp:file/s';
17
18 public function getMediaElements() {
19 return $this->getFromTags();
20 }
21
22 public function getMediaSrcFromAttributes( $attrs ) {
23 return Obj::propOr( '', 'href', $attrs );
24 }
25
26 protected function getFromTags() {
27 // phpcs:disable WordPress.NamingConventions.ValidVariableName.NotSnakeCaseMemberVar
28 return preg_match_all( self::$non_embedded_pdf_expression, $this->blockText, $matches ) ?
29 $this->getAttachments( $matches ) : [];
30 }
31
32 /**
33 * Checks if media element is File Block and has pdf.
34 *
35 * @return bool
36 */
37 public function validate() {
38 return Str::includes( '<!-- wp:file', $this->blockText )
39 || Str::includes( 'pdf', $this->blockText );
40 }
41 }
1 <?php
2
3 /**
4 * Class WPML_Media_Attachment_Image_Update_Factory
5 */
6 class WPML_Media_Attachment_Image_Update_Factory implements IWPML_Backend_Action_Loader {
7
8 public function create() {
9 global $wpdb;
10
11 return new WPML_Media_Attachment_Image_Update( $wpdb );
12 }
13 }
1 <?php
2
3 /**
4 * Class WPML_Media_Attachment_Image_Update
5 * Allows adding a custom image to a translated attachment
6 */
7 class WPML_Media_Attachment_Image_Update implements IWPML_Action {
8
9 const TRANSIENT_FILE_UPLOAD_PREFIX = 'wpml_media_file_update_';
10
11 /**
12 * @var wpdb
13 */
14 private $wpdb;
15
16 /**
17 * WPML_Media_Attachment_Image_Update constructor.
18 *
19 * @param wpdb $wpdb
20 */
21 public function __construct( wpdb $wpdb ) {
22 $this->wpdb = $wpdb;
23 }
24
25 public function add_hooks() {
26 add_action( 'wp_ajax_wpml_media_upload_file', array( $this, 'handle_upload' ) );
27 }
28
29 public function handle_upload() {
30 if ( $this->is_valid_action() ) {
31
32 $original_attachment_id = (int) $_POST['original-attachment-id'];
33 $attachment_id = (int) $_POST['attachment-id'];
34 $file_array = $_FILES['file'];
35 $target_language = $_POST['language'];
36
37 $thumb_path = '';
38 $thumb_url = '';
39
40 $upload_overrides = apply_filters( 'wpml_media_wp_upload_overrides', array( 'test_form' => false ) );
41 $file = wp_handle_upload( $file_array, $upload_overrides );
42
43 if ( ! isset( $file['error'] ) ) {
44
45 if ( wp_image_editor_supports( array( 'mime_type' => $file['type'] ) ) ) {
46
47 $editor = wp_get_image_editor( $file['file'] );
48 if ( ! is_wp_error( $editor ) ) {
49
50 if ( 'application/pdf' === $file['type'] || stripos( $file['type'], 'video' ) !== false ) {
51 $dirname = dirname( $file['file'] ) . '/';
52 $ext = pathinfo( $file['file'], PATHINFO_EXTENSION );
53 $preview_file = $dirname . wp_unique_filename( $dirname, wp_basename( $file['file'], '.' . $ext ) . "-{$ext}.jpg" );
54
55 $editor->save( $preview_file, 'image/jpeg' );
56
57 $thumb = $this->resize_thumbnail( $editor );
58
59 $attachment_metadata = wp_get_attachment_metadata( $attachment_id );
60
61 $attachment_size = [
62 'file' => basename( $preview_file ),
63 'width' => $thumb['width'],
64 'height' => $thumb['height'],
65 'mime-type' => 'image/jpeg',
66 ];
67
68 $attachment_metadata['sizes']['thumbnail'] = $attachment_size;
69 $attachment_metadata['sizes']['full'] = $attachment_size;
70
71 wp_update_attachment_metadata( $attachment_id, $attachment_metadata );
72
73 } else {
74 $thumb = $this->resize_thumbnail( $editor );
75 }
76
77 if ( ! is_wp_error( $thumb ) ) {
78 $uploads_dir = wp_get_upload_dir();
79
80 $thumb_url = $uploads_dir['baseurl'] . $uploads_dir['subdir'] . '/' . $thumb['file'];
81 $thumb_path = $thumb['path'];
82 }
83 } else {
84 $thumb_url = wp_mime_type_icon( $file['type'] );
85
86 if ( $thumb_url ) {
87 $thumb_path = $file['file'];
88 } else {
89 wp_send_json_error( __( 'Failed to load the image editor', 'wpml-media' ) );
90 }
91 }
92 } elseif ( 0 === strpos( $file['type'], 'image/' ) ) {
93 $thumb_url = $file['url'];
94 $thumb_path = $file['file'];
95 } else {
96 $thumb_url = wp_mime_type_icon( $original_attachment_id );
97 }
98
99 set_transient(
100 self::TRANSIENT_FILE_UPLOAD_PREFIX . $original_attachment_id . '_' . $target_language,
101 array(
102 'upload' => $file,
103 'thumb' => $thumb_path,
104 ),
105 HOUR_IN_SECONDS
106 );
107
108 wp_send_json_success(
109 array(
110 'attachment_id' => $attachment_id,
111 'thumb' => $thumb_url,
112 'name' => basename( $file['file'] ),
113 )
114 );
115
116 } else {
117 wp_send_json_error( $file['error'] );
118 }
119 } else {
120 wp_send_json_error( 'invalid action' );
121 }
122 }
123
124 /**
125 * Resize the thumbnail if it is larger than the settings size
126 *
127 * @param WP_Image_Editor $editor
128 * @return array|WP_Error
129 */
130 private function resize_thumbnail( $editor ) {
131
132 $size = $editor->get_size();
133 if ( $size['width'] > get_option( 'thumbnail_size_w' ) || $size['height'] > get_option( 'thumbnail_size_h' ) ) {
134 $resizing = $editor->resize( get_option( 'thumbnail_size_w' ), get_option( 'thumbnail_size_h' ), true );
135 if ( is_wp_error( $resizing ) ) {
136 wp_send_json_error( $resizing->get_error_message() );
137 }
138 }
139
140 return $editor->save();
141 }
142
143 private function is_valid_action() {
144 $is_attachment_id = isset( $_POST['attachment-id'] );
145 $is_post_action = isset( $_POST['action'] ) && 'wpml_media_upload_file' === $_POST['action'];
146
147 return $is_attachment_id && $is_post_action && wp_verify_nonce( $_POST['wpnonce'], 'media-translation' );
148 }
149
150 }
1 <?php
2
3 /**
4 * Class WPML_Media_Caption_Tags_Parse
5 */
6 class WPML_Media_Caption_Tags_Parse {
7
8 /**
9 * @param string $text
10 *
11 * @return array
12 */
13 public function get_captions( $text ) {
14 $captions = array();
15
16 if ( preg_match_all( '/\[caption (.+)\](.+)\[\/caption\]/sU', $text, $matches ) ) {
17
18 for ( $i = 0; $i < count( $matches[0] ); $i++ ) {
19 $captions[] = new WPML_Media_Caption( $matches[0][ $i ], $matches[1][ $i ], $matches[2][ $i ] );
20 }
21 }
22
23 return $captions;
24 }
25
26 }
1 <?php
2
3 /**
4 * Class WPML_Media_Caption
5 */
6 class WPML_Media_Caption {
7
8 private $shortcode;
9 private $content_string;
10 private $attributes;
11 private $attachment_id;
12 private $link;
13 private $img;
14 private $caption;
15
16 public function __construct( $caption_shortcode, $attributes_data, $content_string ) {
17 $this->shortcode = $caption_shortcode;
18 $this->content_string = $content_string;
19
20 $this->attributes = $this->find_attributes_array( $attributes_data );
21 $this->attachment_id = $this->find_attachment_id( $this->attributes );
22
23 $this->link = $this->find_link( $content_string );
24
25 $mediaParsers = ( new \WPML\Media\Factories\WPML_Media_Element_Parser_Factory() )->create( $content_string );
26 $mediaElements = [];
27
28 foreach ( $mediaParsers as $parser ) {
29 if ( $parser instanceof \WPML\Media\Classes\WPML_Media_Image_Parser
30 || $parser instanceof \WPML\Media\Classes\WPML_Media_Classic_Video_Parser
31 ) {
32 $mediaElements = array_merge( $mediaElements, $parser->getMediaElements() );
33 }
34 }
35
36 if ( ! empty( $mediaElements ) ) {
37 $this->img = current( $mediaElements );
38 $this->caption = trim( strip_tags( $content_string ) );
39 }
40 }
41
42
43 /**
44 * @return int
45 */
46 public function get_id() {
47 return $this->attachment_id;
48 }
49
50 public function get_caption() {
51 return $this->caption;
52 }
53
54 public function get_shortcode_string() {
55 return $this->shortcode;
56 }
57
58 public function get_content() {
59 return $this->content_string;
60 }
61
62 public function get_image_alt() {
63 if ( isset( $this->img['attributes']['alt'] ) ) {
64 return $this->img['attributes']['alt'];
65 } else {
66 return '';
67 }
68 }
69
70 public function get_link() {
71 return $this->link;
72 }
73
74 /**
75 * @param string $attributes_list
76 *
77 * @return array
78 */
79 private function find_attributes_array( $attributes_list ) {
80 $attributes = array();
81 if ( preg_match_all( '/(\S+)=["\']?((?:.(?!["\']?\s+(?:\S+)=|[>"\']))+.)["\']?/', $attributes_list, $attribute_matches ) ) {
82 foreach ( $attribute_matches[1] as $k => $key ) {
83 $attributes[ $key ] = $attribute_matches[2][ $k ];
84 }
85 }
86
87 return $attributes;
88 }
89
90 /**
91 * @param array $attributes
92 *
93 * @return null|int
94 */
95 private function find_attachment_id( $attributes ) {
96 $attachment_id = null;
97 if ( isset( $attributes['id'] ) ) {
98 if ( preg_match( '/attachment_([0-9]+)\b/', $attributes['id'], $id_match ) ) {
99 if ( 'attachment' === get_post_type( (int) $id_match[1] ) ) {
100 $attachment_id = (int) $id_match[1];
101 }
102 }
103 }
104
105 return $attachment_id;
106 }
107
108 /**
109 * @param $string
110 *
111 * @return array
112 */
113 private function find_link( $string ) {
114 $link = array();
115 if ( preg_match( '/<a ([^>]+)>(.+)<\/a>/s', $string, $a_match ) ) {
116 if ( preg_match( '/href=["\']([^"]+)["\']/', $a_match[1], $url_match ) ) {
117 $link['url'] = $url_match[1];
118 }
119 }
120
121 return $link;
122 }
123
124 }
1 <?php
2
3 namespace WPML\Media\Classes;
4
5 use WPML\FP\Str;
6
7 /**
8 * Classic audio parser
9 */
10 class WPML_Media_Classic_Audio_Parser extends WPML_Media_Classic_Element_Parser {
11
12 const Media_Element_Expression = '/\[audio ([^]]+)\]/s';
13 const Media_Extension_Expression = '/\[audio.+?(?=="http)/';
14
15 /**
16 * Extracts the extension of the classic audio media element, defaults to mp3.
17 *
18 * @return false|string
19 */
20 protected function extractExtension() {
21 $matches = $this->getExtensionMatches();
22
23 return ! empty( $matches ) ? substr( $matches[0], - 3 ) : 'mp3';
24 }
25
26 protected function getMediaElementRegex() {
27 return self::Media_Element_Expression;
28 }
29
30 protected function getMediaExtensionExpression() {
31 return self::Media_Extension_Expression;
32 }
33
34 /**
35 * Checks if media element is classic audio (audio uploaded in classic editor).
36 *
37 * @return bool
38 */
39 public function validate() {
40 return Str::includes( '[audio', $this->blockText );
41 }
42 }
1 <?php
2
3 namespace WPML\Media\Classes;
4
5 abstract class WPML_Media_Classic_Element_Parser extends WPML_Media_Element_Parser {
6
7 /**
8 * Gets string out of the video element, this string should be ending with the video extension, then last 3 characters from string are returned.
9 *
10 * @return false|string
11 */
12 abstract protected function extractExtension();
13
14 /**
15 * Returns regular expression used to detect matches of the media element in a string.
16 *
17 * @return string
18 */
19 abstract protected function getMediaElementRegex();
20
21 /**
22 * Returns regular expression used to detect the extension of media element in a string.
23 *
24 * @return string
25 */
26 abstract protected function getMediaExtensionExpression();
27
28 public function getMediaElements() {
29 return preg_match_all( $this->getMediaElementRegex(), $this->blockText, $matches )
30 ? $this->getAttachments( $matches ) : [];
31 }
32
33 /**
34 * Returns the source of the media element according to its extension in the attrs array (for example : mp3, mp4., ...).
35 *
36 * @param array $attrs
37 *
38 * @return string
39 */
40 public function getMediaSrcFromAttributes( $attrs ) {
41 $extension = $this->extractExtension();
42
43 return ( $extension && isset( $attrs[ $extension ] ) ) ? $attrs[ $extension ] : '';
44 }
45
46 /**
47 * Applies regular expression match to get the media element extension and returns the matches.
48 *
49 * @return mixed
50 */
51 protected function getExtensionMatches() {
52 preg_match( $this->getMediaExtensionExpression(), $this->blockText, $matches );
53
54 return $matches;
55 }
56 }
1 <?php
2
3 namespace WPML\Media\Classes;
4
5 use WPML\FP\Str;
6
7 /**
8 * Classic video parser
9 */
10 class WPML_Media_Classic_Video_Parser extends WPML_Media_Classic_Element_Parser {
11
12 const Media_Element_Expression = '/\[video ([^]]+)\]/s';
13 const Media_Extension_Expression = '/\[video.+?(?=="http)/';
14
15 /**
16 * Extracts the extension of the classic video media element, defaults to mp4.
17 *
18 * @return false|string
19 */
20 protected function extractExtension() {
21 $matches = $this->getExtensionMatches();
22
23 return ! empty( $matches ) ? substr( $matches[0], - 3 ) : 'mp4';
24 }
25
26 protected function getMediaElementRegex() {
27 return self::Media_Element_Expression;
28 }
29
30 protected function getMediaExtensionExpression() {
31 return self::Media_Extension_Expression;
32 }
33
34 /**
35 * Checks if media element is classic video (video uploaded in classic editor).
36 *
37 * @return bool
38 */
39 public function validate() {
40 return Str::includes( '[video', $this->blockText );
41 }
42 }
1 <?php
2
3 /**
4 * Class WPML_Media_Custom_Field_Images_Translation_Factory
5 */
6 class WPML_Media_Custom_Field_Images_Translation_Factory implements IWPML_Backend_Action_Loader {
7
8 public function create() {
9 global $sitepress, $iclTranslationManagement;
10
11 $media_localization_settings = WPML_Media::get_setting( 'media_files_localization' );
12
13 if ( $media_localization_settings['custom_fields'] || WPML_Media_Custom_Field_Batch_Url_Translation::is_ajax_request() ) {
14 $image_translator = new WPML_Media_Image_Translate(
15 $sitepress,
16 new WPML_Media_Attachment_By_URL_Factory(),
17 new \WPML\Media\Factories\WPML_Media_Attachment_By_URL_Query_Factory()
18 );
19 $image_updater = new WPML_Media_Translated_Images_Update( new \WPML\Media\Factories\WPML_Media_Element_Parser_Factory(), $image_translator, new WPML_Media_Sizes() );
20
21 return new WPML_Media_Custom_Field_Images_Translation( $image_updater, $sitepress, $iclTranslationManagement );
22 }
23
24 return null;
25 }
26
27 }
1 <?php
2
3 /**
4 * Class WPML_Media_Custom_Field_Images_Translation
5 * Translate images in posts custom fields translations when a custom field is created or updated
6 */
7 class WPML_Media_Custom_Field_Images_Translation implements IWPML_Action {
8
9 /**
10 * @var WPML_Media_Custom_Field_Images_Translation
11 */
12 private $images_updater;
13 /**
14 * @var SitePress
15 */
16 private $sitepress;
17 /**
18 * @var TranslationManagement
19 */
20 private $iclTranslationManagement;
21
22 /**
23 * WPML_Media_Custom_Field_Images_Translation constructor.
24 *
25 * @param WPML_Media_Translated_Images_Update $images_updater
26 * @param SitePress $sitepress
27 * @param TranslationManagement $iclTranslationManagement
28 */
29 public function __construct(
30 WPML_Media_Translated_Images_Update $images_updater,
31 SitePress $sitepress,
32 TranslationManagement $iclTranslationManagement
33
34 ) {
35 $this->images_updater = $images_updater;
36 $this->sitepress = $sitepress;
37 $this->iclTranslationManagement = $iclTranslationManagement;
38 }
39
40 public function add_hooks() {
41 add_action( 'updated_post_meta', array( $this, 'translate_images' ), PHP_INT_MAX, 4 );
42 add_action( 'added_post_meta', array( $this, 'translate_images' ), PHP_INT_MAX, 4 );
43 }
44
45 /**
46 * @param int $meta_id
47 * @param int $object_id
48 * @param string $meta_key
49 * @param string $meta_value
50 */
51 public function translate_images( $meta_id, $object_id, $meta_key, $meta_value ) {
52
53 $settings_factory = new WPML_Custom_Field_Setting_Factory( $this->iclTranslationManagement );
54 $setting = $settings_factory->post_meta_setting( $meta_key );
55
56 $is_custom_field_translatable = $this->sitepress->get_wp_api()
57 ->constant( 'WPML_TRANSLATE_CUSTOM_FIELD' ) === $setting->status();
58 $post_type = get_post_type( $object_id );
59 $is_post_translatable = $this->sitepress->is_translated_post_type( $post_type );
60
61 if ( is_string( $meta_value ) && $is_post_translatable && $is_custom_field_translatable ) {
62 $post_element = new WPML_Post_Element( $object_id, $this->sitepress );
63 $source_language = $post_element->get_source_language_code();
64 if ( null !== $source_language ) {
65 $this->filter_meta_value_and_update(
66 $meta_value,
67 $meta_key,
68 $post_element->get_language_code(),
69 $source_language,
70 $object_id
71 );
72 } else {
73 foreach ( array_keys( $this->sitepress->get_active_languages() ) as $language ) {
74 $translation = $post_element->get_translation( $language );
75 if ( $translation ) {
76 $this->filter_meta_value_and_update(
77 $meta_value,
78 $meta_key,
79 $language,
80 $source_language,
81 $translation->get_id()
82 );
83 }
84 }
85 }
86 }
87 }
88
89 /**
90 * @param string $meta_value
91 * @param string $meta_key
92 * @param string $target_language
93 * @param string $source_language
94 * @param int $post_id
95 *
96 * @return string
97 */
98 private function filter_meta_value_and_update( $meta_value, $meta_key, $target_language, $source_language, $post_id ) {
99 $meta_value_filtered = $this->images_updater->replace_images_with_translations(
100 $meta_value,
101 $target_language,
102 $source_language
103 );
104
105 if ( $meta_value_filtered !== $meta_value ) {
106 remove_action( 'updated_post_meta', array( $this, 'translate_images' ), PHP_INT_MAX );
107 update_post_meta( $post_id, $meta_key, wp_slash( $meta_value_filtered ), $meta_value );
108 add_action( 'updated_post_meta', array( $this, 'translate_images' ), PHP_INT_MAX, 4 );
109 }
110
111 return $meta_value_filtered;
112 }
113 }
1 <?php
2
3 namespace WPML\Media\Classes;
4
5 use WPML\LIB\WP\Attachment;
6
7 abstract class WPML_Media_Element_Parser {
8
9 private static $getAttachmentsRegex = '/(\S+)\\s*=\\s*["\']?((?:.(?!["\']?\s+(?:\S+)=|[>"\']))+.)["\']?/';
10
11 protected $blockText;
12
13 public function __construct( $blockText ) {
14 $this->blockText = $blockText;
15 }
16
17 abstract public function getMediaElements();
18
19 abstract public function getMediaSrcFromAttributes( $attrs );
20
21 abstract public function validate();
22
23 protected function getAttachments( $matches ) {
24 $attachments = [];
25
26 foreach ( $matches[1] as $i => $match ) {
27 if ( preg_match_all( self::$getAttachmentsRegex, $match, $attribute_matches ) ) {
28 $attributes = [];
29 foreach ( $attribute_matches[1] as $k => $key ) {
30 $attributes[ $key ] = $attribute_matches[2][ $k ];
31 }
32
33 $attachments[ $i ]['attributes'] = $attributes;
34 }
35 }
36
37 return $attachments;
38 }
39 }
1 <?php
2
3 namespace WPML\Media\Classes;
4
5 use WPML\FP\Obj;
6 use WPML\FP\Str;
7
8 /**
9 * Media file block parser
10 */
11 class WPML_Media_File_Parser extends WPML_Media_Element_Parser {
12
13 private static $objectElementExpression = '/<object ([^>]+)>/s';
14
15 public function getMediaElements() {
16 return $this->getFromTags();
17 }
18
19 public function getMediaSrcFromAttributes( $attrs ) {
20 return Obj::propOr( '', 'data', $attrs );
21 }
22
23 protected function getFromTags() {
24 return preg_match_all( self::$objectElementExpression, $this->blockText, $matches ) ?
25 $this->getAttachments( $matches ) : [];
26 }
27
28 /**
29 * Checks if media element is File Block and 'parse_blocks' function exists.
30 *
31 * @return bool
32 */
33 public function validate() {
34 return Str::includes( '<!-- wp:file', $this->blockText ) && function_exists( 'parse_blocks' );
35 }
36 }
1 <?php
2
3 namespace WPML\Media\Classes;
4
5 use WPML\FP\Obj;
6 use WPML\FP\Str;
7
8 /**
9 * Media in href parser (basically for files included in classic editor)
10 */
11 class WPML_Media_Href_Parser extends WPML_Media_Element_Parser {
12
13 private static $allAnchorsRegex = '/<a.*?>.*?<\/a>/s'; // gets any anchor tag
14 private static $hrefElementRegex = '/<a ([^>]+)/s'; // to get matches for anchors after filtering them (filtering means that we get only anchors without nested tags)
15
16 public function getMediaElements() {
17 return $this->getFromTags();
18 }
19
20 public function getMediaSrcFromAttributes( $attrs ) {
21 return Obj::propOr('', 'href', $attrs);
22 }
23
24 protected function getFromTags() {
25 $anchorsWithoutTags = $this->getAnchorsWithoutNestedTags();
26
27 return preg_match_all( self::$hrefElementRegex, implode( '', $anchorsWithoutTags ), $matches ) ? $this->getAttachments( $matches ) : [];
28 }
29
30 /**
31 * Checks if media element is only anchor with href (basically for files uploaded in classic editor).
32 *
33 * @return bool
34 */
35 public function validate() {
36 return Str::includes( '<a href=', $this->blockText ) && ! empty( $this->getAnchorsWithoutNestedTags() );
37 }
38
39 /**
40 * Gets anchor tags from WP editor that contain neither nested tags not 'wp-block' string in it.
41 *
42 * @return array
43 */
44 public function getAnchorsWithoutNestedTags() {
45 $anchorHasNestedTags = function ( $anchorTag ) {
46 $pattern = '/<a .*?>.*?<.*?<\/a>/s';
47
48 preg_match( $pattern, $anchorTag, $matches );
49
50 return ! empty( $matches );
51 };
52
53 $isBlockAnchor = Str::includes( 'wp-block' );
54
55 preg_match_all( self::$allAnchorsRegex, $this->blockText, $allAnchorTags );
56
57 return wpml_collect( current( $allAnchorTags ) )
58 ->reject( $anchorHasNestedTags )
59 ->reject( $isBlockAnchor )
60 ->toArray();
61 }
62 }
1 <?php
2
3 namespace WPML\Media\Classes;
4
5 use WPML\FP\Obj;
6 use WPML\FP\Str;
7
8 /**
9 * Image block parser
10 */
11 class WPML_Media_Image_Parser extends WPML_Media_Element_Parser {
12
13 protected static $getFromCssBackgroundImagesRegex = '/<\w+[^>]+style\s?=\s?"[^"]*?background-image:url\(\s?([^\s\)]+)\s?\)/';
14 protected static $mediaElementsRegex = [
15 '/<img ([^>]+)>/s',
16 '/<video ([^>]+)>/s',
17 '/<audio ([^>]+)>/s',
18 ];
19
20 public function getMediaElements() {
21 $mediaElements = $this->getFromTags();
22
23 $blocks = parse_blocks( $this->blockText );
24
25 return $blocks ? array_merge( $mediaElements, $this->getFromCssBackgroundImagesInBlocks( $blocks ) )
26 : array_merge( $mediaElements, $this->getFromCssBackgroundImages( $this->blockText ) );
27 }
28
29 public function getMediaSrcFromAttributes( $attrs ) {
30 return Obj::propOr( '', 'src', $attrs );
31 }
32
33 protected function getFromTags() {
34 $mediaElements = wpml_collect( [] );
35
36 foreach ( self::$mediaElementsRegex as $mediaElementExpression ) {
37 if ( preg_match_all( $mediaElementExpression, $this->blockText, $matches ) ) {
38 $mediaElements = $mediaElements->merge( $this->getAttachments( $matches ) );
39 }
40 }
41
42 return $mediaElements->toArray();
43 }
44
45 /**
46 * Checks if media element is Image Block and 'parse_blocks' function exists.
47 *
48 * @return bool
49 */
50 public function validate() {
51 return (
52 ( function_exists( 'parse_blocks' ) &&
53 ( Str::includes( '<!-- wp:image', $this->blockText )
54 || Str::includes( '<!-- wp:video', $this->blockText )
55 || Str::includes( '<!-- wp:audio', $this->blockText )
56 )
57 )
58 ||
59 Str::includes( '<img', $this->blockText )
60 ||
61 Str::includes( '<video', $this->blockText )
62 ||
63 Str::includes( '<audio', $this->blockText )
64 );
65 }
66
67 /**
68 * `parse_blocks` does not specify which kind of collection it should return
69 * (not always an array of `WP_Block_Parser_Block`) and the block parser can be filtered,
70 * so we'll cast it to a standard object for now.
71 *
72 * @param mixed $block
73 *
74 * @return \stdClass|\WP_Block_Parser_Block
75 */
76 protected function sanitizeBlock( $block ) {
77 $block = (object) $block;
78
79 if ( isset( $block->attrs ) ) {
80 /** Sometimes `$block->attrs` is an object or an array, so we'll use an object */
81 $block->attrs = (object) $block->attrs;
82 }
83
84 return $block;
85 }
86
87 /**
88 * @param string $text
89 *
90 * @return array
91 */
92 protected function getFromCssBackgroundImages( $text ) {
93 $images = [];
94
95 if ( preg_match_all( self::$getFromCssBackgroundImagesRegex, $text, $matches ) ) {
96 foreach ( $matches[1] as $src ) {
97 $images[] = [
98 'attributes' => [ 'src' => $src ],
99 ];
100 }
101 }
102
103 return $images;
104 }
105
106 /**
107 * @param array $blocks
108 *
109 * @return array
110 */
111 protected function getFromCssBackgroundImagesInBlocks( $blocks ) {
112 $images = [];
113
114 foreach ( $blocks as $block ) {
115 $block = $this->sanitizeBlock( $block );
116
117 if ( ! empty( $block->innerBlocks ) ) {
118 $inner_images = $this->getFromCssBackgroundImagesInBlocks( $block->innerBlocks );
119 $images = array_merge( $images, $inner_images );
120 continue;
121 }
122
123 if ( ! isset( $block->innerHTML, $block->attrs->id ) ) {
124 continue;
125 }
126
127 $background_images = $this->getFromCssBackgroundImages( $block->innerHTML );
128 $image = reset( $background_images );
129
130 if ( $image ) {
131 $images[] = $image;
132 }
133 }
134
135 return $images;
136 }
137 }
1 <?php
2
3 use WPML\Element\API\PostTranslations;
4
5 /**
6 * Class WPML_Media_Image_Translate
7 * Allows getting translated images in a give language from an attachment
8 */
9 class WPML_Media_Image_Translate {
10
11 /**
12 * @var SitePress
13 */
14 private $sitepress;
15
16 /**
17 * @var WPML_Media_Attachment_By_URL_Factory
18 */
19 private $attachment_by_url_factory;
20
21 /**
22 * @var \WPML\Media\Classes\WPML_Media_Attachment_By_URL_Query
23 */
24 private $media_attachment_by_url_query;
25
26 /**
27 * WPML_Media_Image_Translate constructor.
28 *
29 * @param SitePress $sitepress
30 * @param WPML_Media_Attachment_By_URL_Factory $attachment_by_url_factory
31 * @param \WPML\Media\Factories\WPML_Media_Attachment_By_URL_Query_Factory $media_attachment_by_url_query_factory
32 */
33 public function __construct(
34 SitePress $sitepress,
35 WPML_Media_Attachment_By_URL_Factory $attachment_by_url_factory,
36 \WPML\Media\Factories\WPML_Media_Attachment_By_URL_Query_Factory $media_attachment_by_url_query_factory
37 ) {
38 $this->sitepress = $sitepress;
39 $this->attachment_by_url_factory = $attachment_by_url_factory;
40 $this->media_attachment_by_url_query = $media_attachment_by_url_query_factory->create();
41 }
42
43 /**
44 * @param string $source_language
45 * @param array $items_to_translate
46 */
47 public function prefetchDataForFutureGetTranslatedImageCalls( $source_language, $items_to_translate ) {
48 $this->media_attachment_by_url_query->prefetchAllIdsFromGuids(
49 $source_language,
50 array_merge(
51 array_map(
52 function( $item ) {
53 return WPML_Media_Attachment_By_URL::getUrl( $item['url'] );
54 },
55 $items_to_translate
56 ),
57 array_map(
58 function( $item ) {
59 return WPML_Media_Attachment_By_URL::getUrlNotScaled( $item['url'] );
60 },
61 $items_to_translate
62 )
63 )
64 );
65 $this->media_attachment_by_url_query->prefetchAllIdsFromMetas(
66 $source_language,
67 array_merge(
68 array_map(
69 function( $item ) {
70 return WPML_Media_Attachment_By_URL::getUrlRelativePath( $item['url'] );
71 },
72 $items_to_translate
73 ),
74 array_map(
75 function( $item ) {
76 return WPML_Media_Attachment_By_URL::getUrlRelativePathOriginal(
77 WPML_Media_Attachment_By_URL::getUrlRelativePath( $item['url'] )
78 );
79 },
80 $items_to_translate
81 ),
82 array_map(
83 function( $item ) {
84 return WPML_Media_Attachment_By_URL::getUrlRelativePathScaled( $item['url'] );
85 },
86 $items_to_translate
87 )
88 )
89 );
90 }
91
92 /**
93 * @param int $attachment_id
94 * @param string $language
95 * @param string $size
96 *
97 * @return string
98 */
99 public function get_translated_image( $attachment_id, $language, $size = null ) {
100 $image_url = '';
101 $attachment = new WPML_Post_Element( $attachment_id, $this->sitepress );
102 $attachment_translation = $attachment->get_translation( $language );
103
104 if ( $attachment_translation ) {
105 $uploads_dir = wp_get_upload_dir();
106 $attachment_id = $attachment_translation->get_id();
107 if ( null === $size ) {
108 $image_url = $uploads_dir['baseurl'] . '/' . get_post_meta( $attachment_id, '_wp_attached_file', true );
109 } else {
110 $image_url = $this->get_sized_image_url( $attachment_id, $size, $uploads_dir );
111 }
112 }
113
114 return $image_url;
115 }
116
117 /**
118 * @param string $img_src
119 * @param string $source_language
120 * @param string $target_language
121 *
122 * @return string|bool
123 */
124 public function get_translated_image_by_url( $img_src, $source_language, $target_language ) {
125
126 $attachment_id = $this->get_attachment_id_by_url( $img_src, $source_language );
127
128 if ( $attachment_id ) {
129 $size = $this->get_image_size_from_url( $img_src, $attachment_id );
130 try {
131 $img_src = $this->get_translated_image( $attachment_id, $target_language, $size );
132 } catch ( Exception $e ) {
133 $img_src = false;
134 }
135 } else {
136 $img_src = false;
137 }
138
139 return $img_src;
140 }
141
142 /**
143 * @param string $img_src
144 * @param string $source_language
145 *
146 * @return int
147 */
148 public function get_attachment_id_by_url( $img_src, $source_language ) {
149 $attachment_by_url = $this->attachment_by_url_factory->create( $img_src, $source_language, $this->media_attachment_by_url_query );
150
151 return (int) $attachment_by_url->get_id();
152 }
153
154 /**
155 * @param string $url
156 * @param int $attachment_id
157 *
158 * @return string
159 */
160 private function get_image_size_from_url( $url, $attachment_id ) {
161 $media_sizes = new WPML_Media_Sizes();
162
163 return $media_sizes->get_image_size_from_url( $url, $attachment_id );
164 }
165
166 /**
167 * @param int $attachment_id
168 * @param string $size
169 * @param string $uploads_dir
170 *
171 * @return string
172 */
173 private function get_sized_image_url( $attachment_id, $size, $uploads_dir ) {
174 $image_url = '';
175 $meta_data = wp_get_attachment_metadata( $attachment_id );
176 $image_url_parts = array( $uploads_dir['baseurl'] );
177
178 if ( is_array( $meta_data ) && array_key_exists( 'file', $meta_data ) ) {
179 $file_subdirectory = $meta_data['file'];
180 $file_subdirectory_parts = explode( '/', $file_subdirectory );
181
182 $filename = array_pop( $file_subdirectory_parts );
183 $image_url_parts[] = implode( '/', $file_subdirectory_parts );
184
185 if ( array_key_exists( $size, $meta_data['sizes'] ) ) {
186 $image_url_parts[] = $meta_data['sizes'][ $size ]['file'];
187 } else {
188 $image_url_parts[] = $filename;
189 }
190
191 $image_url = implode( '/', $image_url_parts );
192 }
193
194 return $image_url;
195 }
196 }
1 <?php
2
3 /**
4 * Class WPML_Media_Post_Images_Translation_Factory
5 */
6 class WPML_Media_Post_Images_Translation_Factory implements IWPML_REST_Action_Loader, IWPML_Backend_Action_Loader {
7
8 /**
9 * @return IWPML_Action|null|WPML_Media_Post_Images_Translation
10 */
11 public function create() {
12 global $wpdb, $sitepress;
13
14 $media_localization_settings = WPML_Media::get_setting( 'media_files_localization' );
15 if ( $media_localization_settings['posts'] ) {
16 $image_translator = new WPML_Media_Image_Translate(
17 $sitepress,
18 new WPML_Media_Attachment_By_URL_Factory(),
19 new \WPML\Media\Factories\WPML_Media_Attachment_By_URL_Query_Factory()
20 );
21 $image_updater = new WPML_Media_Translated_Images_Update( new \WPML\Media\Factories\WPML_Media_Element_Parser_Factory(), $image_translator, new WPML_Media_Sizes() );
22
23 return new WPML_Media_Post_Images_Translation(
24 $image_updater,
25 $sitepress,
26 $wpdb,
27 new WPML_Translation_Element_Factory( $sitepress ),
28 new WPML_Media_Custom_Field_Images_Translation_Factory(),
29 new WPML_Media_Usage_Factory()
30 );
31 }
32
33 return null;
34 }
35
36 }
1 <?php
2
3 class WPML_Media_Post_With_Media_Files_Factory {
4 /**
5 * @param $post_id
6 *
7 * @return WPML_Media_Post_With_Media_Files
8 */
9 public function create( $post_id ) {
10 global $sitepress, $iclTranslationManagement;
11
12 return new WPML_Media_Post_With_Media_Files(
13 $post_id,
14 new \WPML\Media\Factories\WPML_Media_Element_Parser_Factory(),
15 new WPML_Media_Attachment_By_URL_Factory(),
16 $sitepress,
17 new WPML_Custom_Field_Setting_Factory( $iclTranslationManagement ),
18 new \WPML\Media\Factories\WPML_Media_Attachment_By_URL_Query_Factory()
19 );
20 }
21 }
1 <?php
2
3 use WPML\FP\Fns;
4 use WPML\LIB\WP\Post;
5
6 class WPML_Media_Post_With_Media_Files {
7
8 /**
9 * @var int
10 */
11 private $post_id;
12 /**
13 * @var WPML_Media_Img_Parse
14 */
15 private $media_parser_factory;
16 /**
17 * @var WPML_Media_Attachment_By_URL_Factory
18 */
19 private $attachment_by_url_factory;
20 /**
21 * @var SitePress $sitepress
22 */
23 private $sitepress;
24 /**
25 * @var WPML_Custom_Field_Setting_Factory
26 */
27 private $cf_settings_factory;
28
29 /**
30 * @var \WPML\Media\Classes\WPML_Media_Attachment_By_URL_Query
31 */
32 private $mediaAttachmentByURLQuery;
33
34 /**
35 * WPML_Media_Post_With_Media_Files constructor.
36 *
37 * @param $post_id
38 * @param \WPML\Media\Factories\WPML_Media_Element_Parser_Factory $media_parser_factory
39 * @param WPML_Media_Attachment_By_URL_Factory $attachment_by_url_factory
40 * @param SitePress $sitepress
41 * @param WPML_Custom_Field_Setting_Factory $cf_settings_factory
42 * @param \WPML\Media\Factories\WPML_Media_Attachment_By_URL_Query_Factory $mediaAttachmentByURLQueryFactory
43 */
44 public function __construct(
45 $post_id,
46 \WPML\Media\Factories\WPML_Media_Element_Parser_Factory $media_parser_factory,
47 WPML_Media_Attachment_By_URL_Factory $attachment_by_url_factory,
48 SitePress $sitepress,
49 WPML_Custom_Field_Setting_Factory $cf_settings_factory,
50 \WPML\Media\Factories\WPML_Media_Attachment_By_URL_Query_Factory $mediaAttachmentByURLQueryFactory
51 ) {
52 $this->post_id = $post_id;
53 $this->media_parser_factory = $media_parser_factory;
54 $this->attachment_by_url_factory = $attachment_by_url_factory;
55 $this->sitepress = $sitepress;
56 $this->cf_settings_factory = $cf_settings_factory;
57 $this->mediaAttachmentByURLQuery = $mediaAttachmentByURLQueryFactory->create();
58 }
59
60 public function get_media_ids() {
61 $media_ids = array();
62
63 if ( $post = get_post( $this->post_id ) ) {
64
65 $content_to_parse = apply_filters( 'wpml_media_content_for_media_usage', $post->post_content, $post );
66 $media_parsers = $this->media_parser_factory->create( $content_to_parse );
67
68 $this->prefetchDataForFutureAttachmentByUrlGetIdCalls( $media_parsers );
69
70 foreach ( $media_parsers as $media_parser ) {
71 $media_ids = $this->unique_array_merge( $media_ids, $this->_get_ids_from_media_array( $media_parser, $media_parser->getMediaElements() ) );
72
73 if ( $featured_image = get_post_meta( $this->post_id, '_thumbnail_id', true ) ) {
74 $media_ids[] = $featured_image;
75 }
76 }
77
78 $media_localization_settings = WPML_Media::get_setting( 'media_files_localization' );
79 if ( $media_localization_settings['custom_fields'] ) {
80 $custom_fields_content = $this->get_content_in_translatable_custom_fields();
81 $media_parsers = $this->media_parser_factory->create( $custom_fields_content );
82
83 foreach ( $media_parsers as $media_parser ) {
84 $media_ids = $this->unique_array_merge( $media_ids, $this->_get_ids_from_media_array( $media_parser, $media_parser->getMediaElements() ) );
85 }
86 }
87
88 if ( $gallery_media_ids = $this->get_gallery_media_ids( $content_to_parse ) ) {
89 $media_ids = array_unique( array_values( array_merge( $media_ids, $gallery_media_ids ) ) );
90 $media_ids = $this->unique_array_merge( $media_ids, $gallery_media_ids );
91 }
92
93 if ( $attached_media_ids = $this->get_attached_media_ids( $this->post_id ) ) {
94 $media_ids = $this->unique_array_merge( $media_ids, $attached_media_ids );
95 }
96
97 }
98
99 return Fns::filter( Post::get(), apply_filters( 'wpml_ids_of_media_used_in_post', $media_ids, $this->post_id ) );
100 }
101
102 /**
103 * @param array $first_array
104 * @param array $second_array
105 *
106 * @return array
107 */
108 private function unique_array_merge( $first_array, $second_array) {
109 return array_unique( array_values( array_merge( $first_array, $second_array ) ) );
110 }
111
112 /**
113 * @param \WPML\Media\Classes\WPML_Media_Element_Parser $media_parser
114 * @param array $media_array
115 *
116 * @return array
117 */
118 private function _get_ids_from_media_array( $media_parser, $media_array ) {
119 $media_ids = array();
120 foreach ( $media_array as $media ) {
121 if ( isset( $media['attachment_id'] ) ) {
122 $media_ids[] = $media['attachment_id'];
123 } else {
124 $attachment_by_url = $this->attachment_by_url_factory->create(
125 $media_parser->getMediaSrcFromAttributes( $media['attributes'] ),
126 wpml_get_current_language(),
127 $this->mediaAttachmentByURLQuery
128 );
129 if ( $attachment_by_url->get_id() ) {
130 $media_ids[] = $attachment_by_url->get_id();
131 }
132 }
133 }
134
135 return $media_ids;
136 }
137
138 /**
139 * @param WPML_Media_Element_Parser[] $mediaParsers
140 */
141 private function prefetchDataForFutureAttachmentByUrlGetIdCalls( $mediaParsers ) {
142 $urls = [];
143 foreach ( $mediaParsers as $mediaParser ) {
144 foreach( $mediaParser->getMediaElements() as $media ) {
145 if ( isset( $media['attachment_id'] ) ) {
146 continue;
147 }
148
149 $urls[] = $mediaParser->getMediaSrcFromAttributes( $media['attributes'] );
150 }
151 }
152
153 $this->mediaAttachmentByURLQuery->prefetchAllIdsFromGuids(
154 wpml_get_current_language(),
155 array_merge(
156 array_map(
157 function( $url ) {
158 return WPML_Media_Attachment_By_URL::getUrl( $url );
159 },
160 $urls
161 ),
162 array_map(
163 function( $url ) {
164 return WPML_Media_Attachment_By_URL::getUrlNotScaled( $url );
165 },
166 $urls
167 )
168 )
169 );
170 $this->mediaAttachmentByURLQuery->prefetchAllIdsFromMetas(
171 wpml_get_current_language(),
172 array_merge(
173 array_map(
174 function( $url ) {
175 return WPML_Media_Attachment_By_URL::getUrlRelativePath( $url );
176 },
177 $urls
178 ),
179 array_map(
180 function( $url ) {
181 return WPML_Media_Attachment_By_URL::getUrlRelativePathOriginal(
182 WPML_Media_Attachment_By_URL::getUrlRelativePath( $url )
183 );
184 },
185 $urls
186 ),
187 array_map(
188 function( $url ) {
189 return WPML_Media_Attachment_By_URL::getUrlRelativePathScaled( $url );
190 },
191 $urls
192 )
193 )
194 );
195 }
196
197 /**
198 * @param string $post_content
199 *
200 * @return array
201 */
202 private function get_gallery_media_ids( $post_content ) {
203
204 $galleries_media_ids = array();
205 $gallery_shortcode_regex = '/\[gallery [^[]*ids=["\']([0-9,\s]+)["\'][^[]*\]/m';
206 if ( preg_match_all( $gallery_shortcode_regex, $post_content, $matches ) ) {
207 foreach ( $matches[1] as $gallery_ids_string ) {
208 $media_ids_array = explode( ',', $gallery_ids_string );
209 $media_ids_array = Fns::map( Fns::unary( 'intval' ), $media_ids_array );
210
211 foreach ( $media_ids_array as $media_id ) {
212 if ( 'attachment' === get_post_type( $media_id ) ) {
213 $galleries_media_ids[] = $media_id;
214 }
215
216 }
217 }
218 }
219
220 return $galleries_media_ids;
221 }
222
223 /**
224 * @param $languages
225 *
226 * @return array
227 */
228 public function get_untranslated_media( $languages ) {
229
230 $untranslated_media = array();
231
232 $post_media = $this->get_media_ids();
233
234 foreach ( $post_media as $attachment_id ) {
235
236 $post_element = new WPML_Post_Element( $attachment_id, $this->sitepress );
237 foreach ( $languages as $language ) {
238 $translation = $post_element->get_translation( $language );
239 if ( null === $translation || ! $this->media_file_is_translated( $attachment_id, $translation->get_id() ) ) {
240 $untranslated_media[] = $attachment_id;
241 break;
242 }
243 }
244
245 }
246
247 return $untranslated_media;
248 }
249
250 private function media_file_is_translated( $attachment_id, $translated_attachment_id ) {
251 return get_post_meta( $attachment_id, '_wp_attached_file', true )
252 !== get_post_meta( $translated_attachment_id, '_wp_attached_file', true );
253 }
254
255 private function get_content_in_translatable_custom_fields() {
256 $content = '';
257
258 $post_meta = get_metadata( 'post', $this->post_id );
259
260 if ( is_array( $post_meta ) ) {
261 foreach ( $post_meta as $meta_key => $meta_value ) {
262 $setting = $this->cf_settings_factory->post_meta_setting( $meta_key );
263 $is_translatable = $this->sitepress->get_wp_api()
264 ->constant( 'WPML_TRANSLATE_CUSTOM_FIELD' ) === $setting->status();
265 if ( is_string( $meta_value[0] ) && $is_translatable ) {
266 $content .= $meta_value[0];
267 }
268 }
269 }
270
271 return $content;
272 }
273
274 private function get_attached_media_ids( $post_id ) {
275 $attachments = get_children(
276 array(
277 'post_parent' => $post_id,
278 'post_status' => 'inherit',
279 'post_type' => 'attachment',
280 )
281 );
282
283 return array_keys( $attachments );
284 }
285 }
1 <?php
2
3 /**
4 * Class WPML_Media_Save_Translation_Factory
5 */
6 class WPML_Media_Save_Translation_Factory implements IWPML_Backend_Action_Loader {
7
8 public function create() {
9 global $sitepress, $wpdb;
10
11 return new WPML_Media_Save_Translation( $sitepress, $wpdb, new WPML_Media_File_Factory(), new WPML_Translation_Element_Factory( $sitepress ) );
12 }
13
14 }
1 <?php
2
3 class WPML_Media_Set_Posts_Media_Flag_Factory implements IWPML_Backend_Action_Loader {
4
5 public function create() {
6 global $wpdb;
7
8 $post_media_usage_factory = new WPML_Media_Post_Media_Usage_Factory();
9
10 return new WPML_Media_Set_Posts_Media_Flag(
11 $wpdb,
12 wpml_get_admin_notices(),
13 $post_media_usage_factory->create(),
14 new WPML_Media_Post_With_Media_Files_Factory()
15 );
16 }
17
18 }
...\ No newline at end of file ...\ No newline at end of file
1 <?php
2
3 class WPML_Media_Set_Posts_Media_Flag implements IWPML_Action {
4 const HAS_MEDIA_POST_FLAG = '_wpml_media_has_media';
5
6 const BATCH_SIZE = 100;
7 /**
8 * @var wpdb $wpdb
9 */
10 private $wpdb;
11 /**
12 * @var WPML_Notices
13 */
14 private $wpml_notices;
15 /**
16 * @var WPML_Media_Post_Media_Usage
17 */
18 private $post_media_usage;
19 /**
20 * @var WPML_Media_Post_With_Media_Files_Factory
21 */
22 private $post_with_media_files_factory;
23
24 public function __construct(
25 wpdb $wpdb,
26 WPML_Notices $wpml_notices,
27 WPML_Media_Post_Media_Usage $post_media_usage,
28 WPML_Media_Post_With_Media_Files_Factory $post_with_media_files_factory
29 ) {
30 $this->wpdb = $wpdb;
31 $this->wpml_notices = $wpml_notices;
32 $this->post_media_usage = $post_media_usage;
33 $this->post_with_media_files_factory = $post_with_media_files_factory;
34 }
35
36 public function add_hooks() {
37 add_action( 'wp_ajax_' . WPML_Media_Posts_Media_Flag_Notice::PREPARE_ACTION, [ $this, 'clear_flags_action' ] );
38 add_action( 'wp_ajax_' . WPML_Media_Posts_Media_Flag_Notice::PROCESS_ACTION, [ $this, 'process_batch_action' ] );
39 add_action( 'save_post', array( $this, 'update_post_flag' ) );
40 }
41
42 public function clear_flags_action() {
43 if ( $this->verify_nonce( WPML_Media_Posts_Media_Flag_Notice::PREPARE_ACTION ) ) {
44 $this->clear_flags();
45 wp_send_json_success( array( 'status' => __( 'Running setup...', 'wpml-media' ) ) );
46
47 } else {
48 wp_send_json_error( array( 'status' => 'Invalid nonce' ) );
49 }
50 }
51
52 public function clear_flags() {
53 if ( ! WPML_Media::has_setup_started() ) {
54 $this->wpdb->delete( $this->wpdb->postmeta, array( 'meta_key' => self::HAS_MEDIA_POST_FLAG ), array( '%s' ) );
55 }
56 }
57
58 public function process_batch_action() {
59 if ( $this->verify_nonce( WPML_Media_Posts_Media_Flag_Notice::PROCESS_ACTION ) ) {
60
61 $offset = isset( $_POST['offset'] ) ? (int) $_POST['offset'] : 0;
62 list( $status, $offset, $continue ) = $this->process_batch( $offset );
63
64 wp_send_json_success( array(
65 'status' => $status,
66 'offset' => $offset,
67 'continue' => $continue
68 ) );
69
70 } else {
71 wp_send_json_error( array( 'status' => 'Invalid nonce' ) );
72 }
73 }
74
75 public function process_batch( $offset ) {
76 $this->mark_started();
77
78 $continue = false;
79 $status = __( 'Setup complete!', 'wpml-media' );
80
81 if ( ! WPML_Media::has_setup_run() ) {
82
83 $sql = $this->wpdb->prepare( "
84 SELECT SQL_CALC_FOUND_ROWS ID, post_content FROM {$this->wpdb->posts} p
85 JOIN {$this->wpdb->prefix}icl_translations t
86 ON t.element_id = p.ID AND t.element_type LIKE 'post_%'
87 LEFT JOIN {$this->wpdb->prefix}postmeta m ON p.ID = m.post_id AND m.meta_key='%s'
88 WHERE p.post_type NOT IN ( 'auto-draft', 'attachment', 'revision' )
89 AND t.source_language_code IS NULL AND m.meta_id IS NULL
90 ORDER BY ID ASC
91 LIMIT %d, %d
92 ", self::HAS_MEDIA_POST_FLAG, $offset, self::BATCH_SIZE );
93
94 $posts = $this->wpdb->get_results( $sql );
95
96 $total_posts_found = $this->wpdb->get_var( 'SELECT FOUND_ROWS()' );
97
98 if ( $continue = ( count( $posts ) > 0 ) ) {
99 $this->flag_posts( $posts );
100 $this->record_media_usage( $posts );
101 $progress = round( 100 * min( $offset, $total_posts_found ) / $total_posts_found );
102 $status = sprintf( __( 'Setup in progress: %d%% complete...', 'wpml-media' ), $progress );
103 }
104 }
105
106 if ( ! $continue ) {
107 $this->mark_complete();
108 }
109
110 return [ $status, $offset + self::BATCH_SIZE, $continue ];
111 }
112
113 private function verify_nonce( $action ) {
114 return isset( $_POST['nonce'] ) && wp_verify_nonce( $_POST['nonce'], $action );
115 }
116
117 /**
118 * @param array $posts
119 */
120 private function flag_posts( $posts ) {
121 foreach ( $posts as $post ) {
122 $this->update_post_flag( $post->ID );
123 }
124 }
125
126 public function update_post_flag( $post_id ) {
127
128 $post_with_media_files = $this->post_with_media_files_factory->create( $post_id );
129
130 if ( $post_with_media_files->get_media_ids() ) {
131 update_post_meta( $post_id, self::HAS_MEDIA_POST_FLAG, 1 );
132 } else {
133 delete_post_meta( $post_id, self::HAS_MEDIA_POST_FLAG );
134 }
135 }
136
137 /**
138 * @param array $posts
139 */
140 private function record_media_usage( $posts ) {
141 foreach ( $posts as $post ) {
142 $this->post_media_usage->update_media_usage( $post->ID );
143 }
144 }
145
146 private function mark_complete() {
147 WPML_Media::set_setup_run();
148 $this->wpml_notices->remove_notice(
149 WPML_Media_Posts_Media_Flag_Notice::NOTICE_GROUP,
150 WPML_Media_Posts_Media_Flag_Notice::NOTICE_ID
151 );
152 }
153
154 private function mark_started() {
155 WPML_Media::set_setup_started();
156 }
157
158
159 }
1 <?php
2
3 /**
4 * @author OnTheGo Systems
5 */
6 class WPML_Media_Sizes {
7 /**
8 * @param array $img
9 *
10 * @return null|string
11 */
12 public function get_size_from_class( array $img ) {
13 if ( array_key_exists( 'attributes', $img ) && array_key_exists( 'class', $img['attributes'] ) ) {
14
15 $classes = explode( ' ', $img['attributes']['class'] );
16 foreach ( $classes as $class ) {
17 if ( strpos( $class, 'size-' ) === 0 ) {
18 $class_parts = explode( '-', $class );
19 if ( count( $class_parts ) >= 2 ) {
20 unset( $class_parts[0] );
21
22 return implode( '-', $class_parts );
23 }
24 }
25 }
26 }
27
28 return null;
29 }
30
31 /**
32 * @param array $img
33 *
34 * @return null|string
35 */
36 public function get_size_from_attributes( array $img ) {
37 if (
38 array_key_exists( 'attributes', $img )
39 && array_key_exists( 'width', $img['attributes'] )
40 && array_key_exists( 'height', $img['attributes'] )
41 ) {
42
43 $width = $img['attributes']['width'];
44 $height = $img['attributes']['height'];
45
46 $size_name = $this->get_image_size_name( $width, $height );
47
48 if ( $size_name ) {
49 return $size_name;
50 }
51 }
52
53 return null;
54 }
55
56 /**
57 * @param array $img
58 *
59 * @return null|string
60 */
61 public function get_attachment_size( array $img ) {
62 $size = null;
63 if ( array_key_exists( 'size', $img ) ) {
64 $size = $img['size'];
65 }
66 if ( ! $size ) {
67 $size = $this->get_size_from_class( $img );
68 }
69 if ( ! $size ) {
70 $size = $this->get_size_from_attributes( $img );
71 }
72 if ( ! $size ) {
73 $size = $this->get_size_from_url( $img );
74 }
75
76 return $size;
77 }
78
79 /**
80 * @param string $width
81 * @param string $height
82 *
83 * @return null|string
84 */
85 private function get_image_size_name( $width, $height ) {
86 global $_wp_additional_image_sizes;
87
88 foreach ( get_intermediate_image_sizes() as $size ) {
89 if ( isset( $_wp_additional_image_sizes[ $size ] ) ) {
90 if ( $width == $_wp_additional_image_sizes[ $size ]['width'] && $height == $_wp_additional_image_sizes[ $size ]['height'] ) {
91 return $size;
92 }
93 } elseif ( in_array( $size, array( 'thumbnail', 'medium', 'medium_large', 'large' ) ) ) {
94 if ( $width == get_option( "{$size}_size_w" ) && $height == get_option( "{$size}_size_h" ) ) {
95 return $size;
96 }
97 }
98 }
99
100 return null;
101 }
102
103 /**
104 * @param array $img
105 *
106 * @return null|string
107 */
108 private function get_size_from_url( array $img ) {
109 $size = null;
110
111 if ( isset( $img['attributes']['src'], $img['attachment_id'] ) ) {
112 $size = $this->get_image_size_from_url( $img['attributes']['src'], $img['attachment_id'] );
113 }
114
115 return $size;
116 }
117
118 /**
119 * @param $url
120 * @param $attachment_id
121 *
122 * @return null|string
123 */
124 public function get_image_size_from_url( $url, $attachment_id ) {
125 $size = null;
126
127 $thumb_file_name = basename( $url );
128
129 $attachment_meta_data = wp_get_attachment_metadata( $attachment_id );
130 if ( isset( $attachment_meta_data['sizes'] ) ) {
131 foreach ( $attachment_meta_data['sizes'] as $key => $size_array ) {
132 if ( $thumb_file_name === $size_array['file'] ) {
133 $size = $key;
134 break;
135 }
136 }
137 }
138
139 return $size;
140 }
141 }
1 <?php
2
3 /**
4 * Class WPML_Media_String_Images_Translation_Factory
5 */
6 class WPML_Media_String_Images_Translation_Factory implements IWPML_Backend_Action_Loader {
7
8 public function create() {
9 global $wpdb, $sitepress;
10
11 $media_localization_settings = WPML_Media::get_setting( 'media_files_localization' );
12
13 if ( $media_localization_settings['strings'] ) {
14 $image_translator = new WPML_Media_Image_Translate(
15 $sitepress,
16 new WPML_Media_Attachment_By_URL_Factory(),
17 new \WPML\Media\Factories\WPML_Media_Attachment_By_URL_Query_Factory()
18 );
19 $image_updater = new WPML_Media_Translated_Images_Update( new \WPML\Media\Factories\WPML_Media_Element_Parser_Factory(), $image_translator, new WPML_Media_Sizes() );
20 $string_factory = new WPML_ST_String_Factory( $wpdb );
21
22 return new WPML_Media_String_Images_Translation( $image_updater, $string_factory );
23 }
24
25 return null;
26 }
27
28 }
1 <?php
2
3 /**
4 * Class WPML_Media_String_Images_Translation
5 * Translate images in posts strings translations when a string translation is created or updated
6 */
7 class WPML_Media_String_Images_Translation implements IWPML_Action {
8
9 /**
10 * @var WPML_Media_String_Images_Translation
11 */
12 private $images_updater;
13 /**
14 * @var WPML_ST_String_Factory
15 */
16 private $string_factory;
17
18 /**
19 * WPML_Media_String_Images_Translation constructor.
20 *
21 * @param WPML_Media_Translated_Images_Update $images_updater
22 * @param WPML_ST_String_Factory $string_factory
23 */
24 public function __construct( WPML_Media_Translated_Images_Update $images_updater, WPML_ST_String_Factory $string_factory ) {
25 $this->images_updater = $images_updater;
26 $this->string_factory = $string_factory;
27 }
28
29 public function add_hooks() {
30 add_filter( 'wpml_st_string_translation_before_save', array( $this, 'translate_images' ), PHP_INT_MAX, 3 );
31 }
32
33 /**
34 * @param array $translation_data
35 * @param string $target_language
36 * @param int $string_id
37 *
38 * @return array
39 */
40 public function translate_images( $translation_data, $target_language, $string_id ) {
41 if ( ! empty( $translation_data['value'] ) ) {
42 $original_string = $this->string_factory->find_by_id( $string_id );
43
44 $translation_data['value'] = $this->images_updater->replace_images_with_translations(
45 $translation_data['value'],
46 $target_language,
47 $original_string->get_language()
48 );
49 }
50
51 return $translation_data;
52 }
53
54 }
1 <?php
2
3 class WPML_Media_Translation_Status {
4
5 const NOT_TRANSLATED = 'media-not-translated';
6 const IN_PROGRESS = 'in-progress';
7 const TRANSLATED = 'media-translated';
8 const NEEDS_MEDIA_TRANSLATION = 'needs-media-translation';
9
10 const STATUS_PREFIX = '_translation_status_';
11 }
1 <?php
2
3 namespace WPML\Media\Factories;
4
5 use WPML\Media\Classes\WPML_Media_Classic_Audio_Parser;
6 use WPML\Media\Classes\WPML_Media_Classic_Video_Parser;
7 use WPML\Media\Classes\WPML_Media_File_Parser;
8 use WPML\Media\Classes\WPML_Media_Href_Parser;
9 use WPML\Media\Classes\WPML_Media_Image_Parser;
10 use WPML\Media\Classes\WPML_Non_Embedded_Pdf_Parser;
11
12 class WPML_Media_Element_Parser_Factory {
13
14 /**
15 * @var array[]
16 */
17 private $availableMediaParsers = [
18 'img-block' => [ 'class-name' => WPML_Media_Image_Parser::class ],
19 'audio-block' => [ 'class-name' => WPML_Media_Image_Parser::class ],
20 'video-block' => [ 'class-name' => WPML_Media_Image_Parser::class ],
21 'file-block' => [ 'class-name' => WPML_Media_File_Parser::class ],
22 'non-embedded-pdf' => [ 'class-name' => WPML_Non_Embedded_Pdf_Parser::class ],
23 'classic-audio' => [ 'class-name' => WPML_Media_Classic_Audio_Parser::class ],
24 'classic-Video' => [ 'class-name' => WPML_Media_Classic_Video_Parser::class ],
25 'href' => [ 'class-name' => WPML_Media_Href_Parser::class ],
26 ];
27
28 /**
29 * Returns array of media parsers according to post content.
30 *
31 * @param string $post_content
32 *
33 * @return array
34 */
35 public function create( $post_content ) {
36 $parsers = [];
37
38 foreach ( $this->availableMediaParsers as $mediaParser ) {
39 $parserInstance = new $mediaParser['class-name']( $post_content );
40 if ( $parserInstance->validate() ) {
41 $parsers [ $mediaParser['class-name'] ] = $parserInstance;
42 }
43 }
44
45 return $parsers;
46 }
47 }
1 <?php
2
3 class WPML_Media_Populate_Media_Strings_Translations_Factory implements IWPML_Backend_Action_Loader {
4 public function create() {
5 global $sitepress;
6
7 if ( class_exists( 'WPML_Element_Translation_Package' ) ) {
8 return new WPML_Media_Populate_Media_Strings_Translations(
9 new WPML_Translation_Element_Factory( $sitepress ),
10 new WPML_Element_Translation_Package()
11 );
12 }
13 }
14 }
...\ No newline at end of file ...\ No newline at end of file
1 <?php
2
3 class WPML_Media_Populate_Media_Strings_Translations implements IWPML_Action {
4
5 /**
6 * @var WPML_Translation_Element_Factory
7 */
8 private $translation_element_factory;
9
10 /**
11 * @var WPML_Element_Translation_Package
12 */
13 private $translation_package;
14
15 public function __construct(
16 WPML_Translation_Element_Factory $translation_element_factory,
17 WPML_Element_Translation_Package $translation_package
18 ) {
19 $this->translation_element_factory = $translation_element_factory;
20 $this->translation_package = $translation_package;
21 }
22
23 public function add_hooks() {
24 add_filter( 'wpml_tm_populate_prev_translation', array( $this, 'populate' ), 10, 3 );
25 }
26
27 public function populate( $prev_translation, $package, $lang ) {
28
29 if ( ! $prev_translation ) {
30 foreach ( $package['contents'] as $field => $data ) {
31 if ( $media_field = $this->is_media_field( $field ) ) {
32
33 $attachment = $this->translation_element_factory->create( $media_field['id'], 'post' );
34 $attachment_translation = $attachment->get_translation( $lang );
35
36 if ( $attachment_translation ) {
37 $original_id = (int) $media_field['id'];
38 $translation_id = $attachment_translation->get_id();
39
40 switch ( $media_field['field'] ) {
41 case 'title':
42 $translated_value = $this->get_post_field( 'post_title', $original_id, $translation_id );
43 break;
44 case 'caption':
45 $translated_value = $this->get_post_field( 'post_excerpt', $original_id, $translation_id );
46 break;
47 case 'description':
48 $translated_value = $this->get_post_field( 'post_content', $original_id, $translation_id );
49 break;
50 case 'alt_text':
51 $translated_value = get_post_meta( $translation_id, '_wp_attachment_image_alt', true );
52 if ( ! $translated_value ) {
53 $translated_value = get_post_meta( $original_id, '_wp_attachment_image_alt', true );
54 }
55 break;
56 default:
57 $translated_value = false;
58
59 }
60
61 if ( $translated_value ) {
62 $prev_translation[ $field ] = wpml_tm_create_translated_field(
63 '', $this->translation_package->encode_field_data( $translated_value ), true
64 );
65 }
66 }
67
68 }
69 }
70
71
72 }
73
74 return $prev_translation;
75 }
76
77 private function is_media_field( $field ) {
78 $media_field = array();
79
80 if ( preg_match( '#^media_([0-9]+)_([a-z_]+)$#', $field, $matches ) ) {
81 $media_field['id'] = $matches[1];
82 $media_field['field'] = $matches[2];
83 }
84
85 return $media_field;
86 }
87
88 /**
89 * @param string $field
90 * @param int $original_id
91 * @param int $translation_id
92 *
93 * @return string
94 */
95 private function get_post_field( $field, $original_id, $translation_id ) {
96 $value = get_post_field( $field, $translation_id );
97
98 if ( ! $value ) {
99 $value = get_post_field( $field, $original_id );
100 }
101
102 return $value;
103 }
104 }
...\ No newline at end of file ...\ No newline at end of file
1 <?php
2
3 class WPML_Media_Post_Media_Usage_Factory implements IWPML_Backend_Action_Loader, IWPML_Frontend_Action_Loader {
4
5 public function create(){
6 global $sitepress;
7 return new WPML_Media_Post_Media_Usage(
8 $sitepress,
9 new WPML_Media_Post_With_Media_Files_Factory(),
10 new WPML_Media_Usage_Factory()
11 );
12 }
13
14 }
...\ No newline at end of file ...\ No newline at end of file
1 <?php
2
3 class WPML_Media_Post_Media_Usage implements IWPML_Action {
4
5 /** @see WPML_Post_Translation::save_post_actions() */
6 const PRIORITY_AFTER_CORE_SAVE_POST_ACTIONS = 200;
7
8 /**
9 * @var SitePress
10 */
11 private $sitepress;
12 /**
13 * @var WPML_Media_Post_With_Media_Files_Factory
14 */
15 private $post_with_media_files_factory;
16 /**
17 * @var WPML_Media_Usage_Factory
18 */
19 private $media_usage_factory;
20
21
22 public function __construct(
23 SitePress $sitepress,
24 WPML_Media_Post_With_Media_Files_Factory $post_with_media_files_factory,
25 WPML_Media_Usage_Factory $media_usage_factory
26 ) {
27 $this->sitepress = $sitepress;
28 $this->post_with_media_files_factory = $post_with_media_files_factory;
29 $this->media_usage_factory = $media_usage_factory;
30 }
31
32 public function add_hooks() {
33 add_action( 'save_post', array( $this, 'update_media_usage' ), self::PRIORITY_AFTER_CORE_SAVE_POST_ACTIONS, 2 );
34 }
35
36 /**
37 * @param int $post_id
38 * @param WP_Post|null $post
39 */
40 public function update_media_usage( $post_id, $post = null ) {
41
42 if ( null === $post ) {
43 $post = get_post( $post_id );
44 }
45
46 if ( $this->sitepress->get_wp_api()->constant( 'DOING_AUTOSAVE' )
47 || ! $this->sitepress->is_translated_post_type( $post->post_type )
48 || $post_id !== (int) $this->sitepress->get_original_element_id( $post_id, 'post_' . $post->post_type )
49 ) {
50 return;
51 }
52
53 $media_ids = $this->post_with_media_files_factory->create( $post_id )->get_media_ids();
54 foreach ( $media_ids as $media_id ) {
55 $this->media_usage_factory->create( $media_id )->add_post( $post->ID );
56 }
57
58 }
59
60
61 }
...\ No newline at end of file ...\ No newline at end of file
1 <?php
2
3 class WPML_Media_Usage_Factory {
4
5 public function create( $attachment_id ) {
6 return new WPML_Media_Usage( $attachment_id );
7 }
8
9 }
1 <?php
2
3 class WPML_Media_Usage {
4
5 const FIELD_NAME = '_wpml_media_usage';
6
7 /**
8 * @var int
9 */
10 private $attachment_id;
11 /**
12 * @var array
13 */
14 private $usage;
15
16 /**
17 * @param int $attachment_id
18 */
19 public function __construct( $attachment_id ) {
20 $this->attachment_id = $attachment_id;
21
22 $usage = get_post_meta( $this->attachment_id, self::FIELD_NAME, true );
23 $this->usage = empty( $usage ) ? array() : $usage;
24 }
25
26 /**
27 * @return array
28 */
29 public function get_posts() {
30 return empty( $this->usage['posts'] ) ? array() : $this->usage['posts'];
31 }
32
33 /**
34 * @param int $post_id
35 */
36 public function add_post( $post_id ) {
37 $posts = $this->get_posts();
38 $posts[] = $post_id;
39 $this->usage['posts'] = array_unique( $posts );
40 $this->update_usage();
41 }
42
43 /**
44 * @param int $post_id
45 */
46 public function remove_post( $post_id ) {
47 $this->usage['posts'] = array_values( array_diff( (array) $this->usage['posts'], array( $post_id ) ) );
48 $this->update_usage();
49 }
50
51 private function update_usage() {
52 update_post_meta( $this->attachment_id, self::FIELD_NAME, $this->usage );
53 }
54
55 }
1 <?php
2
3 class WPML_Media_Help_Tab_Factory implements IWPML_Backend_Action_Loader {
4
5 public function create() {
6 return new WPML_Media_Help_Tab();
7 }
8
9 }
1 <?php
2
3 class WPML_Media_Help_Tab implements IWPML_Action {
4
5 public function add_hooks() {
6
7 add_action( 'admin_head', array( $this, 'add' ) );
8 }
9
10 public function add() {
11 $current_screen = get_current_screen();
12
13 if ( $this->is_media_related_screen( $current_screen ) ) {
14 $media_translation_dashboard = esc_html__( 'WPML &raquo; Media Translation', 'wpml-media' );
15 $wpml_translation_dashboard = esc_html__( 'WPML &raquo; Translation Management', 'wpml-media' );
16 $current_screen->add_help_tab(
17 array(
18 'id' => 'wpml-media-translation',
19 'title' => esc_html__( 'Translating Media', 'wpml-media' ),
20 'content' =>
21 '<p>' . esc_html__( 'There are two ways for you to translate Media:', 'wpml-media' ) . '</p>' .
22 '<ul>' .
23 '<li>' . sprintf(
24 esc_html__( 'Use the dashboard on the %s page to translate your images and other media files.', 'wpml-media' ),
25 $media_translation_dashboard
26 ) . '</li>' .
27 '<li>' . sprintf(
28 esc_html__( 'Use the dashboard on the %s page to send pages that contain media, for translation.', 'wpml-media' ),
29 $wpml_translation_dashboard
30 ) . '</li>' .
31 '</ul>' .
32 '</ul>' .
33 '<a href="https://wpml.org/documentation/getting-started-guide/media-translation/?utm_source=plugin&utm_medium=gui&utm_campaign=wpmlmedia">' . esc_html__( 'Learn more about WPML Media Translation', 'wpml-media' ) . '</a>',
34 )
35 );
36 }
37
38 }
39
40 private function is_media_related_screen( $current_screen ) {
41 $accepted_bases = array(
42 'wpml_page_wpml-media',
43 'upload',
44 'media',
45 'wpml_page_wpml-translation-management/menu/main',
46 );
47
48 return $current_screen && in_array( $current_screen->base, $accepted_bases );
49 }
50
51 }
1 <?php
2
3 /**
4 * Class WPML_Media_Menus_Factory
5 */
6 class WPML_Media_Menus_Factory {
7
8 public function create() {
9 global $sitepress, $wpdb;
10
11 $wpml_wp_api = $sitepress->get_wp_api();
12 $wpml_media_path = $wpml_wp_api->constant( 'WPML_MEDIA_PATH' );
13
14 $template_service_loader = new WPML_Twig_Template_Loader( array( $wpml_media_path . '/templates/menus/' ) );
15 $pagination = new WPML_Admin_Pagination();
16
17 return new WPML_Media_Menus( $template_service_loader, $sitepress, $wpdb, $pagination );
18 }
19
20 }
1 <?php
2
3 class WPML_Media_Menus {
4
5 /**
6 * @var IWPML_Template_Service
7 */
8 private $template_service;
9 /**
10 * @var SitePress
11 */
12 private $sitepress;
13 /**
14 * @var wpdb
15 */
16 private $wpdb;
17 /**
18 * @var WPML_Admin_Pagination
19 */
20 private $pagination;
21
22 /**
23 * WPML_Media_Menus constructor.
24 *
25 * @param WPML_Twig_Template_Loader $template_service
26 * @param SitePress $sitepress
27 * @param wpdb $wpdb
28 */
29 public function __construct( WPML_Twig_Template_Loader $template_service, SitePress $sitepress, wpdb $wpdb, WPML_Admin_Pagination $pagination = null ) {
30 $this->template_service = $template_service;
31 $this->sitepress = $sitepress;
32 $this->wpdb = $wpdb;
33 $this->pagination = $pagination;
34
35 }
36
37 public function display() {
38 global $wp_locale, $wpml_query_filter;
39
40 do_action( 'wpml_media_messages' );
41 do_action( 'wpml_media_menu' );
42
43 $menu_overrides = apply_filters( 'wpml_media_menu_overrides', array() );
44 if ( $menu_overrides ) {
45 foreach ( $menu_overrides as $menu_override ) {
46 call_user_func( $menu_override );
47 }
48
49 return;
50 }
51
52 $wpml_media_url = $this->sitepress->get_wp_api()->constant( 'WPML_MEDIA_URL' );
53 $wpml_media_version = $this->sitepress->get_wp_api()->constant( 'WPML_MEDIA_VERSION' );
54
55 wp_enqueue_style( OTGS_Assets_Handles::POPOVER_TOOLTIP );
56 wp_enqueue_script( OTGS_Assets_Handles::POPOVER_TOOLTIP );
57 wp_enqueue_style( 'wpml-media', $wpml_media_url . '/res/css/media-translation.css', array(), $wpml_media_version );
58 wp_enqueue_script(
59 'wpml-media',
60 $wpml_media_url . '/res/js/media-translation-popup.js',
61 array(
62 'jquery',
63 'jquery-ui-dialog',
64 ),
65 $wpml_media_version,
66 true
67 );
68 $wpml_media_popup_strings = array(
69 'title' => esc_js( __( 'Media Translation', 'wpml-media' ) ),
70 'cancel' => esc_js( __( 'Cancel', 'wpml-media' ) ),
71 'save' => esc_js( __( 'Save media translation', 'wpml-media' ) ),
72 'status_labels' => WPML_Media_Translations_UI::get_translation_status_labels(),
73 );
74 wp_localize_script( 'wpml-media', 'wpml_media_popup', $wpml_media_popup_strings );
75 wp_enqueue_script( 'wpml-media-batch-url-translation', $wpml_media_url . '/res/js/batch-url-translation.js', array( 'jquery' ), $wpml_media_version, true );
76 $batch_translation_vars = array(
77 'complete' => esc_js( __( 'Scan complete!', 'wpml-media' ) ),
78 'is_st_enabled' => (bool) $this->sitepress->get_wp_api()->constant( 'WPML_ST_VERSION' ),
79 );
80 wp_localize_script( 'wpml-media-batch-url-translation', 'wpml_media_batch_translation', $batch_translation_vars );
81
82 wp_enqueue_script( OTGS_Assets_Handles::TABLE_STICKY_HEADER );
83
84 $media_translations_ui = new WPML_Media_Translations_UI(
85 $this->sitepress,
86 $this->wpdb,
87 $wp_locale,
88 $wpml_query_filter,
89 $this->pagination
90 );
91
92 $media_translations_ui->show();
93 }
94
95
96 }
1 <?php
2
3 class WPML_Media_Posts_Media_Flag_Notice_Factory implements IWPML_Backend_Action_Loader {
4
5 public function create() {
6 global $sitepress;
7 if ( current_user_can( 'manage_options' ) && ! WPML_Media::has_setup_run() && $sitepress->is_setup_complete()) {
8 return new WPML_Media_Posts_Media_Flag_Notice( $sitepress );
9 }
10
11 return null;
12 }
13
14 }
1 <?php
2
3 class WPML_Media_Posts_Media_Flag_Notice implements IWPML_Action {
4
5 const PREPARE_ACTION = 'wpml-media-has-media-flag-prepare';
6 const PROCESS_ACTION = 'wpml-media-has-media-flag';
7
8 const NOTICE_ID = 'wpml-media-posts-media-flag';
9 const NOTICE_GROUP = 'wpml-media';
10
11 /**
12 * @var SitePress
13 */
14 private $sitepress;
15
16 /**
17 * WPML_Media_Has_Media_Notice constructor.
18 *
19 * @param SitePress $sitepress
20 */
21 public function __construct( SitePress $sitepress ) {
22 $this->sitepress = $sitepress;
23 }
24
25 public function add_hooks() {
26
27 if ( $this->is_wpml_media_screen() ) {
28 add_filter( 'wpml_media_menu_overrides', array( $this, 'override_default_menu' ) );
29 } else {
30 add_action( 'admin_head', array( $this, 'add_top_notice' ) );
31 }
32
33 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_js' ) );
34 }
35
36 public function override_default_menu( $menu_elements ) {
37 $menu_elements[] = array( $this, 'render_menu' );
38
39 return $menu_elements;
40 }
41
42 public function enqueue_js() {
43 $wpml_media_url = $this->sitepress->get_wp_api()->constant( 'WPML_MEDIA_URL' );
44 wp_enqueue_script( 'wpml-media-setup', $wpml_media_url . '/res/js/wpml-media-posts-media-flag.js', array( 'jquery' ), false, true );
45 }
46
47 private function is_wpml_media_screen() {
48 return isset( $_GET['page'] ) && 'wpml-media' === $_GET['page'];
49 }
50
51 public function add_top_notice() {
52
53 /* translators: name ot WPML-Media plugin */
54 $wpml_media = '<strong>' . __( 'WPML Media Translation', 'wpml-media' ) . '</strong>';
55
56 /* translators: used to build a link in the "Click here to finish the setup" */
57 $here_text = _x( 'here', 'Used to build a link in the "Click here to finish the setup"', 'wpml-media' );
58 $here_link = '<a href="' . admin_url( 'admin.php?page=wpml-media' ) . '">' . $here_text . '</a>';
59
60 /* translators: %1$s will be replaced with a translation of "WPML Media Translation", while %2$s is a link with the translation of the word "here" */
61 $text = vsprintf(
62 esc_html__( 'The %1$s setup is almost complete. Click %2$s to finish the setup.', 'wpml-media' ),
63 array(
64 $wpml_media,
65 $here_link
66 )
67 );
68
69 $notice = new WPML_Notice( self::NOTICE_ID, $text, self::NOTICE_GROUP );
70 $notice->set_css_class_types( 'notice-warning' );
71 $notice->set_hideable( false );
72 $notice->set_dismissible( false );
73 $notice->set_collapsable( false );
74 $notice->add_exclude_from_page( 'wpml-media' );
75 $notice->add_capability_check( array( 'manage_options' ) );
76 $wpml_admin_notices = wpml_get_admin_notices();
77 $wpml_admin_notices->add_notice( $notice );
78
79 }
80
81 public function render_menu() {
82 ?>
83 <div class="wrap wpml-media-setup">
84 <h2><?php esc_html_e( 'Setup required', 'wpml-media' ) ?></h2>
85 <div
86 id="wpml-media-posts-media-flag"
87 class="notice notice-warning"
88 style="padding-bottom:8px"
89
90 data-prepare-action="<?php echo esc_attr( self::PREPARE_ACTION ); ?>"
91 data-prepare-nonce="<?php echo wp_create_nonce( self::PREPARE_ACTION ); ?>"
92
93 data-process-action="<?php echo esc_attr( self::PROCESS_ACTION ); ?>"
94 data-process-nonce="<?php echo wp_create_nonce( self::PROCESS_ACTION ); ?>"
95
96 >
97 <p>
98 <?php esc_html_e( 'In order to get WPML Media Translation fully working, you need to run this set up which takes only a few moments depending on the total number of posts in your WordPress install.', 'wpml-media' ); ?>
99 </p>
100 <input type="button" class="button-primary alignright"
101 value="<?php esc_attr_e( 'Finish setup', 'wpml-media' ) ?>"/>
102
103 <span class="spinner"> </span>
104 <p class="alignleft status description"></p>
105 <br clear="all"/>
106 </div>
107 </div>
108 <?php
109 }
110
111 }
1 <?php
2
3 /**
4 * Class WPML_Media_Screen_Options_Factory
5 */
6 class WPML_Media_Screen_Options_Factory implements IWPML_Backend_Action_Loader {
7
8 /**
9 * @return IWPML_Action|WPML_Media_Screen_Options
10 */
11 public function create() {
12
13 $options = array();
14
15 if ( $this->is_translation_dashboard() ) {
16
17 $option_name = 'wpml_media_translation_dashboard_items_per_page';
18 $options[] = array(
19 'key' => 'per_page',
20 'args' => array(
21 'label' => __( 'Number of items per page:', 'wpml-media' ),
22 'default' => get_option( $option_name, 20 ),
23 'option' => $option_name,
24 ),
25 );
26
27 }
28
29 if ( $options ) {
30 return new WPML_Media_Screen_Options( $options );
31 }
32
33 return null;
34
35 }
36
37 /**
38 * @return bool
39 */
40 private function is_translation_dashboard() {
41 return ! isset( $_GET['sm'] ) || 'media-translation' === $_GET['sm'];
42 }
43
44 }
1 <?php
2
3 class WPML_Media_Screen_Options implements IWPML_Action {
4
5 /**
6 * @var array
7 */
8 private $options = array();
9
10 /**
11 * WPML_Media_Screen_Options constructor.
12 *
13 * @param array $options
14 */
15 public function __construct( $options ) {
16 $this->options = $options;
17 }
18
19 public function add_hooks() {
20 add_action( 'load-wpml_page_wpml-media', array( $this, 'add_options' ) );
21 add_filter( 'set-screen-option', array( $this, 'set_screen_option' ), 10, 3 );
22 }
23
24 public function add_options() {
25 foreach ( $this->options as $option ) {
26 add_screen_option( $option['key'], $option['args'] );
27 }
28 }
29
30 public function set_screen_option( $status, $option_name, $value ) {
31 if ( $this->is_valid_option( $option_name ) ) {
32 update_option( $option_name, $value );
33 }
34 }
35
36 private function is_valid_option( $option_name ) {
37 $valid = false;
38 foreach ( $this->options as $option ) {
39 if ( $option_name === $option['args']['option'] ) {
40 $valid = true;
41 break;
42 }
43 }
44
45 return $valid;
46 }
47
48 }
1 <?php
2
3 /**
4 * Class WPML_Media_Editor_Notices_Factory
5 */
6 class WPML_Media_Editor_Notices_Factory implements IWPML_Backend_Action_Loader {
7
8 public function create() {
9 return new WPML_Media_Editor_Notices();
10 }
11
12 }
1 <?php
2
3 /**
4 * Class WPML_Media_Editor_Notices
5 */
6 class WPML_Media_Editor_Notices implements IWPML_Action {
7 const TEXT_EDIT_NOTICE_DISMISSED = '_wpml_media_editor_text_edit_notice_dismissed';
8
9 public function add_hooks() {
10 add_action( 'wp_ajax_wpml_media_editor_text_edit_notice_dismissed', array( $this, 'dismiss_texts_change_notice' ) );
11 }
12
13 public function dismiss_texts_change_notice() {
14 update_user_meta( get_current_user_id(), self::TEXT_EDIT_NOTICE_DISMISSED, 1 );
15 wp_send_json_success();
16 }
17
18 }
1 <?php
2
3 /**
4 * @author OnTheGo Systems
5 */
6 class WPML_Media_Privacy_Content_Factory implements IWPML_Backend_Action_Loader {
7
8 /**
9 * @return IWPML_Action
10 */
11 public function create() {
12 return new WPML_Media_Privacy_Content();
13 }
14 }
1 <?php
2
3 /**
4 * @author OnTheGo Systems
5 */
6 class WPML_Media_Privacy_Content extends WPML_Privacy_Content {
7
8 /**
9 * @return string
10 */
11 protected function get_plugin_name() {
12 return 'WPML Media Translation';
13 }
14
15 /**
16 * @return string|array
17 */
18 protected function get_privacy_policy() {
19 return __( 'WPML Media Translation will send the email address and name of each manager and assigned translator as well as the content itself to Advanced Translation Editor and to the translation services which are used.', 'wpml-media' );
20 }
21
22 }
1 <?php
2
3 class WPML_Media_Set_Initial_Language_Factory implements IWPML_Backend_Action_Loader {
4
5 public function create() {
6 global $sitepress, $wpdb;
7 return new WPML_Media_Set_Initial_Language( $wpdb, $sitepress->get_default_language() );
8 }
9
10 }
1 <?php
2 /**
3 * Class WPML_Media_Set_Initial_Language
4 */
5 class WPML_Media_Set_Initial_Language implements IWPML_Action {
6 /**
7 * @var wpdb
8 */
9 private $wpdb;
10 /**
11 * @var string
12 */
13 private $language;
14
15 /**
16 * WPML_Media_Set_Initial_Language constructor.
17 *
18 * @param wpdb $wpdb
19 * @param string $language
20 */
21 public function __construct( wpdb $wpdb, $language ) {
22 $this->wpdb = $wpdb;
23 $this->language = $language;
24 }
25
26 public function add_hooks() {
27 add_action( 'wp_ajax_wpml_media_set_initial_language', array( $this, 'set' ) );
28 }
29
30 public function set() {
31
32 $this->update_db();
33
34 $message = __( 'Setting language to media: done!', 'wpml-media' );
35
36 wp_send_json_success(
37 array(
38 'message' => $message,
39 )
40 );
41
42 }
43
44 public function update_db() {
45 $maxtrid = $this->wpdb->get_var( "SELECT MAX(trid) FROM {$this->wpdb->prefix}icl_translations" );
46
47 $this->wpdb->query(
48 $this->wpdb->prepare(
49 "
50 INSERT INTO {$this->wpdb->prefix}icl_translations
51 (element_type, element_id, trid, language_code, source_language_code)
52 SELECT 'post_attachment', ID, %d + ID, %s, NULL
53 FROM {$this->wpdb->posts} p
54 LEFT JOIN {$this->wpdb->prefix}icl_translations t
55 ON p.ID = t.element_id AND t.element_type = 'post_attachment'
56 WHERE post_type = 'attachment' AND t.translation_id IS NULL",
57 $maxtrid,
58 $this->language
59 )
60 );
61 }
62 }
1 <?php
2
3 class WPML_Media_2_3_0_Migration {
4
5 const FLAG = 'wpml_media_2_3_migration';
6 const BATCH_SIZE = 200;
7
8 const MAX_BATCH_REQUEST_TIME = 5;
9
10 /**
11 * @var wpdb
12 */
13 private $wpdb;
14 /**
15 * @var SitePress
16 */
17 private $sitepress;
18
19 /**
20 * WPML_Media_2_3_0_Migration constructor.
21 *
22 * @param wpdb $wpdb
23 * @param SitePress $sitepress
24 */
25 public function __construct( wpdb $wpdb, SitePress $sitepress ) {
26 $this->wpdb = $wpdb;
27 $this->sitepress = $sitepress;
28 }
29
30 public static function migration_complete() {
31 return WPML_Media::get_setting( self::FLAG );
32 }
33
34 private function mark_migration_complete() {
35 return WPML_Media::update_setting( self::FLAG, 1 );
36 }
37
38 public function is_required() {
39 if ( $this->wpdb->get_var( "SELECT COUNT(ID) FROM {$this->wpdb->posts} WHERE post_type='attachment'" ) ) {
40 return true;
41 }
42 self::mark_migration_complete();
43
44 return false;
45 }
46
47 public function add_hooks() {
48 add_filter( 'wpml_media_menu_overrides', array( $this, 'override_default_menu' ) );
49 add_action( 'wp_ajax_wpml_media_2_3_0_upgrade', array( $this, 'run_upgrade' ) );
50 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_js' ) );
51 }
52
53 public function override_default_menu( $menu_elements ) {
54 $menu_elements[] = array( $this, 'render_menu' );
55 return $menu_elements;
56 }
57
58 public function maybe_show_admin_notice() {
59 if ( is_admin() && ! $this->is_wpml_media_screen() ) {
60 add_action( 'admin_notices', array( $this, 'render_menu' ) );
61 }
62 }
63
64 private function is_wpml_media_screen() {
65 return isset( $_GET['page'] ) && $_GET['page'] === 'wpml-media';
66 }
67
68 public function enqueue_js() {
69 $wpml_media_url = $this->sitepress->get_wp_api()->constant( 'WPML_MEDIA_URL' );
70 wp_enqueue_script( 'wpml-media-2-3-0-upgrade', $wpml_media_url . '/res/js/upgrade/upgrade-2-3-0.js', array( 'jquery' ), false, true );
71 }
72
73 public function render_menu() {
74
75 if ( $this->is_wpml_media_screen() ) : ?>
76 <div class="wrap wrap-wpml-media-upgrade">
77 <h2><?php esc_html_e( 'Upgrade required', 'wpml-media' ); ?></h2>
78 <?php endif; ?>
79 <div class="notice notice-warning" id="wpml-media-2-3-0-update" style="padding-bottom:8px">
80 <p>
81 <?php
82 printf(
83 esc_html__( 'The %1$sWPML Media%2$s database needs updating. Please run the updater and leave the tab open until it completes.', 'wpml-media' ),
84 '<strong>',
85 '</strong>'
86 );
87 ?>
88 </p>
89 <input type="button" class="button-primary alignright" value="<?php echo esc_attr_x( 'Update', 'Update button label', 'wpml-media' ); ?>" />
90 <input type="hidden" name="nonce" value="<?php echo wp_create_nonce( 'wpml-media-2-3-0-update' ); ?>" />
91 <span class="spinner"></span>
92 <p class="alignleft status description"></p><br clear="all" />
93 </div>
94 <?php if ( $this->is_wpml_media_screen() ) : ?>
95 </div>
96 <?php
97 endif;
98
99 }
100
101 private function reset_new_content_settings() {
102 $wpml_media_settings = get_option( '_wpml_media' );
103 // reset (will not remove since it's used by WCML)
104 $wpml_media_settings['new_content_settings']['always_translate_media'] = 0;
105 $wpml_media_settings['new_content_settings']['duplicate_media'] = 0;
106 $wpml_media_settings['new_content_settings']['duplicate_featured'] = 0;
107 update_option( '_wpml_media', $wpml_media_settings );
108 }
109
110 public function run_upgrade() {
111
112 if ( isset( $_POST['nonce'] ) && ( $_POST['nonce'] === wp_create_nonce( 'wpml-media-2-3-0-update' ) ) ) {
113
114 $step = isset( $_POST['step'] ) ? $_POST['step'] : '';
115
116 if ( 'reset-new-content-settings' === $step ) {
117 $this->reset_new_content_settings();
118 wp_send_json_success(
119 array( 'status' => esc_html__( 'Reset new content duplication settings', 'wpml-media' ) )
120 );
121 } elseif ( 'migrate-attachments' === $step ) {
122 $offset = isset( $_POST['offset'] ) ? (int) $_POST['offset'] : 0;
123
124 $batch_size = $this->get_dynamic_batch_size( $_POST );
125
126 $left = $this->migrate_attachments( $offset, $batch_size );
127
128 if ( $left ) {
129 $status = sprintf(
130 esc_html__( 'Updating attachments translation status: %d remaining.', 'wpml-media' ),
131 $left
132 );
133 $continue = 1;
134 $offset += $batch_size;
135 } else {
136 $this->mark_migration_complete();
137 $status = esc_html__( 'Update complete!', 'wpml-media' );
138 $continue = 0;
139 $offset = 0;
140 }
141
142 wp_send_json_success(
143 array(
144 'status' => $status,
145 'goon' => $continue,
146 'offset' => $offset,
147 'timestamp' => microtime( true ),
148 )
149 );
150
151 } else {
152 wp_send_json_error( array( 'error' => 'Invalid step' ) );
153 }
154 } else {
155 wp_send_json_error( array( 'error' => 'Invalid nonce' ) );
156 }
157
158 }
159
160 private function migrate_attachments( $offset = 0, $batch_size = self::BATCH_SIZE ) {
161
162 $sql = "SELECT SQL_CALC_FOUND_ROWS p.ID
163 FROM {$this->wpdb->posts} p
164 JOIN {$this->wpdb->prefix}icl_translations t
165 ON t.element_id = p.ID AND p.post_type='attachment'
166 WHERE t.source_language_code IS NULL
167 LIMIT %d, %d";
168
169 $sql_prepared = $this->wpdb->prepare( $sql, $offset, $batch_size );
170
171 $original_attachments = $this->wpdb->get_results( $sql_prepared );
172
173 $total_attachments = $this->wpdb->get_var( 'SELECT FOUND_ROWS() ' );
174
175 if ( $original_attachments ) {
176 foreach ( $original_attachments as $attachment ) {
177
178 $post_element = new WPML_Post_Element( $attachment->ID, $this->sitepress );
179 $translations = $post_element->get_translations();
180
181 $media_file = get_post_meta( $attachment->ID, '_wp_attached_file', true );
182
183 foreach ( $translations as $translation ) {
184 if ( (int) $attachment->ID !== $translation->get_id() ) {
185 $media_translation_status = WPML_Media_Translation_Status::NOT_TRANSLATED;
186 $media_file_translation = get_post_meta( $translation->get_id(), '_wp_attached_file', true );
187 if ( $media_file_translation !== $media_file ) {
188 $media_translation_status = WPML_Media_Translation_Status::TRANSLATED;
189 }
190 update_post_meta(
191 $attachment->ID,
192 WPML_Media_Translation_Status::STATUS_PREFIX . $translation->get_language_code(),
193 $media_translation_status
194 );
195 }
196 }
197 }
198 }
199
200 $left = max( 0, $total_attachments - $offset );
201
202 return $left;
203 }
204
205 private function get_dynamic_batch_size( $request ) {
206 $batch_size_factor = isset( $request['batch_size_factor'] ) ? (int) $request['batch_size_factor'] : 1;
207 if ( ! empty( $request['timestamp'] ) ) {
208 $elapsed_time = microtime( true ) - (float) $request['timestamp'];
209
210 if ( $elapsed_time < self::MAX_BATCH_REQUEST_TIME ) {
211 $batch_size_factor ++;
212 } else {
213 $batch_size_factor = max( 1, $batch_size_factor - 1 );
214 }
215 }
216 return self::BATCH_SIZE * $batch_size_factor;
217 }
218 }
1 <?php
2 define( 'WPML_MEDIA_FOLDER', basename( WPML_MEDIA_PATH ) );
3
4 define( 'WPML_MEDIA_URL', plugins_url( '', dirname( __FILE__ ) ) );
1 <?php
2 class WPML_Media_Dependencies {
3
4 function check() {
5 $all_ok = true;
6
7 // Check if WPML is active. If not display warning message and don't load WPML-media
8 if ( ! defined( 'ICL_SITEPRESS_VERSION' ) || ICL_PLUGIN_INACTIVE ) {
9 add_action( 'admin_notices', array( $this, '_no_wpml_warning' ) );
10
11 $all_ok = false;
12 }
13
14 if ( ! WPML_Core_Version_Check::is_ok( WPML_MEDIA_PATH . '/wpml-dependencies.json' ) ) {
15 $all_ok = false;
16 }
17
18 if ( ! $all_ok ) {
19 return false;
20 }
21
22 return true;
23 }
24
25 function _no_wpml_warning() { ?>
26 <div class="message error wpml-media-inactive"><p>
27 <?php
28 printf(
29 __( 'WPML Media is enabled but not effective. It requires <a href="%s">WPML</a> in order to work.', 'wpml-media' ),
30 'https://wpml.org/'
31 );
32 ?>
33 </p></div>
34 <?php
35 }
36
37 }
1 <?php
2
3 class WPML_Media_Upgrade {
4 private static $versions = array(
5 '2.0',
6 '2.0.1',
7 );
8
9 static function run() {
10 global $wpdb;
11
12 // Workaround, as for some reasons, get_option() doesn't work only in this case
13 $wpml_media_settings_prepared = $wpdb->prepare( "select option_value from {$wpdb->prefix}options where option_name = %s", '_wpml_media' );
14 $wpml_media_settings = $wpdb->get_col( $wpml_media_settings_prepared );
15
16 $needs_version_update = true;
17
18 // Do not run upgrades if this is a new install (i.e.: plugin has no settings)
19 if ( $wpml_media_settings || get_option( '_wpml_media_starting_help' ) ) {
20 $current_version = WPML_Media::get_setting( 'version', null );
21
22 if ( $current_version ) {
23 $needs_version_update = version_compare( $current_version, WPML_MEDIA_VERSION, '<' );
24 self::run_upgrades_before_2_3_0( $current_version );
25 } elseif ( self::is_media_version_older_than_2_0() ) {
26 $needs_version_update = true;
27 self::run_upgrades_before_2_3_0( '1.6' );
28 }
29 }
30
31 if ( $needs_version_update ) {
32 WPML_Media::update_setting( 'version', WPML_MEDIA_VERSION );
33 }
34
35 // Blocking database migration
36 self::upgrade_2_3_0();
37 }
38
39 /** @param int $current_version */
40 private static function run_upgrades_before_2_3_0( $current_version ) {
41 if ( version_compare( $current_version, '2.3.0', '<' ) ) {
42
43 foreach ( self::$versions as $version ) {
44 if ( version_compare( $version, WPML_MEDIA_VERSION, '<=' ) && version_compare( $version, $current_version, '>' ) ) {
45
46 $upgrade_method = 'upgrade_' . str_replace( '.', '_', $version );
47 if ( method_exists( __CLASS__, $upgrade_method ) ) {
48 self::$upgrade_method();
49 }
50 }
51 }
52
53 update_option( 'wpml_media_upgraded_from_prior_2_3_0', 1 );
54 }
55 }
56
57 /** @return bool */
58 private static function is_media_version_older_than_2_0() {
59 global $wpdb;
60 return (bool) $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->postmeta} WHERE meta_key = 'wpml_media_duplicate_of'" );
61 }
62
63 private static function upgrade_2_0() {
64 global $wpdb;
65 global $sitepress;
66
67 // Check if the old options are set and in case move them to the new plugin settings, then delete the old ones
68 $old_starting_help = get_option( '_wpml_media_starting_help' );
69 if ( $old_starting_help ) {
70 WPML_Media::update_setting( 'starting_help', $old_starting_help );
71 delete_option( '_wpml_media_starting_help' );
72 }
73
74 // Create translated media
75
76 $target_language = $sitepress->get_default_language();
77 $attachment_ids_prepared = $wpdb->prepare( "SELECT ID FROM {$wpdb->posts} WHERE post_type = %s", 'attachment' );
78 $attachment_ids = $wpdb->get_col( $attachment_ids_prepared );
79
80 // Let's first set the language of all images in default languages
81 foreach ( $attachment_ids as $attachment_id ) {
82 $wpml_media_lang = get_post_meta( $attachment_id, 'wpml_media_lang', true );
83 $wpml_media_duplicate_of = get_post_meta( $attachment_id, 'wpml_media_duplicate_of', true );
84
85 if ( ! $wpml_media_duplicate_of && ( ! $wpml_media_lang || $wpml_media_lang == $target_language ) ) {
86 $trid = $sitepress->get_element_trid( $attachment_id, 'post_attachment' );
87 if ( $trid ) {
88 // Since trid exists, get the language from there
89 $target_language = $sitepress->get_language_for_element( $attachment_id, 'post_attachment' );
90 }
91
92 $sitepress->set_element_language_details( $attachment_id, 'post_attachment', $trid, $target_language );
93 }
94 }
95
96 // Then all the translations
97 foreach ( $attachment_ids as $attachment_id ) {
98 $wpml_media_lang = get_post_meta( $attachment_id, 'wpml_media_lang', true );
99 $wpml_media_duplicate_of = get_post_meta( $attachment_id, 'wpml_media_duplicate_of', true );
100
101 if ( $wpml_media_duplicate_of ) {
102 $source_language = null;
103 $trid = $sitepress->get_element_trid( $wpml_media_duplicate_of, 'post_attachment' );
104 $source_language = false;
105 if ( $trid ) {
106 // Get the source language of the attachment, just in case is from a language different than the default
107 $source_language = $sitepress->get_language_for_element( $wpml_media_duplicate_of, 'post_attachment' );
108
109 // Fix bug on 1.6, where duplicated images are set to the default language
110 if ( $wpml_media_lang == $source_language ) {
111 $wpml_media_lang = false;
112 $attachment = get_post( $attachment_id );
113 if ( $attachment->post_parent ) {
114 $parent_post = get_post( $attachment->post_parent );
115 $post_parent_language = $sitepress->get_language_for_element( $parent_post->ID, 'post_' . $parent_post->post_type );
116 if ( $post_parent_language ) {
117 $wpml_media_lang = $post_parent_language;
118 }
119 }
120
121 if ( ! $wpml_media_lang ) {
122 // Trash orphan image
123 wp_delete_attachment( $attachment_id );
124 }
125 }
126 }
127
128 if ( $wpml_media_lang ) {
129 $sitepress->set_element_language_details( $attachment_id, 'post_attachment', $trid, $wpml_media_lang, $target_language, $source_language );
130 }
131 }
132 }
133
134 // Remove old media translation meta
135 // Remove both meta just in case
136 $attachment_ids = $wpdb->get_col( $attachment_ids_prepared );
137 foreach ( $attachment_ids as $attachment_id ) {
138 delete_post_meta( $attachment_id, 'wpml_media_duplicate_of' );
139 delete_post_meta( $attachment_id, 'wpml_media_lang' );
140 }
141
142 }
143
144 private static function upgrade_2_0_1() {
145 global $wpdb;
146 global $sitepress;
147
148 // Fixes attachments metadata among translations
149 $sql = "
150 SELECT t.element_id, t.trid, t.language_code
151 FROM {$wpdb->prefix}icl_translations t
152 LEFT JOIN {$wpdb->postmeta} pm
153 ON t.element_id = pm.post_id AND pm.meta_key=%s
154 WHERE t.element_type = %s AND pm.meta_id IS NULL AND element_id IS NOT NULL
155 ";
156 $sql_prepared = $wpdb->prepare( $sql, array( '_wp_attachment_metadata', 'post_attachment' ) );
157
158 $original_attachments = $wpdb->get_results( $sql_prepared );
159
160 foreach ( $original_attachments as $original_attachment ) {
161 $attachment_metadata = get_post_meta( $original_attachment->element_id, '_wp_attachment_metadata', true );
162 if ( ! $attachment_metadata ) {
163 $attachment_translations = $sitepress->get_element_translations( $original_attachment->trid, 'post_attachment', true, true );
164 // Get _wp_attachment_metadata first translation available
165 foreach ( $attachment_translations as $attachment_translation ) {
166 if ( $attachment_translation->language_code != $original_attachment->language_code ) {
167 $attachment_metadata = get_post_meta( $attachment_translation->element_id, '_wp_attachment_metadata', true );
168 // _wp_attachment_metadata found: save it in the original and go to the next attachment
169 if ( $attachment_metadata ) {
170 update_post_meta( $original_attachment->element_id, '_wp_attachment_metadata', $attachment_metadata );
171 break;
172 }
173 }
174 }
175 }
176 }
177
178 return true;
179 }
180
181 private static function upgrade_2_3_0() {
182 global $wpdb, $sitepress;
183
184 if ( ! WPML_Media_2_3_0_Migration::migration_complete() ) {
185
186 $migration = new WPML_Media_2_3_0_Migration( $wpdb, $sitepress );
187 if ( $migration->is_required() ) {
188 $migration->maybe_show_admin_notice();
189 $migration->add_hooks();
190 }
191 }
192 }
193
194 }