| 1 |
<?php |
| 2 |
/** |
| 3 |
* |
| 4 |
* @author palant@UD |
| 5 |
*/ |
| 6 |
use wpCloud\StatelessMedia\Utility; |
| 7 |
|
| 8 |
if( !class_exists( 'SM_CLI_Scaffold' ) ) { |
| 9 |
require_once( dirname( __FILE__ ) . '/class-sm-cli-scaffold.php' ); |
| 10 |
} |
| 11 |
|
| 12 |
class SM_CLI_Sync extends SM_CLI_Scaffold { |
| 13 |
|
| 14 |
/** |
| 15 |
* |
| 16 |
*/ |
| 17 |
public $force = false; |
| 18 |
|
| 19 |
/** |
| 20 |
* |
| 21 |
*/ |
| 22 |
public $continue = false; |
| 23 |
|
| 24 |
/** |
| 25 |
* |
| 26 |
*/ |
| 27 |
public $fix = false; |
| 28 |
|
| 29 |
/** |
| 30 |
* |
| 31 |
*/ |
| 32 |
public $order = ""; |
| 33 |
|
| 34 |
/** |
| 35 |
* |
| 36 |
*/ |
| 37 |
public $start = false; |
| 38 |
|
| 39 |
/** |
| 40 |
* |
| 41 |
*/ |
| 42 |
public $end = false; |
| 43 |
|
| 44 |
/** |
| 45 |
* |
| 46 |
*/ |
| 47 |
public $limit = false; |
| 48 |
|
| 49 |
/** |
| 50 |
* |
| 51 |
*/ |
| 52 |
public $batch = false; |
| 53 |
|
| 54 |
/** |
| 55 |
* |
| 56 |
*/ |
| 57 |
public $batches = false; |
| 58 |
|
| 59 |
/** |
| 60 |
* |
| 61 |
*/ |
| 62 |
public $total = 0; |
| 63 |
|
| 64 |
/** |
| 65 |
* |
| 66 |
*/ |
| 67 |
public $log; |
| 68 |
|
| 69 |
/** |
| 70 |
* Sync images |
| 71 |
* |
| 72 |
*/ |
| 73 |
public function images() { |
| 74 |
global $wpdb; |
| 75 |
|
| 76 |
/** Prepare arguments */ |
| 77 |
$this->_prepare(); |
| 78 |
|
| 79 |
$timer = time(); |
| 80 |
|
| 81 |
$where = ''; |
| 82 |
|
| 83 |
if ( $this->fix ) { |
| 84 |
$failed_attachments = Utility::sync_get_fails( 'cli_images' ); |
| 85 |
|
| 86 |
if ( !empty($failed_attachments) ) { |
| 87 |
$where .= " AND ID IN (" . implode(',', $failed_attachments) . ") "; |
| 88 |
} else { |
| 89 |
WP_CLI::success( "Stateless Media is synced. No failed attachments." ); |
| 90 |
exit; |
| 91 |
} |
| 92 |
} |
| 93 |
|
| 94 |
if ( $this->continue ) { |
| 95 |
$current_progress = Utility::sync_retrieve_current_progress( 'cli_images' ); |
| 96 |
|
| 97 |
if ( isset($current_progress[1]) && !empty( $current_progress[1] )) { |
| 98 |
$where .= " AND ID " . ( $this->order === 'DESC' ? ' < ' : ' > ' ) . $current_progress[1]; |
| 99 |
} |
| 100 |
} |
| 101 |
|
| 102 |
if ( ! $this->force ) { |
| 103 |
$where .= " AND meta_id IS NULL " ; |
| 104 |
} |
| 105 |
|
| 106 |
/** Get Total Amount of Attachments */ |
| 107 |
$this->total = $wpdb->get_var(" |
| 108 |
SELECT |
| 109 |
COUNT(ID) |
| 110 |
FROM {$wpdb->posts} |
| 111 |
LEFT JOIN {$wpdb->postmeta} ON ID = post_id AND meta_key = 'sm_cloud' |
| 112 |
WHERE post_type = 'attachment' AND post_mime_type LIKE 'image/%' " . $where . " |
| 113 |
"); |
| 114 |
|
| 115 |
|
| 116 |
if( $this->batch ) { |
| 117 |
$this->output( "Running Batch {$this->batch} from {$this->batches}" ); |
| 118 |
$range = round( $this->total / $this->batches ); |
| 119 |
$this->start = ( $this->batch * $range ) - $range; |
| 120 |
$this->end = $this->batches == $this->batch ? $this->total : $this->batch * $range; |
| 121 |
$this->output( "Starting from {$this->start} row. " ); |
| 122 |
$this->output( "And proceeding up to {$this->end} row." ); |
| 123 |
} else { |
| 124 |
$this->output( "Running in default way. Starting from {$this->start} row and proceeding up to end." ); |
| 125 |
$this->end = $this->end ? $this->end : $this->total; |
| 126 |
} |
| 127 |
$media_to_proceed = $this->end - $this->start; |
| 128 |
|
| 129 |
//** Counters */ |
| 130 |
$synced_images = 0; |
| 131 |
|
| 132 |
WP_CLI::line( 'Starting extract attachments.' ); |
| 133 |
|
| 134 |
for( $this->start; $this->start < $this->end; $this->start += $this->limit ) { |
| 135 |
|
| 136 |
$limit = ( $this->end - $this->start ) < $this->limit ? ( $this->end - $this->start ) : $this->limit; |
| 137 |
|
| 138 |
$this->output( 'Synced: ' . $synced_images . '. Extracting from ' . ( $this->start + 1 ) . ' to ' . ( $this->start + $limit ) ); |
| 139 |
|
| 140 |
/** |
| 141 |
* Get Attachments data. |
| 142 |
* |
| 143 |
*/ |
| 144 |
$attachments = $wpdb->get_results( $wpdb->prepare( " |
| 145 |
SELECT |
| 146 |
ID |
| 147 |
FROM {$wpdb->posts} |
| 148 |
LEFT JOIN {$wpdb->postmeta} ON ID = post_id AND meta_key = 'sm_cloud' |
| 149 |
WHERE post_type = 'attachment' AND post_mime_type LIKE 'image/%' " . $where . " ORDER BY ID " . $this->order . " |
| 150 |
LIMIT %d, %d; |
| 151 |
", $this->start, $limit ), ARRAY_A ); |
| 152 |
|
| 153 |
if ( ! empty( $attachments ) ) { |
| 154 |
foreach ($attachments as $i => $a) { |
| 155 |
|
| 156 |
try { |
| 157 |
$response = $this->stateless_process_image($a['ID']); |
| 158 |
if (!empty($response)) { |
| 159 |
$synced_images++; |
| 160 |
$this->output($response); |
| 161 |
} |
| 162 |
} catch( \Exception $e ) { |
| 163 |
$this->output($e->getMessage()); |
| 164 |
} |
| 165 |
|
| 166 |
/** Flush data */ |
| 167 |
$wpdb->flush(); |
| 168 |
@ob_flush(); |
| 169 |
@flush(); |
| 170 |
|
| 171 |
} |
| 172 |
|
| 173 |
unset($attachments); |
| 174 |
} |
| 175 |
|
| 176 |
} |
| 177 |
WP_CLI::success( "Stateless Media is synced" ); |
| 178 |
|
| 179 |
WP_CLI::line( 'Media which have been checked: ' . number_format_i18n( $media_to_proceed ) ); |
| 180 |
WP_CLI::line( 'Synced stateless for ' . number_format_i18n( $synced_images ) . ' attachments' ); |
| 181 |
WP_CLI::line( 'Spent Time: ' . ( time() - $timer ) . ' sec' ); |
| 182 |
|
| 183 |
} |
| 184 |
|
| 185 |
|
| 186 |
/** |
| 187 |
* Sync files |
| 188 |
* |
| 189 |
*/ |
| 190 |
public function files() { |
| 191 |
global $wpdb; |
| 192 |
|
| 193 |
/** Prepare arguments */ |
| 194 |
$this->_prepare(); |
| 195 |
|
| 196 |
$timer = time(); |
| 197 |
|
| 198 |
$where = ''; |
| 199 |
|
| 200 |
if ( $this->fix ) { |
| 201 |
$failed_attachments = Utility::sync_get_fails( 'cli_images' ); |
| 202 |
|
| 203 |
if ( !empty( $failed_attachments ) ) { |
| 204 |
$where .= " AND ID IN (" . implode(',', $failed_attachments) . ") "; |
| 205 |
} else { |
| 206 |
WP_CLI::success( "Stateless Media is synced. No failed attachments." ); |
| 207 |
exit; |
| 208 |
} |
| 209 |
} |
| 210 |
|
| 211 |
if ( $this->continue ) { |
| 212 |
$current_progress = Utility::sync_retrieve_current_progress( 'cli_other' ); |
| 213 |
|
| 214 |
if ( isset( $current_progress[1] ) && !empty( $current_progress[1] )) { |
| 215 |
$where .= " AND ID " . ( $this->order === 'DESC' ? ' < ' : ' > ' ) . $current_progress[1]; |
| 216 |
} |
| 217 |
} |
| 218 |
|
| 219 |
if ( ! $this->force ) { |
| 220 |
$where .= " AND meta_id IS NULL " ; |
| 221 |
} |
| 222 |
|
| 223 |
/** Get Total Amount of Attachments */ |
| 224 |
$this->total = $wpdb->get_var(" |
| 225 |
SELECT |
| 226 |
COUNT(ID) |
| 227 |
FROM {$wpdb->posts} |
| 228 |
LEFT JOIN {$wpdb->postmeta} ON ID = post_id AND meta_key = 'sm_cloud' |
| 229 |
WHERE post_type = 'attachment' AND post_mime_type NOT LIKE 'image/%' " . $where . " |
| 230 |
"); |
| 231 |
|
| 232 |
|
| 233 |
if( $this->batch ) { |
| 234 |
$this->output( "Running Batch {$this->batch} from {$this->batches}" ); |
| 235 |
$range = round( $this->total / $this->batches ); |
| 236 |
$this->start = ( $this->batch * $range ) - $range; |
| 237 |
$this->end = $this->batches == $this->batch ? $this->total : $this->batch * $range; |
| 238 |
$this->output( "Starting from {$this->start} row. " ); |
| 239 |
$this->output( "And proceeding up to {$this->end} row." ); |
| 240 |
} else { |
| 241 |
$this->output( "Running in default way. Starting from {$this->start} row and proceeding up to end." ); |
| 242 |
$this->end = $this->end ? $this->end : $this->total; |
| 243 |
} |
| 244 |
$media_to_proceed = $this->end - $this->start; |
| 245 |
|
| 246 |
//** Counters */ |
| 247 |
$synced_files = 0; |
| 248 |
|
| 249 |
WP_CLI::line( 'Starting extract attachments.' ); |
| 250 |
|
| 251 |
for( $this->start; $this->start < $this->end; $this->start += $this->limit ) { |
| 252 |
|
| 253 |
$limit = ( $this->end - $this->start ) < $this->limit ? ( $this->end - $this->start ) : $this->limit; |
| 254 |
|
| 255 |
$this->output( 'Synced: ' . $synced_files . '. Extracting from ' . ( $this->start + 1 ) . ' to ' . ( $this->start + $limit ) ); |
| 256 |
|
| 257 |
/** |
| 258 |
* Get Attachments data. |
| 259 |
* |
| 260 |
*/ |
| 261 |
$attachments = $wpdb->get_results( $wpdb->prepare( " |
| 262 |
SELECT |
| 263 |
ID |
| 264 |
FROM {$wpdb->posts} |
| 265 |
LEFT JOIN {$wpdb->postmeta} ON ID = post_id AND meta_key = 'sm_cloud' |
| 266 |
WHERE post_type = 'attachment' AND post_mime_type NOT LIKE 'image/%' " . $where . " ORDER BY ID " . $this->order . " |
| 267 |
LIMIT %d, %d; |
| 268 |
", $this->start, $limit ), ARRAY_A ); |
| 269 |
|
| 270 |
if ( ! empty( $attachments ) ) { |
| 271 |
foreach ($attachments as $i => $a) { |
| 272 |
|
| 273 |
try { |
| 274 |
$response = $this->stateless_process_file($a['ID']); |
| 275 |
if (!empty($response)) { |
| 276 |
$synced_files++; |
| 277 |
$this->output($response); |
| 278 |
} |
| 279 |
} catch( \Exception $e ) { |
| 280 |
$this->output($e->getMessage()); |
| 281 |
} |
| 282 |
|
| 283 |
/** Flush data */ |
| 284 |
$wpdb->flush(); |
| 285 |
@ob_flush(); |
| 286 |
@flush(); |
| 287 |
|
| 288 |
} |
| 289 |
|
| 290 |
unset($attachments); |
| 291 |
} |
| 292 |
|
| 293 |
} |
| 294 |
WP_CLI::success( "Stateless Media is synced" ); |
| 295 |
|
| 296 |
WP_CLI::line( 'Media which have been checked: ' . number_format_i18n( $media_to_proceed ) ); |
| 297 |
WP_CLI::line( 'Synced stateless for ' . number_format_i18n( $synced_files ) . ' attachments' ); |
| 298 |
WP_CLI::line( 'Spent Time: ' . ( time() - $timer ) . ' sec' ); |
| 299 |
|
| 300 |
} |
| 301 |
|
| 302 |
|
| 303 |
/** |
| 304 |
* @param $id |
| 305 |
* @return string |
| 306 |
* @throws Exception |
| 307 |
* Regenerate image sizes. |
| 308 |
*/ |
| 309 |
public function stateless_process_image( $id ) { |
| 310 |
|
| 311 |
if(ud_get_stateless_media()->is_connected_to_gs() !== true){ |
| 312 |
//WP_CLI::line( 'Starting extract attachments.' ); |
| 313 |
throw new \Exception( __( 'Not connected to GCS', ud_get_stateless_media()->domain) ); |
| 314 |
} |
| 315 |
|
| 316 |
@error_reporting( 0 ); |
| 317 |
timer_start(); |
| 318 |
|
| 319 |
$image = get_post( $id ); |
| 320 |
|
| 321 |
if ( ! $image || 'attachment' != $image->post_type || 'image/' != substr( $image->post_mime_type, 0, 6 ) ) |
| 322 |
throw new \Exception( sprintf( __( 'Failed resize: %s is an invalid image ID.', ud_get_stateless_media()->domain ), esc_html( $id ) ) ); |
| 323 |
|
| 324 |
$fullsizepath = get_attached_file( $image->ID ); |
| 325 |
$upload_dir = wp_upload_dir(); |
| 326 |
|
| 327 |
// If no file found |
| 328 |
if ( false === $fullsizepath || ! file_exists( $fullsizepath ) ) { |
| 329 |
|
| 330 |
// Try get it and save |
| 331 |
$result_code = ud_get_stateless_media()->get_client()->get_media( apply_filters( 'wp_stateless_file_name', str_replace( trailingslashit( $upload_dir[ 'basedir' ] ), '', $fullsizepath ), true, $id, "", $this->use_wildcards), true, $fullsizepath ); |
| 332 |
|
| 333 |
if ( $result_code !== 200 ) { |
| 334 |
if(!Utility::sync_get_attachment_if_exist($image->ID, $fullsizepath)){ // Save file to local from proxy. |
| 335 |
Utility::sync_store_failed_attachment( $image->ID, 'cli_images' ); |
| 336 |
throw new \Exception(sprintf(__('Both local and remote files are missing. Unable to resize. (%s)', ud_get_stateless_media()->domain), $image->guid)); |
| 337 |
} |
| 338 |
} |
| 339 |
} |
| 340 |
|
| 341 |
@set_time_limit( -1 ); |
| 342 |
|
| 343 |
// |
| 344 |
do_action( 'sm:pre::synced::image', $image->ID); |
| 345 |
$metadata = wp_generate_attachment_metadata( $image->ID, $fullsizepath ); |
| 346 |
|
| 347 |
if(get_post_mime_type($image->ID) !== 'image/svg+xml'){ |
| 348 |
if ( is_wp_error( $metadata ) ) { |
| 349 |
Utility::sync_store_failed_attachment( $image->ID, 'cli_images' ); |
| 350 |
throw new \Exception($metadata->get_error_message()); |
| 351 |
} |
| 352 |
|
| 353 |
if ( empty( $metadata ) ) { |
| 354 |
Utility::sync_store_failed_attachment( $image->ID, 'cli_images' ); |
| 355 |
throw new \Exception(sprintf( __('No metadata generated for %1$s (ID %2$s).', ud_get_stateless_media()->domain), esc_html( get_the_title( $image->ID ) ), $image->ID)); |
| 356 |
} |
| 357 |
} |
| 358 |
|
| 359 |
// If this fails, then it just means that nothing was changed (old value == new value) |
| 360 |
wp_update_attachment_metadata( $image->ID, $metadata ); |
| 361 |
Utility::sync_store_current_progress( 'cli_images', $image->ID, true ); |
| 362 |
Utility::sync_maybe_fix_failed_attachment( 'cli_images', $image->ID ); |
| 363 |
do_action( 'sm:synced::image', $image->ID, $metadata); |
| 364 |
|
| 365 |
return sprintf( __( '%1$s (ID %2$s) was successfully synced in %3$s seconds.', ud_get_stateless_media()->domain ), esc_html( get_the_title( $image->ID ) ), $image->ID, timer_stop() ); |
| 366 |
} |
| 367 |
|
| 368 |
/** |
| 369 |
* @param $id |
| 370 |
* @return string |
| 371 |
* @throws \Exception |
| 372 |
*/ |
| 373 |
public function stateless_process_file( $id ) { |
| 374 |
|
| 375 |
@error_reporting( 0 ); |
| 376 |
timer_start(); |
| 377 |
|
| 378 |
if(ud_get_stateless_media()->is_connected_to_gs() !== true){ |
| 379 |
throw new \Exception( __( 'Not connected to GCS', ud_get_stateless_media()->domain) ); |
| 380 |
} |
| 381 |
|
| 382 |
$file = get_post( $id ); |
| 383 |
|
| 384 |
if ( ! $file || 'attachment' != $file->post_type ) |
| 385 |
throw new \Exception( sprintf( __( 'Attachment not found: %s is an invalid file ID.', ud_get_stateless_media()->domain ), esc_html( $id ) ) ); |
| 386 |
|
| 387 |
$fullsizepath = get_attached_file( $file->ID ); |
| 388 |
$local_file_exists = file_exists( $fullsizepath ); |
| 389 |
|
| 390 |
$upload_dir = wp_upload_dir(); |
| 391 |
|
| 392 |
if ( false === $fullsizepath || ! $local_file_exists ) { |
| 393 |
|
| 394 |
// Try get it and save |
| 395 |
$result_code = ud_get_stateless_media()->get_client()->get_media( str_replace( trailingslashit( $upload_dir[ 'basedir' ] ), '', $fullsizepath ), true, $fullsizepath ); |
| 396 |
|
| 397 |
if ( $result_code !== 200 ) { |
| 398 |
if(!Utility::sync_get_attachment_if_exist($file->ID, $fullsizepath)){ // Save file to local from proxy. |
| 399 |
Utility::sync_store_failed_attachment( $file->ID, 'cli_other' ); |
| 400 |
throw new \Exception(sprintf(__('File not found (%s)', ud_get_stateless_media()->domain), $file->guid)); |
| 401 |
} |
| 402 |
else{ |
| 403 |
$local_file_exists = true; |
| 404 |
} |
| 405 |
} |
| 406 |
else{ |
| 407 |
$local_file_exists = true; |
| 408 |
} |
| 409 |
} |
| 410 |
|
| 411 |
if($local_file_exists){ |
| 412 |
|
| 413 |
if ( !ud_get_stateless_media()->get_client()->media_exists( str_replace( trailingslashit( $upload_dir[ 'basedir' ] ), '', $fullsizepath ) ) ) { |
| 414 |
|
| 415 |
@set_time_limit( -1 ); |
| 416 |
|
| 417 |
$metadata = wp_generate_attachment_metadata( $file->ID, $fullsizepath ); |
| 418 |
|
| 419 |
if ( is_wp_error( $metadata ) ) { |
| 420 |
Utility::sync_store_failed_attachment( $file->ID, 'cli_other' ); |
| 421 |
throw new \Exception($metadata->get_error_message()); |
| 422 |
} |
| 423 |
|
| 424 |
wp_update_attachment_metadata( $file->ID, $metadata ); |
| 425 |
do_action( 'sm:synced::nonImage', $file->ID, $metadata); |
| 426 |
|
| 427 |
} |
| 428 |
else{ |
| 429 |
// Stateless mode: we don't need the local version. |
| 430 |
if(ud_get_stateless_media()->get( 'sm.mode' ) === 'ephemeral'){ |
| 431 |
unlink($fullsizepath); |
| 432 |
} |
| 433 |
} |
| 434 |
|
| 435 |
} |
| 436 |
|
| 437 |
Utility::sync_store_current_progress( 'cli_other', $file->ID, true ); |
| 438 |
Utility::sync_maybe_fix_failed_attachment( 'cli_other', $file->ID ); |
| 439 |
|
| 440 |
return sprintf( __( '%1$s (ID %2$s) was successfully synchronised in %3$s seconds.', ud_get_stateless_media()->domain ), esc_html( get_the_title( $file->ID ) ), $file->ID, timer_stop() ); |
| 441 |
} |
| 442 |
|
| 443 |
/** |
| 444 |
* |
| 445 |
*/ |
| 446 |
private function _prepare() { |
| 447 |
$args = $this->assoc_args; |
| 448 |
if( isset( $args[ 'b' ] ) ) { |
| 449 |
WP_CLI::error( 'Invalid parameter --b. Command must not be run directly with --b parameter.' ); |
| 450 |
} |
| 451 |
$this->start = isset( $args[ 'start' ] ) && is_numeric( $args[ 'start' ] ) ? $args[ 'start' ] : 0; |
| 452 |
$this->limit = isset( $args[ 'limit' ] ) && is_numeric( $args[ 'limit' ] ) ? $args[ 'limit' ] : 100; |
| 453 |
$this->force = isset( $args[ 'force' ] ) ? true : false; |
| 454 |
$this->continue = isset( $args[ 'continue' ] ) ? true : false; |
| 455 |
$this->fix = isset( $args[ 'fix' ] ) ? true : false; |
| 456 |
$this->use_wildcards = isset( $args[ 'use_wildcards' ] ) ? true : false; |
| 457 |
$_REQUEST['use_wildcards'] = $this->use_wildcards; |
| 458 |
$this->order = isset( $args[ 'order' ]) && $args[ 'order' ] === 'ASC' ? 'ASC' : 'DESC'; |
| 459 |
if( isset( $args[ 'batch' ] ) ) { |
| 460 |
if( !is_numeric( $args[ 'batch' ] ) || $args[ 'batch' ] <= 0 ) { |
| 461 |
WP_CLI::error( 'Invalid parameter --batch' ); |
| 462 |
} |
| 463 |
$this->batch = $args[ 'batch' ]; |
| 464 |
$this->batches = isset( $args[ 'batches' ] ) ? $args[ 'batches' ] : 10; |
| 465 |
if( !is_numeric( $this->batches ) || $this->batches <= 0 ) { |
| 466 |
WP_CLI::error( 'Invalid parameter --batches' ); |
| 467 |
} elseif ( $this->batch > $this->batches ) { |
| 468 |
WP_CLI::error( '--batch parameter must is invalid. It must not equal or less then --batches' ); |
| 469 |
} |
| 470 |
} else { |
| 471 |
$this->end = isset( $args[ 'end' ] ) && is_numeric( $args[ 'end' ] ) ? $args[ 'end' ] : false; |
| 472 |
} |
| 473 |
} |
| 474 |
|
| 475 |
|
| 476 |
} |