<?php namespace ElementorPro\Modules\Screenshots; use Elementor\Utils; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } class Screenshot { const POST_META_KEY = '_elementor_screenshot'; const IS_SCREENSHOT_POST_META_KEY = '_elementor_is_screenshot'; const FAILED_POST_META_KEY = '_elementor_screenshot_failed'; const SCREENSHOT_DIR = 'elementor/screenshots'; /** * @var int */ protected $post_id; /** * @var string */ protected $base64_image; /** * @var string */ protected $file_name; /** * @var array */ protected $upload_bits; /** * Screenshot constructor. * * @param int $post_id * @param string $base64_image */ public function __construct( $post_id, $base64_image = null ) { $this->post_id = $post_id; $this->base64_image = $base64_image; } /** * Creates the directory if needed + add index.html file for security reasons. * * @return $this */ public function create_dir() { $dir = wp_upload_dir()['basedir'] . '/' . self::SCREENSHOT_DIR; $html_file = $dir . '/index.html'; if ( file_exists( $html_file ) ) { return $this; }; if ( ! file_exists( $dir ) ) { wp_mkdir_p( $dir ); } touch( $html_file ); return $this; } /** * Uploads the base64 image it self. * * TODO: Use Upload Manager when ready. * * @return $this * @throws \Exception */ public function upload() { if ( ! $this->base64_image ) { throw new \Exception( 'Cannot upload an image with out base64_image.' ); } $file_content = substr( $this->base64_image, strlen( 'data:image/png;base64,' ) ); add_filter( 'wp_unique_filename', [ $this, 'get_file_name' ] ); add_filter( 'upload_dir', [ $this, 'extend_upload_dirs_array' ] ); $this->upload_bits = wp_upload_bits( $this->get_file_name(), null, base64_decode( $file_content ) ); remove_filter( 'wp_unique_filename', [ $this, 'get_file_name' ] ); remove_filter( 'upload_dir', [ $this, 'extend_upload_dirs_array' ] ); return $this; } /** * Removes the old attachment if there is an old screenshot image. * * @return $this */ public function remove_old_attachment() { $post_meta = get_post_meta( $this->post_id, self::POST_META_KEY, true ); if ( ! $post_meta ) { return $this; } wp_delete_attachment( $post_meta['id'] ); return $this; } /** * Removes the old post meta of the current post. * * @return $this */ public function remove_old_post_meta() { delete_post_meta( $this->post_id, self::POST_META_KEY ); return $this; } /** * Creates an attachment to the new screenshot and attach it to the original post * via post_meta. * * @return $this * @throws \Exception */ public function create_new_attachment() { $upload_bits = $this->get_upload_bits(); $info = wp_check_filetype( $upload_bits['file'] ); $post_mime_type = null; if ( $info ) { $post_mime_type = $info['type']; } $attachment_id = wp_insert_attachment( [ 'post_title' => $this->get_file_name(), 'guid' => $upload_bits['url'], 'post_mime_type' => $post_mime_type, 'meta_input' => [ self::IS_SCREENSHOT_POST_META_KEY => true, ], ], $upload_bits['file'], $this->post_id ); update_post_meta( $this->post_id, self::POST_META_KEY, [ 'id' => $attachment_id, 'url' => $upload_bits['url'], ] ); return $this; } /** * Mark the post that the screenshot capture was failed. * * @return $this */ public function mark_as_failed() { update_post_meta( $this->post_id, self::FAILED_POST_META_KEY, ( new \DateTime() )->format( 'Y-m-d H:i:s' ) ); return $this; } /** * Remove the failed_screenshot post meta. * * @return $this */ public function unmark_as_failed() { delete_post_meta( $this->post_id, self::FAILED_POST_META_KEY ); return $this; } /** * Get the file name, * if not exists will generate it. * * @return string */ public function get_file_name() { if ( ! $this->file_name ) { $now = ( new \DateTime() )->format( 'Y-m-d-H-i-s' ); $random_str = Utils::generate_random_string(); $this->file_name = "Elementor-post-screenshot_{$this->post_id}_{$now}_{$random_str}.png"; } return $this->file_name; } /** * Extend and change the upload_dirs original method * to update the current screenshot to custom directory. * * @param $upload_dirs * * @return array */ public function extend_upload_dirs_array( $upload_dirs ) { return array_merge( $upload_dirs, [ 'subdir' => $subdir = self::SCREENSHOT_DIR, 'path' => "{$upload_dirs['basedir']}/{$subdir}", 'url' => "{$upload_dirs['baseurl']}/{$subdir}", ] ); } /** * Get wp_upload_bits result. * * This method will be throw an exception if was called before actually upload a screenshot. * * @return array * @throws \Exception */ protected function get_upload_bits() { if ( ! $this->upload_bits ) { throw new \Exception( 'File was not uploaded yet.' ); } return $this->upload_bits; } /** * Get the url of the screenshot. * * @return string * @throws \Exception */ public function get_screenshot_url() { return $this->get_upload_bits()['url']; } }