PluginProbe
Photo Engine (Media Organizer & Lightroom) / trunk
Photo Engine (Media Organizer & Lightroom) vtrunk
6.5.5 6.5.4 6.5.3 6.5.2 trunk 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.8.2 0.8.3 0.8.4 0.8.6 0.8.8 1.2.4 1.3.0 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 All 157 releases
wplr-sync / classes / admin.php

admin.php in Photo Engine (Media Organizer & Lightroom) trunk, at classes/admin.php

1,171 lines 44.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 class Meow_WPLR_Sync_Admin extends MeowKit_WPLR_Admin {
3 protected $wplr;
4
5 public static $logo = 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxIiB2aWV3Qm94PSIwIDAgMTY1IDE2NSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8c3R5bGU+CiAgICAuc3Qye2ZpbGw6IzgwNDYyNX0uc3Qze2ZpbGw6I2ZkYTk2MH0KICA8L3N0eWxlPgogIDxwYXRoIGQ9Ik03MiA3YTc2IDc2IDAgMCAxIDg0IDkxQTc1IDc1IDAgMSAxIDcyIDd6IiBmaWxsPSIjNGE2YjhjIi8+CiAgPHBhdGggZD0iTTQ4IDQ4YzIgNSAyIDEwIDUgMTQgNSA4IDEzIDE3IDIyIDIwbDEtMTBjMS0yIDMtMyA1LTNoMTNjMiAwIDQgMSA1IDNsMyA5IDQtMTBjMi0zIDYtMiA5LTJoMTFjMyAyIDMgNSAzIDhsMiAzN2MwIDMtMSA3LTQgOGgtMTJjLTIgMC0zLTItNS00LTEgMS0yIDMtNCAzLTUgMS05IDEtMTMtMS0zIDItNSAyLTkgMnMtOSAxLTEwLTNjLTItNC0xLTggMC0xMi04LTMtMTUtNy0yMi0xMi03LTctMTUtMTQtMjAtMjMtMy00LTUtOC01LTEzIDEtNCAzLTEwIDYtMTMgNC0zIDEyLTIgMTUgMnoiIGZpbGw9IiMxMDEwMTAiLz4KICA8cGF0aCBjbGFzcz0ic3QyIiBkPSJNNDMgNTFsNCAxMS02IDVoLTZjLTMtNS0zLTExIDAtMTYgMi0yIDYtMyA4IDB6Ii8+CiAgPHBhdGggY2xhc3M9InN0MyIgZD0iTTQ3IDYybDMgNmMwIDMgMCA0LTIgNnMtNCAyLTcgMmwtNi05aDZsNi01eiIvPgogIDxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik01MCA2OGw4IDljLTMgMy01IDYtOSA4bC04LTljMyAwIDUgMCA3LTJzMy0zIDItNnoiLz4KICA8cGF0aCBkPSJNODIgNzRoMTJsNSAxOCAzIDExIDgtMjloMTNsMiA0MmgtOGwtMS0yLTEtMzEtMTAgMzItNyAxLTktMzMtMSAyOS0xIDRoLThsMy00MnoiIGZpbGw9IiNmZmYiLz4KICA8cGF0aCBjbGFzcz0ic3QzIiBkPSJNNTggNzdsNSA1Yy0xIDQtMiA4LTcgOGwtNy01YzQtMiA2LTUgOS04eiIvPgogIDxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik02MyA4Mmw5IDUtNiA5LTEwLTZjNSAwIDYtNCA3LTh6Ii8+CiAgPHBhdGggY2xhc3M9InN0MyIgZD0iTTcyIDg3bDMgMS0xIDExLTgtMyA2LTEweiIvPgo8L3N2Zz4K';
6
7 public function __construct() {
8 parent::__construct( WPLR_SYNC_PREFIX, WPLR_SYNC_ENTRY, WPLR_SYNC_DOMAIN );
9 if ( is_admin() ) {
10 global $wplr;
11 $this->wplr = $wplr;
12
13 if ( current_user_can( 'upload_files' ) ) {
14 add_action( 'show_user_profile', array ( $this, 'auth_token_field' ) );
15 add_action( 'edit_user_profile', array ( $this, 'auth_token_field' ) );
16 add_action( 'admin_menu', array( $this, 'admin_menu' ) );
17 }
18
19 add_filter( 'wplr_meowapps_is_registered', array( $this, '' ) );
20 add_filter( 'media_row_actions', array( $this, 'media_row_actions' ), 10, 2 );
21 add_action( 'wp_ajax_wplrsync_generate_auth_token', array ( $this, 'ajax_generate_auth_token' ) );
22 add_action( 'wp_ajax_wplrsync_link', array( $wplr, 'wplrsync_link' ) );
23 add_action( 'wp_ajax_wplrsync_unlink', array( $wplr, 'wplrsync_unlink' ) );
24 add_action( 'wp_ajax_wplrsync_clean', array( $this, 'wplrsync_clean' ) );
25 add_action( 'wp_ajax_wplrsync_extensions_reset', array( $this, 'wplrsync_extensions_reset' ) );
26 add_action( 'wp_ajax_wplrsync_extensions_init', array( $this, 'wplrsync_extensions_init' ) );
27 add_action( 'wp_ajax_wplrsync_extensions_query', array( $this, 'wplrsync_extensions_query' ) );
28 add_action( 'add_meta_boxes', array( $this, 'add_format_warning_metabox' ) );
29 //if ( get_option( 'wplr_library_show_hierarchy', 'none' ) !== 'none' ) {
30 $wplr_medialib = new Meow_WPLR_Sync_Explorer();
31 //}
32 // Load the scripts only if they are needed by the current screen
33 $uri = $_SERVER['REQUEST_URI'];
34 $page = isset( $_GET["page"] ) ? $_GET["page"] : null;
35 $is_media_library = preg_match( '/wp\-admin\/upload\.php/', $uri );
36 $is_wplr_sync_screen = in_array( $page, [ 'wplr_sync_settings', 'wplr_sync_dashboard' ] );
37 $is_meowapps_dashboard = $page === 'meowapps-main-menu';
38 //if ( $is_meowapps_dashboard || $is_wplr_sync_screen || $is_media_library ) {
39 add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
40 //}
41 }
42 }
43
44 private function wp_ajax_auth_check( $action ) {
45 $nonce = apply_filters( 'wplr_sync_ajax_nonce', check_ajax_referer( $action ) );
46 if ( !$nonce ) {
47 wp_send_json_error( 'Invalid nonce' );
48 }
49
50 $user_can = apply_filters( 'wplr_sync_ajax_user_can', current_user_can( 'manage_options' ) );
51 if ( !$user_can ) {
52 wp_send_json_error( 'You do not have sufficient permissions to perform this action.' );
53 }
54 }
55
56 function admin_enqueue_scripts() {
57
58 // Load the scripts
59 $physical_file = WPLR_SYNC_PATH . '/app/index.js';
60 $cache_buster = file_exists( $physical_file ) ? filemtime( $physical_file ) : WPLR_SYNC_VERSION;
61 wp_register_script( 'pEngine-vendor', WPLR_SYNC_URL . 'app/vendor.js',
62 ['wp-element', 'wp-i18n'], $cache_buster
63 );
64 wp_register_script( 'pEngine', WPLR_SYNC_URL . 'app/index.js',
65 ['pEngine-vendor', 'wp-i18n'], $cache_buster
66 );
67 wp_set_script_translations( 'pEngine', 'wplr-sync' );
68 wp_enqueue_script('pEngine' );
69
70 // Load the fonts
71 wp_register_style( 'meow-neko-ui-lato-font', '//fonts.googleapis.com/css2?family=Lato:wght@100;300;400;700;900&display=swap');
72 wp_enqueue_style( 'meow-neko-ui-lato-font' );
73
74 global $wplr;
75 $screen = get_current_screen();
76 $collections = $wplr->read_collections_recursively();
77
78 // Localize and options
79 $col_id = isset($_GET['col_id']) ? (int)$_GET['col_id'] : 0;
80 wp_localize_script( 'pEngine', 'pEngine', array_merge( [
81 'api_url' => rest_url( 'wplr-sync/v1' ),
82 'rest_url' => rest_url(),
83 'plugin_url' => WPLR_SYNC_URL,
84 'prefix' => WPLR_SYNC_PREFIX,
85 'domain' => WPLR_SYNC_DOMAIN,
86 'is_pro' => true,
87 'is_registered' => true,
88 'rest_nonce' => wp_create_nonce( 'wp_rest' ),
89 'hierarchy' => $collections,
90 'isMediaLibrary' => $screen->base === 'upload',
91 'col_id' => $col_id,
92 'media_library' => get_option( 'wplr_media_library' ),
93 'media_modals' => get_option( 'wplr_media_modals' ),
94 ], $this->get_all_options() ) );
95 }
96
97 function is_registered() {
98 return true;
99 }
100
101 function list_options() {
102 return array(
103 'wplr_use_taken_date' => false,
104 'wplr_upload_folder' => false,
105 'wplr_filename_accents' => false,
106 'wplr_enable_keywords' => false,
107 'wplr_sync_keywords' => '',
108 'wplr_library_show_hierarchy' => 'none',
109 'wplr_public_api' => false,
110 'wr2x_big_image_size_threshold' => false,
111 'wplr_debugging_enabled' => false,
112 'wplr_catch_errors' => false,
113 'wplr_check_same_file' => false,
114 'wplr_debuglogs' => false,
115 'wplr_hide_extensions' => false,
116 'wplr_protocol' => false,
117 'wplr_library_show_filters' => false,
118 'wplr_auth_token' => false,
119 'wplr_qr' => null,
120 'wplr_user_token' => null,
121 'sync_keywords_options' => [],
122 'troubles_issues' => [],
123 'extensions' => [],
124 'wplr_media_organizer' => false,
125 'wplr_media_library' => false,
126 'wplr_media_modals' => false,
127 );
128 }
129
130 /**
131 * Generates a new Public API auth token, stores it as a transient and returns it.
132 * @return string The new token
133 */
134 function generate_public_api_token() {
135 $token = str_replace( '|', '#', wp_generate_password( 12, false, false ) );
136 set_transient( 'wplr_auth_token', $token, YEAR_IN_SECONDS );
137 return $token;
138 }
139
140 /**
141 * Builds the QR payload (home url + token) used by the Public API.
142 * @param string $token
143 * @return string
144 */
145 function get_public_api_qr( $token ) {
146 $home_url = function_exists( 'pll_home_url' ) ? pll_home_url() : get_home_url();
147 return $home_url . '|' . $token;
148 }
149
150 /**
151 * Returns the personal auth token of the current user (used to authenticate as
152 * this WordPress user through the API). Generates one if it does not exist yet.
153 * @return string|null
154 */
155 function get_user_token() {
156 $user_id = get_current_user_id();
157 if ( !$user_id ) {
158 return null;
159 }
160 $token = get_user_meta( $user_id, 'wplr_auth_token', true );
161 if ( empty( $token ) ) {
162 global $wplr;
163 try {
164 $token = $wplr->generate_auth_token( $user_id );
165 }
166 catch ( Exception $e ) {
167 $token = null;
168 }
169 }
170 return $token;
171 }
172
173 function get_all_options() {
174 // sync_keywords_options
175 $taxonomies = get_object_taxonomies( 'attachment' );
176 array_unshift( $taxonomies, "" );
177
178 // Troubleshooting (Potential issues)
179 $troubles = new Meow_WPLR_Sync_Troubleshoot();
180 $issues = $troubles->issues();
181
182 // Extensions
183 $extensions = apply_filters( 'wplr_extensions', [] );
184
185 // Public API
186 $public_api = get_option( 'wplr_public_api', false );
187 $wplr_auth_token = get_transient( 'wplr_auth_token' );
188 $wplr_qr = null;
189 if ( $public_api ) {
190 if ( $wplr_auth_token === false ) {
191 $wplr_auth_token = $this->generate_public_api_token();
192 }
193 $wplr_qr = $this->get_public_api_qr( $wplr_auth_token );
194 }
195 elseif ( $wplr_auth_token !== false ) {
196 delete_transient( 'wplr_auth_token' );
197 }
198
199 $options = $this->list_options();
200 $current_options = array();
201 foreach ( $options as $option => $default ) {
202 if ( $option === 'wplr_public_api' ) {
203 $current_options[$option] = $public_api;
204 } elseif ( $option === 'wplr_auth_token' ) {
205 $current_options[$option] = $wplr_auth_token;
206 } elseif ( $option === 'wplr_qr' ) {
207 $current_options[$option] = $wplr_qr;
208 } elseif ( $option === 'wplr_user_token' ) {
209 $current_options[$option] = $this->get_user_token();
210 } elseif ( $option === 'sync_keywords_options' ) {
211 $current_options[$option] = $taxonomies;
212 } elseif ( $option === 'troubles_issues' ) {
213 $current_options[$option] = $issues;
214 } elseif ( $option === 'extensions' ) {
215 $current_options[$option] = $extensions;
216 } else {
217 $current_options[$option] = get_option( $option, $default );
218 }
219 }
220 return $current_options;
221 }
222
223 /**
224 * Outputs the auth token generator UI
225 * @param $user
226 */
227 function auth_token_field( $user ) {
228 ?>
229 <table class="form-table">
230 <tr>
231 <th><?php _e( 'Token for Photo Engine', 'wplr-sync' ); ?></th>
232 <td id="wplr-auth-token-generator">
233 <button class="button"><?php _e( 'Generate Token', 'wplr-sync' ); ?></button>
234 <?php $token = get_user_meta( $user->ID, 'wplr_auth_token', true ); ?>
235 <p class="description<?php if ( !$token ) echo ' no-token hidden'; ?>">Your token is <var class="token"><?php echo esc_html( $token ); ?></var></p>
236 </td>
237 </tr>
238 </table>
239 <script>
240 (function ($) {
241 var wrap = $('#wplr-auth-token-generator')
242 wrap.find('button').on('click', function (ev) {
243 ev.preventDefault()
244
245 var $this = $(this) // The button itself
246 if ($this.prop('disabled')) return false
247 $this.prop('disabled', true)
248
249 $.ajax(ajaxurl, {
250 method: 'POST',
251 dataType: 'json',
252 data: {
253 _ajax_nonce: '<?php echo wp_create_nonce( 'wplrsync_generate_auth_token' ); ?>',
254 action: 'wplrsync_generate_auth_token'
255 }
256
257 }).always(function () {
258 $this.prop('disabled', false)
259
260 }).fail(function (err) {
261 var msg = '<?php _e( 'Failed to generate an auth token due to the following reason:', 'wplr-sync' ); ?>' + '\n'
262 msg += '"' + err.responseJSON.data + '"' + '\n'
263 msg += 'Status: ' + err.status + ' ' + err.statusText + '\n'
264 console.error(msg)
265 // TODO: Show the error message on the front
266
267 }).done(function (result) {
268 var token = result.data.newToken
269 wrap.find('var.token').text(token)
270 wrap.find('.no-token.hidden').removeClass('hidden')
271 })
272 })
273 })(jQuery)
274 </script>
275 <style>
276 #wplr-auth-token-generator var.token {
277 font-style: normal;
278 font-family: monospace;
279 color: green;
280 }
281 </style>
282 <?php
283 }
284
285 /**
286 * Generates an auth token via ajax
287 * @uses $_POST['action']
288 * @uses $_POST['userId']
289 */
290 function ajax_generate_auth_token() {
291 if ( !check_ajax_referer( $_POST['action'] ) ) {
292 wp_send_json_error( __( 'Invalid nonce', 'wplr-sync' ), 403 );
293 }
294 $userId = get_current_user_id();
295 $user = get_userdata( $userId );
296 if ( !$user ) {
297 wp_send_json_error( __( 'You have to be logged in', 'wplr-sync' ), 401 );
298 }
299 try {
300 $token = $this->wplr->generate_auth_token( $userId );
301 }
302 catch ( Exception $e ) {
303 wp_send_json_error( __( 'Failed to save the auth token to DB', 'wplr-sync' ), 500 ); // I/O failure
304 }
305 wp_send_json_success( array ( 'newToken' => $token ) );
306 }
307
308 function common_url( $file ) {
309 return plugin_dir_url( __FILE__ ) . ( '\/common\/' . $file );
310 }
311
312 function media_row_actions( $actions, $post ) {
313 global $current_screen;
314 if ( 'upload' != $current_screen->id ) {
315 return $actions;
316 }
317 global $wpdb;
318 $table_name = $wpdb->prefix . "lrsync";
319 $res = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $table_name WHERE wp_id = %d", $post->ID ) );
320 if ( !empty( $res ) && isset( $actions['trash'] ) )
321 $actions['trash'] = "Trash";
322 return $actions;
323 }
324
325 function admin_menu() {
326 $hide_extensions = get_option( 'wplr_hide_extensions' ) && !get_option( 'wplr_debugging_enabled' );
327
328 // -- Debugging
329 add_settings_section( 'wplr_settings_debug', null, null, 'wplr-settings-menu-debug' );
330 add_settings_field( 'wplr_debugging_enabled', __( 'Debugging', 'wplr-sync' ),
331 array( $this, 'admin_debugging_callback' ),'wplr-settings-menu-debug', 'wplr_settings_debug',
332 array( "Enable" ) );
333 add_settings_field( 'wplr_catch_errors', __( 'Catch Errors', 'wplr-sync' ),
334 array( $this, 'admin_catch_errors_callback' ),'wplr-settings-menu-debug', 'wplr_settings_debug',
335 array( "Enable" ) );
336 add_settings_field( 'wplr_check_same_file', __( 'Avoid Double Processing', 'wplr-sync' ),
337 array( $this, 'admin_check_same_file_callback' ),'wplr-settings-menu-debug', 'wplr_settings_debug',
338 array( "Enable (BETA)" ) );
339 add_settings_field( 'wplr_debuglogs', __( 'Advanced Logs', 'wplr-sync' ),
340 array( $this, 'admin_debuglogs_callback' ), 'wplr-settings-menu-debug', 'wplr_settings_debug',
341 array( "Enable" ) );
342 register_setting( 'wplr-settings-debug', 'wplr_library_show_hierarchy' );
343 register_setting( 'wplr-settings-debug', 'wplr_debuglogs' );
344 register_setting( 'wplr-settings-debug', 'wplr_catch_errors' );
345 register_setting( 'wplr-settings-debug', 'wplr_debugging_enabled' );
346 register_setting( 'wplr-settings-debug', 'wplr_check_same_file' );
347 register_setting( 'wplr-settings-debug', 'wplr_public_api' );
348
349 add_submenu_page( 'meowapps-main-menu', 'Photo Engine', 'Photo Engine', 'manage_options',
350 'wplr_sync_settings', array( $this, 'admin_settings' ) );
351
352 // Debug
353 if ( get_option( 'wplr_debugging_enabled' ) )
354 add_submenu_page( 'meowapps-main-menu',
355 __( 'Photo Engine Debug', 'wplr-sync' ),
356 __( 'Photo Engine Debug', 'wplr-sync' ), 'manage_options',
357 'wplrsync-debug-menu', array( $this, 'admin_debug' ) );
358 }
359
360 function admin_settings() {
361 echo '<div id="wplr-sync-admin-settings"></div>';
362 }
363
364 /*
365 OPTIONS
366 */
367
368 function wplrsync_clean() {
369 $this->wp_ajax_auth_check( 'wp_ajax_wplrsync_clean' );
370
371 global $wpdb;
372 $tbl_m = $wpdb->prefix . 'lrsync';
373 $tbl_r = $wpdb->prefix . 'lrsync_relations';
374 $tbl_c = $wpdb->prefix . 'lrsync_collections';
375 $tbl_mt = $wpdb->prefix . 'lrsync_meta';
376 try {
377 do_action( 'wplr_clean' );
378 $wpdb->query( "DELETE FROM $tbl_m WHERE wp_id NOT IN (SELECT ID FROM $wpdb->posts WHERE post_type = 'attachment')" );
379 $wpdb->query( "DELETE FROM $tbl_r WHERE wp_id NOT IN (SELECT ID FROM $wpdb->posts WHERE post_type = 'attachment')" );
380 $wpdb->query( "DELETE FROM $tbl_r WHERE wp_col_id NOT IN (SELECT wp_col_id FROM $tbl_c)" );
381 $wpdb->query( "DELETE FROM $tbl_mt WHERE name = 'media_tag' AND id NOT IN (SELECT ID FROM $wpdb->posts WHERE post_type = 'attachment')" );
382 echo json_encode( array( 'success' => true ) );
383 }
384 catch ( Exception $e ) {
385 error_log( print_r( $e, true ) );
386 echo json_encode( array( 'success' => false, 'message' => "An exception was caught and written in the PHP error logs." ) );
387 }
388 die();
389 }
390
391
392
393 function wplrsync_extensions_reset() {
394 $this->wp_ajax_auth_check( 'wp_ajax_wplrsync_extensions_reset' );
395
396 try {
397 do_action( 'wplr_reset' );
398 wp_send_json_success();
399 } catch ( Exception $e ) {
400 error_log( print_r( $e, true ) );
401 wp_send_json_error( 'An exception was caught and written in the PHP error logs.' );
402 }
403 }
404
405 function wplrsync_extensions_query() {
406 $this->wp_ajax_auth_check( 'wp_ajax_wplrsync_extensions_query' );
407
408 try {
409 global $wplr;
410 $task = $_POST['task'];
411 if ( $task['action'] == 'add_collection' ) {
412 if ( $task['is_folder'] )
413 do_action( 'wplr_create_folder', (int)$task['wp_col_id'], (int)$task['wp_folder_id'], array( 'name' => $task['name'] ) );
414 else
415 do_action( 'wplr_create_collection', (int)$task['wp_col_id'], (int)$task['wp_folder_id'], array( 'name' => $task['name'] ) );
416 }
417 else if ( $task['action'] == 'remove_collection' ) {
418 if ( $task['is_folder'] )
419 do_action( 'wplr_remove_folder', (int)$task['wp_col_id'] );
420 else
421 do_action( 'wplr_remove_collection', (int)$task['wp_col_id'] );
422 }
423 else if ( $task['action'] == 'add_media' )
424 do_action( 'wplr_add_media_to_collection', (int)$task['wp_id'], (int)$task['wp_col_id'] );
425 else if ( $task['action'] == 'remove_media' )
426 do_action( 'wplr_remove_media_from_collection', (int)$task['wp_id'], (int)$task['wp_col_id'] );
427 else if ( $task['action'] == 'add_tag' ) {
428 $pTagParent = $wplr->get_meta( 'tag_parent', (int)$task['id'] );
429 do_action( 'wplr_add_tag', (int)$task['id'], $task['name'], $pTagParent );
430 }
431 else if ( $task['action'] == 'remove_tag' )
432 do_action( 'wplr_remove_tag', (int)$task['id'] );
433 else if ( $task['action'] == 'update_tag' )
434 do_action( 'wplr_update_tag', (int)$task['id'], $task['name'], (int)$task['parent'] );
435 else if ( $task['action'] == 'add_media_tag' )
436 do_action( 'wplr_add_media_tag', (int)$task['wp_id'], (int)$task['id'] );
437 else if ( $task['action'] == 'remove_media_tag' )
438 do_action( 'wplr_remove_media_tag', (int)$task['wp_id'], (int)$task['id'] );
439
440 echo json_encode( array( 'success' => true ) );
441 }
442 catch ( Exception $e ) {
443 error_log( print_r( $e, true ) );
444 echo json_encode( array( 'success' => false, 'message' => "An exception was caught and written in the PHP error logs." ) );
445 }
446 die();
447 }
448
449 function wplrsync_extensions_init() {
450 $this->wp_ajax_auth_check( 'wp_ajax_wplrsync_extensions_init' );
451
452 global $wpdb, $wplr;
453 $isRemoval = isset( $_POST['isRemoval'] ) && $_POST['isRemoval'] == 1;
454 $tbl_m = $wpdb->prefix . 'lrsync';
455 $tbl_r = $wpdb->prefix . 'lrsync_relations';
456 $tbl_t = $wpdb->prefix . 'lrsync_meta';
457
458 $tasks = array();
459
460 // Tags
461 $tags = $wpdb->get_results( $wpdb->prepare( "SELECT id as id, value as name FROM $tbl_t WHERE name = %s", 'tag_name' ), ARRAY_A );
462 foreach ( $tags as $tag )
463 array_push( $tasks, array_merge( array( 'action' => $isRemoval ? 'remove_tag' : 'add_tag' ), $tag ) );
464
465 // Tag's Parents
466 if ( !$isRemoval ) {
467 $tags = $wpdb->get_results( $wpdb->prepare( "SELECT m1.id as id, m1.value as name, m2.value as parent
468 FROM $tbl_t m1
469 JOIN $tbl_t m2 ON m2.id = m1.id AND m2.name = %s
470 WHERE m1.name = %s", 'tag_parent', 'tag_name' ), ARRAY_A );
471
472 foreach ( $tags as $tag )
473 array_push( $tasks, array_merge( array( 'action' => 'update_tag' ), $tag ) );
474 }
475
476 // Collections
477 $collections = $wplr->read_collections_recursively( null, array(), $isRemoval );
478 $tasks = array_merge( $tasks, $collections );
479
480 // Photos
481 foreach ( $collections as $c ) {
482 if ( !$c['is_folder'] ) {
483 $photos = $wpdb->get_results( $wpdb->prepare( "SELECT wp_id, wp_col_id, sort FROM $tbl_r WHERE wp_col_id = %d ORDER BY sort", $c['wp_col_id'] ), ARRAY_A );
484 foreach ( $photos as $p )
485 array_push( $tasks, array_merge( array( 'action' => $isRemoval ? 'remove_media' : 'add_media' ), $p ) );
486 }
487 };
488
489 // Media Tags
490 $tags = $wpdb->get_results( $wpdb->prepare( "
491 SELECT id as wp_id, value as id
492 FROM $tbl_t
493 WHERE name = %s", 'media_tag' ), ARRAY_A );
494 foreach ( $tags as $tag )
495 array_push( $tasks, array_merge( array( 'action' => $isRemoval ? 'remove_media_tag' : 'add_media_tag' ), $tag ) );
496 echo json_encode( array( 'success' => true, 'data' => $isRemoval ? $tasks : array_reverse( $tasks ) ) );
497 die();
498 }
499
500 function admin_debugging_callback( $args ) {
501 $html = '<input type="checkbox" id="wplr_debugging_enabled" name="wplr_debugging_enabled" value="1" ' .
502 checked( 1, get_option( 'wplr_debugging_enabled' ), false ) . '/>';
503 $html .= '<label for="wplr_debugging_enabled"> ' . $args[0] . '</label><br>';
504 $html .= '<span class="description">' .
505 __( 'Add a Debugging menu in Photo Engine. For advanced users only.', 'wplr-sync' ) . '</span>';
506 echo $html;
507 }
508
509 function admin_catch_errors_callback( $args ) {
510 $html = '<input type="checkbox" id="wplr_catch_errors" name="wplr_catch_errors" value="1" ' .
511 checked( 1, get_option( 'wplr_catch_errors' ), false ) . '/>';
512 $html .= '<label for="wplr_catch_errors"> ' . $args[0] . '</label><br>';
513 $html .= '<span class="description">' .
514 __( 'Errors (from other plugins, themes, etc) happening during the Photo Engine process will be caught to avoid operations to fail.', 'wplr-sync' ) . '</span>';
515 echo $html;
516 }
517
518 function admin_check_same_file_callback( $args ) {
519 $html = '<input type="checkbox" id="wplr_check_same_file" name="wplr_check_same_file" value="1" ' .
520 checked( 1, get_option( 'wplr_check_same_file' ), false ) . '/>';
521 $html .= '<label for="wplr_check_same_file"> ' . $args[0] . '</label><br>';
522 $html .= '<span class="description">' .
523 __( 'Avoid processing if the new file is the same as the old one, to avoid thumbnails to be generated again, optimizers, etc.', 'wplr-sync' ) . '</span>';
524 echo $html;
525 }
526
527 function admin_debuglogs_callback( $args ) {
528 $clearlogs = isset ( $_GET[ 'clearlogs' ] ) ? $_GET[ 'clearlogs' ] : 0;
529 if ( $clearlogs ) {
530 if ( file_exists( plugin_dir_path( __FILE__ ) . '/wplr-sync.log' ) ) {
531 unlink( plugin_dir_path( __FILE__ ) . '/wplr-sync.log' );
532 }
533 }
534 $html = '<input type="checkbox" id="wplr_debuglogs" name="wplr_debuglogs" value="1" ' .
535 checked( 1, get_option( 'wplr_debuglogs' ), false ) . '/>';
536 $html .= '<label for="wplr_debuglogs"> ' . $args[0] . '</label><br>';
537 $html .= '<span class="description">' . __( 'Create an internal log file. For advanced users only.', 'wplr-sync' );
538 if ( file_exists( plugin_dir_path( __FILE__ ) . '/wplr-sync.log' ) ) {
539 $html .= sprintf( __( '<br />The <a target="_blank" href="%s/wplr-sync.log">log file</a> is available. You can also <a href="?page=wplr-main-menu&clearlogs=true">clear</a> it.', 'wplr-sync' ), plugin_dir_url( __FILE__ ) );
540 }
541 $html .= '</span>';
542 echo $html;
543 }
544
545 function display_hierarchy( $action ) {
546 $mode = isset( $_POST['action'] ) && $action == "keywords" ? 'keywords' : 'hierarchy';
547 $delete_collection = isset( $_POST['delete_collection'] ) ? $_POST['delete_collection'] : null;
548 $delete_keyword = isset( $_POST['delete_keyword'] ) ? $_POST['delete_keyword'] : null;
549 $hasDeleted = false;
550
551 if ( $delete_collection ) {
552 global $wplr;
553 $wplr->delete_collection( $delete_collection );
554 $hasDeleted = true;
555 }
556 if ( $delete_keyword ) {
557 global $wplr;
558 $wplr->delete_keyword( $delete_keyword );
559 $hasDeleted = true;
560 }
561
562 echo "<br /><br />";
563 $hierarchy = $mode == 'keywords' ? $this->wplr->get_keywords_hierarchy() : $this->wplr->get_hierarchy();
564
565 function display_internal_hierarchy( $hierarchy, $level = 0, $mode = '', $hasDeleted = false ) {
566 global $wpdb;
567 $tbl_r = $wpdb->prefix . 'lrsync_relations';
568 $page = empty( $_GET['page'] ) ? 'wplr-collections-tags-menu' : $_GET['page'];
569 echo '<div class="hierarchy-level-' . $level . '">';
570 if ( $level > 0 )
571 echo '<div style="margin-left: 10px; border-left: 1px dotted gray; padding-left: 10px;">';
572 foreach ( $hierarchy as $c ) {
573
574 if ( $mode == 'keywords' ) {
575 echo '<div class="wplr-elem wplr-keyword" wplr-elem-type="keyword" wplr-elem-id="' .
576 $c['id'] . '" wplr-elem-name="' . $c['name'] . '"><span class="dashicons dashicons-carrot"></span> <b>' .
577 $c['name'] . '</b> <small>(ID: ' . $c['id'] . ', Photos: ' . $c['count'] . ')</small>';
578 echo '<form action="" method="post" style="display: inline;">
579 <input type="hidden" name="page" value="' . $page . '">
580 <input type="hidden" name="action" value="' . $mode . '">';
581 wp_nonce_field( 'wplr_debug_action' );
582 echo '<small class="wplr_delete" style="display: ' . ( $hasDeleted ? 'inline' : 'none' ) . ';">
583 <button type="submit" name="delete_keyword" value="' . $c['id'] . '" class="btn-link">' .
584 __( 'Delete', 'wplr-sync' ) . '</button>
585 </small></form>';
586 echo '</div>';
587 }
588 else {
589 $type = $c['type'] == 'folder' ? '<span class="dashicons dashicons-category"></span>' : '<span class="dashicons dashicons-format-gallery"></span>';
590 echo '<div class="wplr-elem wplr-' . $c['type']
591 . '" wplr-elem-type="' . $c['type'] . '" wplr-elem-id="' . $c['id'] . '" wplr-elem-name="' . $c['name'] . '">'
592 . $type . ' ' . $c['name'] . ' <small>(ID: ' . $c['id'] . ')</small>';
593
594 echo '<form action="" method="post" style="display: inline;">
595 <input type="hidden" name="page" value="' . $page. '">';
596 wp_nonce_field( 'wplr_debug_action' );
597 echo '<small class="wplr_delete" style="display: none;">
598 <button type="submit" name="delete_collection" value="' . $c['id'] . '" class="btn-link">' .
599 __( 'Delete', 'wplr-sync' ) . '</button>
600 </small></form>';
601 echo '</div>';
602 }
603
604 if ( ( $mode == 'hierarchy' && $c['type'] == 'folder' ) ||
605 ( $mode == 'keywords' && count( $c['children'] ) > 0 ) ) {
606 display_internal_hierarchy( $c['children'], $level + 1, $mode, $hasDeleted );
607 }
608 else {
609 if ( $mode == 'hierarchy' ) {
610 $photos = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $tbl_r WHERE wp_col_id = %d ORDER BY sort LIMIT 25", $c['id'] ), OBJECT );
611 if ( count( $photos ) > 0 ) {
612 $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $tbl_r WHERE wp_col_id = %d", $c['id'] ) );
613 echo '<div style="margin-left: 0px; padding: 5px 0px; margin-top: 2px; margin-bottom: 5px; border-top: 1px solid lightgray;">';
614 echo '<div style="float: left; margin-right: 2px; width: 32px; height: 26px; background: #444444; text-align: center; padding-top: 6px; font-size: 10px; color: white;">' . $count . '</div>';
615 foreach ( $photos as $photo ) {
616 $src = wp_get_attachment_image_src( $photo->wp_id, 'thumbnail' );
617 if(!$src ){
618 echo '<div><span class="dashicons dashicons-no" style="float: left; margin-right: 2px; margin-bottom: 2px; font-size: 32px; line-height: 32px; color: red;"></span></div>';
619 continue;
620 }
621 echo '<div><a href="media.php?attachment_id=' . $photo->wp_id . '&action=edit"><img style="float: left; margin-right: 2px; margin-bottom: 2px;" width="32" height="32" src="' . $src[0] . '"></a></div>';
622 }
623 echo '<div style="clear: both;"></div></div>';
624 }
625 }
626 }
627 }
628 if ( $level > 0 )
629 echo '</div>';
630 echo '</div>';
631 }
632 display_internal_hierarchy( $hierarchy, 0, $mode, $hasDeleted );
633 ?>
634 <script>
635 function wplr_toggle_delete() {
636 jQuery('.wplr_delete').toggle();
637 }
638 jQuery('.wplr-elem').hover(function () {
639 var id = jQuery(this).attr('wplr-elem-id');
640 var ids = [];
641 var name = jQuery(this).attr('wplr-elem-name');
642 var type = jQuery(this).attr('wplr-elem-type');
643 var content = "";
644
645 if (type == 'folder') {
646 jQuery(this).next('div').find('.wplr-collection').each(function(i, v) {
647 ids.push(jQuery(v).attr('wplr-elem-id'));
648 });
649 id = ids.join(',');
650 }
651 if (type == 'keyword') {
652 ids.push(id);
653 jQuery(this).next('div').find('.wplr-keyword').each(function(i, v) {
654 ids.push(jQuery(v).attr('wplr-elem-id'));
655 });
656 id = ids.join(',');
657 }
658
659 if (type == 'collection' || type == 'folder') {
660 content += 'Here are my photos about <i>' + name + ':</i><br />';
661 content += '[gallery wplr-collection' + (ids.length > 1 ? 's' : '') + '="' + id + '"]';
662 }
663 else if (type == 'keyword') {
664 content += 'Here are my photos about <i>' + name + ':</i><br />';
665 content += '[gallery wplr-keyword' + (ids.length > 1 ? 's' : '') + '="' + id + '"]';
666
667 // content += '<i>Keywords are not supported yet, if you are interested in this feature please come on the forum of the Meow Gallery and ask for it :) Here: <a href="https://wordpress.org/support/plugin/meow-gallery">Meow Gallery Support Forum</a><i>';
668 }
669 jQuery('#shortcode-preview-code').html(content);
670 jQuery('#shortcode-preview-code').show();
671 console.log(jQuery(this));
672 });
673 </script>
674 <p>
675 <small><input type="checkbox" name="unlocked" <?= ( $hasDeleted ? 'checked' : '' ) ?> onclick="wplr_toggle_delete()">
676 <?php _e( 'Unlock <b>delete</b>', 'wplr-sync' ) ?>
677 </small>
678 </p>
679 <?php
680 }
681
682 function admin_css() {
683 ?>
684 <style>
685
686 .left {
687 width: 450px;
688 }
689
690 .right {
691 position: absolute;
692 left: 460px;
693 right: 10px;
694 }
695
696 .left-75 {
697 width: 60%;
698 }
699
700 .right-25 {
701 position: absolute;
702 width: 34%;
703 right: 20px;
704 }
705
706 .wplr-collection, .wplr-folder, .wplr-keyword {
707 cursor: pointer;
708 }
709
710 .shortcode-preview {
711 margin-bottom: 20px;
712 background: #0085ba;
713 padding: 10px;
714 font-size: 12px;
715 word-break: break-all;
716 color: white;
717 margin-top: -10px;
718 }
719
720 h3 {
721 margin-bottom: 5px;
722 }
723
724 p {
725 margin-top: 0px;
726 margin-bottom: 10px;
727 }
728
729 .wplrsync-form {
730 border: 1px solid lightgrey;
731 padding: 5px;
732 background: white;
733 border-radius: 5px;
734 width: 420px;
735 }
736
737 th {
738 text-align: left;
739 }
740
741 #wpfooter {
742 display: none;
743 }
744
745 </style>
746 <?php
747 }
748
749 function display_ads() {
750 return !get_option( 'meowapps_hide_ads', false );
751 }
752
753 function display_title( $title = "Meow Apps",
754 $author = "By <a style='text-decoration: none;' href='https://meowapps.com' target='_blank'>Jordy Meow</a>" ) {
755 if ( !empty( $this->prefix ) && $title !== "Meow Apps" )
756 $title = apply_filters( $this->prefix . '_plugin_title', $title );
757 if ( $this->display_ads() ) {
758 }
759 ?>
760 <h1 style="line-height: 16px;">
761 <img width="42" style="margin-right: 10px; float: left; position: relative; top: -5px;"
762 src="<?php echo self::$logo ?>"><?php echo $title; ?><br />
763 <span style="font-size: 12px"><?php echo $author; ?></span>
764 </h1>
765 <div style="clear: both;"></div>
766 <?php
767 }
768
769 function admin_debug() {
770 if ( !current_user_can( 'manage_options' ) ) {
771 wp_die( __( 'You do not have sufficient permissions to access this page.' ) );
772 }
773
774 // HEADER
775 $this->admin_css();
776 ?>
777 <div class="wrap" >
778
779 <?php echo $this->display_title( __( 'Debugging', 'wplr-sync' ) . " | Photo Engine" ); ?>
780 <p>Those tools should be used for debugging purposed. Be careful when you are using them, especially the colored buttons (usually blue).</p>
781 <?php
782
783 global $wpdb;
784 $table_name = $wpdb->prefix . "lrsync";
785
786 if ( !isset( $_POST['action'] ) && isset( $_GET['action'] ) ) {
787 $_POST['action'] = $_GET['action'];
788 }
789
790 if ( isset( $_POST['action'] ) ) {
791
792 // CSRF Protection: Verify nonce for POST requests
793 if ( !isset( $_POST['_wpnonce'] ) || !wp_verify_nonce( $_POST['_wpnonce'], 'wplr_debug_action' ) ) {
794 wp_die( __( 'Security check failed. Please try again.', 'wplr-sync' ) );
795 }
796
797 $action = $_POST['action'];
798 if ( $action == "reset" ) {
799 $this->wplr->reset_db();
800 echo "<div class='updated'><p>" .
801 __( 'The database was reset.', 'wplr-sync' ) .
802 "</p></div>";
803 }
804 else if ( $action == "duplicates" ) {
805 $duplicates = $this->wplr->list_duplicates();
806 }
807 else if ( $action == "scan" ) {
808 $unlinks = $this->wplr->list_unlinks();
809 }
810 else if ( $action == "list" ) {
811 $list = $this->wplr->list_sync_media();
812 }
813 else if ( $action == "link" ) {
814 if ( $this->wplr->link_media( $_POST['lr_id'], $_POST['wp_id'] ) ) {
815 echo "<div class='updated'><p>" .
816 __( 'Link successful.', 'wplr-sync' ) .
817 "</p></div>";
818 }
819 else {
820 echo "<div class='error'><p>" . $this->wplr->get_error()->message . "</p></div>";
821 }
822 }
823 else if ( $action == "linkinfo_upload" ) {
824 if ( isset( $_FILES['file'], $_FILES['file']['tmp_name'] ) && !empty( $_FILES['file']['tmp_name'] ) ) {
825 $tmp_path = $_FILES['file']['tmp_name'];
826 $linkinfo = $this->wplr->linkinfo_upload( $tmp_path, null );
827 }
828 else {
829 $linkinfo = $this->wplr->linkinfo_media( $_POST['wp_id'] );
830 }
831 if ( $linkinfo )
832 echo "<div class='updated'><p>" .
833 __( 'Link info retrieved.', 'wplr-sync' )
834 . "</p></div>";
835 else
836 echo "<div class='error'><p>" . $this->wplr->get_error()->message . "</p></div>";
837 }
838 else if ( $action == "sync" ) {
839 $tmp_path = $_FILES['file']['tmp_name'];
840 $lrinfo = new Meow_WPLR_Sync_LRInfo();
841 $lrinfo->lr_id = $_POST['lr_id'] == "" ? -1 : $_POST['lr_id'];
842 $lrinfo->lr_file = $_FILES['file']['name'];
843 $lrinfo->lr_title = isset( $_POST['title'] ) ? $_POST['title'] : "";
844 $lrinfo->lr_caption = $_POST['caption'];
845 $lrinfo->lr_desc = isset( $_POST['desc'] ) ? $_POST['desc'] : "";
846 $lrinfo->lr_alt_text = isset( $_POST['altText'] ) ? $_POST['altText'] : "";
847 $lrinfo->sync_title = isset( $_POST['syncTitle'] ) && $_POST['syncTitle'] == 'on';
848 $lrinfo->sync_caption = isset( $_POST['syncCaption'] ) && $_POST['syncCaption'] == 'on';
849 $lrinfo->sync_desc = isset( $_POST['syncDesc'] ) && $_POST['syncDesc'] == 'on';
850 $lrinfo->sync_alt_text = isset( $_POST['syncAltText'] ) && $_POST['syncAltText'] == 'on';
851 $lrinfo->type = $_FILES['file']['type'];
852 if ( $this->wplr->sync_media( $lrinfo, $tmp_path ) ) {
853 echo "<div class='updated'><p>";
854 echo sprintf( __( 'Lr ID %d was synchronized with the attachment.', 'wplr-sync' ), $_POST['lr_id'] );
855 echo "</p></div>";
856 }
857 else
858 echo "<div class='error'><p>" . $this->wplr->get_error()->message . "</p></div>";
859
860 }
861 else if ( $action == "unlink" ) {
862 if ( $this->wplr->unlink_media( $_POST['lr_id'], $_POST['wp_id'] ) ) {
863 echo "<div class='updated'><p>";
864 echo sprintf( __( 'Media ID %d was unlinked.', 'wplr-sync' ), $_POST['lr_id'] );
865 echo "</p></div>";
866 }
867 else
868 echo "<div class='error'><p>" . $this->wplr->get_error()->message . "</p></div>";
869 }
870 else if ( $action == "remove" ) {
871 if ( $this->wplr->delete_media( $_POST['lr_id'] ) ) {
872 echo "<div class='updated'><p>";
873 echo sprintf( __( 'Media ID %d was removed.', 'wplr-sync' ), $_POST['lr_id'] );
874 echo "</p></div>";
875 }
876 else
877 echo "<div class='error'><p>" . $this->wplr->get_error()->message . "</p></div>";
878 }
879 else if ( $action != 'api' && $action != 'keywords' && $action != 'hierarchy' ) {
880 echo "<div class='error'<p>Unknown action.</p></div>";
881 }
882
883 }
884
885 // CONTENT & FORMS
886 ?>
887 <div class="right">
888
889 <h3><?php _e( 'Display', 'wplr-sync' ); ?></h3>
890 <p class="buttons">
891 <form style="float: left; margin-right: 5px;" method="post" action="">
892 <input type="hidden" name="action" value="hierarchy">
893 <?php wp_nonce_field( 'wplr_debug_action' ); ?>
894 <input type="submit" name="submit" id="submit" class="button" value="Show Hierarchy">
895 </form>
896 <form style="float: left; margin-right: 5px;" method="post" action="">
897 <input type="hidden" name="action" value="keywords">
898 <?php wp_nonce_field( 'wplr_debug_action' ); ?>
899 <input type="submit" name="submit" id="submit" class="button" value="Show Keywords">
900 </form>
901 <form style="float: left; margin-right: 5px;" method="post" action="">
902 <input type="hidden" name="action" value="list">
903 <?php wp_nonce_field( 'wplr_debug_action' ); ?>
904 <input type="submit" name="submit" id="submit" class="button" value="List Linked">
905 </form>
906 <form style="float: left; margin-right: 5px;" method="post" action="">
907 <input type="hidden" name="action" value="scan">
908 <?php wp_nonce_field( 'wplr_debug_action' ); ?>
909 <input type="submit" name="submit" id="submit" class="button" value="List Unlinked">
910 </form>
911 <form style="float: left; margin-right: 5px;" method="post" action="">
912 <input type="hidden" name="action" value="duplicates">
913 <?php wp_nonce_field( 'wplr_debug_action' ); ?>
914 <input type="submit" name="submit" id="submit" class="button" value="List Duplicates">
915 </form>
916 <!-- <form style="float: left; margin-right: 5px;" method="post" action="">
917 <input type="hidden" name="action" value="api">
918 <input type="submit" name="submit" id="submit" class="button" value="API Ping">
919 </form> -->
920 </p>
921 <br />
922
923 <?php
924 if ( isset( $_POST['action'] ) && $action == "scan" ) {
925 echo '<br /><h3>' . __( 'Unlinked Media', 'wplr-sync' ) . '</h3>';
926 echo '<pre>';
927 print_r( $unlinks );
928 echo '</pre>';
929 }
930 else if ( isset( $_POST['action'] ) && $action == "list" ) {
931 echo '<br /><h3>' . __( 'Linked Media', 'wplr-sync' ) . '</h3>';
932 echo '<pre>';
933 print_r( $list );
934 echo '</pre>';
935 }
936 else if ( isset( $_POST['action'] ) && $action == "linkinfo_upload" ) {
937 echo '<pre>';
938 print_r( $linkinfo );
939 echo '</pre>';
940 }
941 else if ( isset( $_POST['action'] ) && $action == "duplicates" ) {
942 echo '<br /><h3>' . __( 'Duplicates', 'wplr-sync' ) . '</h3>';
943 echo '<pre>';
944 print_r( $duplicates );
945 echo '</pre>';
946 }
947 else if ( isset( $_POST['action'] ) && $action == "api" ) {
948 $boundary = wp_generate_password(24);
949 $response = wp_remote_post( admin_url( 'admin-ajax.php' ),
950 array(
951 'headers' => array( 'content-type' => 'multipart/form-data; boundary=' . $boundary ),
952 'body' => array( 'action' => 'lrsync_api', 'isAjax' => 1, 'data' => '' )
953 )
954 );
955 echo '<br /><h3>' . __( 'API Response', 'wplr-sync' ) . '</h3>';
956 echo '<pre>';
957 print_r( $response );
958 echo '</pre>';
959 }
960 else {
961 $this->display_hierarchy( isset( $_POST['action'] ) ? $action : null );
962 }
963 ?>
964 </div>
965
966 <div class="left">
967 <h3><?php _e( 'Link Info', 'wplr-sync' ) ?></h3>
968 <p><?php _e( 'Link info for either a WP ID <b>or</b> the Uploaded File.', 'wplr-sync' ) ?></p>
969 <form class="wplrsync-form" method="post" action="" enctype="multipart/form-data">
970 <input type="hidden" name="action" value="linkinfo_upload">
971 <?php wp_nonce_field( 'wplr_debug_action' ); ?>
972 <table>
973 <tr>
974 <th scope="row"><label for="wp_id">WP ID</label></th>
975 <td><input name="wp_id" type="text" id="wp_id" value="" class="regular-text code"></td>
976 </tr>
977 <tr>
978 <th scope="row"><label for="file">File</label></th>
979 <td><input name="file" type="file" id="file"></td>
980 </tr>
981 <tr>
982 <th scope="row"></th>
983 <td><input type="submit" name="submit" id="submit" class="button" value="Check"></td>
984 </tr>
985 </table>
986 </form>
987
988 <h3><?php _e( 'Link', 'wplr-sync' ) ?></h3>
989 <p><?php _e( 'Will link the WP Media ID to the Lr ID.', 'wplr-sync' ) ?></p>
990 <form class="wplrsync-form" method="post" action="" enctype="multipart/form-data">
991 <input type="hidden" name="action" value="link">
992 <?php wp_nonce_field( 'wplr_debug_action' ); ?>
993 <table>
994 <tr>
995 <th scope="row"><label for="lr_id">Lr ID</label></th>
996 <td><input name="lr_id" type="text" id="lr_id" value="" class="regular-text code"></td>
997 </tr>
998 <tr>
999 <th scope="row"><label for="wp_id">Media ID</label></th>
1000 <td><input name="wp_id" type="text" id="wp_id" value="" class="regular-text code"></td>
1001 </tr>
1002 <tr>
1003 <th scope="row"></th>
1004 <td><input type="submit" name="submit" id="submit" class="button button-primary" value="Link Media ID + Lr ID"></td>
1005 </tr>
1006 </table>
1007
1008 </form>
1009
1010 <h3><?php _e( 'Unlink', 'wplr-sync' ) ?></h3>
1011 <p><?php _e( 'Will unlink the media.', 'wplr-sync' ) ?></p>
1012 <form class="wplrsync-form" method="post" action="">
1013 <input type="hidden" name="action" value="unlink">
1014 <?php wp_nonce_field( 'wplr_debug_action' ); ?>
1015 <table>
1016 <tr>
1017 <th scope="row"><label for="lr_id">Lr ID</label></th>
1018 <td><input name="lr_id" type="text" id="lr_id" value="" class="regular-text code"></td>
1019 </tr>
1020 <tr>
1021 <th scope="row"><label for="wp_id">Media ID</label></th>
1022 <td><input name="wp_id" type="text" id="wp_id" value="" class="regular-text code"></td>
1023 </tr>
1024 <tr>
1025 <th scope="row"></th>
1026 <td><input type="submit" name="submit" id="submit" class="button button-primary" value="Unlink"></td>
1027 </tr>
1028 </table>
1029 </form>
1030
1031 <h3><?php _e( 'Sync', 'wplr-sync' ) ?></h3>
1032 <p><?php _e( 'Will create the entry if doesn\'t exist, will update it if exists.', 'wplr-sync' ) ?></p>
1033 <form class="wplrsync-form" method="post" action="" enctype="multipart/form-data">
1034 <input type="hidden" name="action" value="sync">
1035 <?php wp_nonce_field( 'wplr_debug_action' ); ?>
1036 <table>
1037 <tr>
1038 <th scope="row"><label for="lr_id">Lr ID</label></th>
1039 <td><input name="lr_id" type="text" id="lr_id" value="" class="regular-text code"></td>
1040 </tr>
1041 <tr>
1042 <th scope="row"><label for="title">Title</label></th>
1043 <td><input name="title" type="text" id="title" class="regular-text code"></td>
1044 </tr>
1045 <tr>
1046 <th scope="row"><label for="title">Caption</label></th>
1047 <td><input name="caption" type="text" id="caption" class="regular-text code"></td>
1048 </tr>
1049 <tr>
1050 <th scope="row"><label for="title">Desc</label></th>
1051 <td><input name="desc" type="text" id="desc" class="regular-text code"></td>
1052 </tr>
1053 <tr>
1054 <th scope="row"><label for="title">Alt</label></th>
1055 <td><input name="altText" type="text" id="altText" class="regular-text code"></td>
1056 </tr>
1057 <tr>
1058 <th scope="row"><label for="file">File</label></th>
1059 <td><input name="file" type="file" id="file"></td>
1060 </tr>
1061 <tr>
1062 <th scope="row"><label for="sync">Sync</label></th>
1063 <td>
1064 <label><input type="checkbox" id="syncTitle" name="syncTitle">Title</label>
1065 <label><input type="checkbox" id="syncCaption" name="syncCaption">Caption</label>
1066 <label><input type="checkbox" id="syncDesc" name="syncDesc">Desc</label>
1067 <label><input type="checkbox" id="syncAltText" name="syncAltText">Alt</label>
1068 </td>
1069 </tr>
1070 <tr>
1071 <th scope="row"></th>
1072 <td><input type="submit" name="submit" id="submit" class="button button-primary" value="Sync Media"></td>
1073 </tr>
1074 </table>
1075 </form>
1076
1077 <h3><?php _e( 'Remove', 'wplr-sync' ) ?></h3>
1078 <p><?php _e( 'Will remove the media.', 'wplr-sync' ) ?></p>
1079 <form class="wplrsync-form" method="post" action="">
1080 <input type="hidden" name="action" value="remove">
1081 <?php wp_nonce_field( 'wplr_debug_action' ); ?>
1082 <table>
1083 <tr>
1084 <th scope="row"><label for="lr_id">Lr ID</label></th>
1085 <td><input name="lr_id" type="text" id="lr_id" value="" class="regular-text code"></td>
1086 </tr>
1087 <tr>
1088 <th scope="row"></th>
1089 <td><input type="submit" name="submit" id="submit" class="button button-primary" value="Remove Media"></td>
1090 </tr>
1091 </table>
1092 </form>
1093
1094 <h3><?php _e( 'Actions', 'wplr-sync' ) ?></h3>
1095 <p class="buttons">
1096 <?php _e( 'Be careful. Those buttons are dangerous.', 'wplr-sync' ) ?>
1097 <form style="margin-right: 5px;" method="post" action="">
1098 <input type="hidden" name="action" value="reset">
1099 <?php wp_nonce_field( 'wplr_debug_action' ); ?>
1100 <input type="submit" name="submit" id="submit" class="button button-primary"
1101 value="<?php _e( 'Reset Photo Engine DB', 'wplr-sync' ) ?>">
1102 </form>
1103 </p>
1104
1105 </div>
1106 </div>
1107 </div>
1108 <?php
1109 }
1110
1111 // Add metabox for format warning on media edit page
1112 function add_format_warning_metabox() {
1113 global $post;
1114 if ( $post && $post->post_type === 'attachment' ) {
1115 // Check if this media has a format mismatch
1116 global $wplr;
1117 $mismatch_info = $wplr->check_format_mismatch( $post->ID );
1118 if ( $mismatch_info && $mismatch_info['has_mismatch'] ) {
1119 add_meta_box(
1120 'wplr_format_warning',
1121 __( 'Format Notice', 'wplr-sync' ),
1122 array( $this, 'format_warning_metabox_content' ),
1123 'attachment',
1124 'side',
1125 'default'
1126 );
1127 }
1128 }
1129 }
1130
1131 // Display format warning content
1132 function format_warning_metabox_content( $post ) {
1133 global $wplr;
1134 $mismatch_info = $wplr->check_format_mismatch( $post->ID );
1135 if ( $mismatch_info && $mismatch_info['has_mismatch'] ) {
1136 ?>
1137 <div style="padding: 10px; background: #fff3cd; border: 1px solid #ffeeba; border-radius: 4px;">
1138 <p style="margin: 0 0 10px 0;">
1139 <strong>⚠️ <?php _e( 'Format Mismatch Detected', 'wplr-sync' ); ?></strong>
1140 </p>
1141 <p style="margin: 0 0 10px 0; font-size: 13px;">
1142 <?php
1143 printf(
1144 __( 'This file has a .%s extension but contains %s data.', 'wplr-sync' ),
1145 esc_html( $mismatch_info['file_extension'] ),
1146 esc_html( strtoupper( $mismatch_info['actual_format'] ) )
1147 );
1148 ?>
1149 </p>
1150 <p style="margin: 0 0 8px 0; font-size: 12px; color: #666;">
1151 <?php _e( 'This happens when you change export formats in Lightroom after photos have already been published. Due to Lightroom SDK limitations, there is no perfect solution to preserve the original format during re-exports.', 'wplr-sync' ); ?>
1152 </p>
1153 <p style="margin: 0; font-size: 12px; color: #666;">
1154 <strong><?php _e( 'Good news:', 'wplr-sync' ); ?></strong> <?php _e( 'Browsers detect the actual format from the file content, not the extension, so your images will display correctly.', 'wplr-sync' ); ?>
1155 </p>
1156 <p style="margin: 8px 0 0 0; font-size: 12px; color: #666;">
1157 <?php
1158 printf(
1159 __( 'If you want to fix the file extensions, you can use the <a href="%s" target="_blank">Media File Renamer</a> plugin.', 'wplr-sync' ),
1160 'https://wordpress.org/support/plugin/media-file-renamer/'
1161 );
1162 ?>
1163 </p>
1164 </div>
1165 <?php
1166 }
1167 }
1168 }
1169
1170 ?>
1171