d8e970ab by Jeff Balicki

stuff

Signed-off-by: Jeff <jeff@gotenzing.com>
1 parent bdf10794
Showing 143 changed files with 11354 additions and 5 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 <?php
2 /*
3 Plugin Name: Duplicate Page and Post
4 Plugin URI: https://wordpress.org/plugins/duplicate-wp-page-post/
5 Description: Quickly clone a page, post or custom post and supports Gutenberg.
6 Author: Arjun Thakur
7 Author URI: https://profiles.wordpress.org/arjunthakur#content-plugins
8 Version: 2.9.3
9 License: GPLv2 or later
10 Text Domain: dpp_wpp_page
11 */
12 if ( ! defined( 'ABSPATH' ) ) {
13 exit;
14 }
15 if ( ! defined( 'DPP_BASE_NAME' ) ) {
16
17 define( 'DPP_BASE_NAME', plugin_basename( __FILE__ ) );
18 }
19
20 if ( ! class_exists( 'dcc_dpp_wpp_page' ) ):
21 class dpp_wpp_page {
22
23 /*AutoLoad Hooks*/
24 public function __construct() {
25 $opt = get_option( 'dpp_wpp_page_options' );
26 register_activation_hook( __FILE__, array( &$this, 'dpp_wpp_page_install' ) );
27 add_action( 'admin_menu', array( &$this, 'dpp_page_options_page' ) );
28 add_filter( 'plugin_action_links', array( &$this, 'dpp_settings_link' ), 10, 2 );
29 add_action( 'admin_action_dt_dpp_post_as_draft', array( &$this, 'dt_dpp_post_as_draft' ) );
30 add_filter( 'post_row_actions', array( &$this, 'dt_dpp_post_link' ), 10, 2 );
31 add_filter( 'page_row_actions', array( &$this, 'dt_dpp_post_link' ), 10, 2 );
32 if ( isset( $opt['dpp_posteditor'] ) && $opt['dpp_posteditor'] == 'gutenberg' ) {
33 add_action( 'admin_head', array( &$this, 'dpp_wpp_button_guten' ) );
34 } else {
35 add_action( 'post_submitbox_misc_actions', array( &$this, 'dpp_wpp_page_custom_button' ) );
36 }
37 add_action( 'wp_before_admin_bar_render', array( &$this, 'dpp_wpp_page_admin_bar_link' ) );
38 }
39
40
41 /*Activation plugin Hook*/
42 public function dpp_wpp_page_install() {
43 $defaultsettings = array(
44 'dpp_post_status' => 'draft',
45 'dpp_post_redirect' => 'to_list',
46 'dpp_post_suffix' => '',
47 'dpp_posteditor' => 'classic',
48 'dpp_post_link_title' => '',
49 );
50 $opt = get_option( 'dpp_wpp_page_options' );
51 if ( ! $opt['dpp_post_status'] ) {
52 update_option( 'dpp_wpp_page_options', $defaultsettings );
53 }
54 }
55
56
57 /* Page Title and Dashboard Menu (Setting options) */
58 public function dpp_page_options_page() {
59 add_options_page( __( 'Duplicate Page and Post', 'dpp_wpp_page' ), __( 'Duplicate post', 'dpp_wpp_page' ), 'manage_options', 'dpp_page_settings', array(
60 &$this,
61 'dpp_page_settings'
62 ) );
63 }
64
65 /*Include plugin setting file*/
66 public function dpp_page_settings() {
67 if ( current_user_can( 'manage_options' ) ) {
68 include( 'duplicate-wp-page-post-setting.php' );
69 }
70 }
71
72 /*Important function*/
73 public function dt_dpp_post_as_draft() {
74 $nonce = sanitize_text_field( $_REQUEST['nonce'] );
75 $getpost = sanitize_text_field( $_GET['post'] );
76 $post_id = ( isset( $getpost ) ? intval( $getpost ) : intval( $getpost ) );
77
78
79 if ( wp_verify_nonce( $nonce, 'dt-duplicate-page-' . $post_id ) && current_user_can( 'edit_posts' ) ) {
80 global $wpdb;
81
82 /*sanitize_GET POST REQUEST*/
83 //$post_copy = sanitize_text_field( $_POST["post"] );
84 //$get_copy = sanitize_text_field( $_GET['post'] );
85 $request_copy = sanitize_text_field( $_REQUEST['action'] );
86
87 $opt = get_option( 'dpp_wpp_page_options' );
88 $suffix = ! empty( $opt['dpp_post_suffix'] ) ? ' -- ' . esc_attr( $opt['dpp_post_suffix'] ) : '';
89
90 $post_status = ! empty( $opt['dpp_post_status'] ) ? esc_attr( $opt['dpp_post_status'] ) : 'draft';
91 $redirectit = ! empty( $opt['dpp_post_redirect'] ) ? esc_attr( $opt['dpp_post_redirect'] ) : 'to_list';
92
93 if ( ! ( isset( $getpost ) || isset( $getpost ) || ( isset( $request_copy ) && 'dt_dpp_post_as_draft' == $request_copy ) ) ) {
94 wp_die( 'No post!' );
95 }
96 $returnpage = '';
97
98 $post = get_post( $post_id );
99
100 $current_user = wp_get_current_user();
101 $new_post_author = $current_user->ID;
102
103 /*Create the post Copy */
104 if ( isset( $post ) && $post != null ) {
105 /* Post data array */
106 $args = array(
107 'comment_status' => $post->comment_status,
108 'ping_status' => $post->ping_status,
109 'post_author' => $new_post_author,
110 'post_content' => ( isset( $opt['dpp_posteditor'] ) && $opt['dpp_posteditor'] == 'gutenberg' ) ? wp_slash( $post->post_content ) : $post->post_content,
111 'post_excerpt' => $post->post_excerpt,
112 //'post_name' => $post->post_name,
113 'post_parent' => $post->post_parent,
114 'post_password' => $post->post_password,
115 'post_status' => $post_status,
116 'post_title' => $post->post_title . $suffix,
117 'post_type' => $post->post_type,
118 'to_ping' => $post->to_ping,
119 'menu_order' => $post->menu_order
120
121 );
122 $new_post_id = wp_insert_post( $args );
123
124
125 $taxonomies = array_map( 'sanitize_text_field', get_object_taxonomies( $post->post_type ) );
126 if ( ! empty( $taxonomies ) && is_array( $taxonomies ) ):
127 foreach ( $taxonomies as $taxonomy ) {
128 $post_terms = wp_get_object_terms( $post_id, $taxonomy, array( 'fields' => 'slugs' ) );
129 wp_set_object_terms( $new_post_id, $post_terms, $taxonomy, false );
130 }
131 endif;
132
133 $post_meta_infos = $wpdb->get_results( $wpdb->prepare( "SELECT meta_key, meta_value FROM $wpdb->postmeta WHERE post_id=%d", $post_id ) );
134 if ( count( $post_meta_infos ) != 0 ) {
135 $sql_query = "INSERT INTO $wpdb->postmeta (post_id, meta_key, meta_value) ";
136 foreach ( $post_meta_infos as $meta_info ) {
137
138 $meta_key = sanitize_text_field( $meta_info->meta_key );
139 $meta_value = addslashes( $meta_info->meta_value );
140 $sql_query_sel[] = "SELECT $new_post_id, '$meta_key', '$meta_value'";
141 }
142 $sql_query .= implode( " UNION ALL ", $sql_query_sel );
143 $wpdb->query( $sql_query );
144 }
145
146 /*choice redirect */
147 if ( $post->post_type != 'post' ):$returnpage = '?post_type=' . $post->post_type; endif;
148 if ( ! empty( $redirectit ) && $redirectit == 'to_list' ):esc_url_raw( wp_redirect( admin_url( 'edit.php' . $returnpage ) ) );
149 elseif ( ! empty( $redirectit ) && $redirectit == 'to_page' ):esc_url_raw( wp_redirect( admin_url( 'post.php?action=edit&post=' . $new_post_id ) ) );
150 else:
151 wp_redirect( esc_url_raw( admin_url( 'edit.php' . $returnpage ) ) );
152 endif;
153 exit;
154 } else {
155 wp_die( 'Error! Post creation failed: ' . $post_id );
156 }
157 } else {
158 wp_die( 'Security check issue, Please try again.' );
159 }
160 }
161
162
163 /*Add link to action*/
164 public function dt_dpp_post_link( $actions, $post ) {
165 $opt = get_option( 'dpp_wpp_page_options' );
166 $link_title = ! empty( $opt['dpp_post_link_title'] ) ? esc_attr( $opt['dpp_post_link_title'] ) : 'Duplicate';
167 $opt = get_option( 'dpp_wpp_page_options' );
168 $post_status = ! empty( $opt['dpp_post_status'] ) ? esc_attr( $opt['dpp_post_status'] ) : 'draft';
169 if ( current_user_can( 'edit_posts' ) ) {
170 $actions['dpp'] = '<a href="admin.php?action=dt_dpp_post_as_draft&amp;post=' . $post->ID . '&amp;nonce=' . wp_create_nonce( 'dt-duplicate-page-' . $post->ID ) . '" title="Clone this as ' . $post_status . '" rel="permalink">' . $link_title . '</a>';
171 }
172
173 return $actions;
174 }
175
176 /*Add link to edit Post*/
177 public function dpp_wpp_page_custom_button() {
178 $opt = get_option( 'dpp_wpp_page_options' );
179 $link_title = ! empty( $opt['dpp_post_link_title'] ) ? esc_attr( $opt['dpp_post_link_title'] ) : 'Duplicate';
180 global $post;
181 $opt = get_option( 'dpp_wpp_page_options' );
182 $post_status = ! empty( $opt['duplicate_post_status'] ) ? esc_attr( $opt['duplicate_post_status'] ) : 'draft';
183
184 $html = '<div id="major-publishing-actions">';
185 $html .= '<div id="export-action">';
186 $html .= '<a href="admin.php?action=dt_dpp_post_as_draft&amp;post=' . $post->ID . '&amp;nonce=' . wp_create_nonce( 'dt-duplicate-page-' . $post->ID ) . '" title="Duplicate this as ' . $post_status . '" rel="permalink">' . $link_title . '</a>';
187 $html .= '</div>';
188 $html .= '</div>';
189 echo $html;
190 }
191
192 /*
193 * Add the duplicate link to edit screen - gutenberg
194 */
195 public function dpp_wpp_button_guten() {
196 global $post;
197 if ( $post ) {
198 $opt = get_option( 'dpp_wpp_page_options' );
199 $post_status = ! empty( $opt['dpp_post_status'] ) ? esc_attr( $opt['dpp_post_status'] ) : 'draft';
200 if ( isset( $opt['dpp_posteditor'] ) && $opt['dpp_posteditor'] == 'gutenberg' ) {
201 ?>
202 <style> .link_gutenberg {
203 text-align: center;
204 margin-top: 15px;
205 }
206
207 .link_gutenberg a {
208 text-decoration: none;
209 display: block;
210 height: 40px;
211 line-height: 28px;
212 padding: 3px 12px 2px;
213 background: #0073AA;
214 border-radius: 3px;
215 border-width: 1px;
216 border-style: solid;
217 color: #ffffff;
218 font-size: 16px;
219 }
220
221 .link_gutenberg a:hover {
222 background: #23282D;
223 border-color: #23282D;
224 }</style>
225 <script>jQuery(window).load(function (e) {
226 var dpp_postid = "<?php echo esc_attr( $post->ID ); ?>";
227 var dtnonce = "<?php echo wp_create_nonce( 'dt-duplicate-page-' . $post->ID );?>";
228 var dpp_posttitle = "Duplicate this as <?php echo esc_attr( $post_status ); ?>";
229 var dpp_duplicatelink = '<div class="link_gutenberg">';
230 dpp_duplicatelink += '<a href="admin.php?action=dt_dpp_post_as_draft&amp;post=' + dpp_postid + '&amp;nonce=' + dtnonce + '" title="' + dpp_posttitle + '">Duplicate</a>';
231 dpp_duplicatelink += '</div>';
232 jQuery('.edit-post-post-status').append(dpp_duplicatelink);
233 });</script>
234 <?php
235 }
236 }
237 }
238
239
240 /*Click here to clone Admin Bar*/
241 public function dpp_wpp_page_admin_bar_link() {
242 global $wp_admin_bar;
243 global $post;
244 $opt = get_option( 'dpp_wpp_page_options' );
245 $post_status = ! empty( $opt['dpp_post_status'] ) ? esc_attr( $opt['dpp_post_status'] ) : 'draft';
246 $current_object = get_queried_object();
247 if ( empty( $current_object ) ) {
248 return;
249 }
250 if ( ! empty( $current_object->post_type ) && ( $post_type_object = get_post_type_object( $current_object->post_type ) ) && ( $post_type_object->show_ui || $current_object->post_type == 'attachment' ) ) {
251 $wp_admin_bar->add_menu( array(
252 'parent' => 'edit',
253 'id' => 'dpp_this',
254 'title' => __( "Clone this as " . $post_status . "", 'duplicate-wp-page-post' ),
255 'href' => admin_url() . 'admin.php?action=dt_dpp_post_as_draft&amp;post=' . $post->ID . '&amp;nonce=' . wp_create_nonce( 'dt-duplicate-page-' . $post->ID )
256 ) );
257 }
258 }
259
260
261 /*WP Url Redirect*/
262 static function dp_redirect( $url ) {
263 $result = '<script>window.location.href="' . $url . '"</script>';
264 print( $result );
265 }
266
267 /*plugin settings page link*/
268 function dpp_settings_link( $links, $file ) {
269 if ( $file == DPP_BASE_NAME ) {
270
271 $links[] = '<a href="' .
272 admin_url( 'options-general.php?page=dpp_page_settings' ) .
273 '">' . __( 'Settings' ) . '</a>';
274 }
275
276 return $links;
277
278 }
279
280 }
281
282 new dpp_wpp_page();
283
284 endif;
285 ?>
...\ 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 /**
4 * Class WPML_Media
5 */
6 class WPML_Media implements IWPML_Action {
7 const SETUP_RUN = 'setup_run';
8 const SETUP_STARTED = 'setup_started';
9
10 private static $settings;
11 private static $settings_option_key = '_wpml_media';
12 private static $default_settings = array(
13 'version' => false,
14 'media_files_localization' => array(
15 'posts' => true,
16 'custom_fields' => true,
17 'strings' => true,
18 ),
19 'wpml_media_2_3_migration' => true,
20 self::SETUP_RUN => false,
21 );
22
23 public $languages;
24 public $parents;
25 public $unattached;
26 /**
27 * @var wpdb
28 */
29 private $wpdb;
30
31 /**
32 * @var SitePress
33 */
34 private $sitepress;
35
36 /**
37 * @var WPML_Media_Menus_Factory
38 */
39 private $menus_factory;
40
41 /**
42 * WPML_Media constructor.
43 *
44 * @param SitePress $sitepress
45 * @param wpdb $wpdb
46 * @param WPML_Media_Menus_Factory $menus_factory
47 */
48 public function __construct( SitePress $sitepress, wpdb $wpdb, WPML_Media_Menus_Factory $menus_factory ) {
49 $this->sitepress = $sitepress;
50 $this->wpdb = $wpdb;
51 $this->menus_factory = $menus_factory;
52 }
53
54 public function add_hooks() {
55 add_action( 'wpml_loaded', array( $this, 'loaded' ), 2 );
56 }
57
58 public static function has_settings() {
59 return get_option( self::$settings_option_key );
60 }
61
62 public function loaded() {
63 global $sitepress;
64 if ( ! isset( $sitepress ) || ! $sitepress->get_setting( 'setup_complete' ) ) {
65 return null;
66 }
67
68 $this->plugin_localization();
69
70 if ( is_admin() ) {
71 WPML_Media_Upgrade::run();
72 }
73
74 self::init_settings();
75
76 global $sitepress_settings, $pagenow;
77
78 $active_languages = $sitepress->get_active_languages();
79
80 $this->languages = null;
81
82 if ( $this->is_admin_or_xmlrpc() && ! $this->is_uploading_plugin_or_theme() ) {
83
84 add_action( 'wpml_admin_menu_configure', array( $this, 'menu' ) );
85
86 if ( 1 < count( $active_languages ) ) {
87
88 if ( $pagenow == 'media-upload.php' ) {
89 add_action( 'pre_get_posts', array( $this, 'filter_media_upload_items' ), 10, 1 );
90 }
91
92 if ( $pagenow == 'media.php' ) {
93 add_action( 'admin_footer', array( $this, 'media_language_options' ) );
94 }
95
96 add_action( 'wp_ajax_wpml_media_scan_prepare', array( $this, 'batch_scan_prepare' ) );
97
98 add_action( 'wp_ajax_find_posts', array( $this, 'find_posts_filter' ), 0 );
99 }
100 } else {
101 if ( WPML_LANGUAGE_NEGOTIATION_TYPE_DOMAIN === (int) $sitepress_settings['language_negotiation_type'] ) {
102 // Translate media url when in front-end and only when using custom domain
103 add_filter( 'wp_get_attachment_url', array( $this, 'wp_get_attachment_url' ), 10, 2 );
104 }
105 }
106
107 add_filter( 'WPML_filter_link', array( $this, 'filter_link' ), 10, 2 );
108 add_filter( 'icl_ls_languages', array( $this, 'icl_ls_languages' ), 10, 1 );
109
110 return null;
111 }
112
113 function is_admin_or_xmlrpc() {
114 $is_admin = is_admin();
115 $is_xmlrpc = ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST );
116
117 return $is_admin || $is_xmlrpc;
118 }
119
120 function is_uploading_plugin_or_theme() {
121 global $action;
122
123 return ( isset( $action ) && ( $action == 'upload-plugin' || $action == 'upload-theme' ) );
124 }
125
126 function plugin_localization() {
127 load_plugin_textdomain( 'wpml-media', false, WPML_MEDIA_FOLDER . '/locale' );
128 }
129
130 /**
131 * Needed by class init and by all static methods that use self::$settings
132 */
133 public static function init_settings() {
134 if ( ! self::$settings ) {
135 self::$settings = get_option( self::$settings_option_key, array() );
136 }
137
138 self::$settings = array_merge( self::$default_settings, self::$settings );
139 }
140
141 public static function has_setup_run() {
142 return self::get_setting( self::SETUP_RUN );
143 }
144
145 public static function set_setup_run( $value = 1 ) {
146 return self::update_setting( self::SETUP_RUN, $value );
147 }
148
149 public static function has_setup_started() {
150 return self::get_setting( self::SETUP_STARTED );
151 }
152
153 public static function set_setup_started( $value = 1 ) {
154 return self::update_setting( self::SETUP_STARTED, $value );
155 }
156
157 public static function get_setting( $name, $default = false ) {
158 self::init_settings();
159 if ( ! isset( self::$settings[ $name ] ) || ! self::$settings[ $name ] ) {
160 return $default;
161 }
162
163 return self::$settings[ $name ];
164 }
165
166 public static function update_setting( $name, $value ) {
167 self::init_settings();
168 self::$settings[ $name ] = $value;
169
170 return update_option( self::$settings_option_key, self::$settings );
171 }
172
173 function batch_scan_prepare() {
174 global $wpdb;
175
176 $response = array();
177 $wpdb->delete( $wpdb->postmeta, array( 'meta_key' => 'wpml_media_processed' ) );
178
179 $response['message'] = __( 'Started...', 'wpml-media' );
180
181 echo wp_json_encode( $response );
182 exit;
183 }
184
185 static function is_valid_post_type( $post_type ) {
186 global $wp_post_types;
187
188 $post_types = array_keys( (array) $wp_post_types );
189
190 return in_array( $post_type, $post_types );
191 }
192
193 function find_posts_filter() {
194 add_action( 'pre_get_posts', array( $this, 'pre_get_posts' ) );
195 }
196
197 function pre_get_posts( $query ) {
198 $query->query['suppress_filters'] = 0;
199 $query->query_vars['suppress_filters'] = 0;
200 }
201
202 function media_language_options() {
203 global $sitepress;
204 $att_id = filter_input( INPUT_GET, 'attachment_id', FILTER_SANITIZE_NUMBER_INT, FILTER_NULL_ON_FAILURE );
205 $translations = $sitepress->get_element_translations( $att_id, 'post_attachment' );
206 $current_lang = '';
207 foreach ( $translations as $lang => $id ) {
208 if ( $id == $att_id ) {
209 $current_lang = $lang;
210 unset( $translations[ $lang ] );
211 break;
212 }
213 }
214
215 $active_languages = icl_get_languages( 'orderby=id&order=asc&skip_missing=0' );
216 $lang_links = '';
217
218 if ( $current_lang ) {
219
220 $lang_links = '<strong>' . $active_languages[ $current_lang ]['native_name'] . '</strong>';
221
222 }
223
224 foreach ( $translations as $lang => $id ) {
225 $lang_links .= ' | <a href="' . admin_url( 'media.php?attachment_id=' . $id . '&action=edit' ) . '">' . $active_languages[ $lang ]['native_name'] . '</a>';
226 }
227
228 echo '<div id="icl_lang_options" style="display:none">' . $lang_links . '</div>';
229 }
230
231 /**
232 * Synchronizes _wpml_media_* meta fields with all translations
233 *
234 * @param int $meta_id
235 * @param int $object_id
236 * @param string $meta_key
237 * @param string|mixed $meta_value
238 */
239 function updated_postmeta( $meta_id, $object_id, $meta_key, $meta_value ) {
240 if ( in_array( $meta_key, array( '_wpml_media_duplicate', '_wpml_media_featured' ) ) ) {
241 global $sitepress;
242 $el_type = 'post_' . get_post_type( $object_id );
243 $trid = $sitepress->get_element_trid( $object_id, $el_type );
244 $translations = $sitepress->get_element_translations( $trid, $el_type, true, true );
245 foreach ( $translations as $translation ) {
246 if ( $translation->element_id != $object_id ) {
247 $t_meta_value = get_post_meta( $translation->element_id, $meta_key, true );
248 if ( $t_meta_value != $meta_value ) {
249 update_post_meta( $translation->element_id, $meta_key, $meta_value );
250 }
251 }
252 }
253 }
254 }
255
256 /**
257 * Add a filter to fix the links for attachments in the language switcher so
258 * they point to the corresponding pages in different languages.
259 */
260 function filter_link( $url, $lang_info ) {
261 return $url;
262 }
263
264 function wp_get_attachment_url( $url, $post_id ) {
265 global $sitepress;
266
267 return $sitepress->convert_url( $url );
268 }
269
270 function icl_ls_languages( $w_active_languages ) {
271 static $doing_it = false;
272
273 if ( is_attachment() && ! $doing_it ) {
274 $doing_it = true;
275 // Always include missing languages.
276 $w_active_languages = icl_get_languages( 'skip_missing=0' );
277 $doing_it = false;
278 }
279
280 return $w_active_languages;
281 }
282
283 function get_post_metadata( $value, $object_id, $meta_key, $single ) {
284 if ( $meta_key == '_thumbnail_id' ) {
285
286 global $wpdb;
287
288 $thumbnail_prepared = $wpdb->prepare(
289 "SELECT meta_value FROM {$wpdb->postmeta} WHERE post_id = %d AND meta_key = %s",
290 array(
291 $object_id,
292 $meta_key,
293 )
294 );
295 $thumbnail = $wpdb->get_var( $thumbnail_prepared );
296
297 if ( $thumbnail == null ) {
298 // see if it's available in the original language.
299
300 $post_type_prepared = $wpdb->prepare( "SELECT post_type FROM {$wpdb->posts} WHERE ID = %d", array( $object_id ) );
301 $post_type = $wpdb->get_var( $post_type_prepared );
302 $trid_prepared = $wpdb->prepare(
303 "SELECT trid, source_language_code FROM {$wpdb->prefix}icl_translations WHERE element_id=%d AND element_type = %s",
304 array(
305 $object_id,
306 'post_' . $post_type,
307 )
308 );
309 $trid = $wpdb->get_row( $trid_prepared );
310 if ( $trid ) {
311
312 global $sitepress;
313
314 $translations = $sitepress->get_element_translations( $trid->trid, 'post_' . $post_type );
315 if ( isset( $translations[ $trid->source_language_code ] ) ) {
316 $translation = $translations[ $trid->source_language_code ];
317 // see if the original has a thumbnail.
318 $thumbnail_prepared = $wpdb->prepare(
319 "SELECT meta_value FROM {$wpdb->postmeta} WHERE post_id = %d AND meta_key = %s",
320 array(
321 $translation->element_id,
322 $meta_key,
323 )
324 );
325 $thumbnail = $wpdb->get_var( $thumbnail_prepared );
326 if ( $thumbnail ) {
327 $value = $thumbnail;
328 }
329 }
330 }
331 } else {
332 $value = $thumbnail;
333 }
334 }
335
336 return $value;
337 }
338
339 /**
340 * @param string $menu_id
341 */
342 public function menu( $menu_id ) {
343 if ( 'WPML' !== $menu_id ) {
344 return;
345 }
346
347 $menu_label = __( 'Media Translation', 'wpml-media' );
348 $menu = array();
349 $menu['order'] = 600;
350 $menu['page_title'] = $menu_label;
351 $menu['menu_title'] = $menu_label;
352 $menu['capability'] = 'edit_others_posts';
353 $menu['menu_slug'] = 'wpml-media';
354 $menu['function'] = array( $this, 'menu_content' );
355
356 do_action( 'wpml_admin_menu_register_item', $menu );
357 }
358
359 public function menu_content() {
360 $menus = $this->menus_factory->create();
361 $menus->display();
362 }
363
364 /**
365 * @param $ids
366 * @param $target_language
367 *
368 * @return array|string
369 */
370 public function translate_attachment_ids( $ids, $target_language ) {
371 global $sitepress;
372 $return_string = false;
373 if ( ! is_array( $ids ) ) {
374 $attachment_ids = explode( ',', $ids );
375 $return_string = true;
376 }
377
378 $translated_ids = array();
379 if ( ! empty( $attachment_ids ) ) {
380 foreach ( $attachment_ids as $attachment_id ) {
381 // Fallback to the original ID
382 $translated_id = $attachment_id;
383
384 // Find the ID translation
385 $trid = $sitepress->get_element_trid( $attachment_id, 'post_attachment' );
386 if ( $trid ) {
387 $id_translations = $sitepress->get_element_translations( $trid, 'post_attachment', false, true );
388 foreach ( $id_translations as $language_code => $id_translation ) {
389 if ( $language_code == $target_language ) {
390 $translated_id = $id_translation->element_id;
391 break;
392 }
393 }
394 }
395
396 $translated_ids[] = $translated_id;
397 }
398 }
399
400 if ( $return_string ) {
401 return implode( ',', $translated_ids );
402 }
403
404 return $translated_ids;
405
406 }
407
408 /**
409 * Update query for media-upload.php page.
410 *
411 * @param object $query WP_Query
412 */
413 public function filter_media_upload_items( $query ) {
414 $current_lang = $this->sitepress->get_current_language();
415 $ids = icl_cache_get( '_media_upload_attachments' . $current_lang );
416
417 if ( false === $ids ) {
418 $tbl = $this->wpdb->prefix . 'icl_translations';
419 $db_query = "
420 SELECT posts.ID
421 FROM {$this->wpdb->posts} as posts, $tbl as icl_translations
422 WHERE posts.post_type = 'attachment'
423 AND icl_translations.element_id = posts.ID
424 AND icl_translations.language_code = %s
425 ";
426
427 $posts = $this->wpdb->get_results( $this->wpdb->prepare( $db_query, $current_lang ) );
428 $ids = array();
429 if ( ! empty( $posts ) ) {
430 foreach ( $posts as $post ) {
431 $ids[] = absint( $post->ID );
432 }
433 }
434
435 icl_cache_set( '_media_upload_attachments' . $current_lang, $ids );
436 }
437
438 $query->set( 'post__in', $ids );
439 }
440
441 }
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 /**
4 * Class WPML_Media_Post_Images_Translation
5 * Translate images in posts translations when a post is created or updated
6 */
7 class WPML_Media_Post_Images_Translation implements IWPML_Action {
8
9 const ALT_PLACEHOLDER = '{%ALT_TEXT%}';
10 const CAPTION_PLACEHOLDER = '{%CAPTION%}';
11
12 /**
13 * @var WPML_Media_Translated_Images_Update
14 */
15 private $images_updater;
16
17 /**
18 * @var SitePress
19 */
20 private $sitepress;
21
22 /**
23 * @var wpdb
24 */
25 private $wpdb;
26 /**
27 * @var WPML_Translation_Element_Factory
28 */
29 private $translation_element_factory;
30 /**
31 * @var WPML_Media_Custom_Field_Images_Translation_Factory
32 */
33 private $custom_field_images_translation_factory;
34 /**
35 * @var WPML_Media_Usage_Factory
36 */
37 private $media_usage_factory;
38
39 private $captions_map = array();
40
41 private $translate_locks = array();
42
43 public function __construct(
44 WPML_Media_Translated_Images_Update $images_updater,
45 SitePress $sitepress,
46 wpdb $wpdb,
47 WPML_Translation_Element_Factory $translation_element_factory,
48 WPML_Media_Custom_Field_Images_Translation_Factory $custom_field_images_translation_factory,
49 WPML_Media_Usage_Factory $media_usage_factory
50 ) {
51 $this->images_updater = $images_updater;
52 $this->sitepress = $sitepress;
53 $this->wpdb = $wpdb;
54 $this->translation_element_factory = $translation_element_factory;
55 $this->custom_field_images_translation_factory = $custom_field_images_translation_factory;
56 $this->media_usage_factory = $media_usage_factory;
57 }
58
59 public function add_hooks() {
60 add_action( 'save_post', array( $this, 'translate_images' ), PHP_INT_MAX, 1 );
61 add_filter( 'wpml_pre_save_pro_translation', array( $this, 'translate_images_in_content' ), PHP_INT_MAX, 2 );
62 add_filter( 'wpml_pre_save_pro_translation', array(
63 $this,
64 'replace_placeholders_and_id_in_caption_shortcode'
65 ), PHP_INT_MAX, 2 );
66 add_action( 'wpml_tm_job_fields',
67 array( $this, 'replace_caption_placeholders_in_fields' ), 10, 2 );
68 add_filter( 'wpml_tm_job_data_post_content', array(
69 $this,
70 'restore_placeholders_in_translated_job_body'
71 ), 10, 1 );
72
73 add_action( 'icl_make_duplicate', array( $this, 'translate_images_in_duplicate' ), PHP_INT_MAX, 4 );
74
75 add_action( 'wpml_added_media_file_translation', array( $this, 'translate_url_in_post' ), PHP_INT_MAX, 1 );
76 add_action( 'wpml_pro_translation_completed', array( $this, 'translate_images' ), PHP_INT_MAX, 1 );
77 add_action( 'wpml_restored_media_file_translation', array( $this, 'translate_url_in_post' ), PHP_INT_MAX, 2 );
78 }
79
80 /**
81 * @param int $post_id
82 */
83 public function translate_images( $post_id ) {
84 if ( isset( $this->translate_locks[ $post_id ] ) ) {
85 return;
86 }
87
88 $this->translate_locks[ $post_id ] = true;
89
90 $post = get_post( $post_id );
91
92 if ( ! $post ) {
93 return;
94 }
95
96 /** @var WPML_Post_Element $post_element */
97 $post_element = $this->translation_element_factory->create( $post_id, 'post' );
98 $source_language = $post_element->get_source_language_code();
99
100 if ( null !== $source_language ) {
101
102 $this->translate_images_in_post_content( $post, $post_element );
103 $this->translate_images_in_custom_fields( $post_id );
104
105 } else { // is original
106 foreach ( array_keys( $this->sitepress->get_active_languages() ) as $target_language ) {
107 /** @var WPML_Post_Element $translation */
108 $translation = $post_element->get_translation( $target_language );
109 if ( null !== $translation && $post_id !== $translation->get_id() ) {
110 $translatedPost = get_post( $translation->get_id() );
111
112 if ( $translatedPost ) {
113 $this->translate_images_in_post_content( $translatedPost, $translation );
114 $this->translate_images_in_custom_fields( $translation->get_id() );
115 }
116 }
117 }
118 }
119
120 unset( $this->translate_locks[ $post_id ] );
121 }
122
123 /**
124 * @param int $master_post_id
125 * @param string $language
126 * @param array $post_array
127 * @param int $post_id
128 */
129 public function translate_images_in_duplicate( $master_post_id, $language, $post_array, $post_id ) {
130 $this->translate_images( $post_id );
131 }
132
133 /**
134 * @param WP_Post $post
135 * @param WPML_Post_Element $post_element
136 */
137 private function translate_images_in_post_content( WP_Post $post, WPML_Post_Element $post_element ) {
138
139 if ( (bool) apply_filters( 'wpml_pb_should_body_be_translated', true, $post, 'translate_images_in_post_content' ) ) {
140 $post_content_filtered = $this->images_updater->replace_images_with_translations(
141 $post->post_content,
142 $post_element->get_language_code(),
143 $post_element->get_source_language_code()
144 );
145
146 if ( $post_content_filtered !== $post->post_content ) {
147 $this->wpdb->update(
148 $this->wpdb->posts,
149 array( 'post_content' => $post_content_filtered ),
150 array( 'ID' => $post->ID ),
151 array( '%s' ),
152 array( '%d' )
153 );
154 }
155 } elseif ( $this->is_updated_from_media_translation_menu() ) {
156 do_action( 'wpml_pb_resave_post_translation', $post_element );
157 }
158 }
159
160 private function is_updated_from_media_translation_menu() {
161 $allowed_actions = array(
162 'wpml_media_save_translation',
163 'wpml_media_translate_media_url_in_posts',
164 );
165
166 return isset( $_POST['action'] ) && in_array( $_POST['action'], $allowed_actions, true );
167 }
168
169 /**
170 * @param int $post_id
171 */
172 private function translate_images_in_custom_fields( $post_id ) {
173
174 $custom_fields_image_translation = $this->custom_field_images_translation_factory->create();
175 if ( $custom_fields_image_translation ) {
176 $post_meta = get_metadata( 'post', $post_id );
177 foreach ( $post_meta as $meta_key => $meta_value ) {
178 $custom_fields_image_translation->translate_images( null, $post_id, $meta_key, $meta_value[0] );
179 }
180 }
181
182 }
183
184 /**
185 * @param array $postarr
186 * @param stdClass $job
187 *
188 * @return array
189 */
190 public function translate_images_in_content( array $postarr, stdclass $job ) {
191 $postarr['post_content'] = $this->images_updater->replace_images_with_translations(
192 $postarr['post_content'],
193 $job->language_code,
194 $job->source_language_code
195 );
196
197 return $postarr;
198 }
199
200 public function translate_url_in_post( $attachment_id, $posts = array() ) {
201 if ( ! $posts ) {
202 $media_usage = $this->media_usage_factory->create( $attachment_id );
203 $posts = $media_usage->get_posts();
204 }
205
206 foreach ( $posts as $post_id ) {
207 $this->translate_images( $post_id );
208 }
209 }
210
211 public function replace_placeholders_and_id_in_caption_shortcode( array $postarr, stdClass $job ) {
212
213 $media = $this->find_media_in_job( $job );
214
215 $postarr['post_content'] = $this->replace_caption_placeholders_in_string(
216 $postarr['post_content'],
217 $media,
218 $job->language_code
219 );
220
221 return $postarr;
222 }
223
224 public function replace_caption_placeholders_in_string( $text, $media, $language ) {
225
226 $caption_parser = new WPML_Media_Caption_Tags_Parse();
227 $captions = $caption_parser->get_captions( $text );
228
229 foreach ( $captions as $caption ) {
230 $attachment_id = $caption->get_id();
231 $caption_shortcode = $new_caption_shortcode = $caption->get_shortcode_string();
232
233 if ( isset( $media[ $attachment_id ] ) ) {
234
235 if ( isset( $media[ $attachment_id ]['caption'] ) ) {
236 $new_caption_shortcode = $this->replace_placeholder_with_caption( $new_caption_shortcode, $caption, $media[ $attachment_id ]['caption'] );
237 }
238
239 if ( isset( $media[ $attachment_id ]['alt'] ) ) {
240 $new_caption_shortcode = $this->replace_placeholder_with_alt_text( $new_caption_shortcode, $caption, $media[ $attachment_id ]['alt'] );
241 }
242
243 }
244
245 if ( $attachment_id ) {
246 $new_caption_shortcode = $this->replace_caption_id_with_translated_id(
247 $new_caption_shortcode,
248 $attachment_id,
249 $language
250 );
251 }
252
253 if ( $new_caption_shortcode !== $caption_shortcode ) {
254 $text = str_replace( $caption_shortcode, $new_caption_shortcode, $text );
255 $this->captions_map[ $caption_shortcode ] = $new_caption_shortcode;
256 }
257
258 }
259
260 return $text;
261 }
262
263 /**
264 * @param int $new_post_id
265 * @param array $fields
266 * @param stdClass $job
267 */
268 public function replace_caption_placeholders_in_fields( array $fields, stdClass $job ) {
269
270 $media = $this->find_media_in_job( $job );
271
272 foreach ( $fields as $field_id => $field ) {
273 $fields[ $field_id ]['data'] = $this->replace_caption_placeholders_in_string(
274 $field['data'],
275 $media,
276 $job->language_code
277 );
278 }
279
280 return $fields;
281 }
282
283 private function replace_placeholder_with_caption( $caption_shortcode, WPML_Media_Caption $caption, $new_caption ) {
284 $caption_content = $caption->get_content();
285 $new_caption_content = str_replace( self::CAPTION_PLACEHOLDER, $new_caption, $caption_content );
286
287 return str_replace( $caption_content, $new_caption_content, $caption_shortcode );
288 }
289
290 private function replace_placeholder_with_alt_text( $caption_shortcode, WPML_Media_Caption $caption, $new_alt_text ) {
291 return str_replace( 'alt="' . self::ALT_PLACEHOLDER . '"', 'alt="' . $new_alt_text . '"', $caption_shortcode );
292 }
293
294 private function replace_caption_id_with_translated_id( $caption_shortcode, $attachment_id, $language ) {
295 $post_element = $this->translation_element_factory->create( $attachment_id, 'post' );
296 $translation = $post_element->get_translation( $language );
297 if ( $translation ) {
298
299 $translated_id = $translation->get_id();
300
301 $caption_shortcode = str_replace(
302 'id="attachment_' . $attachment_id . '"',
303 'id="attachment_' . $translated_id . '"',
304 $caption_shortcode
305 );
306 }
307
308 return $caption_shortcode;
309 }
310 private function find_media_in_job( stdClass $job ) {
311 $media = array();
312
313 foreach ( $job->elements as $element ) {
314 $field_type = explode( '_', $element->field_type );
315 if ( 'media' === $field_type[0] ) {
316 if ( ! isset( $media[ $field_type[1] ] ) ) {
317 $media[ $field_type[1] ] = array();
318 }
319 $media[ $field_type[1] ][ $field_type[2] ] = base64_decode( $element->field_data_translated );
320 }
321 }
322
323 return $media;
324 }
325
326 public function restore_placeholders_in_translated_job_body( $new_body ) {
327 /**
328 * Translation management is updating the translated job data with the post_content
329 * from the translated post.
330 * We want the translated job data to contain the placeholders so we need to
331 * find the captions we replaced and restore it with the version with the placeholders
332 */
333
334 foreach ( $this->captions_map as $caption_with_placeholder => $caption_in_post ) {
335 $new_body = str_replace( $caption_in_post, $caption_with_placeholder, $new_body );
336 }
337 $this->captions_map = array();
338
339 return $new_body;
340 }
341
342 }
...\ No newline at end of file ...\ No newline at end of file
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_Save_Translation implements IWPML_Action {
4
5 /**
6 * @var SitePress
7 */
8 private $sitepress;
9 /**
10 * @var wpdb
11 */
12 private $wpdb;
13 /**
14 * @var WPML_Media_File_Factory
15 */
16 private $media_file_factory;
17 /**
18 * @var array
19 */
20 private $post_data;
21 /**
22 * @var WPML_Translation_Element_Factory
23 */
24 private $translation_element_factory;
25
26
27 /**
28 * WPML_Media_Save_Translation constructor.
29 *
30 * @param SitePress $sitepress
31 * @param wpdb $wpdb
32 * @param WPML_Media_File_Factory $media_file_factory
33 * @param WPML_Translation_Element_Factory $translation_element_factory
34 */
35 public function __construct( SitePress $sitepress, wpdb $wpdb, WPML_Media_File_Factory $media_file_factory, WPML_Translation_Element_Factory $translation_element_factory ) {
36 $this->sitepress = $sitepress;
37 $this->wpdb = $wpdb;
38 $this->media_file_factory = $media_file_factory;
39 $this->translation_element_factory = $translation_element_factory;
40 }
41
42 public function add_hooks() {
43 add_action( 'wp_ajax_wpml_media_save_translation', array( $this, 'save_media_translation' ) );
44 }
45
46 public function save_media_translation() {
47
48 if ( wp_verify_nonce( $_POST['wpnonce'], 'media-translation' ) ) {
49 $post_array['ID'] = (int) $_POST['translated-attachment-id'];
50 $original_attachment_id = (int) $_POST['original-attachment-id'];
51 $translated_language = sanitize_text_field( $_POST['translated-language'] );
52
53 if ( isset( $_POST['translation']['title'] ) ) {
54 $post_array['post_title'] = sanitize_text_field( $_POST['translation']['title'] );
55 }
56 if ( isset( $_POST['translation']['caption'] ) ) {
57 $post_array['post_excerpt'] = sanitize_text_field( $_POST['translation']['caption'] );
58 }
59 if ( isset( $_POST['translation']['description'] ) ) {
60 $post_array['post_content'] = sanitize_text_field( $_POST['translation']['description'] );
61 }
62
63 if ( $post_array['ID'] ) {
64 $attachment_id = wp_update_post( $post_array );
65
66 if ( $this->should_restore_media() ) {
67 $this->restore_media_file( $attachment_id, $original_attachment_id, $translated_language );
68 }
69 } else {
70
71 $post_array['post_type'] = 'attachment';
72 $post_array['post_status'] = 'inherit';
73 $post_array['guid'] = get_post_field( 'guid', $original_attachment_id );
74 $post_array['post_mime_type'] = get_post_field( 'post_mime_type', $original_attachment_id );
75
76 $attachment_id = $this->create_attachment_translation( $original_attachment_id, $post_array );
77
78 $this->sitepress->set_element_language_details(
79 $attachment_id,
80 'post_attachment',
81 $this->get_post_trid_value(),
82 $this->get_post_lang_value()
83 );
84
85 if ( ! $this->has_media_upload() ) {
86 $this->copy_attached_file_info_from_original( $attachment_id, $original_attachment_id );
87 }
88
89 $this->mark_media_as_not_translated( $attachment_id, $translated_language );
90
91 }
92 $translation_status = WPML_Media_Translation_Status::NEEDS_MEDIA_TRANSLATION;
93
94 if ( $this->has_media_upload() ) {
95 $this->update_media_file( $attachment_id, $original_attachment_id, $translated_language );
96 $translation_status = WPML_Media_Translation_Status::TRANSLATED;
97 }
98
99 if ( isset( $_POST['translation']['alt-text'] ) ) {
100 update_post_meta(
101 $attachment_id,
102 '_wp_attachment_image_alt',
103 sanitize_text_field( $_POST['translation']['alt-text'] )
104 );
105 }
106
107 if ( 0 === strpos( get_post_field( 'post_mime_type', $original_attachment_id ), 'image/' ) ) {
108 $translated_thumb = wp_get_attachment_thumb_url( $attachment_id );
109 $original_thumb = wp_get_attachment_thumb_url( $original_attachment_id );
110 $media_file_is_translated = $translated_thumb !== $original_thumb;
111 } else {
112 $media_file_is_translated = get_attached_file( $attachment_id ) !== get_attached_file( $original_attachment_id );
113 $translated_thumb = wp_mime_type_icon( $original_attachment_id );
114 }
115
116 $media_usage = get_post_meta( $original_attachment_id, WPML_Media_Usage::FIELD_NAME, true );
117 $posts_list = array();
118 if ( isset( $media_usage['posts'] ) ) {
119 foreach ( $media_usage['posts'] as $post_id ) {
120 $post_element = $this->translation_element_factory->create( $post_id, 'post' );
121 $post_translation = $post_element->get_translation( $translated_language );
122 if ( $post_translation ) {
123 $posts_list[] = array(
124 'url' => get_edit_post_link( $post_translation->get_id() ),
125 'title' => get_post_field( 'post_title', $post_translation->get_id() ),
126 );
127 }
128 }
129 }
130
131 if ( isset( $media_usage['posts'] ) && $this->should_restore_media() ) {
132 do_action( 'wpml_restored_media_file_translation', $attachment_id, $media_usage['posts'] );
133 }
134
135 $response = array(
136 'attachment_id' => $attachment_id,
137 'thumb' => $media_file_is_translated ? $translated_thumb : false,
138 'status' => $translation_status,
139 'usage' => $posts_list,
140 );
141
142 wp_send_json_success( $response );
143 } else {
144 wp_send_json_error( array( 'error' => __( 'Invalid nonce', 'wpml-media' ) ) );
145 }
146 }
147
148 private function has_media_upload() {
149 return ! empty( $_POST['update-media-file'] );
150 }
151
152 private function should_restore_media() {
153 return ! empty( $_POST['restore-media'] );
154 }
155
156 /**
157 * @param int $original_attachment_id
158 * @param array $post_array
159 *
160 * @return int
161 */
162 private function create_attachment_translation( $original_attachment_id, $post_array ) {
163
164 $post_element = $this->translation_element_factory->create( $original_attachment_id, 'post' );
165 $this->set_post_trid_value( $post_element->get_trid() );
166 $this->set_post_lang_value( $_POST['translated-language'] );
167
168 add_filter( 'wpml_tm_save_post_trid_value', array( $this, 'get_post_trid_value' ) );
169 add_filter( 'wpml_tm_save_post_lang_value', array( $this, 'get_post_lang_value' ) );
170
171 $attachment_id = wp_insert_post( $post_array );
172
173 remove_filter( 'wpml_tm_save_post_trid_value', array( $this, 'get_post_trid_value' ) );
174 remove_filter( 'wpml_tm_save_post_lang_value', array( $this, 'get_post_lang_value' ) );
175
176 return $attachment_id;
177 }
178
179 private function set_post_trid_value( $value ) {
180 $this->post_data['post_icl_trid'] = $value;
181 }
182
183 public function get_post_trid_value() {
184 return $this->post_data['post_icl_trid'];
185 }
186
187 private function set_post_lang_value( $value ) {
188 $this->post_data['post_icl_language'] = $value;
189 }
190
191 public function get_post_lang_value() {
192 return $this->post_data['post_icl_language'];
193 }
194
195 private function restore_media_file( $attachment_id, $original_attachment_id, $language ) {
196
197 $media_file = $this->media_file_factory->create( $attachment_id );
198 $media_file->delete();
199
200 $this->copy_attached_file_info_from_original( $attachment_id, $original_attachment_id );
201 $this->mark_media_as_not_translated( $original_attachment_id, $language );
202 }
203
204 private function copy_attached_file_info_from_original( $attachment_id, $original_attachment_id ) {
205 $meta_keys = array(
206 '_wp_attachment_metadata',
207 '_wp_attached_file',
208 '_wp_attachment_backup_sizes',
209 );
210 foreach ( $meta_keys as $meta_key ) {
211 update_post_meta(
212 $attachment_id,
213 $meta_key,
214 get_post_meta( $original_attachment_id, $meta_key, true )
215 );
216 }
217
218 /**
219 * Fires after attachment post meta is copied
220 *
221 * @since 4.1.0
222 *
223 * @param int $original_attachment_id The ID of the source/original attachment.
224 * @param int $attachment_id The ID of the duplicated attachment.
225 */
226 do_action( 'wpml_after_copy_attached_file_postmeta', $original_attachment_id, $attachment_id );
227 }
228
229 private function mark_media_as_not_translated( $attachment_id, $language ) {
230 update_post_meta( $attachment_id, WPML_Media_Translation_Status::STATUS_PREFIX . $language, WPML_Media_Translation_Status::NEEDS_MEDIA_TRANSLATION );
231 }
232
233 private function mark_media_as_translated( $attachment_id, $language ) {
234 update_post_meta( $attachment_id, WPML_Media_Translation_Status::STATUS_PREFIX . $language, WPML_Media_Translation_Status::TRANSLATED );
235 }
236
237 /**
238 * @param array $file
239 *
240 * @return array
241 */
242 private function get_attachment_post_data( $file ) {
243 $postarr = array(
244 'post_mime_type' => $file['type'],
245 'guid' => $file['url'],
246 'post_modified' => current_time( 'mysql' ),
247 'post_modified_gmt' => current_time( 'mysql', 1 ),
248 );
249
250 return $postarr;
251 }
252
253 private function update_media_file( $attachment_id, $original_attachment_id, $translated_language_code ) {
254 $transient_key = WPML_Media_Attachment_Image_Update::TRANSIENT_FILE_UPLOAD_PREFIX . $original_attachment_id . '_' . $translated_language_code;
255
256 $media_file_upload = get_transient( $transient_key );
257 if ( $media_file_upload ) {
258
259 $file = $media_file_upload['upload'];
260
261 // delete previous media file + sizes.
262 $media_file = $this->media_file_factory->create( $attachment_id );
263 $media_file->delete();
264
265 // Saving the GUID of media before updating it so it can be used in WPML_Media_Translated_Images_Update::replace_images_with_translations
266 // The reason for that is the update function in line 270 here is running before replace_images_with_translations.,
267 // So when replace_images_with_translations tries to get attachment ID of media with its GUID from DB it fails.,
268 // and this is causing media update in post fails if it's done more than one time.
269 $_POST['pre-update-translated-media-guid'] = get_post_field( 'guid', $attachment_id );
270
271 $post_data = $this->get_attachment_post_data( $file );
272 $this->wpdb->update( $this->wpdb->posts, $post_data, array( 'ID' => $attachment_id ) );
273 update_attached_file( $attachment_id, $file['file'] );
274
275 /**
276 * Fires after attached file is updated
277 *
278 * @since 4.1.0
279 *
280 * @param int $attachment_id The ID of uploaded attachment.
281 * @param array $file {
282 * Uploaded file data.
283 *
284 * @type string file Absolute path to file.
285 *
286 * @type string url File URL.
287 *
288 * @type string type File type.
289 * }
290 * @param string $translated_language_code Attachment language code.
291 */
292 do_action( 'wpml_updated_attached_file', $attachment_id, $file, $translated_language_code );
293
294 $attachment_metadata = wp_generate_attachment_metadata( $attachment_id, $file['file'] );
295
296 $current_attachment_metadata = wp_get_attachment_metadata( $attachment_id );
297
298 if ( $current_attachment_metadata && isset( $current_attachment_metadata['sizes'] ) ) {
299 $attachment_metadata['sizes'] = $current_attachment_metadata['sizes'];
300 }
301
302 wp_update_attachment_metadata( $attachment_id, $attachment_metadata );
303
304 if ( 'application/pdf' === $file['type'] ) {
305 wp_delete_file( $media_file_upload['thumb'] );
306 }
307
308 $this->mark_media_as_translated( $original_attachment_id, $translated_language_code );
309 do_action( 'wpml_added_media_file_translation', $original_attachment_id, $file, $translated_language_code );
310
311 delete_transient( $transient_key );
312 }
313 }
314
315 }
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 /**
4 * Class WPML_Media_Translated_Images_Update
5 * Translates images in a given text
6 */
7 class WPML_Media_Translated_Images_Update {
8
9 /**
10 * @var \WPML\Media\Factories\WPML_Media_Element_Parser_Factory
11 */
12 private $media_parser_factory;
13
14 /**
15 * @var \WPML_Media_Image_Translate
16 */
17 private $image_translator;
18 /**
19 * @var \WPML_Media_Sizes
20 */
21 private $media_sizes;
22
23 /**
24 * WPML_Media_Translated_Images_Update constructor.
25 *
26 * @param \WPML\Media\Factories\WPML_Media_Element_Parser_Factory $media_parser_factory
27 * @param WPML_Media_Image_Translate $image_translator
28 * @param WPML_Media_Sizes $media_sizes
29 */
30 public function __construct(
31 \WPML\Media\Factories\WPML_Media_Element_Parser_Factory $media_parser_factory,
32 WPML_Media_Image_Translate $image_translator,
33 WPML_Media_Sizes $media_sizes
34 ) {
35 $this->media_parser_factory = $media_parser_factory;
36 $this->image_translator = $image_translator;
37 $this->media_sizes = $media_sizes;
38 }
39
40 /**
41 * @param string $text
42 * @param string $target_language
43 * @param string $source_language
44 *
45 * @return string
46 */
47 public function replace_images_with_translations( $text, $target_language, $source_language = null ) {
48 $media_parsers = $this->media_parser_factory->create( $text );
49
50 // We have original and translated attachment IDs already saved in the $_POST variable.
51 // So I started using them instead of grabbing them again.
52 // phpcs:disable WordPress.CSRF.NonceVerification.NoNonceVerification
53 $attachment_id = isset( $_POST['original-attachment-id'] ) ? $_POST['original-attachment-id'] : null;
54 $translated_id = isset( $_POST['translated-attachment-id'] ) ? $_POST['translated-attachment-id'] : null;
55
56 $pre_update_translated_media_guid = isset( $_POST['pre-update-translated-media-guid'] ) ? $_POST['pre-update-translated-media-guid'] : '';
57
58 /**
59 * Checks if media src in post content (not updated yet) is equal to old media that was uploaded but now its guid replaced in database.
60 *
61 * @param $media_src
62 *
63 * @return bool
64 */
65 $media_src_same_as_pre_update = function ( $media_src ) use ( $pre_update_translated_media_guid ) {
66 return $media_src === $pre_update_translated_media_guid;
67 };
68
69 /**
70 * Checks if media src in post content (not updated yet) contains old media that was uploaded but now its guid replaced in database.
71 *
72 * @param $media_src
73 *
74 * @return bool
75 */
76 $media_src_contains_pre_update = function ( $media_src ) use ( $pre_update_translated_media_guid ) {
77 $thumb_file_name = basename( $pre_update_translated_media_guid );
78 $pre_update_translated_media_parts = explode( '.', $thumb_file_name );
79
80 $media_src_extension = pathinfo( $media_src, PATHINFO_EXTENSION );
81 $pre_update_translated_media_extension = is_array( $pre_update_translated_media_parts ) ? end( $pre_update_translated_media_parts ) : null;
82
83 return $pre_update_translated_media_parts[0] && false !== strpos( $media_src, $pre_update_translated_media_parts[0] ) && $media_src_extension === $pre_update_translated_media_extension;
84 };
85
86 if ( ! empty( $media_parsers ) ) {
87 $items_to_translate = [];
88 foreach ( $media_parsers as $media_parser ) {
89 $media_items = $media_parser->getMediaElements();
90
91 foreach ( $media_items as $media ) {
92 $media_src = $media_parser->getMediaSrcFromAttributes( $media['attributes'] );
93 $original_media_guid = isset( $attachment_id ) ? $this->getSizedOriginalMediaGuid( $attachment_id, $source_language ) : $media_src;
94
95 // This if condition checks that the value for media GUID saved in $_POST is same as media subject to get updated.
96 // OR if the media src is equal to original src (in case of translated post contains same already uploaded media) so media that exists will be replaced with the translated one.
97 if (
98 ( $media_src_same_as_pre_update( $media_src ) || $media_src_contains_pre_update( $media_src ) ) || ( $media_src === $original_media_guid )
99 ) {
100 $items_to_translate[] = [
101 'attachment_id' => $attachment_id,
102 'translated_id' => $translated_id,
103 'media' => $media,
104 'mediaParser' => $media_parser,
105 'mediaSrc' => $media_src,
106 'target_language' => $target_language,
107 ];
108 } else { // to handle reverting media to original.
109 if ( empty( $pre_update_translated_media_guid ) && $this->mediaSrcContainsMediaFileName( $translated_id, $media_src ) ) {
110 $text = $this->replace_image_src( $text, $media_src, $original_media_guid );
111 $text = $this->replace_att_class( $text, $translated_id, $attachment_id );
112 $text = $this->replace_att_in_block( $text, $translated_id, $attachment_id );
113 }
114 }
115 }
116 }
117
118 $this->image_translator->prefetchDataForFutureGetTranslatedImageCalls(
119 $this->getSourceLanguage( $source_language ),
120 array_map(
121 function( $source_item ) {
122 $item = $source_item;
123 $item['url'] = $this->getMediaParserImgSrc( $item['mediaParser'], $item['media'] );
124
125 return $item;
126 },
127 $items_to_translate
128 )
129 );
130
131 foreach ( $items_to_translate as $item_to_translate ) {
132 $attachment_id = $item_to_translate['attachment_id'];
133 $translated_id = $item_to_translate['translated_id'];
134 $media = $item_to_translate['media'];
135 $media_parser = $item_to_translate['mediaParser'];
136 $media_src = $item_to_translate['mediaSrc'];
137 $target_language = $item_to_translate['target_language'];
138
139 if ( isset( $attachment_id ) && $attachment_id ) {
140 $size = $this->media_sizes->get_attachment_size( $media );
141 $translated_src = $this->image_translator->get_translated_image( $attachment_id, $target_language, $size );
142 } else {
143 $translated_src = $this->get_translated_image_by_url( $media_parser, $target_language, $source_language, $media );
144 }
145
146 if ( $translated_src ) {
147 if ( $translated_src !== $media_src ) {
148 $text = $this->replace_image_src( $text, $media_src, $translated_src );
149 }
150
151 // to replace value in href if it couldn't be replaced in replace_image_src.
152 $text = $this->replaceAttributeInHref( $text, $media_src, $translated_src, $source_language );
153 }
154 if ( $attachment_id && $attachment_id !== $translated_id ) {
155 $text = $this->replace_att_class( $text, $attachment_id, $translated_id );
156 $text = $this->replace_att_in_block( $text, $attachment_id, $translated_id );
157 }
158 }
159 }
160
161 return $text;
162 }
163
164 private function getMediaGuid( $id ) {
165 return get_post_field( 'guid', $id );
166 }
167
168 private function mediaSrcContainsMediaFileName( $attachment_id, $media_src ) {
169 $guid = $this->getMediaGuid( $attachment_id );
170 $media_extension = substr( $guid, - 4 );
171 $media_filename = explode( $media_extension, basename( $guid ) )[0];
172
173 return \WPML\FP\Str::includes( $media_filename, $media_src );
174 }
175
176 private function getSizedOriginalMediaGuid( $attachment_id, $source_lang ) {
177 $original_media_guid = $this->getMediaGuid( $attachment_id );
178 $original_media_size = $this->media_sizes->get_image_size_from_url( $original_media_guid, $attachment_id );
179
180 return $this->image_translator->get_translated_image( $attachment_id, $source_lang, $original_media_size );
181 }
182
183 /**
184 * @param string $text
185 * @param string $from
186 * @param string $to
187 *
188 * @return string
189 */
190 private function replace_image_src( $text, $from, $to ) {
191 return str_replace( $from, $to, $text );
192 }
193
194 /**
195 * @param string $text
196 * @param string $from
197 * @param string $to
198 *
199 * @return string
200 */
201 private function replace_att_class( $text, $from, $to ) {
202 $pattern = '/\bwp-image-' . $from . '\b/u';
203 $replacement = 'wp-image-' . $to;
204
205 return preg_replace( $pattern, $replacement, $text );
206 }
207
208 /**
209 * @param string $text
210 * @param string $from
211 * @param string $to
212 *
213 * @return string
214 */
215 private function replace_att_in_block( $text, $from, $to ) {
216 $pattern = '';
217
218 $blocks = [ 'wp:image', 'wp:file', 'wp:audio', 'wp:video' ];
219 foreach ( $blocks as $block ) {
220 if ( \WPML\FP\Str::startsWith( '<!-- ' . $block, $text ) ) {
221 // phpcs:disable Generic.Strings.UnnecessaryStringConcat.Found
222 $pattern = '/<!-- ' . $block . ' ' . '{.*?"id":(' . $from . '),.*?-->/u';
223 }
224 }
225
226 $replacement = function ( $matches ) use ( $to ) {
227 return str_replace( '"id":' . $matches[1], '"id":' . $to, $matches[0] );
228 };
229
230 return (bool) strlen( $pattern ) ? preg_replace_callback( $pattern, $replacement, $text ) : $text;
231 }
232
233 /**
234 * Replaces value in href for classic images and files added in classic editor
235 *
236 * @param string $text
237 * @param string $from
238 * @param string $to
239 * @param string $source_lang
240 *
241 * @return array|string|string[]|null
242 */
243 private function replaceAttributeInHref( $text, $from, $to, $source_lang ) {
244 $pattern = '/<a.*?href="(.*?)".*?>/u';
245
246 $attach_id = $this->image_translator->get_attachment_id_by_url( $from, $source_lang );
247 $from = get_post_field( 'guid', $attach_id );
248
249 $replacement = function ( $matches ) use ( $from, $to ) {
250 return str_replace( 'href="' . $from . '"', 'href="' . $to . '"', $matches[0] );
251 };
252
253 return preg_replace_callback( $pattern, $replacement, $text );
254 }
255
256 /**
257 * @param null|string $source_language
258 *
259 * @return string
260 */
261 private function getSourceLanguage( $source_language ) {
262 if ( null === $source_language ) {
263 $source_language = wpml_get_current_language();
264 }
265
266 return $source_language;
267 }
268
269 /**
270 * @param WPML_Media_Element_Parser $media_parser
271 * @param array $img
272 *
273 * @return string
274 */
275 private function getMediaParserImgSrc( $media_parser, $img ) {
276 return $media_parser->getMediaSrcFromAttributes( $img['attributes'] );
277 }
278
279 /**
280 * @param WPML_Media_Element_Parser $media_parser
281 * @param string $target_language
282 * @param string $source_language
283 * @param array $img
284 *
285 * @return bool|string
286 */
287 private function get_translated_image_by_url( $media_parser, $target_language, $source_language, $img ) {
288 $source_language = $this->getSourceLanguage( $source_language );
289 $translated_src = $this->image_translator->get_translated_image_by_url(
290 $this->getMediaParserImgSrc( $media_parser, $img ),
291 $source_language,
292 $target_language
293 );
294
295 return $translated_src;
296 }
297
298 }
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 * Class WPML_Media_Translations_UI
5 */
6 class WPML_Media_Translations_UI extends WPML_Templates_Factory {
7 const PREVIEW_MAX_WIDTH = 800;
8 const PREVIEW_MAX_HEIGHT = 600;
9 /**
10 * @var SitePress
11 */
12 private $sitepress;
13 /**
14 * @var wpdb
15 */
16 private $wpdb;
17 /**
18 * @var WP_Locale
19 */
20 private $wp_locale;
21 /**
22 * @var WPML_Query_Filter
23 */
24 private $wpml_query_filter;
25 /**
26 * @var WPML_Admin_Pagination
27 */
28 private $pagination;
29 /**
30 * @var array
31 */
32 private $query_args = array();
33
34 /**
35 * WPML_Media_Translations_UI constructor.
36 *
37 * @param SitePress $sitepress
38 * @param wpdb $wpdb
39 * @param WP_Locale $wp_locale
40 * @param WPML_Query_Filter $wpml_query_filter
41 * @param WPML_Admin_Pagination $pagination
42 */
43 public function __construct(
44 SitePress $sitepress,
45 wpdb $wpdb,
46 WP_Locale $wp_locale,
47 WPML_Query_Filter $wpml_query_filter,
48 WPML_Admin_Pagination $pagination
49 ) {
50 parent::__construct();
51 $this->sitepress = $sitepress;
52 $this->wpdb = $wpdb;
53 $this->wp_locale = $wp_locale;
54 $this->wpml_query_filter = $wpml_query_filter;
55 $this->pagination = $pagination;
56 }
57
58 /**
59 * @return array
60 */
61 public function get_model() {
62
63 $this->set_query_args();
64
65 $languages = $this->get_languages();
66
67 $model = array(
68 'strings' => array(
69 'heading' => __( 'Media Translation', 'wpml-media' ),
70 'filter_by_date' => __( 'Filter by date', 'wpml-media' ),
71 'all_dates' => __( 'All dates', 'wpml-media' ),
72 'in' => __( 'in', 'wpml_media' ),
73 'to' => __( 'to', 'wpml_media' ),
74 'filter_by_status' => __( 'Filter by translation status', 'wpml-media' ),
75 'status_all' => __( 'All translation statuses', 'wpml-media' ),
76 'status_not' => __( 'Media file not translated', 'wpml-media' ),
77 'status_translated' => __( 'Translated media uploaded', 'wpml-media' ),
78 'status_in_progress' => __( 'Translation in progress', 'wpml-media' ),
79 'status_needs_translation' => __( 'Needs media file translation', 'wpml-media' ),
80 'filter_by_language' => __( 'Filter by language', 'wpml-media' ),
81 'any_language' => __( 'Any language', 'wpml-media' ),
82 'search_media' => __( 'Search Media:', 'wpml-media' ),
83 'search_placeholder' => __( 'Title, caption or description', 'wpml-media' ),
84 'search_button_label' => __( 'Filter', 'wpml-media' ),
85 'original_language' => __( 'Original language', 'wpml-media' ),
86 'no_attachments' => __( 'No attachments found', 'wpml-media' ),
87 'add_translation' => __( 'Add media file translation', 'wpml-media' ),
88 'edit_translation' => __( 'Edit %s translation', 'wpml-media' ),
89 'original' => __( 'Original:', 'wpml-media' ),
90 'translation' => __( 'Translation:', 'wpml-media' ),
91 'file' => __( 'File', 'wpml-media' ),
92 'name' => __( 'Name', 'wpml-media' ),
93 'caption' => __( 'Caption', 'wpml-media' ),
94 'alt_text' => __( 'Alt text', 'wpml-media' ),
95 'description' => __( 'Description', 'wpml-media' ),
96 'copy_from_original' => __( 'Copy from original', 'wpml-media' ),
97 'upload_translated_media' => __( 'Upload translated media file', 'wpml-media' ),
98 'use_different_file' => __( 'Use a different file', 'wpml-media' ),
99 'revert_to_original' => __( 'Revert to original', 'wpml-media' ),
100 'restore_original_media' => __( 'Restore original media file', 'wpml-media' ),
101 'statuses' => self::get_translation_status_labels(),
102 'texts_change_notice' => __( 'Any changes you make to the text here will not affect any previous publications of this media on your website. This edited version will only appear if you select it from the library to be embedded.', 'wpml-media' ),
103 ),
104 'months' => $this->get_months(),
105 'selected_month' => isset( $this->query_args['m'] ) ? (int) $this->query_args['m'] : 0,
106 'selected_status' => isset( $this->query_args['status'] ) ? $this->query_args['status'] : '',
107 'from_language' => isset( $this->query_args['slang'] ) ? $this->query_args['slang'] : '',
108 'to_language' => isset( $this->query_args['tlang'] ) ? $this->query_args['tlang'] : '',
109 'statuses' => array(
110 'not_translated' => WPML_Media_Translation_Status::NOT_TRANSLATED,
111 'in_progress' => WPML_Media_Translation_Status::IN_PROGRESS,
112 'translated' => WPML_Media_Translation_Status::TRANSLATED,
113 'needs_translation' => WPML_Media_Translation_Status::NEEDS_MEDIA_TRANSLATION,
114 ),
115 'search' => isset( $this->query_args['s'] ) ? $this->query_args['s'] : '',
116 'languages' => $languages,
117 'attachments' => $this->get_attachments( $languages ),
118 'nonce' => wp_nonce_field( 'media-translation', 'wpnonce', false, false ),
119 'pagination' => $this->get_pagination(),
120 'target_language' => $this->should_filter_by_target_language() ? $this->query_args['tlang'] : '',
121
122 'batch_translation' => $this->get_batch_translation(),
123
124 'show_text_change_notice' => ! get_user_meta( get_current_user_id(), WPML_Media_Editor_Notices::TEXT_EDIT_NOTICE_DISMISSED, true ),
125 );
126
127 return $model;
128 }
129
130 public static function get_translation_status_labels() {
131 return array(
132 WPML_Media_Translation_Status::NOT_TRANSLATED => __( 'Not translated', 'wpml-media' ),
133 WPML_Media_Translation_Status::IN_PROGRESS => __( 'In progress', 'wpml-media' ),
134 WPML_Media_Translation_Status::TRANSLATED => __( 'Translated', 'wpml-media' ),
135 WPML_Media_Translation_Status::NEEDS_MEDIA_TRANSLATION => __( 'Needs translation', 'wpml-media' ),
136 );
137 }
138
139 private function set_query_args() {
140 $arg_keys = array( 'm', 'status', 'slang', 'tlang', 's', 'paged' );
141 foreach ( $arg_keys as $key ) {
142 if ( isset( $_GET[ $key ] ) ) {
143 $this->query_args[ $key ] = sanitize_text_field( $_GET[ $key ] );
144 }
145 }
146 }
147
148 /**
149 * @return array
150 */
151 private function get_months() {
152 $months = array();
153
154 $month_results = $this->wpdb->get_results(
155 "
156 SELECT DISTINCT YEAR( post_date ) AS year, MONTH( post_date ) AS month
157 FROM {$this->wpdb->posts}
158 WHERE post_type = 'attachment'
159 ORDER BY post_date DESC
160 "
161 );
162
163 foreach ( $month_results as $month ) {
164 $months[] = array(
165 'id' => $month->year . zeroise( $month->month, 2 ),
166 'label' => sprintf( __( '%1$s %2$d' ), $this->wp_locale->get_month( $month->month ), $month->year ),
167 );
168 }
169
170 return $months;
171 }
172
173 /**
174 * @return array
175 */
176 private function get_languages() {
177 $languages = array();
178
179 $active_languages = $this->sitepress->get_active_languages();
180 foreach ( $active_languages as $language ) {
181 $languages[ $language['code'] ] = array(
182 'name' => $language['display_name'],
183 'flag' => $this->sitepress->get_flag_url( $language['code'] ),
184 );
185 }
186
187 return $languages;
188 }
189
190 private function get_items_per_page() {
191 return get_option( 'wpml_media_translation_dashboard_items_per_page', 20 );
192 }
193
194 /**
195 * @param array $languages
196 *
197 * @return array
198 */
199 private function get_attachments( $languages ) {
200 $attachments = array();
201
202 $args = array(
203 'post_type' => 'attachment',
204 'orderby' => 'ID',
205 'order' => 'DESC',
206 'posts_per_page' => $this->get_items_per_page(),
207 );
208 $args = array_merge( $args, $this->query_args );
209
210 if ( $this->should_filter_by_status() ) {
211 $args['meta_query'] = array(
212 'key' => '_media_translation_status',
213 'value' => $this->query_args['status'],
214 );
215 }
216
217 $this->add_query_filters();
218
219 $attachment_posts = get_posts( $args );
220
221 $this->remove_query_filters();
222
223 foreach ( $attachment_posts as $attachment ) {
224 $attachment_id = $attachment->ID;
225 $post_element = new WPML_Post_Element( $attachment_id, $this->sitepress );
226 $media_file_original = get_post_meta( $attachment_id, '_wp_attached_file', true );
227 $translations = array();
228
229 $is_image = (int) ( 0 === strpos( $attachment->post_mime_type, 'image/' ) );
230
231 foreach ( $languages as $code => $language ) {
232 $translation = $post_element->get_translation( $code );
233
234 if ( $translation ) {
235 $translation_post = $translation->get_wp_object();
236 $media_file_translated = get_post_meta( $translation->get_id(), '_wp_attached_file', true );
237
238 $translations[ $code ] = array(
239 'id' => $translation->get_id(),
240 'file_name' => basename( $media_file_translated ),
241 'title' => $translation_post->post_title,
242 'caption' => $translation_post->post_excerpt,
243 'description' => $translation_post->post_content,
244 'alt' => get_post_meta( $translation->get_id(), '_wp_attachment_image_alt', true ),
245 'thumb' => $this->get_attachment_thumb( $translation->get_id(), $is_image ),
246 'media_is_translated' => $media_file_translated && $media_file_translated !== $media_file_original,
247 'status' => $this->get_translation_status( $attachment_id, $translation ),
248 );
249
250 } else {
251 $translations[ $code ] = array(
252 'status' => WPML_Media_Translation_Status::NOT_TRANSLATED,
253 );
254 }
255 }
256
257 $attachments[] = array(
258 'post' => $attachment,
259 'mime_type' => $is_image ? 'image/*' : $attachment->post_mime_type,
260 'is_image' => $is_image,
261 'file_name' => basename( $media_file_original ),
262 'alt' => get_post_meta( $attachment_id, '_wp_attachment_image_alt', true ),
263 'meta' => get_post_meta( $attachment_id, '_wp_attachment_metadata', true ),
264 'language' => $post_element->get_language_code(),
265 'thumb' => $this->get_attachment_thumb( $attachment_id, $is_image ),
266 'url' => $is_image ? $this->get_attachment_url( $attachment_id ) : '',
267 'translations' => $translations,
268 'preview' => [
269 'width' => self::PREVIEW_MAX_WIDTH,
270 'height' => self::PREVIEW_MAX_HEIGHT,
271 ],
272 );
273
274 }
275
276 return $attachments;
277 }
278
279 /**
280 * @param int $attachment_id
281 *
282 * @return array
283 */
284 private function get_attachment_url( $attachment_id ) {
285 $image = wp_get_attachment_image_src( $attachment_id, [ self::PREVIEW_MAX_WIDTH, self::PREVIEW_MAX_HEIGHT ] );
286
287 return $image[0];
288 }
289
290 /**
291 * @param int $attachment_id
292 * @param bool $is_image
293 *
294 * @return array
295 */
296 private function get_attachment_thumb( $attachment_id, $is_image = true ) {
297 $image = wp_get_attachment_image_src( $attachment_id, 'thumbnail', true );
298
299 $thumb = array(
300 'src' => $image ? $image[0] : '',
301 'width' => $is_image ? 60 : $image[1],
302 'height' => $is_image ? 60 : $image[2],
303 );
304
305 return $thumb;
306 }
307
308 public function set_total_items_in_pagination( $query ) {
309
310 $query_count = preg_replace(
311 '/^SELECT /',
312 'SELECT SQL_CALC_FOUND_ROWS ',
313 trim( $query )
314 );
315 $this->wpdb->get_results( $query_count );
316
317 $this->pagination->set_total_items( $this->wpdb->get_var( 'SELECT FOUND_ROWS()' ) );
318
319 return $query;
320 }
321
322 private function get_pagination() {
323
324 $this->pagination->set_items_per_page( $this->get_items_per_page() );
325 $current_page = isset( $this->query_args['paged'] ) ? $this->query_args['paged'] : 1;
326 $this->pagination->set_current_page( $current_page );
327
328 $model = array(
329 'strings' => array(
330 'list_navigation' => __( 'Navigation', 'wpml-media' ),
331 'of' => __( 'of', 'wpml-media' ),
332 ),
333 'pagination' => array(
334 'get_first_page_url' => $this->pagination->get_first_page_url(),
335 'first_page' => __( 'First page', 'wpml-media' ),
336
337 'get_previous_page_url' => $this->pagination->get_previous_page_url(),
338 'previous_page' => __( 'Previous page', 'wpml-media' ),
339
340 'get_current_page' => $this->pagination->get_current_page(),
341 'get_total_pages' => $this->pagination->get_total_pages(),
342
343 'get_next_page_url' => $this->pagination->get_next_page_url(),
344 'next_page' => __( 'Next page', 'wpml-media' ),
345
346 'get_last_page_url' => $this->pagination->get_last_page_url(),
347 'last_page' => __( 'Last page', 'wpml-media' ),
348 ),
349 'total_items' => sprintf(
350 _n( '%s item', '%s items', $this->pagination->get_total_items() ),
351 $this->pagination->get_total_items()
352 ),
353 );
354
355 return $model;
356 }
357
358 private function get_batch_translation() {
359 $model = array(
360 'strings' => array(
361 'close' => __( 'Close', 'wpml-media' ),
362 'was_replaced' => __( 'The media that you uploaded was replaced in these translated posts:', 'wpml-media' ),
363 'other_posts' => __( 'The same media might be used in other posts too. Do you want to scan and replace it?', 'wpml-media' ),
364 'without_usage' => __( 'The media that you uploaded will be used in future post translations. The same media might be used in already existing posts. Do you want to scan and replace it now?', 'wpml-media' ),
365 'scan_for_this_media' => __( 'Scan for content that has specifically this media (faster)', 'wpml-media' ),
366 'scan_for_all_media' => __( 'Scan for content that has any of the media files that I translated (takes more time)', 'wpml-media' ),
367 'button_label' => _x( 'Scan & Replace', 'Button label (verb)', 'wpml-media' ),
368 ),
369 );
370
371 return $model;
372 }
373
374 private function add_query_filters() {
375 remove_filter( 'posts_join', array( $this->wpml_query_filter, 'posts_join_filter' ), 10 );
376 remove_filter( 'posts_where', array( $this->wpml_query_filter, 'posts_where_filter' ), 10 );
377
378 add_filter( 'posts_join', array( $this, 'filter_request_clause_join' ), PHP_INT_MAX );
379 add_filter( 'posts_where', array( $this, 'filter_request_clause_where' ), PHP_INT_MAX );
380 add_filter( 'posts_request', array( $this, 'set_total_items_in_pagination' ), - PHP_INT_MAX );
381 }
382
383 private function remove_query_filters() {
384 add_filter( 'posts_join', array( $this->wpml_query_filter, 'posts_join_filter' ), 10, 2 );
385 add_filter( 'posts_where', array( $this->wpml_query_filter, 'posts_where_filter' ), 10, 2 );
386
387 remove_filter( 'posts_join', array( $this, 'filter_request_clause_join' ), PHP_INT_MAX );
388 remove_filter( 'posts_where', array( $this, 'filter_request_clause_where' ), PHP_INT_MAX );
389 remove_filter( 'posts_request', array( $this, 'set_total_items_in_pagination' ), - PHP_INT_MAX );
390 }
391
392 public function filter_request_clause_join( $join ) {
393
394 $join .= " LEFT JOIN {$this->wpdb->prefix}icl_translations icl_translations_source
395 ON icl_translations_source.element_id = {$this->wpdb->posts}.ID ";
396
397 if ( $this->should_filter_by_status() ) {
398 if ( $this->should_filter_by_target_language() ) {
399 $join .= $this->wpdb->prepare(
400 " LEFT JOIN {$this->wpdb->postmeta} post_meta
401 ON icl_translations_source.element_id = post_meta.post_id
402 AND post_meta.meta_key = %s
403 ",
404 WPML_Media_Translation_Status::STATUS_PREFIX . $this->query_args['tlang']
405 );
406 } else {
407 $active_language = $this->sitepress->get_active_languages();
408 $default_lanuage = $this->sitepress->get_default_language();
409
410 foreach ( $active_language as $language ) {
411 if ( $language['code'] !== $default_lanuage ) {
412 $sanitized_code = str_replace( '-', '_', $language['code'] );
413
414 $join .= $this->wpdb->prepare(
415 " LEFT JOIN {$this->wpdb->postmeta} post_meta_{$sanitized_code}
416 ON {$this->wpdb->posts}.ID = post_meta_{$sanitized_code}.post_id
417 AND post_meta_{$sanitized_code}.meta_key=%s",
418 WPML_Media_Translation_Status::STATUS_PREFIX . $language['code']
419 );
420 }
421 }
422 }
423 } else {
424 if ( $this->should_filter_by_target_language() ) {
425 $join .= " JOIN {$this->wpdb->prefix}icl_translations icl_translations_target
426 ON icl_translations_target.trid = icl_translations_source.trid
427 AND icl_translations_target.language_code='{$this->query_args['tlang']}'";
428 }
429 }
430
431 return $join;
432 }
433
434 public function filter_request_clause_where( $where ) {
435
436 $where .= " AND icl_translations_source.element_type='post_attachment'
437 AND icl_translations_source.source_language_code IS NULL ";
438
439 if ( ! empty( $this->query_args['slang'] ) && $this->query_args['slang'] !== 'all' ) {
440 $where .= $this->wpdb->prepare( ' AND icl_translations_source.language_code = %s ', $this->query_args['slang'] );
441 }
442
443 if ( $this->should_filter_by_status() ) {
444 if ( $this->should_filter_by_target_language() ) {
445 if ( $this->query_args['status'] === WPML_Media_Translation_Status::NOT_TRANSLATED ) {
446 $where .= $this->wpdb->prepare(
447 ' AND (
448 post_meta.meta_value IS NULL OR
449 post_meta.meta_value = %s OR
450 post_meta.meta_value = %s OR
451 post_meta.meta_value = %s
452 )',
453 WPML_Media_Translation_Status::NOT_TRANSLATED,
454 WPML_Media_Translation_Status::IN_PROGRESS,
455 WPML_Media_Translation_Status::NEEDS_MEDIA_TRANSLATION
456 );
457 } else {
458 $where .= $this->wpdb->prepare( ' AND post_meta.meta_value = %s', $this->query_args['status'] );
459 }
460 } else {
461 $active_language = $this->sitepress->get_active_languages();
462 $default_language = $this->sitepress->get_default_language();
463
464 if ( $this->query_args['status'] === WPML_Media_Translation_Status::TRANSLATED ) {
465 foreach ( $active_language as $language ) {
466 $sanitized_code = str_replace( '-', '_', $language['code'] );
467 if ( $language['code'] !== $default_language ) {
468 $where .= $this->wpdb->prepare(
469 "AND post_meta_{$sanitized_code}.meta_value = %s",
470 WPML_Media_Translation_Status::TRANSLATED
471 );
472 }
473 }
474 } elseif ( $this->query_args['status'] === WPML_Media_Translation_Status::NOT_TRANSLATED ) {
475 $where .= 'AND ( 0 ';
476 foreach ( $active_language as $language ) {
477 $sanitized_code = str_replace( '-', '_', $language['code'] );
478 if ( $language['code'] !== $default_language ) {
479 $where .= $this->wpdb->prepare(
480 "
481 OR post_meta_{$sanitized_code}.meta_value = %s
482 OR post_meta_{$sanitized_code}.meta_value IS NULL",
483 $this->query_args['status']
484 );
485 }
486 }
487 $where .= ') ';
488 } else {
489 $where .= 'AND ( 0 ';
490 foreach ( $active_language as $language ) {
491 $sanitized_code = str_replace( '-', '_', $language['code'] );
492 if ( $language['code'] !== $default_language ) {
493 $where .= $this->wpdb->prepare(
494 "
495 OR post_meta_{$sanitized_code}.meta_value = %s",
496 $this->query_args['status']
497 );
498 }
499 }
500 $where .= ') ';
501 }
502 }
503 }
504
505 return $where;
506 }
507
508 private function should_filter_by_status() {
509 return isset( $this->query_args['status'] ) && $this->query_args['status'] !== '';
510 }
511
512 private function should_filter_by_target_language() {
513 return isset( $this->query_args['tlang'] ) && $this->query_args['tlang'] !== '';
514 }
515
516 protected function init_template_base_dir() {
517 $this->template_paths = array(
518 WPML_MEDIA_PATH . '/templates/menus/',
519 WPML_PLUGIN_PATH . '/templates/pagination/',
520 );
521 }
522
523 /**
524 * @return string
525 */
526 public function get_template() {
527 return 'media-translation.twig';
528 }
529
530 /**
531 * @param $attachment_id
532 * @param $translation
533 *
534 * @return mixed|string
535 */
536 private function get_translation_status( $attachment_id, $translation ) {
537 $translation_status = get_post_meta(
538 $attachment_id,
539 WPML_Media_Translation_Status::STATUS_PREFIX . $translation->get_language_code(),
540 true
541 );
542 if ( ! $translation_status ) {
543 $translation_status = WPML_Media_Translation_Status::NOT_TRANSLATED;
544 }
545
546 return $translation_status;
547 }
548
549
550 }
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 }
1 # This file was generated by WPML
2 # WPML is a WordPress plugin that can turn any WordPress or WordPressMU site into a full featured multilingual content management system.
3 # https://wpml.org
4 msgid ""
5 msgstr ""
6 "Content-Type: text/plain; charset=utf-8\n"
7 "Content-Transfer-Encoding: 8bit\n"
8 "Project-Id-Version:WPML_EXPORT\n"
9 "POT-Creation-Date: \n"
10 "PO-Revision-Date: \n"
11 "Last-Translator: \n"
12 "Language-Team: \n"
13 "Language:en\n"
14 "MIME-Version: 1.0\n"
15
16 # return sprintf(
17 # __( 'Translating media urls in custom field translations: %s', 'wpml-media' ),
18 # $number_of_custom_fields_left > 0 ?
19
20 msgid "Translating media urls in custom field translations: %s"
21 msgstr ""
22
23 # $number_of_custom_fields_left > 0 ?
24 # sprintf( __( '%d left', 'wpml-media' ), $number_of_custom_fields_left ) :
25 # __( 'done!', 'wpml-media' )
26
27 msgid "%d left"
28 msgstr ""
29
30 # sprintf( __( '%d left', 'wpml-media' ), $number_of_custom_fields_left ) :
31 # __( 'done!', 'wpml-media' )
32 # );
33
34 msgid "done!"
35 msgstr ""
36
37 # 'key' => 'wpml_media_batch_urls_update_error_custom_fields',
38 # 'value' => esc_js( __( 'Translating media urls in custom fields translations failed: Please try again (%s)', 'wpml-media' ) )
39 # );
40
41 msgid "Translating media urls in custom fields translations failed: Please try again (%s)"
42 msgstr ""
43
44 # return sprintf(
45 # __( 'Translating media urls in post translations: %s', 'wpml-media' ),
46 # $number_of_posts_left > 0 ?
47
48 msgid "Translating media urls in post translations: %s"
49 msgstr ""
50
51 # 'key' => 'wpml_media_batch_urls_update_errors_posts',
52 # 'value' => esc_js( __( 'Translating media urls in posts translations failed: Please try again (%s)', 'wpml-media' ) )
53 # );
54
55 msgid "Translating media urls in posts translations failed: Please try again (%s)"
56 msgstr ""
57
58 # return sprintf(
59 # __( 'Translating media urls in string translations: %s', 'wpml-media' ),
60 # $number_of_strings_left > 0 ?
61
62 msgid "Translating media urls in string translations: %s"
63 msgstr ""
64
65 # 'key' => 'wpml_media_batch_urls_update_error_strings',
66 # 'value' => esc_js( __( 'Translating media urls in string translations failed: Please try again (%s)', 'wpml-media' ) )
67 # );
68
69 msgid "Translating media urls in string translations failed: Please try again (%s)"
70 msgstr ""
71
72 # 'has_posts' => sprintf(
73 # __(
74 # 'Choose which media to translate with this %s',
75
76 msgid "Choose which media to translate with this %s"
77 msgstr ""
78
79 # ),
80 # 'loading' => __( 'Loading...', 'wpml-media' )
81 # ),
82
83 msgid "Loading..."
84 msgstr ""
85
86 # /* translators: WPML plugin name */
87 # $wpml_plugin_name = __( 'WPML', 'wpml-media' );
88 # /* translators: WPML Media Translation saddon/section name */
89
90 msgid "WPML"
91 msgstr ""
92
93 # /* translators: WPML Media Translation saddon/section name */
94 # $media_translation_name = __( 'Media Translation', 'wpml-media' );
95 #
96
97 msgid "Media Translation"
98 msgstr ""
99
100 # /* translators: media file string used in "if you want to use a different media file for each language..." */
101 # $media_file_string = __( 'media file', 'wpml-media' );
102 # $redirect_url = '#';
103
104 msgid "media file"
105 msgstr ""
106
107 # 'strings' => array(
108 # 'dialog_title' => __( 'Media sent to translation', 'wpml-media' ),
109 # 'content_with_media_sent' => __( 'You have sent content which contains media attachments for translation.', 'wpml-media' ),
110
111 msgid "Media sent to translation"
112 msgstr ""
113
114 # 'dialog_title' => __( 'Media sent to translation', 'wpml-media' ),
115 # 'content_with_media_sent' => __( 'You have sent content which contains media attachments for translation.', 'wpml-media' ),
116 # 'media_texts_translated' => sprintf( __( 'Translators will translate all your %smedia texts%s.', 'wpml-media' ), '<strong>', '</strong>' ),
117
118 msgid "You have sent content which contains media attachments for translation."
119 msgstr ""
120
121 # 'content_with_media_sent' => __( 'You have sent content which contains media attachments for translation.', 'wpml-media' ),
122 # 'media_texts_translated' => sprintf( __( 'Translators will translate all your %smedia texts%s.', 'wpml-media' ), '<strong>', '</strong>' ),
123 # 'use_different_media' => sprintf( __( 'If you want to use a different %s for each language, you can set them in: %s.', 'wpml-media' ),
124
125 msgid "Translators will translate all your %smedia texts%s."
126 msgstr ""
127
128 # 'media_texts_translated' => sprintf( __( 'Translators will translate all your %smedia texts%s.', 'wpml-media' ), '<strong>', '</strong>' ),
129 # 'use_different_media' => sprintf( __( 'If you want to use a different %s for each language, you can set them in: %s.', 'wpml-media' ),
130 # '<strong>' . $media_file_string . '</strong>', $media_translation_link ),
131
132 msgid "If you want to use a different %s for each language, you can set them in: %s."
133 msgstr ""
134
135 # '<strong>' . $media_file_string . '</strong>', $media_translation_link ),
136 # 'learn_more' => __( 'Learn more about Media Translation', 'wpml-media' ),
137 # 'wpml' => _x( 'WPML', 'plugin name', 'wpml-media' ),
138
139 msgid "Learn more about Media Translation"
140 msgstr ""
141
142 # 'learn_more' => __( 'Learn more about Media Translation', 'wpml-media' ),
143 # 'wpml' => _x( 'WPML', 'plugin name', 'wpml-media' ),
144 # 'media_translation' => _x( 'Media Translation', 'wpml addon name', 'wpml-media' )
145
146 msgctxt "plugin name"
147 msgid "WPML"
148 msgstr ""
149
150 # 'wpml' => _x( 'WPML', 'plugin name', 'wpml-media' ),
151 # 'media_translation' => _x( 'Media Translation', 'wpml addon name', 'wpml-media' )
152 # ),
153
154 msgctxt "wpml addon name"
155 msgid "Media Translation"
156 msgstr ""
157
158 # } else {
159 # wp_send_json_error( __( 'Failed to load the image editor', 'wpml-media' ) );
160 # }
161
162 msgid "Failed to load the image editor"
163 msgstr ""
164
165 # } else {
166 # wp_send_json_error( array( 'error' => __( 'Invalid nonce', 'wpml-media' ) ) );
167 # }
168
169 msgid "Invalid nonce"
170 msgstr ""
171
172 # }
173 # wp_send_json_success( array( 'status' => __( 'Running setup...', 'wpml-media' ) ) );
174 #
175
176 msgid "Running setup..."
177 msgstr ""
178
179 # $continue = false;
180 # $status = __( 'Setup complete!', 'wpml-media' );
181 # $offset = isset( $_POST['offset'] ) ? (int) $_POST['offset'] : 0;
182
183 msgid "Setup complete!"
184 msgstr ""
185
186 # $progress = round( 100*min( $offset, $total_posts_found )/$total_posts_found );
187 # $status = sprintf( __( 'Setup in progress: %d%% complete...', 'wpml-media' ), $progress );
188 # }
189
190 msgid "Setup in progress: %d%% complete..."
191 msgstr ""
192
193 # if ( $this->is_media_related_screen( $current_screen ) ) {
194 # $media_translation_dashboard = esc_html__( 'WPML &raquo; Media Translation', 'wpml-media' );
195 # $wpml_translation_dashboard = esc_html__( 'WPML &raquo; Translation Management', 'wpml-media' );
196
197 msgid "WPML &raquo; Media Translation"
198 msgstr ""
199
200 # $media_translation_dashboard = esc_html__( 'WPML &raquo; Media Translation', 'wpml-media' );
201 # $wpml_translation_dashboard = esc_html__( 'WPML &raquo; Translation Management', 'wpml-media' );
202 # $current_screen->add_help_tab( array(
203
204 msgid "WPML &raquo; Translation Management"
205 msgstr ""
206
207 # 'id' => 'wpml-media-translation',
208 # 'title' => esc_html__( 'Translating Media', 'wpml-media' ),
209 # 'content' =>
210
211 msgid "Translating Media"
212 msgstr ""
213
214 # 'content' =>
215 # '<p>' . esc_html__( 'There are two ways for you to translate Media:', 'wpml-media' ) . '</p>' .
216 # '<ul>' .
217
218 msgid "There are two ways for you to translate Media:"
219 msgstr ""
220
221 # '<li>' . sprintf(
222 # esc_html__( 'Use the dashboard on the %s page to translate your images and other media files.', 'wpml-media' ),
223 # $media_translation_dashboard
224
225 msgid "Use the dashboard on the %s page to translate your images and other media files."
226 msgstr ""
227
228 # '<li>' . sprintf(
229 # esc_html__( 'Use the dashboard on the %s page to send pages that contain media, for translation.', 'wpml-media' ),
230 # $wpml_translation_dashboard
231
232 msgid "Use the dashboard on the %s page to send pages that contain media, for translation."
233 msgstr ""
234
235 # '</ul>' .
236 # '<a href="https://wpml.org/?page_id=113610">' . esc_html__( 'Learn more about WPML Media Translation', 'wpml-media' ) . '</a>'
237 # ) );
238
239 msgid "Learn more about WPML Media Translation"
240 msgstr ""
241
242 # 'title' => esc_js( __( 'Media Translation', 'wpml-media' ) ),
243 # 'cancel' => esc_js( __( 'Cancel', 'wpml-media' ) ),
244 # 'save' => esc_js( __( 'Save media translation', 'wpml-media' ) ),
245
246 msgid "Cancel"
247 msgstr ""
248
249 # 'cancel' => esc_js( __( 'Cancel', 'wpml-media' ) ),
250 # 'save' => esc_js( __( 'Save media translation', 'wpml-media' ) ),
251 # 'status_labels' => WPML_Media_Translations_UI::get_translation_status_labels()
252
253 msgid "Save media translation"
254 msgstr ""
255
256 # $batch_translation_vars = array(
257 # 'complete' => esc_js( __( 'Scan complete!', 'wpml-media' ) ),
258 # 'is_st_enabled' => (bool) $this->sitepress->get_wp_api()->constant( 'WPML_ST_VERSION' ),
259
260 msgid "Scan complete!"
261 msgstr ""
262
263 # /* translators: name ot WPML-Media plugin */
264 # $wpml_media = '<strong>' . __( 'WPML Media Translation', 'wpml-media' ) . '</strong>';
265 #
266
267 msgid "WPML Media Translation"
268 msgstr ""
269
270 # $text = vsprintf(
271 # esc_html__( 'The %1$s setup is almost complete. Click %2$s to finish the setup.', 'wpml-media' ),
272 # array(
273
274 msgid "The %1$s setup is almost complete. Click %2$s to finish the setup."
275 msgstr ""
276
277 # <input type="button" class="button-primary alignright"
278 # value="<?php esc_attr_e( 'Finish setup', 'wpml-media' ) ?>"/>
279 #
280
281 msgid "Finish setup"
282 msgstr ""
283
284 # <div class="wrap wpml-media-setup">
285 # <h2><?php esc_html_e( 'Setup required', 'wpml-media' ) ?></h2>
286 # <div
287
288 msgid "Setup required"
289 msgstr ""
290
291 # <p>
292 # <?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' ); ?>
293 # </p>
294
295 msgid "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."
296 msgstr ""
297
298 # /* translators: used to build a link in the "Click here to finish the setup" */
299 # $here_text = _x( 'here', 'Used to build a link in the "Click here to finish the setup"', 'wpml-media' );
300 # $here_link = '<a href="' . admin_url( 'admin.php?page=wpml-media' ) . '">' . $here_text . '</a>';
301
302 msgctxt "Used to build a link in the \"Click here to finish the setup\""
303 msgid "here"
304 msgstr ""
305
306 # 'args' => array(
307 # 'label' => __( 'Number of items per page:', 'wpml-media' ),
308 # 'default' => get_option( $option_name, 20 ),
309
310 msgid "Number of items per page:"
311 msgstr ""
312
313 # 'heading' => __( 'Media Translation', 'wpml-media' ),
314 # 'filter_by_date' => __( 'Filter by date', 'wpml-media' ),
315 # 'all_dates' => __( 'All dates', 'wpml-media' ),
316
317 msgid "Filter by date"
318 msgstr ""
319
320 # 'filter_by_date' => __( 'Filter by date', 'wpml-media' ),
321 # 'all_dates' => __( 'All dates', 'wpml-media' ),
322 # 'in' => __( 'in', 'wpml_media' ),
323
324 msgid "All dates"
325 msgstr ""
326
327 # 'to' => __( 'to', 'wpml_media' ),
328 # 'filter_by_status' => __( 'Filter by translation status', 'wpml-media' ),
329 # 'status_all' => __( 'All translation statuses', 'wpml-media' ),
330
331 msgid "Filter by translation status"
332 msgstr ""
333
334 # 'filter_by_status' => __( 'Filter by translation status', 'wpml-media' ),
335 # 'status_all' => __( 'All translation statuses', 'wpml-media' ),
336 # 'status_not' => __( 'Media file not translated', 'wpml-media' ),
337
338 msgid "All translation statuses"
339 msgstr ""
340
341 # 'status_all' => __( 'All translation statuses', 'wpml-media' ),
342 # 'status_not' => __( 'Media file not translated', 'wpml-media' ),
343 # 'status_translated' => __( 'Translated media uploaded', 'wpml-media' ),
344
345 msgid "Media file not translated"
346 msgstr ""
347
348 # 'status_not' => __( 'Media file not translated', 'wpml-media' ),
349 # 'status_translated' => __( 'Translated media uploaded', 'wpml-media' ),
350 # 'status_in_progress' => __( 'Translation in progress', 'wpml-media' ),
351
352 msgid "Translated media uploaded"
353 msgstr ""
354
355 # 'status_translated' => __( 'Translated media uploaded', 'wpml-media' ),
356 # 'status_in_progress' => __( 'Translation in progress', 'wpml-media' ),
357 # 'status_needs_translation' => __( 'Needs media file translation', 'wpml-media' ),
358
359 msgid "Translation in progress"
360 msgstr ""
361
362 # 'status_in_progress' => __( 'Translation in progress', 'wpml-media' ),
363 # 'status_needs_translation' => __( 'Needs media file translation', 'wpml-media' ),
364 # 'filter_by_language' => __( 'Filter by language', 'wpml-media' ),
365
366 msgid "Needs media file translation"
367 msgstr ""
368
369 # 'status_needs_translation' => __( 'Needs media file translation', 'wpml-media' ),
370 # 'filter_by_language' => __( 'Filter by language', 'wpml-media' ),
371 # 'any_language' => __( 'Any language', 'wpml-media' ),
372
373 msgid "Filter by language"
374 msgstr ""
375
376 # 'filter_by_language' => __( 'Filter by language', 'wpml-media' ),
377 # 'any_language' => __( 'Any language', 'wpml-media' ),
378 # 'search_media' => __( 'Search Media:', 'wpml-media' ),
379
380 msgid "Any language"
381 msgstr ""
382
383 # 'any_language' => __( 'Any language', 'wpml-media' ),
384 # 'search_media' => __( 'Search Media:', 'wpml-media' ),
385 # 'search_placeholder' => __( 'Title, caption or description', 'wpml-media' ),
386
387 msgid "Search Media:"
388 msgstr ""
389
390 # 'search_media' => __( 'Search Media:', 'wpml-media' ),
391 # 'search_placeholder' => __( 'Title, caption or description', 'wpml-media' ),
392 # 'search_button_label' => __( 'Filter', 'wpml-media' ),
393
394 msgid "Title, caption or description"
395 msgstr ""
396
397 # 'search_placeholder' => __( 'Title, caption or description', 'wpml-media' ),
398 # 'search_button_label' => __( 'Filter', 'wpml-media' ),
399 # 'original_language' => __( 'Original language', 'wpml-media' ),
400
401 msgid "Filter"
402 msgstr ""
403
404 # 'search_button_label' => __( 'Filter', 'wpml-media' ),
405 # 'original_language' => __( 'Original language', 'wpml-media' ),
406 # 'no_attachments' => __( 'No attachments found', 'wpml-media' ),
407
408 msgid "Original language"
409 msgstr ""
410
411 # 'original_language' => __( 'Original language', 'wpml-media' ),
412 # 'no_attachments' => __( 'No attachments found', 'wpml-media' ),
413 # 'add_translation' => __( 'Add media file translation', 'wpml-media' ),
414
415 msgid "No attachments found"
416 msgstr ""
417
418 # 'no_attachments' => __( 'No attachments found', 'wpml-media' ),
419 # 'add_translation' => __( 'Add media file translation', 'wpml-media' ),
420 # 'edit_translation' => __( 'Edit %s translation', 'wpml-media' ),
421
422 msgid "Add media file translation"
423 msgstr ""
424
425 # 'add_translation' => __( 'Add media file translation', 'wpml-media' ),
426 # 'edit_translation' => __( 'Edit %s translation', 'wpml-media' ),
427 # 'original' => __( 'Original:', 'wpml-media' ),
428
429 msgid "Edit %s translation"
430 msgstr ""
431
432 # 'edit_translation' => __( 'Edit %s translation', 'wpml-media' ),
433 # 'original' => __( 'Original:', 'wpml-media' ),
434 # 'translation' => __( 'Translation:', 'wpml-media' ),
435
436 msgid "Original:"
437 msgstr ""
438
439 # 'original' => __( 'Original:', 'wpml-media' ),
440 # 'translation' => __( 'Translation:', 'wpml-media' ),
441 # 'file' => __( 'File', 'wpml-media' ),
442
443 msgid "Translation:"
444 msgstr ""
445
446 # 'translation' => __( 'Translation:', 'wpml-media' ),
447 # 'file' => __( 'File', 'wpml-media' ),
448 # 'name' => __( 'Name', 'wpml-media' ),
449
450 msgid "File"
451 msgstr ""
452
453 # 'file' => __( 'File', 'wpml-media' ),
454 # 'name' => __( 'Name', 'wpml-media' ),
455 # 'caption' => __( 'Caption', 'wpml-media' ),
456
457 msgid "Name"
458 msgstr ""
459
460 # 'name' => __( 'Name', 'wpml-media' ),
461 # 'caption' => __( 'Caption', 'wpml-media' ),
462 # 'alt_text' => __( 'Alt text', 'wpml-media' ),
463
464 msgid "Caption"
465 msgstr ""
466
467 # 'caption' => __( 'Caption', 'wpml-media' ),
468 # 'alt_text' => __( 'Alt text', 'wpml-media' ),
469 # 'description' => __( 'Description', 'wpml-media' ),
470
471 msgid "Alt text"
472 msgstr ""
473
474 # 'alt_text' => __( 'Alt text', 'wpml-media' ),
475 # 'description' => __( 'Description', 'wpml-media' ),
476 # 'copy_from_original' => __( 'Copy from original', 'wpml-media' ),
477
478 msgid "Description"
479 msgstr ""
480
481 # 'description' => __( 'Description', 'wpml-media' ),
482 # 'copy_from_original' => __( 'Copy from original', 'wpml-media' ),
483 # 'upload_translated_media' => __( 'Upload translated media file', 'wpml-media' ),
484
485 msgid "Copy from original"
486 msgstr ""
487
488 # 'copy_from_original' => __( 'Copy from original', 'wpml-media' ),
489 # 'upload_translated_media' => __( 'Upload translated media file', 'wpml-media' ),
490 # 'use_different_file' => __( 'Use a different file', 'wpml-media' ),
491
492 msgid "Upload translated media file"
493 msgstr ""
494
495 # 'upload_translated_media' => __( 'Upload translated media file', 'wpml-media' ),
496 # 'use_different_file' => __( 'Use a different file', 'wpml-media' ),
497 # 'revert_to_original' => __( 'Revert to original', 'wpml-media' ),
498
499 msgid "Use a different file"
500 msgstr ""
501
502 # 'use_different_file' => __( 'Use a different file', 'wpml-media' ),
503 # 'revert_to_original' => __( 'Revert to original', 'wpml-media' ),
504 # 'restore_original_media' => __( 'Restore original media file', 'wpml-media' ),
505
506 msgid "Revert to original"
507 msgstr ""
508
509 # 'revert_to_original' => __( 'Revert to original', 'wpml-media' ),
510 # 'restore_original_media' => __( 'Restore original media file', 'wpml-media' ),
511 # 'statuses' => self::get_translation_status_labels(),
512
513 msgid "Restore original media file"
514 msgstr ""
515
516 # 'statuses' => self::get_translation_status_labels(),
517 # 'texts_change_notice' => __( 'Any changes you make to the text here will not affect any previous publications of this media on your website. This edited version will only appear if you select it from the library to be embedded.', 'wpml-media' )
518 # ),
519
520 msgid "Any changes you make to the text here will not affect any previous publications of this media on your website. This edited version will only appear if you select it from the library to be embedded."
521 msgstr ""
522
523 # return array(
524 # WPML_Media_Translation_Status::NOT_TRANSLATED => __( 'Not translated', 'wpml-media' ),
525 # WPML_Media_Translation_Status::IN_PROGRESS => __( 'In progress', 'wpml-media' ),
526
527 msgid "Not translated"
528 msgstr ""
529
530 # WPML_Media_Translation_Status::NOT_TRANSLATED => __( 'Not translated', 'wpml-media' ),
531 # WPML_Media_Translation_Status::IN_PROGRESS => __( 'In progress', 'wpml-media' ),
532 # WPML_Media_Translation_Status::TRANSLATED => __( 'Translated', 'wpml-media' ),
533
534 msgid "In progress"
535 msgstr ""
536
537 # WPML_Media_Translation_Status::IN_PROGRESS => __( 'In progress', 'wpml-media' ),
538 # WPML_Media_Translation_Status::TRANSLATED => __( 'Translated', 'wpml-media' ),
539 # WPML_Media_Translation_Status::NEEDS_MEDIA_TRANSLATION => __( 'Needs translation', 'wpml-media' )
540
541 msgid "Translated"
542 msgstr ""
543
544 # WPML_Media_Translation_Status::TRANSLATED => __( 'Translated', 'wpml-media' ),
545 # WPML_Media_Translation_Status::NEEDS_MEDIA_TRANSLATION => __( 'Needs translation', 'wpml-media' )
546 # );
547
548 msgid "Needs translation"
549 msgstr ""
550
551 # 'strings' => array(
552 # 'list_navigation' => __( 'Navigation', 'wpml-media' ),
553 # 'of' => __( 'of', 'wpml-media' )
554
555 msgid "Navigation"
556 msgstr ""
557
558 # 'list_navigation' => __( 'Navigation', 'wpml-media' ),
559 # 'of' => __( 'of', 'wpml-media' )
560 # ),
561
562 msgid "of"
563 msgstr ""
564
565 # 'get_first_page_url' => $this->pagination->get_first_page_url(),
566 # 'first_page' => __( 'First page', 'wpml-media' ),
567 #
568
569 msgid "First page"
570 msgstr ""
571
572 # 'get_previous_page_url' => $this->pagination->get_previous_page_url(),
573 # 'previous_page' => __( 'Previous page', 'wpml-media' ),
574 #
575
576 msgid "Previous page"
577 msgstr ""
578
579 # 'get_next_page_url' => $this->pagination->get_next_page_url(),
580 # 'next_page' => __( 'Next page', 'wpml-media' ),
581 #
582
583 msgid "Next page"
584 msgstr ""
585
586 # 'get_last_page_url' => $this->pagination->get_last_page_url(),
587 # 'last_page' => __( 'Last page', 'wpml-media' )
588 # ),
589
590 msgid "Last page"
591 msgstr ""
592
593 # 'strings' => array(
594 # 'close' => __( 'Close', 'wpml-media' ),
595 # 'was_replaced' => __( 'The media that you uploaded was replaced in these translated posts:', 'wpml-media' ),
596
597 msgid "Close"
598 msgstr ""
599
600 # 'close' => __( 'Close', 'wpml-media' ),
601 # 'was_replaced' => __( 'The media that you uploaded was replaced in these translated posts:', 'wpml-media' ),
602 # 'other_posts' => __( 'The same media might be used in other posts too. Do you want to scan and replace it?', 'wpml-media' ),
603
604 msgid "The media that you uploaded was replaced in these translated posts:"
605 msgstr ""
606
607 # 'was_replaced' => __( 'The media that you uploaded was replaced in these translated posts:', 'wpml-media' ),
608 # 'other_posts' => __( 'The same media might be used in other posts too. Do you want to scan and replace it?', 'wpml-media' ),
609 # 'without_usage' => __( 'The media that you uploaded will be used in future post translations. The same media might be used in already existing posts. Do you want to scan and replace it now?', 'wpml-media' ),
610
611 msgid "The same media might be used in other posts too. Do you want to scan and replace it?"
612 msgstr ""
613
614 # 'other_posts' => __( 'The same media might be used in other posts too. Do you want to scan and replace it?', 'wpml-media' ),
615 # 'without_usage' => __( 'The media that you uploaded will be used in future post translations. The same media might be used in already existing posts. Do you want to scan and replace it now?', 'wpml-media' ),
616 # 'scan_for_this_media' => __( 'Scan for content that has specifically this media (faster)', 'wpml-media' ),
617
618 msgid "The media that you uploaded will be used in future post translations. The same media might be used in already existing posts. Do you want to scan and replace it now?"
619 msgstr ""
620
621 # 'without_usage' => __( 'The media that you uploaded will be used in future post translations. The same media might be used in already existing posts. Do you want to scan and replace it now?', 'wpml-media' ),
622 # 'scan_for_this_media' => __( 'Scan for content that has specifically this media (faster)', 'wpml-media' ),
623 # 'scan_for_all_media' => __( 'Scan for content that has any of the media files that I translated (takes more time)', 'wpml-media' ),
624
625 msgid "Scan for content that has specifically this media (faster)"
626 msgstr ""
627
628 # 'scan_for_this_media' => __( 'Scan for content that has specifically this media (faster)', 'wpml-media' ),
629 # 'scan_for_all_media' => __( 'Scan for content that has any of the media files that I translated (takes more time)', 'wpml-media' ),
630 # 'button_label' => _x( 'Scan & Replace', 'Button label (verb)', 'wpml-media' )
631
632 msgid "Scan for content that has any of the media files that I translated (takes more time)"
633 msgstr ""
634
635 # 'scan_for_all_media' => __( 'Scan for content that has any of the media files that I translated (takes more time)', 'wpml-media' ),
636 # 'button_label' => _x( 'Scan & Replace', 'Button label (verb)', 'wpml-media' )
637 # )
638
639 msgctxt "Button label (verb)"
640 msgid "Scan & Replace"
641 msgstr ""
642
643 # $link = '<a href="' . $url . '" target="_blank" class="wpml-external-link">' .
644 # esc_html__( 'Media Translation documentation', 'wpml-media' ) . '</a>';
645 # $button = '<a href="' . $url . '" target="_blank" class="button button-primary button-wpml button-lg">' .
646
647 msgid "Media Translation documentation"
648 msgstr ""
649
650 # $button = '<a href="' . $url . '" target="_blank" class="button button-primary button-wpml button-lg">' .
651 # esc_html__( 'Learn more', 'wpml-media' ) . '</a>';
652 #
653
654 msgid "Learn more"
655 msgstr ""
656
657 # if ( $this->is_new_install() ) {
658 # $title = esc_html__( 'Learn how to translate media', 'wpml-media' );
659 # $body1 = esc_html__( 'WPML allows you to use different media (images, etc.) for translated content. The Media Translation process is integrated with WPML’s content translation, but requires additional steps.', 'wpml-media' );
660
661 msgid "Learn how to translate media"
662 msgstr ""
663
664 # $title = esc_html__( 'Learn how to translate media', 'wpml-media' );
665 # $body1 = esc_html__( 'WPML allows you to use different media (images, etc.) for translated content. The Media Translation process is integrated with WPML’s content translation, but requires additional steps.', 'wpml-media' );
666 # $body2 = sprintf( esc_html__( 'Before you start, we highly recommend that you review the %s. Then, you’ll be able to close this message.', 'wpml-media' ), $link );
667
668 msgid "WPML allows you to use different media (images, etc.) for translated content. The Media Translation process is integrated with WPML’s content translation, but requires additional steps."
669 msgstr ""
670
671 # $body1 = esc_html__( 'WPML allows you to use different media (images, etc.) for translated content. The Media Translation process is integrated with WPML’s content translation, but requires additional steps.', 'wpml-media' );
672 # $body2 = sprintf( esc_html__( 'Before you start, we highly recommend that you review the %s. Then, you’ll be able to close this message.', 'wpml-media' ), $link );
673 # } else {
674
675 msgid "Before you start, we highly recommend that you review the %s. Then, you’ll be able to close this message."
676 msgstr ""
677
678 # } else {
679 # $title = esc_html__( 'Media Translation is completely different now', 'wpml-media' );
680 # $body1 = esc_html__( 'This release of Media Translation includes a complete new workflow, with complete new features and possibilities.', 'wpml-media' );
681
682 msgid "Media Translation is completely different now"
683 msgstr ""
684
685 # $title = esc_html__( 'Media Translation is completely different now', 'wpml-media' );
686 # $body1 = esc_html__( 'This release of Media Translation includes a complete new workflow, with complete new features and possibilities.', 'wpml-media' );
687 # $body2 = sprintf( esc_html__( 'Even if you’re already familiar with WPML, you should read the new %s. Then, you’ll be able to close this message.', 'wpml-media' ), $link );
688
689 msgid "This release of Media Translation includes a complete new workflow, with complete new features and possibilities."
690 msgstr ""
691
692 # $body1 = esc_html__( 'This release of Media Translation includes a complete new workflow, with complete new features and possibilities.', 'wpml-media' );
693 # $body2 = sprintf( esc_html__( 'Even if you’re already familiar with WPML, you should read the new %s. Then, you’ll be able to close this message.', 'wpml-media' ), $link );
694 # }
695
696 msgid "Even if you’re already familiar with WPML, you should read the new %s. Then, you’ll be able to close this message."
697 msgstr ""
698
699 # }
700 # $minimize_label = esc_html__( 'Minimize', 'wpml-media' );
701 # $maximize_label = esc_html__( 'Maximize', 'wpml-media' );
702
703 msgid "Minimize"
704 msgstr ""
705
706 # $minimize_label = esc_html__( 'Minimize', 'wpml-media' );
707 # $maximize_label = esc_html__( 'Maximize', 'wpml-media' );
708 # $is_minimized = $this->is_minimized();
709
710 msgid "Maximize"
711 msgstr ""
712
713 # <a class="js-dismiss notice-dismiss<?php if ( ! $this->can_dismiss() ): ?> hidden<?php endif ?>">
714 # <span class="screen-reader-text"><?php esc_html_e( 'Dismiss', 'wpml-media' ) ?></span></a>
715 # <div class="wpml-media-welcome-notice-bg"><i class="otgs-ico-wpml"></i></div>
716
717 msgid "Dismiss"
718 msgstr ""
719
720 # protected function get_privacy_policy() {
721 # 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' );
722 # }
723
724 msgid "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."
725 msgstr ""
726
727 #
728 # $message = __( 'Setting language to media: done!', 'wpml-media' );
729 #
730
731 msgid "Setting language to media: done!"
732 msgstr ""
733
734 # 'field_type' => 'tm-section',
735 # 'title' => __( 'Media', 'wpml-media' ),
736 # 'fields' => array(),
737
738 msgid "Media"
739 msgstr ""
740
741 # case 'title':
742 # $label = __( 'Title', 'wpml-media' );
743 # break;
744
745 msgid "Title"
746 msgstr ""
747
748 # case 'alt_text':
749 # $label = __( 'Alt Text', 'wpml-media' );
750 # break;
751
752 msgid "Alt Text"
753 msgstr ""
754
755 # <p>
756 # <?php printf( esc_html__( 'The %sWPML Media%s database needs updating. Please run the updater and leave the tab open until it completes.', 'wpml-media' ),
757 # '<strong>', '</strong>' ); ?>
758
759 msgid "The %sWPML Media%s database needs updating. Please run the updater and leave the tab open until it completes."
760 msgstr ""
761
762 # wp_send_json_success(
763 # array( 'status' => esc_html__( 'Reset new content duplication settings', 'wpml-media' ), )
764 # );
765
766 msgid "Reset new content duplication settings"
767 msgstr ""
768
769 # $status = sprintf(
770 # esc_html__( 'Updating attachments translation status: %d remaining.', 'wpml-media' ),
771 # $left );
772
773 msgid "Updating attachments translation status: %d remaining."
774 msgstr ""
775
776 # $this->mark_migration_complete();
777 # $status = esc_html__( 'Update complete!', 'wpml-media' );
778 # $continue = 0;
779
780 msgid "Update complete!"
781 msgstr ""
782
783 # <div class="wrap wrap-wpml-media-upgrade">
784 # <h2><?php esc_html_e( 'Upgrade required', 'wpml-media' ) ?></h2>
785 # <?php endif; ?>
786
787 msgid "Upgrade required"
788 msgstr ""
789
790 # </p>
791 # <input type="button" class="button-primary alignright" value="<?php echo esc_attr_x( 'Update', 'Update button label', 'wpml-media' ); ?>" />
792 # <input type="hidden" name="nonce" value="<?php echo wp_create_nonce( 'wpml-media-2-3-0-update' ); ?>" />
793
794 msgctxt "Update button label"
795 msgid "Update"
796 msgstr ""
797
798 #
799 # $response['message'] = __( 'Started...', 'wpml-media' );
800 #
801
802 msgid "Started..."
803 msgstr ""
804
805 # ?>
806 # <div class="message error wpml-media-inactive"><p><?php printf( __( 'WPML Media is enabled but not effective. It requires <a href="%s">WPML</a> in order to work.', 'wpml-media' ),
807 # 'https://wpml.org/' ); ?></p></div>
808
809 msgid "WPML Media is enabled but not effective. It requires <a href=\"%s\">WPML</a> in order to work."
810 msgstr ""
1 <?php
2 /**
3 * Plugin Name: WPML Media
4 * Plugin URI: https://wpml.org/
5 * Description: Add multilingual support for Media files | <a href="https://wpml.org/documentation/getting-started-guide/media-translation/?utm_source=plugin&utm_medium=gui&utm_campaign=wpmlmedia">Documentation</a> | <a href="https://wpml.org/version/media-translation-2-7-3/">WPML Media Translation 2.7.3 release notes</a>
6 * Author: OnTheGoSystems
7 * Author URI: http://www.onthegosystems.com/
8 * Version: 2.7.3
9 * Plugin Slug: wpml-media-translation
10 *
11 * @package wpml/media
12 */
13
14 if ( defined( 'WPML_MEDIA_VERSION' ) ) {
15 return;
16 }
17
18 define( 'WPML_MEDIA_VERSION', '2.7.3' );
19 define( 'WPML_MEDIA_PATH', dirname( __FILE__ ) );
20
21 require_once WPML_MEDIA_PATH . '/vendor/autoload.php';
22
23 require WPML_MEDIA_PATH . '/inc/constants.inc';
24 require WPML_MEDIA_PATH . '/inc/wpml-media-dependencies.class.php';
25 require WPML_MEDIA_PATH . '/inc/wpml-media-upgrade.class.php';
26 if ( is_admin() ) {
27 require_once ABSPATH . 'wp-admin/includes/image.php';
28 }
29
30 function wpml_media_remove_flag_notice() {
31 $wpml_admin_notices = wpml_get_admin_notices();
32 $wpml_admin_notices->remove_notice(
33 WPML_Media_Posts_Media_Flag_Notice::NOTICE_GROUP,
34 WPML_Media_Posts_Media_Flag_Notice::NOTICE_ID
35 );
36 }
37
38 global $WPML_media, $wpdb, $sitepress, $iclTranslationManagement;
39
40 $media_dependencies = new WPML_Media_Dependencies();
41 if ( $media_dependencies->check() ) {
42
43 add_action( 'plugins_loaded', 'wpml_media_core_action_filter_loader', 0 );
44 function wpml_media_core_action_filter_loader() {
45
46 $loaders = array(
47 'WPML_Media_Factory',
48 'WPML_Media_Save_Translation_Factory',
49 'WPML_Media_Attachment_Image_Update_Factory',
50 'WPML_Media_Screen_Options_Factory',
51 'WPML_Media_Posts_Media_Flag_Notice_Factory',
52 'WPML_Media_Set_Posts_Media_Flag_Factory',
53 'WPML_Media_Set_Initial_Language_Factory',
54 'WPML_Media_Selector_Factory',
55 'WPML_Media_Post_Media_Usage_Factory',
56 'WPML_Media_Privacy_Content_Factory',
57 WPML\Media\Widgets\Block\DisplayTranslation::class,
58 );
59
60 $action_filter_loader = new WPML_Action_Filter_Loader();
61 $action_filter_loader->load( $loaders );
62
63 }
64
65 add_action( 'wpml_loaded', 'wpml_media_load_components' );
66 function wpml_media_load_components() {
67
68 if ( class_exists( 'WPML_Current_Screen_Loader_Factory' ) ) {
69
70 $loaders = array(
71 'WPML_Media_Attachments_Query_Factory',
72 'WPML_Media_Post_Images_Translation_Factory',
73 'WPML_Media_Post_Batch_Url_Translation_Factory',
74 'WPML_Media_Custom_Field_Images_Translation_Factory',
75 'WPML_Media_Custom_Field_Batch_Url_Translation_Factory',
76 'WPML_Media_Editor_Notices_Factory',
77 'WPML_Media_Help_Tab_Factory',
78 );
79
80 $action_filter_loader = new WPML_Action_Filter_Loader();
81 $action_filter_loader->load( $loaders );
82 }
83 }
84
85 add_action( 'wpml_st_loaded', 'wpml_media_load_components_st' );
86 function wpml_media_load_components_st() {
87
88 $loaders = array(
89 'WPML_Media_String_Images_Translation_Factory',
90 'WPML_Media_String_Batch_Url_Translation_Factory',
91 );
92
93 $action_filter_loader = new WPML_Action_Filter_Loader();
94 $action_filter_loader->load( $loaders );
95
96 }
97
98 add_action( 'wpml_after_tm_loaded', 'wpml_media_load_components_tm' );
99 function wpml_media_load_components_tm() {
100
101 $loaders = [
102 WPML_Media_Add_To_Basket_Factory::class,
103 WPML_Media_Submitted_Basket_Notice_Factory::class,
104 WPML_Media_Populate_Media_Strings_Translations_Factory::class,
105 ];
106
107 $action_filter_loader = new WPML_Action_Filter_Loader();
108 $action_filter_loader->load( $loaders );
109
110 }
111 }
112
113 add_action( 'deactivate_' . WPML_MEDIA_FOLDER . '/plugin.php', 'wpml_media_deactivation_actions' );
114 function wpml_media_deactivation_actions() {
115 if ( defined( 'ICL_SITEPRESS_VERSION' ) ) {
116 wpml_media_remove_flag_notice();
117 }
118 }
1 === WPML Media Translation ===
2 Stable tag: 2.7.3
...\ No newline at end of file ...\ No newline at end of file
1 .wpml-media-selector td{padding-top:0}.wpml-media-selector a{cursor:pointer}.wpml-media-selector-wrapper-inner{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap}.wpml-media-selector-wrapper .explanation-text{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%}.wpml-media-selector-wrapper label{display:block;text-align:center;-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;margin-bottom:.8em}.wpml-media-selector-wrapper label+label{-webkit-margin-start:5px;-moz-webkit-start:5px;margin-start:5px}.wpml-media-selector-wrapper input[type="checkbox"]{margin-top:0}.wpml-media-selector-zoom{display:block;margin:0 auto 3px;position:relative;height:60px;width:60px}.wpml-media-selector-zoom.translated::before{position:absolute;top:3px;right:3px;color:#fff;content:"\63";font-family:otgs-icons;font-size:9px;background:#46b450;border-radius:30px;width:13px;height:13px;line-height:13px}.wpml-media-selector-zoom.translated::before .rtl{right:auto;left:3px}.wpml-media-selector-zoom::after{content:"\f179";font-family:Dashicons;font-size:24px;color:rgba(255,255,255,0.45);position:absolute;top:50%;left:50%;-webkit-transform:translate(-50%, -50%);transform:translate(-50%, -50%)}.wpml-media-selector-zoom img{max-width:100%;height:auto;-webkit-transition:-webkit-transform .1s linear;transition:-webkit-transform .1s linear;transition:transform .1s linear;transition:transform .1s linear, -webkit-transform .1s linear}.wpml-media-selector-placeholder{width:60px;height:60px;background:#e0e0e0}.wpml-media-selector-placeholder+.wpml-media-selector-placeholder{-webkit-margin-start:5px;-moz-webkit-start:5px;margin-start:5px}.wpml-media-selector-wrapper label:hover img,.wpml-media-selector-wrapper label:focus img{-webkit-transform:scale(2.5);transform:scale(2.5);z-index:10;position:absolute;bottom:45px;left:-2px;-webkit-box-shadow:0 0 10px 0 rgba(0,0,0,0.3);box-shadow:0 0 10px 0 rgba(0,0,0,0.3);background-color:#fff}.wpml-media-selector-wrapper label:hover .wpml-media-selector-zoom::after,.wpml-media-selector-wrapper label:focus .wpml-media-selector-zoom::after{display:none}.wpml-media-selector-toggle{color:#666;margin-bottom:.6em;display:inline-block}.wpml-media-selector-toggle::after{content:"\f140";font-family:Dashicons;font-size:18px;vertical-align:bottom;line-height:1}.wpml-media-selector-toggle.collapsed::after{content:"\f139"}
1 .wpml-media-tablenav{height:auto}.wpml-media-table thead{background:#fff}.wpml-media-dialog .wpml-media-translation-image,.wpml-media-table a{cursor:pointer}.wpml-col-media-title{-webkit-padding-start:0;-moz-padding-start:0;padding-start:0}.wpml-col-media-translations>span,td.wpml-col-media-title>span{width:60px;display:flex;align-items:center;justify-content:center;float:left;padding:1px}td.wpml-col-media-title>span,td.wpml-col-media-translations>span{height:60px}td.wpml-col-media-title .is-non-image,td.wpml-col-media-translations .is-non-image{height:45px;width:32px}td.wpml-col-media-title>span.wpml-col-media-filename{width:auto;padding-left:10px}.wpml-media-translation-image{position:relative;transition:.15s}.wpml-media-translation-image img{display:block;transition:.15s}.wpml-media-translation-image::before{content:'\68';font-family:otgs-icons;color:#fff;opacity:0;position:absolute;left:50%;top:50%;transform:translate(-50%,-50%)}.wpml-media-translation-image .wpml-media-upload-text{opacity:1;position:absolute;bottom:0;left:0;right:0;padding:7px 3px;color:#fff;font-size:.9em;text-align:center;background:#21759b;transition:.15s}.wpml-media-translation-image .wpml-media-upload-text::before{content:"\f317";font-family:dashicons;font-size:20px;vertical-align:top;-webkit-margin-end:6px;moz-margin-end:6px;margin-end:6px}.wpml-media-translation-image:hover{background:#21759b}.wpml-media-translation-image:hover .wpml-media-upload-text,.wpml-media-translation-image:hover img{opacity:0}.wpml-media-translation-image:hover::before{opacity:1}.wpml-media-dialog .wpml-header-original,.wpml-media-dialog .wpml-header-translation{font-size:1.2em;text-align:center}.wpml-media-dialog .wpml-header-translation{width:calc(50% - 5% - 30px - 15px - 35px - 5px)}.wpml-media-dialog .wpml-media-wrapper{width:calc(50% - 5% - 30px - 15px - 35px - 7px);margin:7px 15px;display:inline-block;box-sizing:border-box;vertical-align:top;text-align:center}.wpml-media-dialog .wpml-media-wrapper.wpml-media-upload-handle{-moz-margin-start:83px;-webkit-margin-start:83px;margin-start:83px}.wpml-media-dialog .wpml-media-original-image,.wpml-media-dialog .wpml-media-translation-image{display:inline-block;position:relative;box-shadow:inset 0 0 15px rgba(0,0,0,.1),inset 0 0 0 1px rgba(0,0,0,.05);box-sizing:border-box;width:150px;height:150px}.wpml-media-dialog .wpml-media-original-image img,.wpml-media-dialog .wpml-media-translation-image img{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);max-width:100%;height:150px;width:auto}.wpml-media-dialog .wpml-media-original-image img[src$=".svg"],.wpml-media-dialog .wpml-media-translation-image img[src$=".svg"]{max-height:80%}.wpml-media-dialog .wpml-media-translation-image:before{font-size:50px;content:"\f317";font-family:dashicons}.wpml-media-dialog .wpml-media-original-title,.wpml-media-dialog .wpml-media-translated-title{font-size:.95em;line-height:1.3;margin:0;padding:8px;color:#666;word-break:break-all;word-wrap:break-word;position:absolute;background:#fff;left:0;right:0;bottom:0;overflow:hidden;max-height:100%;text-align:center;box-shadow:inset 0 0 0 1px rgba(0,0,0,.15)}.wpml-media-dialog .wpml-media-original-title:empty,.wpml-media-dialog .wpml-media-translated-title:empty{display:none}.wpml-media-dialog .ui-dialog-buttonpane{padding:8px 16px}.wpml-media-dialog .ui-dialog-buttonpane .ui-dialog-buttonset{float:none}.wpml-media-dialog .ui-dialog-buttonpane .ui-dialog-buttonset .ui-button{margin-left:0}.wpml-media-dialog .notice.text-change-notice{margin-top:30px}#wpml-media-upload-progress-animation{border:1px solid #09c;padding:1px;position:relative;height:16px;border-radius:1px;margin:10px;text-align:left;background:#ddd;box-shadow:inset 1px 3px 6px rgba(0,0,0,.12);display:none}#wpml-media-upload-progress-animation .upload-progress-bar{height:100%;border-radius:3px;background-color:#0073aa;width:0;box-shadow:inset 1px 1px 10px rgba(0,0,0,.11)}#wpml-media-upload-progress-animation .status{left:50%;margin-top:-16px;position:absolute;display:inline-block;color:#fff}@media (min-width:783px){.batch-media-translation-action-wrap{display:flex;align-items:center;justify-content:space-between;margin-bottom:1em}}.batch-media-translation-action-wrap .button-primary{margin:0!important}.batch-media-translation-post-list{list-style:disc;margin-top:-.5em;margin-inline-start:30px}.batch-media-translation-post-list a{text-decoration:none}.batch-media-translation-action-list{margin:0 13px}
...\ No newline at end of file ...\ No newline at end of file
1 .wpml-media-welcome-notice{padding:0;padding-right:30px;position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;border:1px solid #ccd0d4}.rtl .wpml-media-welcome-notice{padding:0;padding-left:30px}.wrap .wpml-media-welcome-notice{margin:15px 0 40px}@media (max-width: 480px){.wpml-media-welcome-notice{display:block}}.wpml-media-welcome-notice *{-webkit-transition:all .2s linear;transition:all .2s linear}.wpml-media-welcome-notice-bg{-webkit-box-flex:0;-ms-flex:0 0 85px;flex:0 0 85px;background:#21759b url("../img/welcome-notice-bg.jpg") no-repeat;background-position:-80px -20px;padding:15px;-webkit-margin-end:20px;margin-inline-end:20px;position:relative}.minimized .wpml-media-welcome-notice-bg{-webkit-box-flex:0;-ms-flex:0 0 60px;flex:0 0 60px;background-position:-80px -105px}@media (max-width: 640px){.wpml-media-welcome-notice-bg{display:none}}.wpml-media-welcome-notice-bg i{position:absolute;top:50%;left:50%;-webkit-transform:translate(-50%, -50%);transform:translate(-50%, -50%)}.wpml-media-welcome-notice-bg i::before{color:#fff;font-size:80px}.minimized .wpml-media-welcome-notice-bg i::before{font-size:50px}.wpml-media-welcome-notice-content{padding:20px;color:#666}.minimized .wpml-media-welcome-notice-content{-ms-flex-item-align:center;align-self:center;padding:15px}.wpml-media-welcome-notice-header{margin-top:0;color:#555}@media (max-width: 480px){.wpml-media-welcome-notice-header{text-align:center}}.minimized .wpml-media-welcome-notice-header{margin-bottom:0}.minimized .wpml-media-welcome-notice-body{display:none}.wpml-media-welcome-notice-action{-ms-flex-item-align:center;align-self:center;padding:15px}@media (max-width: 480px){.wpml-media-welcome-notice-action{text-align:center;padding:0 15px 20px}}.wpml-media-welcome-notice .button-wpml{background:#21759b;border-color:#1d6586;-webkit-box-shadow:0 1px 0 #1d6586;box-shadow:0 1px 0 #1d6586;color:#fff;text-shadow:0 -1px 1px #1d6586,1px 0 1px #1d6586,0 1px 1px #1d6586,-1px 0 1px #1d6586}.wpml-media-welcome-notice .button-wpml:hover,.wpml-media-welcome-notice .button-wpml:focus,.wpml-media-welcome-notice .button-wpml:active{background:#1d688a;border-color:#1d6586}.wpml-media-welcome-notice-toggle{font-size:.85em;position:absolute;bottom:5px;right:15px;color:#aaa;cursor:pointer}.rtl .wpml-media-welcome-notice-toggle{right:auto;left:15px}@media (max-width: 640px){.wpml-media-welcome-notice-toggle{bottom:0}}.wpml-media-welcome-notice-toggle::after{content:'';vertical-align:middle;-webkit-margin-start:.3em;margin-inline-start:.3em;display:inline-block;border:.3em solid transparent}.expanded .wpml-media-welcome-notice-toggle::after{border-bottom:.45em solid;margin-top:-.25em}.minimized .wpml-media-welcome-notice-toggle::after{border-left:.5em solid}.rtl .minimized .wpml-media-welcome-notice-toggle::after{border-left:.3em solid transparent;border-right:.5em solid}.icl_tm_wrap{position:relative}.icl_tm_wrap .overlay{position:absolute;top:0;right:0;bottom:0;left:0;background:rgba(255,255,255,0.95);z-index:10}.icl_tm_wrap .wpml-media-welcome-notice{position:absolute;top:105px;left:50%;width:80%;-webkit-transform:translateX(-50%);transform:translateX(-50%)}
1 var WPML_Media_Batch_Url_Translation = WPML_Media_Batch_Url_Translation || {
2
3 hasDialog: false,
4 dialog: jQuery('#batch-media-translation-wrap'),
5 form: jQuery('#batch-media-translation-form'),
6 globalScope: 0,
7 attachmentId: 0,
8
9 createDialog: function (attachmentId, postsList) {
10 this.hasDialog = true;
11 this.attachmentId = attachmentId;
12
13 if (postsList.length > 0) {
14 this.dialog.find('.usage').show();
15 var ul = this.dialog.find('.usage ul');
16 for (var i in postsList) {
17 var li = postsList[i].url ?
18 '<a href="' + postsList[i].url + '">' + postsList[i].title + '</a>' :
19 postsList[i].title;
20 ul.append('<li>' + li + '</li>');
21 }
22 } else {
23 this.dialog.find('.no-usage').show();
24 }
25 },
26
27 showDialog: function () {
28 this.dialog.show();
29 this.dialog.scrollTop(0);
30 },
31
32 closeDialog: function (event) {
33 var self = WPML_Media_Batch_Url_Translation;
34 if (typeof event !== 'undefined') {
35 event.preventDefault();
36 }
37 self.dialog.hide();
38 self.reset();
39 },
40
41 setInProgress: function (on) {
42 this.form.find('input.button-primary:submit').prop('disabled', on);
43 this.form.find('input[name=global-scan-scope]').prop('disabled', on);
44 },
45
46 runScan: function () {
47 var self = WPML_Media_Batch_Url_Translation;
48 var form = jQuery(this);
49
50 self.globalScope = form.find('input[name=global-scan-scope]:checked').val();
51
52 var nextAction = [];
53 nextAction['wpml_media_translate_media_url_in_posts'] = 'wpml_media_translate_media_url_in_custom_fields';
54
55 if ( wpml_media_batch_translation.is_st_enabled ) {
56 nextAction['wpml_media_translate_media_url_in_custom_fields'] = 'wpml_media_translate_media_url_in_strings';
57 }
58
59 self.setInProgress(true);
60 jQuery.ajax({
61 url: ajaxurl,
62 type: 'post',
63 dataType: 'json',
64 data: form.serialize(),
65 success: function (response) {
66 self.setStatus(response.message);
67 self.scan(null, 'wpml_media_translate_media_url_in_posts', nextAction);
68 }
69 })
70 return false;
71 },
72
73 reset: function () {
74 this.dialog.find('.usage').hide();
75 this.dialog.find('.no-usage').hide();
76 this.dialog.find('.usage ul').html('');
77 this.attachmentId = 0;
78 this.setStatus('');
79 this.dialog.hide();
80 this.hasDialog = false;
81 },
82
83 setStatus: function (text) {
84 this.dialog.find('.status').html(text);
85 },
86
87 setComplete: function (text) {
88 this.setStatus(text);
89 this.setInProgress(false);
90 this.form.hide();
91 this.dialog.removeClass('notice-info').addClass('notice-success');
92 window.setTimeout(this.closeDialog, 3000);
93 },
94
95 scan: function (offset, action, nextAction) {
96 var self = WPML_Media_Batch_Url_Translation;
97 if (typeof offset === 'undefined') {
98 offset = 0;
99 }
100 jQuery.ajax(
101 {
102 url: ajaxurl,
103 type: 'POST',
104 data: {
105 action: action,
106 global: self.globalScope,
107 attachment_id: self.attachmentId,
108 offset: offset
109 },
110 dataType: 'json',
111 success: function (response) {
112 self.setStatus(response.data.message);
113 if (response.data.continue > 0) {
114 self.scan(response.data.offset, action, nextAction);
115 } else {
116 if (nextAction[action]) {
117 self.scan(null, nextAction[action], nextAction);
118 } else {
119 self.setComplete(wpml_media_batch_translation.complete);
120 }
121 }
122 }
123 }
124 );
125 }
126
127 };
128
129
130 jQuery(function ($) {
131 "use strict";
132
133 WPML_Media_Batch_Url_Translation.form.on('submit', WPML_Media_Batch_Url_Translation.runScan);
134 WPML_Media_Batch_Url_Translation.dialog.on('click', '.js-close', WPML_Media_Batch_Url_Translation.closeDialog);
135
136 });
...\ No newline at end of file ...\ No newline at end of file
1 var WPML_Media_Selector = WPML_Media_Selector || {};
2
3 jQuery(function ($) {
4
5 "use strict";
6
7 var dashboardTable = $('#icl-tm-translation-dashboard');
8
9 dashboardTable.find('tbody :checkbox').on('change', showMediaSelector);
10
11 function showMediaSelector() {
12
13 var hasMedia = $(this).closest('tr').data('has-media');
14 if (hasMedia) {
15 var checkbox = $(this);
16 var postSelected = checkbox.prop('checked');
17 var currentRow = $(this).closest('tr');
18 var postId = currentRow.attr('id').replace(/^row_/, '');
19
20 var mediaSelectorRow = $('#js-wpml-media-selector-' + postId);
21 }
22
23 if (postSelected) {
24 if (mediaSelectorRow.length) {
25 mediaSelectorRow.show();
26 } else {
27 loadMediaSelectorContent(currentRow, postId, checkbox);
28 }
29 } else if ( mediaSelectorRow ) {
30 mediaSelectorRow.find('label :checkbox').prop('checked', false);
31 mediaSelectorRow.hide();
32 }
33 }
34
35 function loadMediaSelectorContent(currentRow, postId, checkbox) {
36
37 var mediaSelectorContainer = {};
38 var rowWidth = currentRow.find('td:visible').length;
39 var data = {
40 action: "wpml_media_load_image_selector",
41 post_id: postId,
42 languages: getTargetLanguages()
43 };
44
45 var mediaSelectorRow = $(
46 '<tr class="hidden"><td colspan="' + rowWidth + '"></td></tr>' +
47 '<tr id="js-wpml-media-selector-' + postId + '" class="wpml-media-selector">' +
48 '<td colspan="' + rowWidth + '"></td>' +
49 '</tr>'
50 );
51 mediaSelectorRow.insertAfter(currentRow);
52
53 var postType = currentRow.data('post-type');
54 var preLoader = $('#wpml-media-selector-preloader').html().replace(/%POST_TYPE%/, postType);
55 mediaSelectorContainer = $('#js-wpml-media-selector-' + postId).find('td');
56 mediaSelectorContainer.html(preLoader);
57
58 checkbox.prop('disabled', true);
59
60 $.ajax({
61 url: ajaxurl,
62 type: "POST",
63 dataType: "json",
64 data: data,
65 success: function (response) {
66 if (response.success && response.data.media_files_count) {
67 mediaSelectorContainer.find('.wpml-media-selector-wrapper-inner').html(response.data.html);
68 } else {
69 mediaSelectorRow.remove();
70 }
71 checkbox.prop('disabled', false);
72 }
73 });
74 }
75
76 function getTargetLanguages() {
77 var languages = [];
78 $(':radio[name^="tr_action"][value="1"]:checked', '#icl_tm_languages').each(function () {
79 languages.push($(this).attr('name').replace(/^tr_action\[/, '').replace(/\]$/, ''));
80 });
81
82 return languages;
83 }
84
85 $('#wpml-media-basket-notice').on('click', hideBasketNotice);
86
87 function hideBasketNotice() {
88 $.ajax({
89 url: ajaxurl,
90 type: "POST",
91 dataType: "json",
92 data: {action: 'dismiss_media_basket_notice'},
93 success: function (response) {
94 if (response.data.status) {
95 $('#wpml-media-basket-notice').fadeOut();
96 }
97 }
98 });
99 }
100
101 $('body').on('click', '.js-wpml-media-selector-toggle', toogleMediaList);
102
103 function toogleMediaList(event) {
104 event.preventDefault();
105 $(this).toggleClass('collapsed');
106 $('.wpml-media-selector-wrapper').toggle();
107 $.ajax({
108 url: ajaxurl,
109 type: "POST",
110 dataType: "json",
111 data: {action: 'wpml_media_toogle_show_media_selector'}
112 });
113 return false;
114 }
115
116 });
...\ No newline at end of file ...\ No newline at end of file
1 /* globals wpml_media_popup */
2
3 jQuery(function ($) {
4 var dialogBox = $('#wpml-media-dialog');
5 var dialogForm = $('#wpml-media-dialog-form');
6 var mediaFileUploadForm = $('#wpml-media-file-upload-form');
7
8 dialogBox.dialog({
9 resizable: false,
10 draggable: false,
11 height : 'auto',
12 width : 800,
13 autoOpen : false,
14 modal: true,
15 closeOnEscape: false,
16 dialogClass: 'otgs-ui-dialog wpml-media-dialog wpml-dialog-translate',
17 title: wpml_media_popup.title,
18 create: function () {
19 $('#jquery-ui-style-css').prop('disabled', true);
20 },
21 open: function (event, ui) {
22 $('.ui-dialog-titlebar-close', ui.dialog | ui).hide();
23 repositionDialog();
24 if (WPML_Media_Batch_Url_Translation.hasDialog) {
25 WPML_Media_Batch_Url_Translation.reset();
26 }
27 },
28 close: function () {
29 $('#jquery-ui-style-css').prop('disabled', false);
30 if (WPML_Media_Batch_Url_Translation.hasDialog) {
31 WPML_Media_Batch_Url_Translation.showDialog();
32 }
33 },
34 buttons: [
35 {
36 text: wpml_media_popup.cancel,
37 class: 'alignleft',
38 click: function () {
39 $(this).find('.spinner').remove();
40 $(this).dialog('close');
41 }
42 },
43 {
44 text: wpml_media_popup.save,
45 class: 'button-primary alignright',
46 disabled: true,
47 click: function () {
48 var thisDialog = $(this);
49 disableFormSave();
50 var ajaxLoader = $('<span class="spinner"></span>');
51 var translationForm = thisDialog.find('form');
52 ajaxLoader.insertBefore('.wpml-media-dialog .button-primary').css({
53 visibility: 'visible',
54 float: 'none'
55 });
56 $.ajax({
57 url: ajaxurl,
58 type: 'POST',
59 dataType: 'json',
60 data: translationForm.serialize(),
61 success: function (response) {
62
63 if (response.success) {
64 var originalAttachmentId = translationForm.find('input[name="original-attachment-id"]').val();
65 var translatedLanguage = translationForm.find('input[name="translated-language"]').val();
66 var mediaTranslationWrap = $('#media-attachment-' + originalAttachmentId + '-' + translatedLanguage);
67 var batchMediaTranslationWrap = $('#batch-media-translation-wrap');
68
69 var isMediaUpload = false;
70 var isRestoreMedia = false;
71 if (response.data.thumb) {
72 mediaTranslationWrap.find('img').attr('src', response.data.thumb).fadeIn();
73 mediaTranslationWrap.data('thumb', response.data.thumb);
74 mediaTranslationWrap.data('media-is-translated', 1);
75 mediaTranslationWrap.find('.otgs-ico-edit').hide();
76
77 isMediaUpload = translationForm.find('input[name=update-media-file]').val();
78 } else {
79 mediaTranslationWrap.find('img').attr('src', '').hide();
80 mediaTranslationWrap.data('thumb', '');
81 mediaTranslationWrap.find('.otgs-ico-edit').show();
82 mediaTranslationWrap.find('img')
83 .closest('.js-open-media-translation-dialog')
84 .removeClass('wpml-media-translation-image');
85 mediaTranslationWrap.data('media-is-translated', 0);
86
87 isRestoreMedia = translationForm.find('input[name=restore-media]').val();
88 }
89
90 if (isMediaUpload || isRestoreMedia) {
91 WPML_Media_Batch_Url_Translation.createDialog(originalAttachmentId, response.data.usage);
92 batchMediaTranslationWrap.find('#batch-media-translation-form').show();
93 batchMediaTranslationWrap.removeClass('notice-success');
94 batchMediaTranslationWrap.addClass('notice-info');
95 }
96
97 mediaTranslationWrap.attr('title', mediaTranslationWrap.data('language-name') + ': ' +
98 wpml_media_popup.status_labels[response.data.status]);
99
100 mediaTranslationWrap.data('title', $('#media-title-translation').val());
101 mediaTranslationWrap.data('caption', $('#media-caption-translation').val());
102 mediaTranslationWrap.data('alt_text', $('#media-alt-text-translation').val());
103 mediaTranslationWrap.data('description', $('#media-description-translation').val());
104
105 if (response.data.attachment_id) {
106 mediaTranslationWrap.data('attachment-id', response.data.attachment_id);
107 }
108
109 if (mediaTranslationWrap.find('.otgs-ico-add:visible').length) {
110 var addIcon = mediaTranslationWrap.find('.otgs-ico-add');
111 addIcon.removeClass('otgs-ico-add').addClass('otgs-ico-edit');
112 if (response.data.thumb) {
113 addIcon.hide();
114 }
115 if (response.data.thumb) {
116 mediaTranslationWrap.find('img')
117 .closest('.js-open-media-translation-dialog')
118 .addClass('wpml-media-translation-image');
119 }
120 }
121
122 thisDialog.dialog('close');
123 ajaxLoader.remove();
124
125 translationForm.find('input[name=restore-media]').val(0);
126 translationForm.find('input[name=update-media-file]').val(0);
127 }
128
129 }
130 });
131 }
132 }
133 ]
134 });
135
136 function disableFormSave() {
137 $('.wpml-media-dialog .ui-dialog-buttonset .button-primary').prop('disabled', true);
138 }
139
140 function enableFormSave(e) {
141 if (typeof e !== 'undefined') {
142 var charCode = (e.which) ? e.which : e.keyCode;
143 }
144 if (typeof e === 'undefined' || charCode >= 32 || charCode === 8) {
145 $('.wpml-media-dialog .ui-dialog-buttonset .button-primary').prop('disabled', false);
146 }
147 }
148
149 dialogForm.on('keyup', 'input, textarea', enableFormSave);
150
151 $(window).resize(repositionDialog);
152
153 function repositionDialog() {
154 var winH = $(window).height() - 180;
155 $('.wpml-media-dialog').css({
156 'max-width': '95%'
157 });
158
159 $('.wpml-media-dialog .ui-dialog-content').css({
160 'max-height': winH
161 });
162
163 dialogBox.dialog('option', 'position', {
164 my: 'center',
165 at: 'center',
166 of: window
167 });
168 }
169
170 $('.js-open-media-translation-dialog').click(function () {
171
172 var attachmentRow = $(this).closest('.wpml-media-attachment-row');
173 var translatedMedia = $(this).closest('.wpml-media-wrapper');
174
175 hideAllMediaTextFields();
176 resetProgressAnimation();
177
178 updateDialogImages(attachmentRow, translatedMedia);
179
180 if (translatedMedia.data('media-is-translated')) {
181 enableUsingTranslatedMediaFile();
182 } else {
183 enableUsingOriginalMediaFile();
184 }
185
186 updateDialogFormFields(attachmentRow, translatedMedia);
187
188 updateDialogHiddenFormFields(attachmentRow, translatedMedia);
189
190 dialogBox.dialog('open');
191
192 });
193
194 function updateDialogImages(attachmentRow, translatedMedia) {
195
196 dialogBox.find('.wpml-header-original .wpml-title-flag img').attr('src', attachmentRow.data('flag'));
197 dialogBox.find('.wpml-header-translation .wpml-title-flag img').attr('src', translatedMedia.data('flag'));
198
199 $('.wpml-media-original-image .wpml-media-original-title')
200 .html(attachmentRow.data('is-image') ? '' : attachmentRow.data('file-name'));
201 $('.wpml-media-upload-handle .wpml-media-translated-title')
202 .html(attachmentRow.data('is-image') || !translatedMedia.data('media-is-translated') ? '' : translatedMedia.data('file-name'));
203
204 dialogBox.find('.wpml-header-original strong').html(attachmentRow.data('language-name'));
205 dialogBox.find('.wpml-header-translation strong').html(translatedMedia.data('language-name'));
206
207 var originalImg = dialogBox.find('.wpml-form-row .wpml-media-original-image img');
208 var translatedImg = dialogBox.find('.wpml-form-row .wpml-media-upload-handle img');
209
210 originalImg.attr('src', attachmentRow.data('thumb')).attr('alt', attachmentRow.data('language-code'));
211
212 translatedImg.attr('src', translatedMedia.data('thumb') ? translatedMedia.data('thumb') : attachmentRow.data('thumb')).attr('alt', translatedMedia.data('language-code'));
213 if (!attachmentRow.data('is-image')) {
214 originalImg.addClass('is-non-image');
215 translatedImg.addClass('is-non-image');
216 } else {
217 originalImg.removeClass('is-non-image');
218 translatedImg.removeClass('is-non-image');
219 }
220
221 mediaFileUploadForm.find('input:file').attr('accept', attachmentRow.data('mime-type'));
222 }
223
224 function updateDialogFormFields(attachmentRow, translatedMedia) {
225 if (attachmentRow.data('title')) {
226 $('#media-title-original').val(attachmentRow.data('title'));
227 $('#media-title-translation').val(translatedMedia.data('title'));
228 $('.wpml-form-row-title').show();
229 }
230 if (attachmentRow.data('caption')) {
231 $('#media-caption-original').val(attachmentRow.data('caption'));
232 $('#media-caption-translation').val(translatedMedia.data('caption'));
233 $('.wpml-form-row-caption').show();
234 }
235 if (attachmentRow.data('alt_text')) {
236 $('#media-alt-text-original').val(attachmentRow.data('alt_text'));
237 $('#media-alt-text-translation').val(translatedMedia.data('alt_text'));
238 $('.wpml-form-row-alt-text').show();
239 }
240 if (attachmentRow.data('description')) {
241 $('#media-description-original').val(attachmentRow.data('description'));
242 $('#media-description-translation').val(translatedMedia.data('description'));
243 $('.wpml-form-row-description').show();
244 }
245 }
246
247 function updateDialogHiddenFormFields(attachmentRow, translatedMedia) {
248 dialogForm.find('input[name=original-attachment-id]').val(attachmentRow.data('attachment-id'));
249 dialogForm.find('input[name=translated-attachment-id]').val(translatedMedia.data('attachment-id'));
250 dialogForm.find('input[name=translated-language]').val(translatedMedia.data('language-code'));
251
252 dialogForm.find('input[name=restore-media]').val(0);
253
254 mediaFileUploadForm.find('input[name=attachment-id]').val(translatedMedia.data('attachment-id'));
255 mediaFileUploadForm.find('input[name=original-attachment-id]').val(attachmentRow.data('attachment-id'));
256 mediaFileUploadForm.find('input[name=language]').val(translatedMedia.data('language-code'));
257 }
258
259 function enableUsingTranslatedMediaFile() {
260 dialogForm.find('.wpml-media-upload-text').hide();
261 dialogForm.find('.js-wpml-media-revert').show();
262 }
263
264 function enableUsingOriginalMediaFile() {
265 dialogForm.find('.wpml-media-upload-text').show();
266 dialogForm.find('.js-wpml-media-revert').hide();
267 }
268
269 function hideAllMediaTextFields() {
270 dialogBox
271 .find('.wpml-form-row-title, .wpml-form-row-caption, .wpml-form-row-alt-text, .wpml-form-row-description')
272 .hide();
273 }
274
275 function resetProgressAnimation() {
276 $('.wpml-media-dialog').find('.spinner').remove();
277 }
278
279 dialogBox.find('.js-button-copy').click(function (event) {
280 event.preventDefault();
281 var formRow = $(this).closest('.wpml-form-row');
282 var originalInput = formRow.find('input[id$="original"],textarea[id$="original"]');
283 var translationInput = formRow.find('input[id$="translation"],textarea[id$="translation"]');
284 if (translationInput.val() !== originalInput.val()) {
285 translationInput.val(originalInput.val());
286 enableFormSave();
287 }
288 return false;
289 });
290
291 function triggerMediaUpload(event) {
292 event.preventDefault();
293 mediaFileUploadForm.find('input[type=file]').trigger('click');
294 return false;
295 }
296
297 function restoreMediaFile(event) {
298 event.preventDefault();
299
300 var imagesRow = $(this).closest('.wpml-form-row');
301 var originalImage = imagesRow.find('.wpml-media-original-image img');
302 var translatedImage = imagesRow.find('.wpml-media-translation-image img');
303
304 dialogForm.find('input[name=update-media-file]').val(0);
305
306 translatedImage.attr('src', originalImage.attr('src'));
307
308 dialogForm.find('input[name=restore-media]').val(1);
309
310 enableUsingOriginalMediaFile();
311 enableFormSave();
312
313 return false;
314 }
315
316 $('.js-wpml-media-revert').on('click', 'a', restoreMediaFile);
317
318 dialogBox.find('.wpml-form-row').on('click', '.wpml-media-translation-image', triggerMediaUpload);
319
320 mediaFileUploadForm.find('input[type=file]').change(
321 function () {
322 var file = $(this)[0].files[0];
323 var upload = new Upload(file);
324 upload.doUpload();
325 }
326 );
327
328 // Async file upload
329 var Upload = function (file) {
330 this.file = file;
331 this.progressBar = $('#wpml-media-upload-progress-animation');
332 };
333
334 Upload.prototype.getType = function () {
335 return this.file.type;
336 };
337 Upload.prototype.getSize = function () {
338 return this.file.size;
339 };
340 Upload.prototype.getName = function () {
341 return this.file.name;
342 };
343 Upload.prototype.doUpload = function () {
344 var that = this;
345 var formData = new FormData();
346
347 this.resetError();
348
349 var attachmentId = 0;
350
351 formData.append('file', this.file, this.getName());
352 var fields = mediaFileUploadForm.serializeArray();
353 $.each(fields, function (i, field) {
354 formData.append(field.name, field.value);
355
356 if (field.name === 'attachment-id') {
357 attachmentId = field.value;
358 }
359 });
360
361 that.progressBar.show();
362
363 $.ajax({
364 type: 'POST',
365 url: ajaxurl,
366 xhr: function () {
367 var myXhr = $.ajaxSettings.xhr();
368 if (myXhr.upload) {
369 myXhr.upload.addEventListener('progress', that.progressHandling, false);
370 }
371 return myXhr;
372 },
373 success: function (response) {
374 that.progressBar.hide();
375 if (response.success) {
376 var translatedImgTag = dialogBox.find('.wpml-form-row .wpml-media-upload-handle img');
377 translatedImgTag.attr('src', response.data.thumb);
378 dialogForm.find('input[name=translated-attachment-id]').val(response.data.attachment_id);
379 if (translatedImgTag.hasClass('is-non-image')) {
380 dialogForm.find('.wpml-media-translated-title').html(response.data.name);
381 }
382
383 enableFormSave();
384 dialogForm.find('input[name=update-media-file]').val(1);
385
386 enableUsingTranslatedMediaFile();
387
388 // Reset 'file' field
389 mediaFileUploadForm.find('input[type=file]').val('');
390 } else {
391 that.setError(response.data);
392 }
393 },
394 async: true,
395 data: formData,
396 cache: false,
397 contentType: false,
398 processData: false,
399 timeout: 60000
400 });
401 };
402
403 Upload.prototype.progressHandling = function (event) {
404 var percent = 0;
405 var position = event.loaded || event.position;
406 var total = event.total;
407 var progress_bar_id = '#wpml-media-upload-progress-animation';
408 if (event.lengthComputable) {
409 percent = Math.ceil(position / total * 100);
410 }
411 $(progress_bar_id + ' .upload-progress-bar').css('width', +percent + '%');
412 $(progress_bar_id + ' .status').text(percent + '%');
413 };
414
415 Upload.prototype.setError = function (text) {
416 $('#wpml-media-upload-error').html(text);
417 };
418
419 Upload.prototype.resetError = function () {
420 this.setError('');
421 };
422
423
424 function showTextsChangedNotice(e) {
425 var charCode = (e.which) ? e.which : e.keyCode;
426 if (charCode >= 32 || charCode === 8) {
427 dialogBox.find('.text-change-notice').show();
428 }
429 }
430
431 dialogForm.on('keyup', 'input, textarea', showTextsChangedNotice);
432
433 function dismissTextsChangedNotice() {
434 dialogBox.find('.text-change-notice').fadeOut();
435 $.ajax({
436 type: 'POST',
437 url: ajaxurl,
438 data: {action: 'wpml_media_editor_text_edit_notice_dismissed'},
439 success: function () {
440 },
441 });
442 return false;
443 }
444
445 dialogBox.find('.text-change-notice').on('click', '.notice-dismiss', dismissTextsChangedNotice);
446
447
448 });
1 jQuery(function ($) {
2
3 "use strict";
4
5 var notice = $('#wpml-media-welcome-notice');
6
7 notice.on('click', '.js-toggle', toggleWelcomeNotice);
8 notice.on('click', '.js-dismiss', dismissWelcomeNotice);
9
10 function toggleWelcomeNotice() {
11 notice.toggleClass('minimized expanded');
12
13 var a = $(this);
14 var altText = a.html();
15 a.html(a.data('alt-text'));
16 a.data('alt-text', altText);
17
18 jQuery.ajax({
19 url: ajaxurl,
20 type: 'post',
21 data: {
22 action: wpmlMediaWelcomeNotice.toggleAjaxAction,
23 nonce: wpmlMediaWelcomeNotice.nonce
24 }
25 })
26
27 return false;
28 }
29
30 function dismissWelcomeNotice() {
31 notice.fadeOut(function () {
32 $(this).remove();
33 $('.icl_tm_wrap .overlay').remove();
34 });
35 jQuery.ajax({
36 url: ajaxurl,
37 type: 'post',
38 data: {
39 action: wpmlMediaWelcomeNotice.dismissAjaxAction,
40 nonce: wpmlMediaWelcomeNotice.nonce
41 }
42 })
43 return false;
44 }
45
46 if (!notice.is(':visible')) {
47 var overlay = $('<div class="overlay"></div>');
48 var tmWrap = $('.icl_tm_wrap');
49 overlay.append(notice)
50 tmWrap.prepend(overlay);
51
52 notice.show();
53 }
54
55 notice.on('click', '.wpml-external-link, .button-lg', function (event) {
56 var url = $(this).attr('href');
57 window.open(url, $(this).attr('target'));
58 notice.find('.js-dismiss').show();
59 event.preventDefault();
60 return false;
61 })
62
63 });
...\ No newline at end of file ...\ No newline at end of file
1 /* globals wpml_media_basket_notice_data */
2 var WPML_Media_Submitted_Basket_Notice = WPML_Media_Submitted_Basket_Notice || {};
3
4 jQuery(function ($) {
5 "use strict";
6
7 var form = jQuery('#translation-jobs-translators-form');
8 form.on('wpml-tm-basket-submitted', function(){
9
10 var dialogBox = $('#submitted-basket-notice-dialog');
11 dialogBox.dialog({
12 modal:true,
13 closeOnEscape: false,
14 dialogClass: "no-close otgs-ui-dialog",
15 resizable: false,
16 draggable: false,
17 width: 600,
18 open: function() {
19 repositionDialog();
20 wpmlTMBasket.dialogs.push( 'media' );
21 wpmlTMBasket.redirect = false;
22 },
23 buttons: [
24 {
25 text: wpml_media_basket_notice_data.button_label,
26 class: 'button-primary',
27 click: function() {
28 dialogBox.dialog('close');
29 }
30 }
31 ],
32 close: function() {
33 wpmlTMBasket.dialogs.splice( wpmlTMBasket.dialogs.indexOf( 'media' ), 1 );
34
35 if(0 === wpmlTMBasket.dialogs.length) {
36 location.href = dialogBox.data('redirect-url');
37 }
38 }
39 });
40
41 $(window).resize(repositionDialog);
42
43 function repositionDialog() {
44 var winH = $(window).height() - 180;
45 $(".otgs-ui-dialog .ui-dialog-content").css({
46 "max-height": winH
47 });
48 $(".otgs-ui-dialog").css({
49 "max-width": "95%"
50 });
51 dialogBox.dialog("option", "position", {
52 my: "center",
53 at: "center",
54 of: window
55 });
56 }
57 });
58 });
1 var WPML_Media_2_3_0_Upgrade = WPML_Media_2_3_0_Upgrade || {};
2
3 jQuery(function ($) {
4
5 "use strict";
6
7 var updateContainer = $("#wpml-media-2-3-0-update");
8 var updateButton = updateContainer.find(".button-primary");
9 var spinner = updateContainer.find(".spinner");
10 var nonce = updateContainer.find("input[name=nonce]").val();
11 var statusContainer = updateContainer.find('.status');
12
13 var mediaFlagNoticeContainer = false;
14 if ($('#wpml-media-posts-media-flag').length) {
15 mediaFlagNoticeContainer = $('#wpml-media-posts-media-flag');
16 } else if ($('.otgs-notice[data-id=wpml-media-posts-media-flag]').length) {
17 mediaFlagNoticeContainer = $('.otgs-notice[data-id=wpml-media-posts-media-flag]');
18 }
19 if (mediaFlagNoticeContainer) {
20 mediaFlagNoticeContainer.hide();
21 $('.wrap-wpml-media-upgrade h2').hide();
22 }
23
24 updateButton.on("click", function () {
25 showProgress();
26 runUpgrade();
27 });
28
29 function showProgress() {
30 spinner.css({visibility: "visible"});
31 updateButton.prop("disabled", true);
32 }
33
34 function hideProgress() {
35 spinner.css({visibility: "hidden"});
36 updateButton.prop("disabled", false);
37 }
38
39 function setStatus(statusText) {
40 statusContainer.html(statusText);
41 }
42
43 function runUpgrade() {
44 var data = {
45 action: "wpml_media_2_3_0_upgrade",
46 nonce: nonce,
47 step: "reset-new-content-settings"
48 };
49 $.ajax({
50 url: ajaxurl,
51 type: "POST",
52 dataType: "json",
53 data: data,
54 success: function (response) {
55 if (response.data.status) {
56 setStatus(response.data.status);
57 }
58 runAttachmentMigration(0, 1, 0);
59 }
60 });
61 }
62
63 function runAttachmentMigration(offset, batchSizeFactor, timestamp) {
64 var data = {
65 action: "wpml_media_2_3_0_upgrade",
66 nonce: nonce,
67 step: "migrate-attachments",
68 offset: offset,
69 batch_size_factor: batchSizeFactor,
70 timestamp: timestamp
71 };
72 $.ajax({
73 url: ajaxurl,
74 type: "POST",
75 dataType: "json",
76 data: data,
77 success: function (response) {
78 if (response.data.status) {
79 setStatus(response.data.status);
80 }
81 if (response.data.goon) {
82 runAttachmentMigration(response.data.offset, response.data.batch_size_factor, response.data.timestamp);
83 } else {
84 if (mediaFlagNoticeContainer) {
85 $('#wpml-media-2-3-0-update').hide();
86 if (mediaFlagNoticeContainer.find('input.button-primary').length) {
87 mediaFlagNoticeContainer.show();
88 mediaFlagNoticeContainer.find('input.button-primary').trigger('click');
89 } else {
90 location.href = mediaFlagNoticeContainer.find('a').attr('href')+'&run_setup=1&redirect_to='+location.href;
91 }
92 } else {
93 hideProgress();
94 location.reload();
95 }
96 }
97 }
98 });
99 }
100
101 });
1 var WPML_Media_Posts_Media_Flag = WPML_Media_Posts_Media_Flag || {};
2
3 jQuery(function ($) {
4
5 "use strict";
6
7 var updateContainer = $('#wpml-media-posts-media-flag');
8
9 var updateButton = updateContainer.find('.button-primary');
10 var spinner = updateContainer.find('.spinner');
11
12 var prepareAction = updateContainer.data('prepareAction');
13 var prepareNonce = updateContainer.data('prepareNonce');
14
15 var processAction = updateContainer.data('processAction');
16 var processNonce = updateContainer.data('processNonce');
17
18 var statusContainer = updateContainer.find('.status');
19
20 function getQueryParams(qs) {
21 qs = qs.split('+').join(' ');
22
23 var params = {},
24 tokens,
25 re = /[?&]?([^=]+)=([^&]*)/g;
26
27 while (tokens = re.exec(qs)) {
28 params[decodeURIComponent(tokens[1])] = decodeURIComponent(tokens[2]);
29 }
30
31 return params;
32 }
33
34
35 var queryParams = getQueryParams(location.search);
36 if (queryParams.run_setup) {
37 showProgress();
38 runSetup();
39 }
40
41 updateButton.on("click", function () {
42 showProgress();
43 runSetup();
44 });
45
46 function showProgress() {
47 spinner.css({visibility: "visible"});
48 updateButton.prop("disabled", true);
49 }
50
51 function hideProgress() {
52 spinner.css({visibility: "hidden"});
53 updateButton.prop("disabled", false);
54 }
55
56 function setStatus(statusText) {
57 statusContainer.html(statusText);
58 }
59
60 function runSetup() {
61 var data = {
62 action: prepareAction,
63 nonce: prepareNonce
64 };
65 $.ajax({
66 url: ajaxurl,
67 type: "POST",
68 dataType: "json",
69 data: data,
70 success: function (response) {
71 handleResponse(response);
72 if (!response.success) {
73 return;
74 }
75
76 if (response.data.status) {
77 setStatus(response.data.status);
78 }
79 setInitialLanguage();
80 },
81 error: function (jqXHR, status, error) {
82 statusContainer.html(jqXHR.statusText || status || error);
83 }
84 });
85 }
86
87 function handleResponse(response) {
88 var error = [];
89
90 if (response.error) {
91 error.push(response.error);
92 }
93 if (!response.success && response.data) {
94 error.push(response.data);
95 }
96
97 if (error.length) {
98 statusContainer.html('<pre>' + error.join('</pre><pre>') + '</pre>');
99 }
100 }
101
102 function setInitialLanguage() {
103 var data = {
104 action: processAction,
105 nonce: processNonce
106 };
107 $.ajax({
108 url: ajaxurl,
109 type: "POST",
110 dataType: "json",
111 data: data,
112 success: function (response) {
113 handleResponse(response);
114 if (!response.success) {
115 return;
116 }
117
118 var message = response.message ? response.message : response.data.message;
119 setStatus(message);
120 setHasMediaFlag(0);
121 },
122 error: function (jqXHR, status, error) {
123 statusContainer.html(jqXHR.statusText || status || error);
124 }
125 });
126 }
127
128 function setHasMediaFlag(offset) {
129 var data = {
130 action: processAction,
131 nonce: processNonce,
132 offset: offset
133 };
134 $.ajax({
135 url: ajaxurl,
136 type: "POST",
137 dataType: "json",
138 data: data,
139 success: function (response) {
140 handleResponse(response);
141 if (!response.success) {
142 return;
143 }
144
145 if (response.data.status) {
146 setStatus(response.data.status);
147 }
148 if (response.data.continue) {
149 setHasMediaFlag(response.data.offset);
150 } else {
151 if (queryParams.redirect_to) {
152 location.href = queryParams.redirect_to;
153 } else {
154 location.reload();
155 }
156 }
157 },
158 error: function (jqXHR, status, error) {
159 statusContainer.html(jqXHR.statusText || status || error);
160 }
161 });
162 }
163
164 });
1 <div id="wpml-media-selector-preloader" class="hidden">
2 <a class="js-wpml-media-selector-toggle wpml-media-selector-toggle {% if hide_selector %}collapsed{% endif %}">{{ strings.has_posts }}</a>
3 <div class="wpml-media-selector-wrapper" {% if hide_selector %}style="display: none"{% endif %}>
4 <div class="wpml-media-selector-wrapper-inner">
5 <div class="wpml-media-selector-placeholder">&nbsp;</div>
6 <div class="wpml-media-selector-placeholder">&nbsp;</div>
7 <div class="wpml-media-selector-placeholder">&nbsp;</div>
8 <p class="explanation-text small"><i>{{ strings.loading }}</i></p>
9 </div>
10 </div>
11 </div>
...\ No newline at end of file ...\ No newline at end of file
1 {% for id, file in files %}
2 <label>
3 <span class="wpml-media-selector-zoom{% if file.translated %} translated{% endif %}">
4 <img src="{{ file.thumbnail }}" title="{{ file.name }}" alt="{{ file.name }}"/>
5 </span>
6 <input type="checkbox" name="post[{{ post_id }}][media-translation][]" value="{{ id }}"/>
7 </label>
8 {% endfor %}
1 <div id="submitted-basket-notice-dialog" class="otgs-confirmation-dialog" data-redirect-url="{{ redirect_url }}" title="{{ strings.dialog_title }}" style="display: none">
2 <div class="otgs-confirmation-dialog-icon">
3 <span class="otgs-ico-media"></span>
4 </div>
5 <div class="otgs-confirmation-dialog-content">
6 <p>
7 <strong>{{ strings.content_with_media_sent }}</strong>
8 {{ strings.media_texts_translated|raw }}
9 </p>
10 <p>
11 {{ strings.use_different_media|raw }}
12 </p>
13 <p>
14 <a href="{{ learn_more_url }}" class="wpml-external-link" target="_blank">{{ strings.learn_more }}</a>
15 </p>
16 </div>
17 </div>
1 <div id="batch-media-translation-wrap" class="batch-media-translation-wrap notice otgs-notice notice-info otgs-is-dismissible" style="display: none">
2 <span class="notice-dismiss js-close"><span class="screen-reader-text">{{ strings.close }}</span></span>
3 <form id="batch-media-translation-form" method="post" action="">
4 <input type="hidden" name="action" value="wpml_media_scan_prepare" />
5 <div class="usage" style="display: none">
6 <p>{{ strings.was_replaced }}</p>
7 <ul class="batch-media-translation-post-list"></ul>
8 <p>{{ strings.other_posts }}</p>
9 </div>
10 <div class="no-usage" style="display: none">
11 <p>{{ strings.without_usage }}</p>
12 </div>
13 <div class="batch-media-translation-action-wrap">
14 <ul class="batch-media-translation-action-list">
15 <li>
16 <label>
17 <input type="radio" name="global-scan-scope" value="0" checked/>{{ strings.scan_for_this_media }}
18 </label>
19 </li>
20 <li>
21 <label>
22 <input type="radio" name="global-scan-scope" value="1"/>{{ strings.scan_for_all_media }}
23 </label>
24 </li>
25 </ul>
26 <p class="text-right">
27 <input type="submit" class="button-primary" value="{{ strings.button_label }}"/>
28 </p>
29 </div>
30 </form>
31 <p class="status"></p>
32 </div>
...\ No newline at end of file ...\ No newline at end of file
1 <form id="posts-filter" method="get">
2 <input type="hidden" name="page" value="wpml-media"/>
3 <input type="hidden" name="sm" value="media-translation"/>
4 {{ nonce|raw }}
5 <label for="filter-by-date" class="screen-reader-text">{{ strings.filter_by_date }}</label>
6 <select id="filter-by-date" name="m">
7 <option {% if( selected_month == 0 ) %}selected="selected"{% endif %}
8 value="0">{{ strings.all_dates }}</option>
9 {% for month in months %}
10 <option {% if( selected_month == month.id ) %}selected="selected"{% endif %}
11 value="{{ month.id }}">{{ month.label }}</option>
12 {% endfor %}
13 </select>
14 {{ strings.in }}
15 <label for="filter-by-language" class="screen-reader-text">{{ strings.filter_by_language }}</label>
16 <select id="filter-by-language" name="slang">
17 <option value="">{{ strings.any_language }}</option>
18 {% for code, language in languages %}
19 <option {% if( from_language == code ) %}selected="selected"{% endif %}
20 value="{{ code }}">{{ language.name }}</option>
21 {% endfor %}
22 </select>
23 {{ strings.to }}
24 <select id="filter-by-language" name="tlang">
25 <option value="">{{ strings.any_language }}</option>
26 {% for code, language in languages %}
27 <option {% if( to_language == code ) %}selected="selected"{% endif %}
28 value="{{ code }}">{{ language.name }}</option>
29 {% endfor %}
30 </select>
31
32 <label for="filter-by-translation-status" class="screen-reader-text">{{ strings.filter_by_status }}</label>
33 <select id="filter-by-translation-status" name="status">
34 <option {% if( selected_status|length == 0 ) %}selected="selected"{% endif %}
35 value="">{{ strings.status_all }}</option>
36 <option {% if( selected_status == statuses.not_translated and selected_status|length > 0 ) %}selected="selected"{% endif %}
37 value="{{ statuses.not_translated }}">{{ strings.status_not }}</option>
38 <option {% if( selected_status == statuses.in_progress and selected_status|length > 0 ) %}selected="selected"{% endif %}
39 value="{{ statuses.in_progress }}">{{ strings.status_in_progress }}</option>
40 <option {% if( selected_status == statuses.translated ) %}selected="selected"{% endif %}
41 value="{{ statuses.translated }}">{{ strings.status_translated }}</option>
42 <option {% if( selected_status == statuses.needs_translation ) %}selected="selected"{% endif %}
43 value="{{ statuses.needs_translation }}">{{ strings.status_needs_translation }}</option>
44 </select>
45
46 <label class="screen-reader-text" for="media-search-input">{{ strings.search_media }}</label>
47 <input size="25" type="search" id="media-search-input" placeholder="{{ strings.search_placeholder }}" name="s"
48 value="{{ search }}">
49 <input type="submit" class="button action" value="{{ strings.search_button_label }}">
50 </form>
...\ No newline at end of file ...\ No newline at end of file
1 <div id="wpml-media-dialog">
2 <span class="ajax-loader"></span>
3 <form id="wpml-media-dialog-form" method="post" enctype="multipart/form-data">
4 {{ nonce|raw }}
5 <input type="hidden" name="original-attachment-id" value=""/>
6 <input type="hidden" name="translated-attachment-id" value=""/>
7 <input type="hidden" name="translated-language" value=""/>
8 <input type="hidden" name="restore-media" value="0"/>
9 <input type="hidden" name="update-media-file" value="0"/>
10 <input type="hidden" name="action" value="wpml_media_save_translation"/>
11 <header class="wpml-media-translation-header">
12 <h3 class="wpml-header-original">{{ strings.original }}<span class="wpml-title-flag"><img
13 src="#"></span><strong>%from_language%</strong>
14 </h3>
15 <h3 class="wpml-header-translation">{{ strings.translation }}<span class="wpml-title-flag"><img
16 src="#"></span><strong>%to_language%</strong>
17 </h3>
18 </header>
19
20
21 <div class="wpml-form-row">
22
23 <label>{{ strings.file }}</label>
24
25 <span class="wpml-media-wrapper">
26 <span class="wpml-media-original-image">
27 <img src="#" alt="#">
28 <span class="wpml-media-original-title"></span>
29 </span>
30 </span>
31
32 <span class="wpml-media-wrapper wpml-media-upload-handle">
33 <a class="wpml-media-translation-image drag-drop-area">
34 <img src="#" alt="#">
35 <span class="wpml-media-translated-title"></span>
36 <span class="wpml-media-upload-text hidden">{{ strings.use_different_file }}</span>
37 </a>
38
39 <span class="js-wpml-media-revert wpml-display-block text-center hidden">
40 <a class="button button-secondary button-small">{{ strings.revert_to_original }}</a>
41 </span>
42 </span>
43
44 </div>
45
46 <div class="wpml-form-row wpml-form-row-title hidden">
47 <label for="media-title">{{ strings.name }}</label>
48 <input readonly value="" id="media-title-original" type="text">
49 <button class="button-copy button-secondary js-button-copy otgs-ico-copy"
50 title="{{ strings.copy_from_original }}"></button>
51 <input name="translation[title]" id="media-title-translation" value="" type="text">
52 </div>
53 <div class="wpml-form-row wpml-form-row-caption hidden">
54 <label for="media-caption">{{ strings.caption }}</label>
55 <textarea readonly id="media-caption-original" cols="22" rows="4"></textarea>
56 <button class="button-copy button-secondary js-button-copy otgs-ico-copy"
57 title="{{ strings.copy_from_original }}"></button>
58 <textarea name="translation[caption]" id="media-caption-translation" cols="22" rows="4"></textarea>
59 </div>
60 <div class="wpml-form-row wpml-form-row-alt-text hidden">
61 <label for="media-alt-text">{{ strings.alt_text }}</label>
62 <input readonly value="" id="media-alt-text-original" type="text">
63 <button class="button-copy button-secondary js-button-copy otgs-ico-copy"
64 title="{{ strings.copy_from_original }}"></button>
65 <input name="translation[alt-text]" id="media-alt-text-translation" value="" type="text">
66 </div>
67 <div class="wpml-form-row wpml-form-row-description hidden">
68 <label for="media-description">{{ strings.description }}</label>
69 <textarea readonly id="media-description-original" cols="22" rows="4"></textarea>
70 <button class="button-copy button-secondary js-button-copy otgs-ico-copy"
71 title="{{ strings.copy_from_original }}"></button>
72 <textarea name="translation[description]" id="media-description-translation" cols="22" rows="4"></textarea>
73 </div>
74 </form>
75 {% if( show_text_change_notice) %}
76 <div class="text-change-notice notice notice-info inline is-dismissible hidden">
77 <p>{{ strings.texts_change_notice }}</p>
78 </div>
79 {% endif %}
80 <div id="wpml-media-upload-progress-animation">
81 <div class="upload-progress-bar"></div>
82 <div class="status">0%</div>
83 </div>
84 <span id="wpml-media-upload-error" class="icl_error_text"></span>
85 </div>
86 <form id="wpml-media-file-upload-form" enctype="multipart/form-data">
87 <input type="hidden" name="action" value="wpml_media_upload_file">
88 <input type="hidden" name="attachment-id" value="">
89 <input type="hidden" name="original-attachment-id" value="">
90 <input type="hidden" name="language" value="">
91 {{ nonce|raw }}
92 <input style="display:none" type="file" name="image" accept="image/*" />
93 </form>
1 <tr class="wpml-media-attachment-row" data-attachment-id="{{ attachment.post.ID }}"
2 data-language-code="{{ attachment.language }}"
3 data-language-name="{{ languages[attachment.language].name }}"
4 data-is-image="{{ attachment.is_image }}"
5 data-thumb="{{ attachment.thumb.src }}"
6 data-file-name="{{ attachment.file_name }}"
7 data-mime-type="{{ attachment.mime_type }}"
8 data-title="{{ attachment.post.post_title }}"
9 data-caption="{{ attachment.post.post_excerpt }}"
10 data-alt_text="{{ attachment.alt }}"
11 data-description="{{ attachment.post.post_content }}"
12 data-flag="{{ languages[attachment.language].flag }}">
13 <td class="wpml-col-media-title">
14 <span title="{{ languages[attachment.language].name }}" class="wpml-media-original-flag js-otgs-popover-tooltip"
15 data-tippy-distance="-12">
16 <img src="{{ languages[attachment.language].flag }}" width="16" height="12" alt="{{ attachment.language }}">
17 </span>
18 <span class="wpml-media-wrapper">
19 <img src="{{ attachment.thumb.src }}"
20 width="{{ attachment.thumb.width }}"
21 height="{{ attachment.thumb.height }}"
22 alt="{{ attachment.language }}"
23 {% if not attachment.is_image %}
24 class="is-non-image"
25 {% else %}
26 data-tippy-boundary="viewport"
27 data-tippy-flip="true"
28 data-tippy-placement="right"
29 data-tippy-maxWidth= "{{ attachment.preview.width }}px"
30 data-tippy-content="{{ '<img style=\"max-width:100%;width:auto;max-height:' ~ attachment.preview.height ~ 'px;height:auto;\" src="' ~ attachment.url ~ '" />' }}"
31 class="js-otgs-popover-tooltip"
32 {% endif %}
33 >
34 </span>
35 <span class="wpml-col-media-filename">
36 {{ attachment.file_name }}
37 </span>
38 </td>
39 <td class="wpml-col-media-translations">
40 {% for code, language in languages %}
41 {% if target_language is empty or target_language == code %}
42 {% if attachment.language == code %}
43 <span class="js-otgs-popover-tooltip" data-tippy-distance="-12"
44 title="{{ languages[attachment.language].name }}: {{ strings.original_language }}">
45 <i class="otgs-ico-original"></i>
46 </span>
47 {% else %}
48 <span class="wpml-media-wrapper js-otgs-popover-tooltip"
49 id="media-attachment-{{ attachment.post.ID }}-{{ code }}"
50 data-file-name="{{ attachment.translations[code].file_name }}"
51 title="{{ strings.edit_translation | format(languages[code].name ) }}"
52 {% if not attachment.translations[code].media_is_translated %}
53 data-tippy-distance="-12"
54 {% endif %}
55 data-attachment-id="{{ attachment.translations[code].id }}"
56 data-language-code="{{ code }}"
57 data-language-name="{{ language.name }}"
58 data-url="{{ attachment.url }}"
59 data-thumb="{{ attachment.translations[code].thumb.src }}"
60 data-title="{{ attachment.translations[code].title }}"
61 data-caption="{{ attachment.translations[code].caption }}"
62 data-alt_text="{{ attachment.translations[code].alt }}"
63 data-description="{{ attachment.translations[code].description }}"
64 data-flag="{{ languages[code].flag }}"
65 data-media-is-translated="{{ attachment.translations[code].media_is_translated }}">
66 <a class="js-open-media-translation-dialog {% if attachment.translations[code].media_is_translated %}wpml-media-translation-image{% endif %}">
67 <img src="{{ attachment.translations[code].thumb.src }}"
68 width="{{ attachment.thumb.width }}" height="{{ attachment.thumb.height }}"
69 alt="{{ attachment.language }}"
70 {% if not attachment.is_image %}class="is-non-image"{% endif %}
71 {% if not attachment.translations[code].media_is_translated %}style="display:none"{% endif %}>
72 <i class="{% if attachment.translations[code].id %}otgs-ico-edit{% else %}otgs-ico-add{% endif %}"
73 {% if attachment.translations[code].media_is_translated %}style="display:none"{% endif %}></i>
74 </a>
75 </span>
76 {% endif %}
77 {% endif %}
78 {% endfor %}
79 </td>
80 </tr>
1 <div class="wrap">
2
3 <h2>{{ strings.heading }}</h2>
4
5 {% include 'batch-translation.twig' with batch_translation %}
6
7 <div class="tablenav top wpml-media-tablenav">
8 {% include 'media-translation-filters.twig' %}
9 </div>
10
11 <table class="widefat striped wpml-media-table js-otgs-table-sticky-header">
12 <thead>
13 <tr>
14 <th class="wpml-col-media-title">{{ strings.original_language }}</th>
15 <th class="wpml-col-media-translations">
16 {% for code, language in languages %}
17 {% if target_language is empty or target_language == code %}
18 <span class="js-otgs-popover-tooltip" title="{{ language.name }}"><img src="{{ language.flag }}"
19 width="16" height="12"
20 alt="{{ language.code }}"></span>
21 {% endif %}
22 {% endfor %}
23 </th>
24 </tr>
25 </thead>
26 <tbody>
27 {% if attachments %}
28 {% for attachment in attachments %}
29 {% include 'media-translation-table-row.twig' with {'attachment' : attachment, 'languages': languages, 'strings': strings, 'target_language': target_language } only %}
30 {% endfor %}
31 {% else %}
32 <tr>
33 <td colspan="2">{{ strings.no_attachments }}</td>
34 </tr>
35 {% endif %}
36 </tbody>
37
38 </table>
39
40 <div class="tablenav bottom">
41 {% include 'pagination.twig' with {'pagination_model' : pagination} only %}
42
43 {% include 'media-translation-popup.twig' %}
44
45 </div>
46
47 </div>
...\ No newline at end of file ...\ No newline at end of file
1 <?php
2
3 // autoload.php @generated by Composer
4
5 require_once __DIR__ . '/composer/autoload_real.php';
6
7 return ComposerAutoloaderInitf770f44c420bc322254dccc6e9ae55eb::getLoader();
1 <?php
2
3 /*
4 * This file is part of Composer.
5 *
6 * (c) Nils Adermann <naderman@naderman.de>
7 * Jordi Boggiano <j.boggiano@seld.be>
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12
13 namespace Composer\Autoload;
14
15 /**
16 * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
17 *
18 * $loader = new \Composer\Autoload\ClassLoader();
19 *
20 * // register classes with namespaces
21 * $loader->add('Symfony\Component', __DIR__.'/component');
22 * $loader->add('Symfony', __DIR__.'/framework');
23 *
24 * // activate the autoloader
25 * $loader->register();
26 *
27 * // to enable searching the include path (eg. for PEAR packages)
28 * $loader->setUseIncludePath(true);
29 *
30 * In this example, if you try to use a class in the Symfony\Component
31 * namespace or one of its children (Symfony\Component\Console for instance),
32 * the autoloader will first look for the class under the component/
33 * directory, and it will then fallback to the framework/ directory if not
34 * found before giving up.
35 *
36 * This class is loosely based on the Symfony UniversalClassLoader.
37 *
38 * @author Fabien Potencier <fabien@symfony.com>
39 * @author Jordi Boggiano <j.boggiano@seld.be>
40 * @see https://www.php-fig.org/psr/psr-0/
41 * @see https://www.php-fig.org/psr/psr-4/
42 */
43 class ClassLoader
44 {
45 /** @var ?string */
46 private $vendorDir;
47
48 // PSR-4
49 /**
50 * @var array[]
51 * @psalm-var array<string, array<string, int>>
52 */
53 private $prefixLengthsPsr4 = array();
54 /**
55 * @var array[]
56 * @psalm-var array<string, array<int, string>>
57 */
58 private $prefixDirsPsr4 = array();
59 /**
60 * @var array[]
61 * @psalm-var array<string, string>
62 */
63 private $fallbackDirsPsr4 = array();
64
65 // PSR-0
66 /**
67 * @var array[]
68 * @psalm-var array<string, array<string, string[]>>
69 */
70 private $prefixesPsr0 = array();
71 /**
72 * @var array[]
73 * @psalm-var array<string, string>
74 */
75 private $fallbackDirsPsr0 = array();
76
77 /** @var bool */
78 private $useIncludePath = false;
79
80 /**
81 * @var string[]
82 * @psalm-var array<string, string>
83 */
84 private $classMap = array();
85
86 /** @var bool */
87 private $classMapAuthoritative = false;
88
89 /**
90 * @var bool[]
91 * @psalm-var array<string, bool>
92 */
93 private $missingClasses = array();
94
95 /** @var ?string */
96 private $apcuPrefix;
97
98 /**
99 * @var self[]
100 */
101 private static $registeredLoaders = array();
102
103 /**
104 * @param ?string $vendorDir
105 */
106 public function __construct($vendorDir = null)
107 {
108 $this->vendorDir = $vendorDir;
109 }
110
111 /**
112 * @return string[]
113 */
114 public function getPrefixes()
115 {
116 if (!empty($this->prefixesPsr0)) {
117 return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
118 }
119
120 return array();
121 }
122
123 /**
124 * @return array[]
125 * @psalm-return array<string, array<int, string>>
126 */
127 public function getPrefixesPsr4()
128 {
129 return $this->prefixDirsPsr4;
130 }
131
132 /**
133 * @return array[]
134 * @psalm-return array<string, string>
135 */
136 public function getFallbackDirs()
137 {
138 return $this->fallbackDirsPsr0;
139 }
140
141 /**
142 * @return array[]
143 * @psalm-return array<string, string>
144 */
145 public function getFallbackDirsPsr4()
146 {
147 return $this->fallbackDirsPsr4;
148 }
149
150 /**
151 * @return string[] Array of classname => path
152 * @psalm-return array<string, string>
153 */
154 public function getClassMap()
155 {
156 return $this->classMap;
157 }
158
159 /**
160 * @param string[] $classMap Class to filename map
161 * @psalm-param array<string, string> $classMap
162 *
163 * @return void
164 */
165 public function addClassMap(array $classMap)
166 {
167 if ($this->classMap) {
168 $this->classMap = array_merge($this->classMap, $classMap);
169 } else {
170 $this->classMap = $classMap;
171 }
172 }
173
174 /**
175 * Registers a set of PSR-0 directories for a given prefix, either
176 * appending or prepending to the ones previously set for this prefix.
177 *
178 * @param string $prefix The prefix
179 * @param string[]|string $paths The PSR-0 root directories
180 * @param bool $prepend Whether to prepend the directories
181 *
182 * @return void
183 */
184 public function add($prefix, $paths, $prepend = false)
185 {
186 if (!$prefix) {
187 if ($prepend) {
188 $this->fallbackDirsPsr0 = array_merge(
189 (array) $paths,
190 $this->fallbackDirsPsr0
191 );
192 } else {
193 $this->fallbackDirsPsr0 = array_merge(
194 $this->fallbackDirsPsr0,
195 (array) $paths
196 );
197 }
198
199 return;
200 }
201
202 $first = $prefix[0];
203 if (!isset($this->prefixesPsr0[$first][$prefix])) {
204 $this->prefixesPsr0[$first][$prefix] = (array) $paths;
205
206 return;
207 }
208 if ($prepend) {
209 $this->prefixesPsr0[$first][$prefix] = array_merge(
210 (array) $paths,
211 $this->prefixesPsr0[$first][$prefix]
212 );
213 } else {
214 $this->prefixesPsr0[$first][$prefix] = array_merge(
215 $this->prefixesPsr0[$first][$prefix],
216 (array) $paths
217 );
218 }
219 }
220
221 /**
222 * Registers a set of PSR-4 directories for a given namespace, either
223 * appending or prepending to the ones previously set for this namespace.
224 *
225 * @param string $prefix The prefix/namespace, with trailing '\\'
226 * @param string[]|string $paths The PSR-4 base directories
227 * @param bool $prepend Whether to prepend the directories
228 *
229 * @throws \InvalidArgumentException
230 *
231 * @return void
232 */
233 public function addPsr4($prefix, $paths, $prepend = false)
234 {
235 if (!$prefix) {
236 // Register directories for the root namespace.
237 if ($prepend) {
238 $this->fallbackDirsPsr4 = array_merge(
239 (array) $paths,
240 $this->fallbackDirsPsr4
241 );
242 } else {
243 $this->fallbackDirsPsr4 = array_merge(
244 $this->fallbackDirsPsr4,
245 (array) $paths
246 );
247 }
248 } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
249 // Register directories for a new namespace.
250 $length = strlen($prefix);
251 if ('\\' !== $prefix[$length - 1]) {
252 throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
253 }
254 $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
255 $this->prefixDirsPsr4[$prefix] = (array) $paths;
256 } elseif ($prepend) {
257 // Prepend directories for an already registered namespace.
258 $this->prefixDirsPsr4[$prefix] = array_merge(
259 (array) $paths,
260 $this->prefixDirsPsr4[$prefix]
261 );
262 } else {
263 // Append directories for an already registered namespace.
264 $this->prefixDirsPsr4[$prefix] = array_merge(
265 $this->prefixDirsPsr4[$prefix],
266 (array) $paths
267 );
268 }
269 }
270
271 /**
272 * Registers a set of PSR-0 directories for a given prefix,
273 * replacing any others previously set for this prefix.
274 *
275 * @param string $prefix The prefix
276 * @param string[]|string $paths The PSR-0 base directories
277 *
278 * @return void
279 */
280 public function set($prefix, $paths)
281 {
282 if (!$prefix) {
283 $this->fallbackDirsPsr0 = (array) $paths;
284 } else {
285 $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
286 }
287 }
288
289 /**
290 * Registers a set of PSR-4 directories for a given namespace,
291 * replacing any others previously set for this namespace.
292 *
293 * @param string $prefix The prefix/namespace, with trailing '\\'
294 * @param string[]|string $paths The PSR-4 base directories
295 *
296 * @throws \InvalidArgumentException
297 *
298 * @return void
299 */
300 public function setPsr4($prefix, $paths)
301 {
302 if (!$prefix) {
303 $this->fallbackDirsPsr4 = (array) $paths;
304 } else {
305 $length = strlen($prefix);
306 if ('\\' !== $prefix[$length - 1]) {
307 throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
308 }
309 $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
310 $this->prefixDirsPsr4[$prefix] = (array) $paths;
311 }
312 }
313
314 /**
315 * Turns on searching the include path for class files.
316 *
317 * @param bool $useIncludePath
318 *
319 * @return void
320 */
321 public function setUseIncludePath($useIncludePath)
322 {
323 $this->useIncludePath = $useIncludePath;
324 }
325
326 /**
327 * Can be used to check if the autoloader uses the include path to check
328 * for classes.
329 *
330 * @return bool
331 */
332 public function getUseIncludePath()
333 {
334 return $this->useIncludePath;
335 }
336
337 /**
338 * Turns off searching the prefix and fallback directories for classes
339 * that have not been registered with the class map.
340 *
341 * @param bool $classMapAuthoritative
342 *
343 * @return void
344 */
345 public function setClassMapAuthoritative($classMapAuthoritative)
346 {
347 $this->classMapAuthoritative = $classMapAuthoritative;
348 }
349
350 /**
351 * Should class lookup fail if not found in the current class map?
352 *
353 * @return bool
354 */
355 public function isClassMapAuthoritative()
356 {
357 return $this->classMapAuthoritative;
358 }
359
360 /**
361 * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
362 *
363 * @param string|null $apcuPrefix
364 *
365 * @return void
366 */
367 public function setApcuPrefix($apcuPrefix)
368 {
369 $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
370 }
371
372 /**
373 * The APCu prefix in use, or null if APCu caching is not enabled.
374 *
375 * @return string|null
376 */
377 public function getApcuPrefix()
378 {
379 return $this->apcuPrefix;
380 }
381
382 /**
383 * Registers this instance as an autoloader.
384 *
385 * @param bool $prepend Whether to prepend the autoloader or not
386 *
387 * @return void
388 */
389 public function register($prepend = false)
390 {
391 spl_autoload_register(array($this, 'loadClass'), true, $prepend);
392
393 if (null === $this->vendorDir) {
394 return;
395 }
396
397 if ($prepend) {
398 self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
399 } else {
400 unset(self::$registeredLoaders[$this->vendorDir]);
401 self::$registeredLoaders[$this->vendorDir] = $this;
402 }
403 }
404
405 /**
406 * Unregisters this instance as an autoloader.
407 *
408 * @return void
409 */
410 public function unregister()
411 {
412 spl_autoload_unregister(array($this, 'loadClass'));
413
414 if (null !== $this->vendorDir) {
415 unset(self::$registeredLoaders[$this->vendorDir]);
416 }
417 }
418
419 /**
420 * Loads the given class or interface.
421 *
422 * @param string $class The name of the class
423 * @return true|null True if loaded, null otherwise
424 */
425 public function loadClass($class)
426 {
427 if ($file = $this->findFile($class)) {
428 includeFile($file);
429
430 return true;
431 }
432
433 return null;
434 }
435
436 /**
437 * Finds the path to the file where the class is defined.
438 *
439 * @param string $class The name of the class
440 *
441 * @return string|false The path if found, false otherwise
442 */
443 public function findFile($class)
444 {
445 // class map lookup
446 if (isset($this->classMap[$class])) {
447 return $this->classMap[$class];
448 }
449 if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
450 return false;
451 }
452 if (null !== $this->apcuPrefix) {
453 $file = apcu_fetch($this->apcuPrefix.$class, $hit);
454 if ($hit) {
455 return $file;
456 }
457 }
458
459 $file = $this->findFileWithExtension($class, '.php');
460
461 // Search for Hack files if we are running on HHVM
462 if (false === $file && defined('HHVM_VERSION')) {
463 $file = $this->findFileWithExtension($class, '.hh');
464 }
465
466 if (null !== $this->apcuPrefix) {
467 apcu_add($this->apcuPrefix.$class, $file);
468 }
469
470 if (false === $file) {
471 // Remember that this class does not exist.
472 $this->missingClasses[$class] = true;
473 }
474
475 return $file;
476 }
477
478 /**
479 * Returns the currently registered loaders indexed by their corresponding vendor directories.
480 *
481 * @return self[]
482 */
483 public static function getRegisteredLoaders()
484 {
485 return self::$registeredLoaders;
486 }
487
488 /**
489 * @param string $class
490 * @param string $ext
491 * @return string|false
492 */
493 private function findFileWithExtension($class, $ext)
494 {
495 // PSR-4 lookup
496 $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
497
498 $first = $class[0];
499 if (isset($this->prefixLengthsPsr4[$first])) {
500 $subPath = $class;
501 while (false !== $lastPos = strrpos($subPath, '\\')) {
502 $subPath = substr($subPath, 0, $lastPos);
503 $search = $subPath . '\\';
504 if (isset($this->prefixDirsPsr4[$search])) {
505 $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
506 foreach ($this->prefixDirsPsr4[$search] as $dir) {
507 if (file_exists($file = $dir . $pathEnd)) {
508 return $file;
509 }
510 }
511 }
512 }
513 }
514
515 // PSR-4 fallback dirs
516 foreach ($this->fallbackDirsPsr4 as $dir) {
517 if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
518 return $file;
519 }
520 }
521
522 // PSR-0 lookup
523 if (false !== $pos = strrpos($class, '\\')) {
524 // namespaced class name
525 $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
526 . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
527 } else {
528 // PEAR-like class name
529 $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
530 }
531
532 if (isset($this->prefixesPsr0[$first])) {
533 foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
534 if (0 === strpos($class, $prefix)) {
535 foreach ($dirs as $dir) {
536 if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
537 return $file;
538 }
539 }
540 }
541 }
542 }
543
544 // PSR-0 fallback dirs
545 foreach ($this->fallbackDirsPsr0 as $dir) {
546 if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
547 return $file;
548 }
549 }
550
551 // PSR-0 include paths.
552 if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
553 return $file;
554 }
555
556 return false;
557 }
558 }
559
560 /**
561 * Scope isolated include.
562 *
563 * Prevents access to $this/self from included files.
564 *
565 * @param string $file
566 * @return void
567 * @private
568 */
569 function includeFile($file)
570 {
571 include $file;
572 }
1 <?php
2
3 /*
4 * This file is part of Composer.
5 *
6 * (c) Nils Adermann <naderman@naderman.de>
7 * Jordi Boggiano <j.boggiano@seld.be>
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12
13 namespace Composer;
14
15 use Composer\Autoload\ClassLoader;
16 use Composer\Semver\VersionParser;
17
18 /**
19 * This class is copied in every Composer installed project and available to all
20 *
21 * See also https://getcomposer.org/doc/07-runtime.md#installed-versions
22 *
23 * To require its presence, you can require `composer-runtime-api ^2.0`
24 */
25 class InstalledVersions
26 {
27 /**
28 * @var mixed[]|null
29 * @psalm-var array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}|array{}|null
30 */
31 private static $installed;
32
33 /**
34 * @var bool|null
35 */
36 private static $canGetVendors;
37
38 /**
39 * @var array[]
40 * @psalm-var array<string, array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
41 */
42 private static $installedByVendor = array();
43
44 /**
45 * Returns a list of all package names which are present, either by being installed, replaced or provided
46 *
47 * @return string[]
48 * @psalm-return list<string>
49 */
50 public static function getInstalledPackages()
51 {
52 $packages = array();
53 foreach (self::getInstalled() as $installed) {
54 $packages[] = array_keys($installed['versions']);
55 }
56
57 if (1 === \count($packages)) {
58 return $packages[0];
59 }
60
61 return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
62 }
63
64 /**
65 * Returns a list of all package names with a specific type e.g. 'library'
66 *
67 * @param string $type
68 * @return string[]
69 * @psalm-return list<string>
70 */
71 public static function getInstalledPackagesByType($type)
72 {
73 $packagesByType = array();
74
75 foreach (self::getInstalled() as $installed) {
76 foreach ($installed['versions'] as $name => $package) {
77 if (isset($package['type']) && $package['type'] === $type) {
78 $packagesByType[] = $name;
79 }
80 }
81 }
82
83 return $packagesByType;
84 }
85
86 /**
87 * Checks whether the given package is installed
88 *
89 * This also returns true if the package name is provided or replaced by another package
90 *
91 * @param string $packageName
92 * @param bool $includeDevRequirements
93 * @return bool
94 */
95 public static function isInstalled($packageName, $includeDevRequirements = true)
96 {
97 foreach (self::getInstalled() as $installed) {
98 if (isset($installed['versions'][$packageName])) {
99 return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']);
100 }
101 }
102
103 return false;
104 }
105
106 /**
107 * Checks whether the given package satisfies a version constraint
108 *
109 * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
110 *
111 * Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
112 *
113 * @param VersionParser $parser Install composer/semver to have access to this class and functionality
114 * @param string $packageName
115 * @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
116 * @return bool
117 */
118 public static function satisfies(VersionParser $parser, $packageName, $constraint)
119 {
120 $constraint = $parser->parseConstraints($constraint);
121 $provided = $parser->parseConstraints(self::getVersionRanges($packageName));
122
123 return $provided->matches($constraint);
124 }
125
126 /**
127 * Returns a version constraint representing all the range(s) which are installed for a given package
128 *
129 * It is easier to use this via isInstalled() with the $constraint argument if you need to check
130 * whether a given version of a package is installed, and not just whether it exists
131 *
132 * @param string $packageName
133 * @return string Version constraint usable with composer/semver
134 */
135 public static function getVersionRanges($packageName)
136 {
137 foreach (self::getInstalled() as $installed) {
138 if (!isset($installed['versions'][$packageName])) {
139 continue;
140 }
141
142 $ranges = array();
143 if (isset($installed['versions'][$packageName]['pretty_version'])) {
144 $ranges[] = $installed['versions'][$packageName]['pretty_version'];
145 }
146 if (array_key_exists('aliases', $installed['versions'][$packageName])) {
147 $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
148 }
149 if (array_key_exists('replaced', $installed['versions'][$packageName])) {
150 $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
151 }
152 if (array_key_exists('provided', $installed['versions'][$packageName])) {
153 $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
154 }
155
156 return implode(' || ', $ranges);
157 }
158
159 throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
160 }
161
162 /**
163 * @param string $packageName
164 * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
165 */
166 public static function getVersion($packageName)
167 {
168 foreach (self::getInstalled() as $installed) {
169 if (!isset($installed['versions'][$packageName])) {
170 continue;
171 }
172
173 if (!isset($installed['versions'][$packageName]['version'])) {
174 return null;
175 }
176
177 return $installed['versions'][$packageName]['version'];
178 }
179
180 throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
181 }
182
183 /**
184 * @param string $packageName
185 * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
186 */
187 public static function getPrettyVersion($packageName)
188 {
189 foreach (self::getInstalled() as $installed) {
190 if (!isset($installed['versions'][$packageName])) {
191 continue;
192 }
193
194 if (!isset($installed['versions'][$packageName]['pretty_version'])) {
195 return null;
196 }
197
198 return $installed['versions'][$packageName]['pretty_version'];
199 }
200
201 throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
202 }
203
204 /**
205 * @param string $packageName
206 * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
207 */
208 public static function getReference($packageName)
209 {
210 foreach (self::getInstalled() as $installed) {
211 if (!isset($installed['versions'][$packageName])) {
212 continue;
213 }
214
215 if (!isset($installed['versions'][$packageName]['reference'])) {
216 return null;
217 }
218
219 return $installed['versions'][$packageName]['reference'];
220 }
221
222 throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
223 }
224
225 /**
226 * @param string $packageName
227 * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
228 */
229 public static function getInstallPath($packageName)
230 {
231 foreach (self::getInstalled() as $installed) {
232 if (!isset($installed['versions'][$packageName])) {
233 continue;
234 }
235
236 return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
237 }
238
239 throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
240 }
241
242 /**
243 * @return array
244 * @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}
245 */
246 public static function getRootPackage()
247 {
248 $installed = self::getInstalled();
249
250 return $installed[0]['root'];
251 }
252
253 /**
254 * Returns the raw installed.php data for custom implementations
255 *
256 * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
257 * @return array[]
258 * @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}
259 */
260 public static function getRawData()
261 {
262 @trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
263
264 if (null === self::$installed) {
265 // only require the installed.php file if this file is loaded from its dumped location,
266 // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
267 if (substr(__DIR__, -8, 1) !== 'C') {
268 self::$installed = include __DIR__ . '/installed.php';
269 } else {
270 self::$installed = array();
271 }
272 }
273
274 return self::$installed;
275 }
276
277 /**
278 * Returns the raw data of all installed.php which are currently loaded for custom implementations
279 *
280 * @return array[]
281 * @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
282 */
283 public static function getAllRawData()
284 {
285 return self::getInstalled();
286 }
287
288 /**
289 * Lets you reload the static array from another file
290 *
291 * This is only useful for complex integrations in which a project needs to use
292 * this class but then also needs to execute another project's autoloader in process,
293 * and wants to ensure both projects have access to their version of installed.php.
294 *
295 * A typical case would be PHPUnit, where it would need to make sure it reads all
296 * the data it needs from this class, then call reload() with
297 * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
298 * the project in which it runs can then also use this class safely, without
299 * interference between PHPUnit's dependencies and the project's dependencies.
300 *
301 * @param array[] $data A vendor/composer/installed.php data set
302 * @return void
303 *
304 * @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>} $data
305 */
306 public static function reload($data)
307 {
308 self::$installed = $data;
309 self::$installedByVendor = array();
310 }
311
312 /**
313 * @return array[]
314 * @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
315 */
316 private static function getInstalled()
317 {
318 if (null === self::$canGetVendors) {
319 self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
320 }
321
322 $installed = array();
323
324 if (self::$canGetVendors) {
325 foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
326 if (isset(self::$installedByVendor[$vendorDir])) {
327 $installed[] = self::$installedByVendor[$vendorDir];
328 } elseif (is_file($vendorDir.'/composer/installed.php')) {
329 $installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php';
330 if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
331 self::$installed = $installed[count($installed) - 1];
332 }
333 }
334 }
335 }
336
337 if (null === self::$installed) {
338 // only require the installed.php file if this file is loaded from its dumped location,
339 // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
340 if (substr(__DIR__, -8, 1) !== 'C') {
341 self::$installed = require __DIR__ . '/installed.php';
342 } else {
343 self::$installed = array();
344 }
345 }
346 $installed[] = self::$installed;
347
348 return $installed;
349 }
350 }
1
2 Copyright (c) Nils Adermann, Jordi Boggiano
3
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is furnished
9 to do so, subject to the following conditions:
10
11 The above copyright notice and this permission notice shall be included in all
12 copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 THE SOFTWARE.
21
1 <?php
2
3 // autoload_classmap.php @generated by Composer
4
5 $vendorDir = dirname(dirname(__FILE__));
6 $baseDir = dirname($vendorDir);
7
8 return array(
9 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
10 'WPML\\Media\\Classes\\WPML_Media_Attachment_By_URL_Query' => $baseDir . '/classes/class-wpml-media-attachment-by-url-query.php',
11 'WPML\\Media\\Classes\\WPML_Media_Classic_Audio_Parser' => $baseDir . '/classes/media-translation/class-wpml-media-classic-audio-parser.php',
12 'WPML\\Media\\Classes\\WPML_Media_Classic_Element_Parser' => $baseDir . '/classes/media-translation/class-wpml-media-classic-element-parser.php',
13 'WPML\\Media\\Classes\\WPML_Media_Classic_Video_Parser' => $baseDir . '/classes/media-translation/class-wpml-media-classic-video-parser.php',
14 'WPML\\Media\\Classes\\WPML_Media_Element_Parser' => $baseDir . '/classes/media-translation/class-wpml-media-element-parser.php',
15 'WPML\\Media\\Classes\\WPML_Media_File_Parser' => $baseDir . '/classes/media-translation/class-wpml-media-file-parser.php',
16 'WPML\\Media\\Classes\\WPML_Media_Href_Parser' => $baseDir . '/classes/media-translation/class-wpml-media-href-parser.php',
17 'WPML\\Media\\Classes\\WPML_Media_Image_Parser' => $baseDir . '/classes/media-translation/class-wpml-media-image-parser.php',
18 'WPML\\Media\\Classes\\WPML_Non_Embedded_Pdf_Parser' => $baseDir . '/classes/media-translation/class-non-embedded-pdf-parser.php',
19 'WPML\\Media\\Factories\\WPML_Media_Attachment_By_URL_Query_Factory' => $baseDir . '/classes/class-wpml-media-attachment-by-url-query-factory.php',
20 'WPML\\Media\\Factories\\WPML_Media_Element_Parser_Factory' => $baseDir . '/classes/media-translation/factories/class-wpml-media-element-parser-factory.php',
21 'WPML\\Media\\Widgets\\Block\\DisplayTranslation' => $baseDir . '/classes/Widgets/Block/DisplayTranslation.php',
22 'WPML_Cache_Directory' => $vendorDir . '/wpml-shared/wpml-lib-cache/src/cache/class-wpml-cache-directory.php',
23 'WPML_Core_Version_Check' => $vendorDir . '/wpml-shared/wpml-lib-dependencies/src/dependencies/class-wpml-core-version-check.php',
24 'WPML_Dependencies' => $vendorDir . '/wpml-shared/wpml-lib-dependencies/src/dependencies/class-wpml-dependencies.php',
25 'WPML_Media' => $baseDir . '/classes/class-wpml-media.php',
26 'WPML_Media_2_3_0_Migration' => $baseDir . '/classes/upgrade/class-wpml-media-2-3-0-migration.php',
27 'WPML_Media_Add_To_Basket' => $baseDir . '/classes/media-selector/class-wpml-media-add-to-basket.php',
28 'WPML_Media_Add_To_Basket_Factory' => $baseDir . '/classes/media-selector/class-wpml-media-add-to-basket-factory.php',
29 'WPML_Media_Attachment_By_URL' => $baseDir . '/classes/class-wpml-media-attachment-by-url.php',
30 'WPML_Media_Attachment_By_URL_Factory' => $baseDir . '/classes/class-wpml-media-attachment-by-url-factory.php',
31 'WPML_Media_Attachment_Image_Update' => $baseDir . '/classes/media-translation/class-wpml-media-attachment-image-update.php',
32 'WPML_Media_Attachment_Image_Update_Factory' => $baseDir . '/classes/media-translation/class-wpml-media-attachment-image-update-factory.php',
33 'WPML_Media_Attachments_Query' => $baseDir . '/classes/class-wpml-media-attachments-query.php',
34 'WPML_Media_Attachments_Query_Factory' => $baseDir . '/classes/class-wpml-media-attachments-query-factory.php',
35 'WPML_Media_Batch_Url_Translation' => $baseDir . '/classes/batch-media-url-translation/wpml-media-batch-media-url-translation.php',
36 'WPML_Media_Caption' => $baseDir . '/classes/media-translation/class-wpml-media-caption.php',
37 'WPML_Media_Caption_Tags_Parse' => $baseDir . '/classes/media-translation/class-wpml-media-caption-tags-parse.php',
38 'WPML_Media_Custom_Field_Batch_Url_Translation' => $baseDir . '/classes/batch-media-url-translation/wpml-media-custom-field-batch-media-url-translation.php',
39 'WPML_Media_Custom_Field_Batch_Url_Translation_Factory' => $baseDir . '/classes/batch-media-url-translation/wpml-media-custom-field-batch-media-url-translation-factory.php',
40 'WPML_Media_Custom_Field_Images_Translation' => $baseDir . '/classes/media-translation/class-wpml-media-custom-field-images-translation.php',
41 'WPML_Media_Custom_Field_Images_Translation_Factory' => $baseDir . '/classes/media-translation/class-wpml-media-custom-field-images-translation-factory.php',
42 'WPML_Media_Dependencies' => $baseDir . '/inc/wpml-media-dependencies.class.php',
43 'WPML_Media_Editor_Notices' => $baseDir . '/classes/menus/wpml-media-translations-editor-notices.php',
44 'WPML_Media_Editor_Notices_Factory' => $baseDir . '/classes/menus/wpml-media-translations-editor-notices-factory.php',
45 'WPML_Media_Factory' => $baseDir . '/classes/class-wpml-media-factory.php',
46 'WPML_Media_File' => $baseDir . '/classes/class-wpml-media-file.php',
47 'WPML_Media_File_Factory' => $baseDir . '/classes/class-wpml-media-file-factory.php',
48 'WPML_Media_Help_Tab' => $baseDir . '/classes/menus/wpml-media-help-tab.php',
49 'WPML_Media_Help_Tab_Factory' => $baseDir . '/classes/menus/wpml-media-help-tab-factory.php',
50 'WPML_Media_Image_Translate' => $baseDir . '/classes/media-translation/class-wpml-media-image-translate.php',
51 'WPML_Media_Menus' => $baseDir . '/classes/menus/wpml-media-menus.php',
52 'WPML_Media_Menus_Factory' => $baseDir . '/classes/menus/wpml-media-menus-factory.php',
53 'WPML_Media_Populate_Media_Strings_Translations' => $baseDir . '/classes/media-translation/wpml-translation-editor/class-wpml-media-populate-media-strings-translations.php',
54 'WPML_Media_Populate_Media_Strings_Translations_Factory' => $baseDir . '/classes/media-translation/wpml-translation-editor/class-wpml-media-populate-media-strings-translations-factory.php',
55 'WPML_Media_Post_Batch_Url_Translation' => $baseDir . '/classes/batch-media-url-translation/wpml-media-post-batch-media-url-translation.php',
56 'WPML_Media_Post_Batch_Url_Translation_Factory' => $baseDir . '/classes/batch-media-url-translation/wpml-media-post-batch-media-url-translation-factory.php',
57 'WPML_Media_Post_Images_Translation' => $baseDir . '/classes/media-translation/class-wpml-media-post-images-translation.php',
58 'WPML_Media_Post_Images_Translation_Factory' => $baseDir . '/classes/media-translation/class-wpml-media-post-images-translation-factory.php',
59 'WPML_Media_Post_Media_Usage' => $baseDir . '/classes/media-usage/class-wpml-media-post-media-usage.php',
60 'WPML_Media_Post_Media_Usage_Factory' => $baseDir . '/classes/media-usage/class-wpml-media-post-media-usage-factory.php',
61 'WPML_Media_Post_With_Media_Files' => $baseDir . '/classes/media-translation/class-wpml-media-post-with-media-files.php',
62 'WPML_Media_Post_With_Media_Files_Factory' => $baseDir . '/classes/media-translation/class-wpml-media-post-with-media-files-factory.php',
63 'WPML_Media_Posts_Media_Flag_Notice' => $baseDir . '/classes/menus/wpml-media-posts-media-flag-notice.php',
64 'WPML_Media_Posts_Media_Flag_Notice_Factory' => $baseDir . '/classes/menus/wpml-media-posts-media-flag-notice-factory.php',
65 'WPML_Media_Privacy_Content' => $baseDir . '/classes/privacy/class-wpml-media-privacy-content.php',
66 'WPML_Media_Privacy_Content_Factory' => $baseDir . '/classes/privacy/class-wpml-media-privacy-content-factory.php',
67 'WPML_Media_Save_Translation' => $baseDir . '/classes/media-translation/class-wpml-media-save-translation.php',
68 'WPML_Media_Save_Translation_Factory' => $baseDir . '/classes/media-translation/class-wpml-media-save-translation-factory.php',
69 'WPML_Media_Screen_Options' => $baseDir . '/classes/menus/wpml-media-screen-options.php',
70 'WPML_Media_Screen_Options_Factory' => $baseDir . '/classes/menus/wpml-media-screen-options-factory.php',
71 'WPML_Media_Selector' => $baseDir . '/classes/media-selector/class-wpml-media-selector.php',
72 'WPML_Media_Selector_Factory' => $baseDir . '/classes/media-selector/class-wpml-media-selector-factory.php',
73 'WPML_Media_Set_Initial_Language' => $baseDir . '/classes/setup/class-wpml-media-set-initial-language.php',
74 'WPML_Media_Set_Initial_Language_Factory' => $baseDir . '/classes/setup/class-wpml-media-set-initial-language-factory.php',
75 'WPML_Media_Set_Posts_Media_Flag' => $baseDir . '/classes/media-translation/class-wpml-media-set-posts-media-flag.php',
76 'WPML_Media_Set_Posts_Media_Flag_Factory' => $baseDir . '/classes/media-translation/class-wpml-media-set-posts-media-flag-factory.php',
77 'WPML_Media_Sizes' => $baseDir . '/classes/media-translation/class-wpml-media-sizes.php',
78 'WPML_Media_String_Batch_Url_Translation' => $baseDir . '/classes/batch-media-url-translation/wpml-media-string-batch-media-url-translation.php',
79 'WPML_Media_String_Batch_Url_Translation_Factory' => $baseDir . '/classes/batch-media-url-translation/wpml-media-string-batch-media-url-translation-factory.php',
80 'WPML_Media_String_Images_Translation' => $baseDir . '/classes/media-translation/class-wpml-media-string-images-translation.php',
81 'WPML_Media_String_Images_Translation_Factory' => $baseDir . '/classes/media-translation/class-wpml-media-string-images-translation-factory.php',
82 'WPML_Media_Submitted_Basket_Notice' => $baseDir . '/classes/media-selector/class-wpml-media-submitted-basket-notice.php',
83 'WPML_Media_Submitted_Basket_Notice_Factory' => $baseDir . '/classes/media-selector/class-wpml-media-submitted-basket-notice-factory.php',
84 'WPML_Media_Translated_Images_Update' => $baseDir . '/classes/media-translation/class-wpml-media-translated-images-update.php',
85 'WPML_Media_Translation_Status' => $baseDir . '/classes/media-translation/class-wpml-media-translation-status.php',
86 'WPML_Media_Translations_UI' => $baseDir . '/classes/menus/wpml-media-translations-ui.php',
87 'WPML_Media_Upgrade' => $baseDir . '/inc/wpml-media-upgrade.class.php',
88 'WPML_Media_Usage' => $baseDir . '/classes/media-usage/class-wpml-media-usage.php',
89 'WPML_Media_Usage_Factory' => $baseDir . '/classes/media-usage/class-wpml-media-usage-factory.php',
90 'WPML_PHP_Version_Check' => $vendorDir . '/wpml-shared/wpml-lib-dependencies/src/dependencies/class-wpml-php-version-check.php',
91 );
1 <?php
2
3 // autoload_namespaces.php @generated by Composer
4
5 $vendorDir = dirname(dirname(__FILE__));
6 $baseDir = dirname($vendorDir);
7
8 return array(
9 );
1 <?php
2
3 // autoload_psr4.php @generated by Composer
4
5 $vendorDir = dirname(dirname(__FILE__));
6 $baseDir = dirname($vendorDir);
7
8 return array(
9 );
1 <?php
2
3 // autoload_real.php @generated by Composer
4
5 class ComposerAutoloaderInitf770f44c420bc322254dccc6e9ae55eb
6 {
7 private static $loader;
8
9 public static function loadClassLoader($class)
10 {
11 if ('Composer\Autoload\ClassLoader' === $class) {
12 require __DIR__ . '/ClassLoader.php';
13 }
14 }
15
16 /**
17 * @return \Composer\Autoload\ClassLoader
18 */
19 public static function getLoader()
20 {
21 if (null !== self::$loader) {
22 return self::$loader;
23 }
24
25 require __DIR__ . '/platform_check.php';
26
27 spl_autoload_register(array('ComposerAutoloaderInitf770f44c420bc322254dccc6e9ae55eb', 'loadClassLoader'), true, true);
28 self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
29 spl_autoload_unregister(array('ComposerAutoloaderInitf770f44c420bc322254dccc6e9ae55eb', 'loadClassLoader'));
30
31 $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
32 if ($useStaticLoader) {
33 require __DIR__ . '/autoload_static.php';
34
35 call_user_func(\Composer\Autoload\ComposerStaticInitf770f44c420bc322254dccc6e9ae55eb::getInitializer($loader));
36 } else {
37 $map = require __DIR__ . '/autoload_namespaces.php';
38 foreach ($map as $namespace => $path) {
39 $loader->set($namespace, $path);
40 }
41
42 $map = require __DIR__ . '/autoload_psr4.php';
43 foreach ($map as $namespace => $path) {
44 $loader->setPsr4($namespace, $path);
45 }
46
47 $classMap = require __DIR__ . '/autoload_classmap.php';
48 if ($classMap) {
49 $loader->addClassMap($classMap);
50 }
51 }
52
53 $loader->register(true);
54
55 return $loader;
56 }
57 }
1 <?php
2
3 // autoload_static.php @generated by Composer
4
5 namespace Composer\Autoload;
6
7 class ComposerStaticInitf770f44c420bc322254dccc6e9ae55eb
8 {
9 public static $classMap = array (
10 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
11 'WPML\\Media\\Classes\\WPML_Media_Attachment_By_URL_Query' => __DIR__ . '/../..' . '/classes/class-wpml-media-attachment-by-url-query.php',
12 'WPML\\Media\\Classes\\WPML_Media_Classic_Audio_Parser' => __DIR__ . '/../..' . '/classes/media-translation/class-wpml-media-classic-audio-parser.php',
13 'WPML\\Media\\Classes\\WPML_Media_Classic_Element_Parser' => __DIR__ . '/../..' . '/classes/media-translation/class-wpml-media-classic-element-parser.php',
14 'WPML\\Media\\Classes\\WPML_Media_Classic_Video_Parser' => __DIR__ . '/../..' . '/classes/media-translation/class-wpml-media-classic-video-parser.php',
15 'WPML\\Media\\Classes\\WPML_Media_Element_Parser' => __DIR__ . '/../..' . '/classes/media-translation/class-wpml-media-element-parser.php',
16 'WPML\\Media\\Classes\\WPML_Media_File_Parser' => __DIR__ . '/../..' . '/classes/media-translation/class-wpml-media-file-parser.php',
17 'WPML\\Media\\Classes\\WPML_Media_Href_Parser' => __DIR__ . '/../..' . '/classes/media-translation/class-wpml-media-href-parser.php',
18 'WPML\\Media\\Classes\\WPML_Media_Image_Parser' => __DIR__ . '/../..' . '/classes/media-translation/class-wpml-media-image-parser.php',
19 'WPML\\Media\\Classes\\WPML_Non_Embedded_Pdf_Parser' => __DIR__ . '/../..' . '/classes/media-translation/class-non-embedded-pdf-parser.php',
20 'WPML\\Media\\Factories\\WPML_Media_Attachment_By_URL_Query_Factory' => __DIR__ . '/../..' . '/classes/class-wpml-media-attachment-by-url-query-factory.php',
21 'WPML\\Media\\Factories\\WPML_Media_Element_Parser_Factory' => __DIR__ . '/../..' . '/classes/media-translation/factories/class-wpml-media-element-parser-factory.php',
22 'WPML\\Media\\Widgets\\Block\\DisplayTranslation' => __DIR__ . '/../..' . '/classes/Widgets/Block/DisplayTranslation.php',
23 'WPML_Cache_Directory' => __DIR__ . '/..' . '/wpml-shared/wpml-lib-cache/src/cache/class-wpml-cache-directory.php',
24 'WPML_Core_Version_Check' => __DIR__ . '/..' . '/wpml-shared/wpml-lib-dependencies/src/dependencies/class-wpml-core-version-check.php',
25 'WPML_Dependencies' => __DIR__ . '/..' . '/wpml-shared/wpml-lib-dependencies/src/dependencies/class-wpml-dependencies.php',
26 'WPML_Media' => __DIR__ . '/../..' . '/classes/class-wpml-media.php',
27 'WPML_Media_2_3_0_Migration' => __DIR__ . '/../..' . '/classes/upgrade/class-wpml-media-2-3-0-migration.php',
28 'WPML_Media_Add_To_Basket' => __DIR__ . '/../..' . '/classes/media-selector/class-wpml-media-add-to-basket.php',
29 'WPML_Media_Add_To_Basket_Factory' => __DIR__ . '/../..' . '/classes/media-selector/class-wpml-media-add-to-basket-factory.php',
30 'WPML_Media_Attachment_By_URL' => __DIR__ . '/../..' . '/classes/class-wpml-media-attachment-by-url.php',
31 'WPML_Media_Attachment_By_URL_Factory' => __DIR__ . '/../..' . '/classes/class-wpml-media-attachment-by-url-factory.php',
32 'WPML_Media_Attachment_Image_Update' => __DIR__ . '/../..' . '/classes/media-translation/class-wpml-media-attachment-image-update.php',
33 'WPML_Media_Attachment_Image_Update_Factory' => __DIR__ . '/../..' . '/classes/media-translation/class-wpml-media-attachment-image-update-factory.php',
34 'WPML_Media_Attachments_Query' => __DIR__ . '/../..' . '/classes/class-wpml-media-attachments-query.php',
35 'WPML_Media_Attachments_Query_Factory' => __DIR__ . '/../..' . '/classes/class-wpml-media-attachments-query-factory.php',
36 'WPML_Media_Batch_Url_Translation' => __DIR__ . '/../..' . '/classes/batch-media-url-translation/wpml-media-batch-media-url-translation.php',
37 'WPML_Media_Caption' => __DIR__ . '/../..' . '/classes/media-translation/class-wpml-media-caption.php',
38 'WPML_Media_Caption_Tags_Parse' => __DIR__ . '/../..' . '/classes/media-translation/class-wpml-media-caption-tags-parse.php',
39 'WPML_Media_Custom_Field_Batch_Url_Translation' => __DIR__ . '/../..' . '/classes/batch-media-url-translation/wpml-media-custom-field-batch-media-url-translation.php',
40 'WPML_Media_Custom_Field_Batch_Url_Translation_Factory' => __DIR__ . '/../..' . '/classes/batch-media-url-translation/wpml-media-custom-field-batch-media-url-translation-factory.php',
41 'WPML_Media_Custom_Field_Images_Translation' => __DIR__ . '/../..' . '/classes/media-translation/class-wpml-media-custom-field-images-translation.php',
42 'WPML_Media_Custom_Field_Images_Translation_Factory' => __DIR__ . '/../..' . '/classes/media-translation/class-wpml-media-custom-field-images-translation-factory.php',
43 'WPML_Media_Dependencies' => __DIR__ . '/../..' . '/inc/wpml-media-dependencies.class.php',
44 'WPML_Media_Editor_Notices' => __DIR__ . '/../..' . '/classes/menus/wpml-media-translations-editor-notices.php',
45 'WPML_Media_Editor_Notices_Factory' => __DIR__ . '/../..' . '/classes/menus/wpml-media-translations-editor-notices-factory.php',
46 'WPML_Media_Factory' => __DIR__ . '/../..' . '/classes/class-wpml-media-factory.php',
47 'WPML_Media_File' => __DIR__ . '/../..' . '/classes/class-wpml-media-file.php',
48 'WPML_Media_File_Factory' => __DIR__ . '/../..' . '/classes/class-wpml-media-file-factory.php',
49 'WPML_Media_Help_Tab' => __DIR__ . '/../..' . '/classes/menus/wpml-media-help-tab.php',
50 'WPML_Media_Help_Tab_Factory' => __DIR__ . '/../..' . '/classes/menus/wpml-media-help-tab-factory.php',
51 'WPML_Media_Image_Translate' => __DIR__ . '/../..' . '/classes/media-translation/class-wpml-media-image-translate.php',
52 'WPML_Media_Menus' => __DIR__ . '/../..' . '/classes/menus/wpml-media-menus.php',
53 'WPML_Media_Menus_Factory' => __DIR__ . '/../..' . '/classes/menus/wpml-media-menus-factory.php',
54 'WPML_Media_Populate_Media_Strings_Translations' => __DIR__ . '/../..' . '/classes/media-translation/wpml-translation-editor/class-wpml-media-populate-media-strings-translations.php',
55 'WPML_Media_Populate_Media_Strings_Translations_Factory' => __DIR__ . '/../..' . '/classes/media-translation/wpml-translation-editor/class-wpml-media-populate-media-strings-translations-factory.php',
56 'WPML_Media_Post_Batch_Url_Translation' => __DIR__ . '/../..' . '/classes/batch-media-url-translation/wpml-media-post-batch-media-url-translation.php',
57 'WPML_Media_Post_Batch_Url_Translation_Factory' => __DIR__ . '/../..' . '/classes/batch-media-url-translation/wpml-media-post-batch-media-url-translation-factory.php',
58 'WPML_Media_Post_Images_Translation' => __DIR__ . '/../..' . '/classes/media-translation/class-wpml-media-post-images-translation.php',
59 'WPML_Media_Post_Images_Translation_Factory' => __DIR__ . '/../..' . '/classes/media-translation/class-wpml-media-post-images-translation-factory.php',
60 'WPML_Media_Post_Media_Usage' => __DIR__ . '/../..' . '/classes/media-usage/class-wpml-media-post-media-usage.php',
61 'WPML_Media_Post_Media_Usage_Factory' => __DIR__ . '/../..' . '/classes/media-usage/class-wpml-media-post-media-usage-factory.php',
62 'WPML_Media_Post_With_Media_Files' => __DIR__ . '/../..' . '/classes/media-translation/class-wpml-media-post-with-media-files.php',
63 'WPML_Media_Post_With_Media_Files_Factory' => __DIR__ . '/../..' . '/classes/media-translation/class-wpml-media-post-with-media-files-factory.php',
64 'WPML_Media_Posts_Media_Flag_Notice' => __DIR__ . '/../..' . '/classes/menus/wpml-media-posts-media-flag-notice.php',
65 'WPML_Media_Posts_Media_Flag_Notice_Factory' => __DIR__ . '/../..' . '/classes/menus/wpml-media-posts-media-flag-notice-factory.php',
66 'WPML_Media_Privacy_Content' => __DIR__ . '/../..' . '/classes/privacy/class-wpml-media-privacy-content.php',
67 'WPML_Media_Privacy_Content_Factory' => __DIR__ . '/../..' . '/classes/privacy/class-wpml-media-privacy-content-factory.php',
68 'WPML_Media_Save_Translation' => __DIR__ . '/../..' . '/classes/media-translation/class-wpml-media-save-translation.php',
69 'WPML_Media_Save_Translation_Factory' => __DIR__ . '/../..' . '/classes/media-translation/class-wpml-media-save-translation-factory.php',
70 'WPML_Media_Screen_Options' => __DIR__ . '/../..' . '/classes/menus/wpml-media-screen-options.php',
71 'WPML_Media_Screen_Options_Factory' => __DIR__ . '/../..' . '/classes/menus/wpml-media-screen-options-factory.php',
72 'WPML_Media_Selector' => __DIR__ . '/../..' . '/classes/media-selector/class-wpml-media-selector.php',
73 'WPML_Media_Selector_Factory' => __DIR__ . '/../..' . '/classes/media-selector/class-wpml-media-selector-factory.php',
74 'WPML_Media_Set_Initial_Language' => __DIR__ . '/../..' . '/classes/setup/class-wpml-media-set-initial-language.php',
75 'WPML_Media_Set_Initial_Language_Factory' => __DIR__ . '/../..' . '/classes/setup/class-wpml-media-set-initial-language-factory.php',
76 'WPML_Media_Set_Posts_Media_Flag' => __DIR__ . '/../..' . '/classes/media-translation/class-wpml-media-set-posts-media-flag.php',
77 'WPML_Media_Set_Posts_Media_Flag_Factory' => __DIR__ . '/../..' . '/classes/media-translation/class-wpml-media-set-posts-media-flag-factory.php',
78 'WPML_Media_Sizes' => __DIR__ . '/../..' . '/classes/media-translation/class-wpml-media-sizes.php',
79 'WPML_Media_String_Batch_Url_Translation' => __DIR__ . '/../..' . '/classes/batch-media-url-translation/wpml-media-string-batch-media-url-translation.php',
80 'WPML_Media_String_Batch_Url_Translation_Factory' => __DIR__ . '/../..' . '/classes/batch-media-url-translation/wpml-media-string-batch-media-url-translation-factory.php',
81 'WPML_Media_String_Images_Translation' => __DIR__ . '/../..' . '/classes/media-translation/class-wpml-media-string-images-translation.php',
82 'WPML_Media_String_Images_Translation_Factory' => __DIR__ . '/../..' . '/classes/media-translation/class-wpml-media-string-images-translation-factory.php',
83 'WPML_Media_Submitted_Basket_Notice' => __DIR__ . '/../..' . '/classes/media-selector/class-wpml-media-submitted-basket-notice.php',
84 'WPML_Media_Submitted_Basket_Notice_Factory' => __DIR__ . '/../..' . '/classes/media-selector/class-wpml-media-submitted-basket-notice-factory.php',
85 'WPML_Media_Translated_Images_Update' => __DIR__ . '/../..' . '/classes/media-translation/class-wpml-media-translated-images-update.php',
86 'WPML_Media_Translation_Status' => __DIR__ . '/../..' . '/classes/media-translation/class-wpml-media-translation-status.php',
87 'WPML_Media_Translations_UI' => __DIR__ . '/../..' . '/classes/menus/wpml-media-translations-ui.php',
88 'WPML_Media_Upgrade' => __DIR__ . '/../..' . '/inc/wpml-media-upgrade.class.php',
89 'WPML_Media_Usage' => __DIR__ . '/../..' . '/classes/media-usage/class-wpml-media-usage.php',
90 'WPML_Media_Usage_Factory' => __DIR__ . '/../..' . '/classes/media-usage/class-wpml-media-usage-factory.php',
91 'WPML_PHP_Version_Check' => __DIR__ . '/..' . '/wpml-shared/wpml-lib-dependencies/src/dependencies/class-wpml-php-version-check.php',
92 );
93
94 public static function getInitializer(ClassLoader $loader)
95 {
96 return \Closure::bind(function () use ($loader) {
97 $loader->classMap = ComposerStaticInitf770f44c420bc322254dccc6e9ae55eb::$classMap;
98
99 }, null, ClassLoader::class);
100 }
101 }
1 <?php return array(
2 'root' => array(
3 'pretty_version' => '2.7.3',
4 'version' => '2.7.3.0',
5 'type' => 'wordpress-plugin',
6 'install_path' => __DIR__ . '/../../',
7 'aliases' => array(),
8 'reference' => '8092d6db990d4c9926fffc1c8267cc990d7dfa1d',
9 'name' => 'wpml/media',
10 'dev' => false,
11 ),
12 'versions' => array(
13 'roave/security-advisories' => array(
14 'pretty_version' => 'dev-master',
15 'version' => 'dev-master',
16 'type' => 'metapackage',
17 'install_path' => NULL,
18 'aliases' => array(),
19 'reference' => 'dad1e44d86f958c5be9c5f355c9554ce22f1b1a7',
20 'dev_requirement' => false,
21 ),
22 'wpml-shared/wpml-lib-cache' => array(
23 'pretty_version' => '0.1.0',
24 'version' => '0.1.0.0',
25 'type' => 'library',
26 'install_path' => __DIR__ . '/../wpml-shared/wpml-lib-cache',
27 'aliases' => array(),
28 'reference' => '4bbcffee0b82285f4037b2b0fbeee72e924e3fe5',
29 'dev_requirement' => false,
30 ),
31 'wpml-shared/wpml-lib-dependencies' => array(
32 'pretty_version' => '0.1.6',
33 'version' => '0.1.6.0',
34 'type' => 'library',
35 'install_path' => __DIR__ . '/../wpml-shared/wpml-lib-dependencies',
36 'aliases' => array(),
37 'reference' => '723824a61bfec7dd535afcd7260fad50d8bfe6e8',
38 'dev_requirement' => false,
39 ),
40 'wpml/media' => array(
41 'pretty_version' => '2.7.3',
42 'version' => '2.7.3.0',
43 'type' => 'wordpress-plugin',
44 'install_path' => __DIR__ . '/../../',
45 'aliases' => array(),
46 'reference' => '8092d6db990d4c9926fffc1c8267cc990d7dfa1d',
47 'dev_requirement' => false,
48 ),
49 ),
50 );
1 <?php
2
3 // platform_check.php @generated by Composer
4
5 $issues = array();
6
7 if (!(PHP_VERSION_ID >= 50600)) {
8 $issues[] = 'Your Composer dependencies require a PHP version ">= 5.6.0". You are running ' . PHP_VERSION . '.';
9 }
10
11 if ($issues) {
12 if (!headers_sent()) {
13 header('HTTP/1.1 500 Internal Server Error');
14 }
15 if (!ini_get('display_errors')) {
16 if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
17 fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
18 } elseif (!headers_sent()) {
19 echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
20 }
21 }
22 trigger_error(
23 'Composer detected issues in your platform: ' . implode(' ', $issues),
24 E_USER_ERROR
25 );
26 }
1 <?php
2
3 class WPML_Cache_Directory {
4
5 const DIR_PERMISSIONS = 0775;
6 const MAIN_DIRECTORY_NAME = 'wpml';
7 const NOTICE_GROUP = 'wpml-cache-directory';
8 const NOTICE_INVALID_CACHE = 'invalid-cache';
9 private $cache_disabled = false;
10
11 /**
12 * @var WPML_WP_API
13 */
14 private $wp_api;
15
16 /**
17 * @var WP_Filesystem_Direct
18 */
19 private $filesystem;
20
21 /**
22 * WPML_Cache_Directory constructor.
23 *
24 * @param WPML_WP_API $wp_api
25 */
26 public function __construct( WPML_WP_API $wp_api ) {
27 $this->wp_api = $wp_api;
28 $this->filesystem = $wp_api->get_wp_filesystem_direct();
29 }
30
31 /**
32 * @return string
33 */
34 private function get_main_directory_path() {
35 $main_directory_path = null;
36 $cache_path_root = $this->wp_api->constant( 'WPML_CACHE_PATH_ROOT' );
37
38 if ( $cache_path_root ) {
39 $main_directory_path = trailingslashit( $cache_path_root ) . self::MAIN_DIRECTORY_NAME;
40 return trailingslashit( $main_directory_path );
41 }else {
42 $upload_dir = wp_upload_dir();
43
44 if ( empty( $upload_dir['error'] ) ) {
45 $base_dir = $upload_dir['basedir'];
46 $main_directory_path = trailingslashit( $base_dir ) . 'cache/' . self::MAIN_DIRECTORY_NAME;
47 return trailingslashit( $main_directory_path );
48 }
49 }
50
51 return null;
52 }
53
54 /**
55 * The function `wp_mkdir_p` will create directories recursively
56 *
57 * @param string $absolute_path
58 *
59 * @return string|bool absolute path or false if we can't have a writable and readable directory
60 */
61 private function maybe_create_directory( $absolute_path ) {
62 $result = true;
63
64 if ( ! $this->filesystem->is_dir( $absolute_path ) ) {
65 $result = wp_mkdir_p( $absolute_path );
66 }
67
68 return $result ? $absolute_path : false;
69 }
70
71 /**
72 * @param string $relative_path
73 *
74 * @return string|bool absolute path or false if we can't have a writable and readable directory
75 */
76 public function get( $relative_path = '' ) {
77 $absolute_path = false;
78 $main_directory_path = $this->maybe_create_directory( $this->get_main_directory_path() );
79
80 if ( $main_directory_path ) {
81 $absolute_path = trailingslashit( $main_directory_path . ltrim( $relative_path, '/\\' ) );
82 $absolute_path = $this->maybe_create_directory( $absolute_path );
83 }
84
85 return $absolute_path;
86 }
87
88 /**
89 * @param string $relative_path
90 */
91 public function remove( $relative_path = '' ) {
92 $main_directory_path = $this->get_main_directory_path();
93 if ( $main_directory_path ) {
94 $absolute_path = trailingslashit( $main_directory_path . ltrim( $relative_path, '/\\' ) );
95 $this->filesystem->delete( $absolute_path, true );
96 }
97 }
98 }
1 <?php
2
3 class WPML_Core_Version_Check {
4
5 public static function is_ok( $package_file_path ) {
6
7 $is_ok = false;
8
9 /** @var array $bundle */
10 $bundle = json_decode( file_get_contents( $package_file_path ), true );
11 if ( defined( 'ICL_SITEPRESS_VERSION' ) && is_array( $bundle ) ) {
12 $core_version_stripped = ICL_SITEPRESS_VERSION;
13 $dev_or_beta_pos = strpos( ICL_SITEPRESS_VERSION, '-' );
14 if ( $dev_or_beta_pos > 0 ) {
15 $core_version_stripped = substr( ICL_SITEPRESS_VERSION, 0, $dev_or_beta_pos );
16 }
17 if ( version_compare( $core_version_stripped, $bundle['sitepress-multilingual-cms'], '>=' ) ) {
18 $is_ok = true;
19 }
20 }
21
22 return $is_ok;
23 }
24 }
...\ No newline at end of file ...\ No newline at end of file
1 <?php
2 /*
3 Module Name: WPML Dependency Check Module
4 Description: This is not a plugin! This module must be included in other plugins (WPML and add-ons) to handle compatibility checks
5 Author: OnTheGoSystems
6 Author URI: http://www.onthegosystems.com/
7 Version: 2.1
8 */
9
10 /** @noinspection PhpUndefinedClassInspection */
11 class WPML_Dependencies {
12 protected static $instance;
13 private $admin_notice;
14 private $current_product;
15 private $current_version = array();
16 private $expected_versions = array();
17 private $installed_plugins = array();
18 private $invalid_plugins = array();
19 private $valid_plugins = array();
20 private $validation_results = array();
21
22 public $data_key = 'wpml_dependencies:';
23 public $needs_validation_key = 'wpml_dependencies:needs_validation';
24
25 private function __construct() {
26 if ( null === self::$instance ) {
27 $this->remove_old_admin_notices();
28 $this->init_hooks();
29 }
30 }
31
32 private function collect_data() {
33 $active_plugins = wp_get_active_and_valid_plugins();
34 $this->init_bundle( $active_plugins );
35 foreach ( $active_plugins as $plugin ) {
36 $this->add_installed_plugin( $plugin );
37 }
38 }
39
40 protected function remove_old_admin_notices() {
41 if ( class_exists( 'WPML_Bundle_Check' ) ) {
42 global $WPML_Bundle_Check;
43
44 remove_action( 'admin_notices', array( $WPML_Bundle_Check, 'admin_notices_action' ) );
45 }
46 }
47
48 private function init_hooks() {
49 add_action( 'init', array( $this, 'init_plugins_action' ) );
50 add_action( 'extra_plugin_headers', array( $this, 'extra_plugin_headers_action' ) );
51 add_action( 'admin_notices', array( $this, 'admin_notices_action' ) );
52 add_action( 'activated_plugin', array( $this, 'activated_plugin_action' ) );
53 add_action( 'deactivated_plugin', array( $this, 'deactivated_plugin_action' ) );
54 add_action( 'upgrader_process_complete', array( $this, 'upgrader_process_complete_action' ), 10, 2 );
55 add_action( 'load-plugins.php', array( $this, 'run_validation_on_plugins_page' ) );
56 }
57
58 public function run_validation_on_plugins_page() {
59 $this->reset_validation();
60 }
61
62 public function activated_plugin_action() {
63 $this->reset_validation();
64 }
65
66 public function deactivated_plugin_action() {
67 $this->reset_validation();
68 }
69
70 public function upgrader_process_complete_action( $upgrader_object, $options ) {
71 if ( 'update' === $options['action'] && 'plugin' === $options['type'] ) {
72 $this->reset_validation();
73 }
74 }
75
76 private function reset_validation() {
77 update_option( $this->needs_validation_key, true );
78 $this->validate_plugins();
79 }
80
81 private function flag_as_validated() {
82 update_option( $this->needs_validation_key, false );
83 }
84
85 private function needs_validation() {
86 return get_option( $this->needs_validation_key );
87 }
88
89 public function admin_notices_action() {
90 $this->maybe_init_admin_notice();
91 if ( $this->admin_notice && ( is_admin() && ! $this->is_doing_ajax_cron_or_xmlrpc() ) ) {
92 echo $this->admin_notice;
93 }
94 }
95
96 private function is_doing_ajax_cron_or_xmlrpc() {
97 return ( $this->is_doing_ajax() || $this->is_doing_cron() || $this->is_doing_xmlrpc() );
98 }
99
100 private function is_doing_ajax() {
101 return ( defined( 'DOING_AJAX' ) && DOING_AJAX );
102 }
103
104 private function is_doing_cron() {
105 return ( defined( 'DOING_CRON' ) && DOING_CRON );
106 }
107
108 private function is_doing_xmlrpc() {
109 return ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST );
110 }
111
112 public function extra_plugin_headers_action( array $extra_headers = array() ) {
113 $new_extra_header = array(
114 'PluginSlug' => 'Plugin Slug',
115 );
116
117 return array_merge( $new_extra_header, (array) $extra_headers );
118 }
119
120 /**
121 * @return WPML_Dependencies
122 */
123 public static function get_instance() {
124 if ( null === self::$instance ) {
125 self::$instance = new WPML_Dependencies();
126 }
127
128 return self::$instance;
129 }
130
131 public function get_plugins() {
132 return $this->installed_plugins;
133 }
134
135 public function init_plugins_action() {
136 if ( $this->needs_validation() && is_admin() && ! $this->is_doing_ajax_cron_or_xmlrpc() ) {
137 $this->init_plugins();
138 $this->validate_plugins();
139 $this->flag_as_validated();
140 }
141 }
142
143 private function init_plugins() {
144 if ( ! $this->installed_plugins ) {
145 if ( ! function_exists( 'get_plugin_data' ) ) {
146 /** @noinspection PhpIncludeInspection */
147 include_once ABSPATH . '/wp-admin/includes/plugin.php';
148 }
149 if ( function_exists( 'get_plugin_data' ) ) {
150 $this->collect_data();
151 }
152 }
153 update_option( $this->data_key . 'installed_plugins', $this->installed_plugins );
154 }
155
156 private function init_bundle( array $active_plugins ) {
157
158 foreach ( $active_plugins as $plugin_file ) {
159 $filename = dirname( $plugin_file ) . '/wpml-dependencies.json';
160 if ( file_exists( $filename ) ) {
161 $data = file_get_contents( $filename );
162 $bundle = json_decode( $data, true );
163 $this->set_expected_versions( $bundle );
164 }
165 }
166 }
167
168 private function add_installed_plugin( $plugin ) {
169 $data = get_plugin_data( $plugin );
170 $plugin_dir = realpath( dirname( $plugin ) );
171
172 $wp_plugin_dir = realpath( WP_PLUGIN_DIR ) . DIRECTORY_SEPARATOR;
173 if ( false !== $plugin_dir && $wp_plugin_dir !== $plugin_dir ) {
174 $plugin_folder = str_replace( $wp_plugin_dir, '', $plugin_dir );
175 $plugin_slug = $this->guess_plugin_slug( $data, $plugin_folder );
176
177 if ( $this->is_valid_plugin( $plugin_slug ) ) {
178 $this->installed_plugins[ $plugin_slug ] = $data['Version'];
179 }
180 }
181 }
182
183 private function set_expected_versions( array $bundle ) {
184 foreach ( $bundle as $plugin => $version ) {
185 if ( ! array_key_exists( $plugin, $this->expected_versions ) ) {
186 $this->expected_versions[ $plugin ] = $version;
187 } else {
188 if ( version_compare( $this->expected_versions[ $plugin ], $version, '<' ) ) {
189 $this->expected_versions[ $plugin ] = $version;
190 }
191 }
192 }
193 }
194
195 private function guess_plugin_slug( $plugin_data, $plugin_folder ) {
196 $plugin_slug = null;
197 $plugin_slug = $plugin_folder;
198 if ( array_key_exists( 'Plugin Slug', $plugin_data ) && $plugin_data['Plugin Slug'] ) {
199 $plugin_slug = $plugin_data['Plugin Slug'];
200 }
201
202 return $plugin_slug;
203 }
204
205 private function validate_plugins() {
206 $validation_results = $this->get_plugins_validation();
207
208 $this->valid_plugins = array();
209 $this->invalid_plugins = array();
210 foreach ( $validation_results as $plugin => $validation_result ) {
211 if ( true === $validation_result ) {
212 $this->valid_plugins[] = $plugin;
213 } else {
214 $this->invalid_plugins[] = $plugin;
215 }
216 }
217
218 update_option( $this->data_key . 'valid_plugins', $this->valid_plugins );
219 update_option( $this->data_key . 'invalid_plugins', $this->invalid_plugins );
220 update_option( $this->data_key . 'expected_versions', $this->expected_versions );
221 }
222
223 public function get_plugins_validation() {
224 foreach ( $this->installed_plugins as $plugin => $version ) {
225 $this->current_product = $plugin;
226 if ( $this->is_valid_plugin() ) {
227 $this->current_version = $version;
228 $validation_result = $this->is_plugin_version_valid();
229 $this->validation_results[ $plugin ] = $validation_result;
230 }
231 }
232
233 return $this->validation_results;
234 }
235
236 private function is_valid_plugin( $product = false ) {
237 $result = false;
238
239 if ( ! $product ) {
240 $product = $this->current_product;
241 }
242 if ( $product ) {
243 $versions = $this->get_expected_versions();
244 $result = array_key_exists( $product, $versions );
245 }
246
247 return $result;
248 }
249
250 public function is_plugin_version_valid() {
251 $expected_version = $this->filter_version( $this->get_expected_product_version() );
252
253 return $expected_version ? version_compare( $this->filter_version( $this->current_version ), $expected_version, '>=' ) : null;
254 }
255
256 private function filter_version( $version ) {
257 return preg_replace( '#[^\d.].*#', '', $version );
258 }
259
260 public function get_expected_versions() {
261 return $this->expected_versions;
262 }
263
264 private function get_expected_product_version( $product = false ) {
265 $result = null;
266
267 if ( ! $product ) {
268 $product = $this->current_product;
269 }
270 if ( $product ) {
271 $versions = $this->get_expected_versions();
272
273 $result = isset( $versions[ $product ] ) ? $versions[ $product ] : null;
274 }
275
276 return $result;
277 }
278
279 private function maybe_init_admin_notice() {
280 $this->admin_notice = null;
281 $this->installed_plugins = get_option( $this->data_key . 'installed_plugins', [] );
282 $this->invalid_plugins = get_option( $this->data_key . 'invalid_plugins', [] );
283 $this->expected_versions = get_option( $this->data_key . 'expected_versions', [] );
284 $this->valid_plugins = get_option( $this->data_key . 'valid_plugins', [] );
285
286 if ( $this->has_invalid_plugins() ) {
287 $notice_paragraphs = array();
288
289 $notice_paragraphs[] = $this->get_invalid_plugins_report_header();
290 $notice_paragraphs[] = $this->get_invalid_plugins_report_list();
291 $notice_paragraphs[] = $this->get_invalid_plugins_report_footer();
292
293 $this->admin_notice = '<div class="error wpml-admin-notice">';
294 $this->admin_notice .= '<h3>' . __( 'WPML Update is Incomplete', 'sitepress' ) . '</h3>';
295 $this->admin_notice .= '<p>' . implode( '</p><p>', $notice_paragraphs ) . '</p>';
296 $this->admin_notice .= '</div>';
297 }
298 }
299
300 public function has_invalid_plugins() {
301 return count( $this->invalid_plugins );
302 }
303
304 private function get_invalid_plugins_report_header() {
305 if ( $this->has_valid_plugins() ) {
306 if ( count( $this->valid_plugins ) === 1 ) {
307 $paragraph = __( 'You are running updated %s, but the following component is not updated:', 'sitepress' );
308 $paragraph = sprintf( $paragraph, '<strong>' . $this->valid_plugins[0] . '</strong>' );
309 } else {
310 $paragraph = __( 'You are running updated %s and %s, but the following components are not updated:', 'sitepress' );
311 $first_valid_plugins = implode( ', ', array_slice( $this->valid_plugins, 0, - 1 ) );
312 $last_valid_plugin = array_slice( $this->valid_plugins, - 1 );
313 $paragraph = sprintf( $paragraph, '<strong>' . $first_valid_plugins . '</strong>', '<strong>' . $last_valid_plugin[0] . '</strong>' );
314 }
315 } else {
316 $paragraph = __( 'The following components are not updated:', 'sitepress' );
317 }
318
319 return $paragraph;
320 }
321
322 private function get_invalid_plugins_report_list() {
323 /* translators: %s: Version number */
324 $required_version = __( 'required version: %s', 'sitepress' );
325 $invalid_plugins_list = '<ul class="ul-disc">';
326 foreach ( $this->invalid_plugins as $invalid_plugin ) {
327 $plugin_name_html = '<li data-installed-version="' . $this->installed_plugins[ $invalid_plugin ] . '">';
328 $required_version_string = '';
329 if ( isset( $this->expected_versions[ $invalid_plugin ] ) ) {
330 $required_version_string = ' (' . sprintf( $required_version, $this->expected_versions[ $invalid_plugin ] ) . ')';
331 }
332 $plugin_name_html .= $invalid_plugin . $required_version_string;
333 $plugin_name_html .= '</li>';
334
335 $invalid_plugins_list .= $plugin_name_html;
336 }
337 $invalid_plugins_list .= '</ul>';
338
339 return $invalid_plugins_list;
340 }
341
342 private function get_invalid_plugins_report_footer() {
343 $wpml_org_url = '<a href="https://wpml.org/account/" title="WPML.org account">' . __( 'WPML.org account', 'sitepress' ) . '</a>';
344
345 $notice_paragraph = __( 'Your site will not work as it should in this configuration', 'sitepress' );
346 $notice_paragraph .= ' ';
347 $notice_paragraph .= __( 'Please update all components which you are using.', 'sitepress' );
348 $notice_paragraph .= ' ';
349 $notice_paragraph .= sprintf( __( 'For WPML components you can receive updates from your %s or automatically, after you register WPML.', 'sitepress' ), $wpml_org_url );
350
351 return $notice_paragraph;
352 }
353
354 private function has_valid_plugins() {
355 return $this->valid_plugins && count( $this->valid_plugins );
356 }
357 }
1 <?php
2 /**
3 * WPML_PHP_Version_Check class file.
4 *
5 * @package WPML\LibDependencies
6 */
7
8 if ( ! class_exists( 'WPML_PHP_Version_Check' ) ) {
9
10 /**
11 * Class WPML_PHP_Version_Check
12 */
13 class WPML_PHP_Version_Check {
14
15 /**
16 * Required php version.
17 *
18 * @var string
19 */
20 private $required_php_version;
21
22 /**
23 * Plugin name.
24 *
25 * @var string
26 */
27 private $plugin_name;
28
29 /**
30 * Plugin file.
31 *
32 * @var string
33 */
34 private $plugin_file;
35
36 /**
37 * Text domain.
38 *
39 * @var string
40 */
41 private $text_domain;
42
43 /**
44 * WPML_PHP_Version_Check constructor.
45 *
46 * @param string $required_version Required php version.
47 * @param string $plugin_name Plugin name.
48 * @param string $plugin_file Plugin file.
49 * @param string $text_domain Text domain.
50 */
51 public function __construct( $required_version, $plugin_name, $plugin_file, $text_domain ) {
52 $this->required_php_version = $required_version;
53 $this->plugin_name = $plugin_name;
54 $this->plugin_file = $plugin_file;
55 $this->text_domain = $text_domain;
56 }
57
58 /**
59 * Check php version.
60 *
61 * @return bool
62 */
63 public function is_ok() {
64 if ( version_compare( $this->required_php_version, phpversion(), '>' ) ) {
65 add_action( 'admin_notices', array( $this, 'php_requirement_message' ) );
66
67 return false;
68 }
69
70 return true;
71 }
72
73 /**
74 * Show notice with php requirement.
75 */
76 public function php_requirement_message() {
77 load_plugin_textdomain( $this->text_domain, false, dirname( plugin_basename( $this->plugin_file ) ) . '/locale' );
78
79 $errata_page_link = 'https://wpml.org/errata/parse-error-syntax-error-unexpected-t_class-and-other-errors-when-using-php-versions-older-than-5-6/';
80
81 // phpcs:disable WordPress.WP.I18n.NonSingularStringLiteralDomain
82 /* translators: 1: Current PHP version number, 2: Plugin version, 3: Minimum required PHP version number */
83 $message = sprintf( __( 'Your server is running PHP version %1$s but %2$s requires at least %3$s.', $this->text_domain ), phpversion(), $this->plugin_name, $this->required_php_version );
84
85 $message .= '<br>';
86 /* translators: Link to errata page */
87 $message .= sprintf( __( 'You can find version of the plugin suitable for your environment <a href="%s">here</a>.', $this->text_domain ), $errata_page_link );
88 // phpcs:enable WordPress.WP.I18n.NonSingularStringLiteralDomain
89 ?>
90 <div class="message error">
91 <p>
92 <?php echo wp_kses_post( $message ); ?>
93 </p>
94 </div>
95 <?php
96 }
97 }
98 }
1 <wpml-config>
2 <custom-fields>
3 <custom-field action="copy">_wpml_media_duplicate</custom-field>
4 <custom-field action="copy">_wpml_media_featured</custom-field>
5 </custom-fields>
6 </wpml-config>
...\ No newline at end of file ...\ No newline at end of file
1 {
2 "sitepress-multilingual-cms": "4.5.0",
3 "wpml-string-translation": "3.2.0",
4 "wpml-media-translation": "2.7.0",
5 "wpml-sticky-links": "1.5.0",
6 "wpml-cms-nav": "1.5.0",
7 "gravityforms-multilingual": "1.5.0"
8 }
...\ No newline at end of file ...\ No newline at end of file
...@@ -87,13 +87,11 @@ $carousel_style = get_field('carousel_style'); ...@@ -87,13 +87,11 @@ $carousel_style = get_field('carousel_style');
87 ); 87 );
88 $new_src = add_query_arg($params, $src); 88 $new_src = add_query_arg($params, $src);
89 $iframe = str_replace($src, $new_src, $iframe); 89 $iframe = str_replace($src, $new_src, $iframe);
90 error_log($src); 90
91 error_log($iframe);
92 // Add extra attributes to iframe HTML. 91 // Add extra attributes to iframe HTML.
93 $attributes = 'frameborder="0"'; 92 $attributes = 'frameborder="0"';
94 $iframe = str_replace('></iframe>', ' ' . $attributes . '></iframe>', $iframe); 93 $iframe = str_replace('></iframe>', ' ' . $attributes . '></iframe>', $iframe);
95 $iframe = str_replace('<iframe', '<iframe class="yt_player_iframe" ', $iframe); 94 $iframe = str_replace('<iframe', '<iframe class="yt_player_iframe" ', $iframe);?>
96 error_log($iframe); ?>
97 <div class="modal" id="<?php echo $modalId; ?>" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true"> 95 <div class="modal" id="<?php echo $modalId; ?>" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
98 <div class="modal-dialog modal-dialog-centered modal-xl"> 96 <div class="modal-dialog modal-dialog-centered modal-xl">
99 <div class="modal-content"> 97 <div class="modal-content">
......
...@@ -2,6 +2,6 @@ ...@@ -2,6 +2,6 @@
2 Theme Name: MSF CA Child 2 Theme Name: MSF CA Child
3 Author: Tenzing Communications 3 Author: Tenzing Communications
4 Template: msf-ca 4 Template: msf-ca
5 Version: 1.0.510 5 Version: 1.0.511
6 */ 6 */
7 7
......