From 8bf247f747aa05b1e0814aa073220123791fb7d3 Mon Sep 17 00:00:00 2001 From: spacedmonkey Date: Wed, 22 Jul 2020 11:51:20 +0100 Subject: [PATCH 01/22] Add block directory support. --- assets/src/block-editor/blocks/image/block.json | 1 + php/class-block-type.php | 8 +++++--- readme.txt | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/assets/src/block-editor/blocks/image/block.json b/assets/src/block-editor/blocks/image/block.json index 0ca7c278..871d1ff2 100644 --- a/assets/src/block-editor/blocks/image/block.json +++ b/assets/src/block-editor/blocks/image/block.json @@ -1,5 +1,6 @@ { "name": "unsplash/image", + "title": "Unsplash", "category": "common", "supports": { "lightBlockWrapper": true, diff --git a/php/class-block-type.php b/php/class-block-type.php index 936bce8b..f11ce776 100644 --- a/php/class-block-type.php +++ b/php/class-block-type.php @@ -40,6 +40,10 @@ public function init() { * @action init */ public function register_blocks() { + if ( ! function_exists( 'register_block_type' ) ) { + return; + } + $blocks_dir = $this->plugin->dir_path . '/assets/js/blocks/'; $block_folders = [ 'image', @@ -64,9 +68,7 @@ public function register_blocks() { $metadata['render_callback'] = [ $this, $callback ]; } - if ( function_exists( '\register_block_type' ) ) { - \register_block_type( $metadata['name'], $metadata ); - } + register_block_type( $metadata['name'], $metadata ); } } diff --git a/readme.txt b/readme.txt index 2ece3254..475d03a2 100644 --- a/readme.txt +++ b/readme.txt @@ -1,6 +1,6 @@ === Unsplash === Contributors: unsplash, xwp -Tags: unsplash, images, media, free, photographs, photos +Tags: unsplash, images, media, free, photographs, photos, block Requires at least: 4.9 Stable tag: 1.0.0 Tested up to: 5.4.2 From 4a3698e0c96d984065b519c8e92055f99db11452 Mon Sep 17 00:00:00 2001 From: spacedmonkey Date: Thu, 23 Jul 2020 18:32:55 +0100 Subject: [PATCH 02/22] Convert comment in php. --- instance.php | 2 + php/class-block-type.php | 7 +- php/class-hotlink.php | 40 ++++------ php/class-plugin-base.php | 140 ---------------------------------- php/class-plugin.php | 28 +++---- php/class-rest-controller.php | 4 +- php/class-settings.php | 10 +-- 7 files changed, 31 insertions(+), 200 deletions(-) diff --git a/instance.php b/instance.php index 382c9ba2..1e6d64d6 100644 --- a/instance.php +++ b/instance.php @@ -19,6 +19,8 @@ $unsplash_plugin = new Plugin(); +add_action( 'plugins_loaded', [ $unsplash_plugin, 'init' ] ); + /** * Unsplash Plugin Instance * diff --git a/php/class-block-type.php b/php/class-block-type.php index 936bce8b..34788860 100644 --- a/php/class-block-type.php +++ b/php/class-block-type.php @@ -31,13 +31,12 @@ public function __construct( $plugin ) { * Initiate the class. */ public function init() { - $this->plugin->add_doc_hooks( $this ); + add_action( 'init', [ $this, 'register_blocks' ] ); + add_action( 'enqueue_block_editor_assets', [ $this, 'register_block_editor_assets' ] ); } /** * Register our custom blocks. - * - * @action init */ public function register_blocks() { $blocks_dir = $this->plugin->dir_path . '/assets/js/blocks/'; @@ -72,8 +71,6 @@ public function register_blocks() { /** * Load Gutenberg assets. - * - * @action enqueue_block_editor_assets */ public function register_block_editor_assets() { // Register block editor assets. diff --git a/php/class-hotlink.php b/php/class-hotlink.php index 3896d5fb..f3d1c44b 100644 --- a/php/class-hotlink.php +++ b/php/class-hotlink.php @@ -35,14 +35,24 @@ public function __construct( Plugin $plugin ) { * Initiate the class. */ public function init() { - $this->plugin->add_doc_hooks( $this ); + add_action( 'attachment_submitbox_misc_actions', [ $this, 'attachment_submitbox_misc_actions' ], 11 ); + add_filter( 'wp_get_attachment_url', [ $this, 'wp_get_attachment_url' ], 10, 2 ); + add_filter( 'wp_prepare_attachment_for_js', [ $this, 'wp_prepare_attachment_for_js' ], 99, 2 ); + add_filter( 'rest_prepare_attachment', [ $this, 'rest_prepare_attachment' ], 99, 3 ); + add_filter( 'content_save_pre', [ $this, 'replace_hotlinked_images_in_content' ], 99, 1 ); + add_filter( 'the_content', [ $this, 'hotlink_images_in_content' ], 99, 1 ); + add_filter( 'wp_get_attachment_image_src', [ $this, 'wp_get_attachment_image_src' ], 10, 5 ); + add_filter( 'image_downsize', [ $this, 'image_downsize' ], 10, 3 ); + add_filter( 'wp_calculate_image_srcset', [ $this, 'wp_calculate_image_srcset' ], 99, 5 ); + add_filter( 'get_image_tag', [ $this, 'get_image_tag' ], 10, 6 ); + add_filter( 'wp_get_attachment_caption', [ $this, 'wp_get_attachment_caption' ], 10, 2 ); + add_filter( 'render_block', [ $this, 'render_block' ], 10, 2 ); + add_filter( 'wp_edited_image_metadata', [ $this, 'add_edited_attachment_metadata' ], 10, 3 ); } /** * Filter wp_get_attachment_url * - * @filter wp_get_attachment_url, 10, 2 - * * @param string $url Original URL. * @param int $id Attachment ID. * @@ -77,8 +87,6 @@ public function get_attachment_url( $attachment_id ) { * @param array $response Data for admin ajax. * @param WP_Post $attachment Attachment object. * - * @filter wp_prepare_attachment_for_js, 99, 2 - * * @return mixed */ public function wp_prepare_attachment_for_js( array $response, $attachment ) { @@ -112,8 +120,6 @@ public function wp_prepare_attachment_for_js( array $response, $attachment ) { * @param WP_Post $attachment The original attachment post. * @param WP_REST_Request $wp_request Request used to generate the response. * - * @filter rest_prepare_attachment, 99, 3 - * * @return mixed */ public function rest_prepare_attachment( $wp_response, $attachment, $wp_request ) { @@ -176,8 +182,6 @@ public function rest_prepare_attachment( $wp_response, $attachment, $wp_request /** * Add unsplash original link to attachment edit page. - * - * @action attachment_submitbox_misc_actions, 11 */ public function attachment_submitbox_misc_actions() { $post = get_post(); @@ -240,8 +244,6 @@ public function change_full_url( array $sizes, $field, $url ) { /** * Filter image downsize. * - * @filter image_downsize, 10, 3 - * * @param array $should_resize Array. * @param int $id Attachment ID. * @param array|string $size Size. @@ -289,8 +291,6 @@ public function image_downsize( $should_resize, $id, $size ) { /** * Work around for image preview in Attachment screen. * - * @filter wp_get_attachment_image_src, 10, 5 - * * @param array|false $image rray of image data, or boolean false if no image is available. * @param int $attachment_id Image attachment ID. * @param string|int[] $size Requested size of image. Image size name, or array of width @@ -357,8 +357,6 @@ public function get_attachments_from_content( $content ) { /** * Replace hotlinked image URLs in content with ones from WordPress. * - * @filter content_save_pre, 99, 1 - * * @param string $content Content. * @return string Converted content with local images. */ @@ -411,8 +409,6 @@ public function replace_hotlinked_images_in_content( $content ) { * * @param string $content The raw post content to be filtered. * - * @filter the_content, 99, 1 - * * @return string Converted content with hotlinked images. */ public function hotlink_images_in_content( $content ) { @@ -432,8 +428,6 @@ public function hotlink_images_in_content( $content ) { /** * Filter source sets to give hotlink images. * - * @filter wp_calculate_image_srcset, 99, 5 - * * @param array $sources { * One or more arrays of source data to include in the 'srcset'. * @@ -648,8 +642,6 @@ public function prime_post_caches( array $attachment_ids ) { * @param string|array $size Size of image. Image size or array of width and height values (in that order). * Default 'medium'. * - * @filter get_image_tag, 10, 6 - * * @return string Image tag. */ public function get_image_tag( $html, $id, $alt, $title, $align, $size ) { @@ -668,8 +660,6 @@ public function get_image_tag( $html, $id, $alt, $title, $align, $size ) { /** * Remove html for captions, as some themes esc_html captions before displaying. * - * @filter wp_get_attachment_caption, 10, 2 - * * @param string $caption Caption for the given attachment. * @param int $attachment_id Attachment ID. * @@ -688,8 +678,6 @@ public function wp_get_attachment_caption( $caption, $attachment_id ) { /** * Filters the content of a single block. * - * @filter render_block, 10, 2 - * * @param string $block_content The block content about to be appended. * @param array $block The full block, including name and attributes. * @@ -710,8 +698,6 @@ public function render_block( $block_content, $block ) { /** * Add Unsplash metadata for edited attachment * - * @filter wp_edited_image_metadata, 10, 3 - * * @param array $data Array of updated attachment meta data. * @param int $new_attachment_id Attachment post ID. * @param int $attachment_id Original Attachment post ID. diff --git a/php/class-plugin-base.php b/php/class-plugin-base.php index 8a3448c6..52f4e2dc 100644 --- a/php/class-plugin-base.php +++ b/php/class-plugin-base.php @@ -61,14 +61,6 @@ abstract class Plugin_Base { */ protected $autoload_matches_cache = []; - /** - * Required instead of a static variable inside the add_doc_hooks method - * for the sake of unit testing. - * - * @var array - */ - protected $_called_doc_hooks = []; - /** * Plugin_Base constructor. */ @@ -80,14 +72,6 @@ public function __construct() { $this->file = trailingslashit( basename( $this->dir_path ) ) . 'unsplash.php'; spl_autoload_register( [ $this, 'autoload' ] ); - $this->add_doc_hooks(); - } - - /** - * Plugin_Base destructor. - */ - public function __destruct() { - $this->remove_doc_hooks(); } /** @@ -285,128 +269,4 @@ public function asset_version() { return $this->version(); } - - /** - * Hooks a function on to a specific filter. - * - * @param string $name The hook name. - * @param array $callback The class object and method. - * @param array $args An array with priority and arg_count. - * - * @return mixed - */ - public function add_filter( - $name, - $callback, - $args = [ - 'priority' => 10, - 'arg_count' => PHP_INT_MAX, - ] - ) { - return $this->add_hook( 'filter', $name, $callback, $args ); - } - - /** - * Hooks a function on to a specific action. - * - * @param string $name The hook name. - * @param array $callback The class object and method. - * @param array $args An array with priority and arg_count. - * - * @return mixed - */ - public function add_action( - $name, - $callback, - $args = [ - 'priority' => 10, - 'arg_count' => PHP_INT_MAX, - ] - ) { - return $this->add_hook( 'action', $name, $callback, $args ); - } - - /** - * Hooks a function on to a specific action/filter. - * - * @param string $type The hook type. Options are action/filter. - * @param string $name The hook name. - * @param array $callback The class object and method. - * @param array $args An array with priority and arg_count. - * - * @return mixed - */ - protected function add_hook( $type, $name, $callback, $args = [] ) { - $priority = isset( $args['priority'] ) ? $args['priority'] : 10; - $arg_count = isset( $args['arg_count'] ) ? $args['arg_count'] : PHP_INT_MAX; - $fn = sprintf( '\add_%s', $type ); - $retval = \call_user_func( $fn, $name, $callback, $priority, $arg_count ); - - return $retval; - } - - /** - * Add actions/filters from the methods of a class based on DocBlocks. - * - * @param object $object The class object. - */ - public function add_doc_hooks( $object = null ) { - if ( is_null( $object ) ) { - $object = $this; - } - $class_name = get_class( $object ); - if ( isset( $this->_called_doc_hooks[ $class_name ] ) ) { - $notice = sprintf( 'The add_doc_hooks method was already called on %s. Note that the Plugin_Base constructor automatically calls this method.', $class_name ); - if ( ! $this->is_wpcom_vip_prod() ) { - // phpcs:disable - trigger_error( esc_html( $notice ), \E_USER_NOTICE ); - // phpcs:enable - } - - return; - } - $this->_called_doc_hooks[ $class_name ] = true; - - $reflector = new \ReflectionObject( $object ); - foreach ( $reflector->getMethods() as $method ) { - $doc = $method->getDocComment(); - $arg_count = $method->getNumberOfParameters(); - if ( preg_match_all( '#\* @(?Pfilter|action)\s+(?P[a-z0-9\-\._/=]+)(?:,\s+(?P\-?[0-9]+))?#', $doc, $matches, PREG_SET_ORDER ) ) { - foreach ( $matches as $match ) { - $type = $match['type']; - $name = $match['name']; - $priority = empty( $match['priority'] ) ? 10 : intval( $match['priority'] ); - $callback = [ $object, $method->getName() ]; - call_user_func( [ $this, "add_{$type}" ], $name, $callback, compact( 'priority', 'arg_count' ) ); - } - } - } - } - - /** - * Removes the added DocBlock hooks. - * - * @param object $object The class object. - */ - public function remove_doc_hooks( $object = null ) { - if ( is_null( $object ) ) { - $object = $this; - } - $class_name = get_class( $object ); - - $reflector = new \ReflectionObject( $object ); - foreach ( $reflector->getMethods() as $method ) { - $doc = $method->getDocComment(); - if ( preg_match_all( '#\* @(?Pfilter|action)\s+(?P[a-z0-9\-\._/=]+)(?:,\s+(?P\-?[0-9]+))?#', $doc, $matches, PREG_SET_ORDER ) ) { - foreach ( $matches as $match ) { - $type = $match['type']; - $name = $match['name']; - $priority = empty( $match['priority'] ) ? 10 : intval( $match['priority'] ); - $callback = [ $object, $method->getName() ]; - call_user_func( "remove_{$type}", $name, $callback, $priority ); - } - } - } - unset( $this->_called_doc_hooks[ $class_name ] ); - } } diff --git a/php/class-plugin.php b/php/class-plugin.php index 24167889..840ee33a 100644 --- a/php/class-plugin.php +++ b/php/class-plugin.php @@ -51,8 +51,6 @@ class Plugin extends Plugin_Base { /** * Initiate the plugin resources. - * - * @action plugins_loaded */ public function init() { $this->hotlink = new Hotlink( $this ); @@ -71,6 +69,16 @@ public function init() { // Manually add this filter as the plugin file name is dynamic. add_filter( 'plugin_action_links_' . $this->file, [ $this, 'action_links' ] ); + + add_action( 'wp_default_scripts', [ $this, 'register_polyfill_scripts' ] ); + add_action( 'wp_enqueue_media', [ $this, 'enqueue_media_scripts' ] ); + add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_admin_scripts' ] ); + add_action( 'init', [ $this, 'register_meta' ] ); + add_action( 'init', [ $this, 'register_taxonomy' ] ); + add_action( 'admin_notices', [ $this, 'admin_notice' ] ); + add_action( 'print_media_templates', [ $this, 'add_media_templates' ] ); + + add_filter( 'wp_prepare_attachment_for_js', [ $this, 'add_unsplash_author_meta' ], 10, 2 ); } /** @@ -78,8 +86,6 @@ public function init() { * * @codeCoverageIgnore * - * @action wp_default_scripts - * * @param /WP_Scripts $wp_scripts Scripts. */ public function register_polyfill_scripts( $wp_scripts ) { @@ -127,8 +133,6 @@ public function register_polyfill_scripts( $wp_scripts ) { /** * Load our media selector assets. - * - * @action wp_enqueue_media */ public function enqueue_media_scripts() { if ( ! current_user_can( 'upload_files' ) ) { @@ -223,8 +227,6 @@ public function enqueue_media_scripts() { /** * Load our admin assets. - * - * @action admin_enqueue_scripts */ public function enqueue_admin_scripts() { wp_enqueue_style( @@ -467,8 +469,6 @@ public function image_sizes() { /** * Register meta field for attachments. - * - * @action init */ public function register_meta() { $default_args = [ @@ -532,8 +532,6 @@ public function register_meta() { /** * Register taxonomies for attachments. - * - * @action init */ public function register_taxonomy() { $default_args = [ @@ -570,8 +568,6 @@ public function register_taxonomy() { /** * Add an admin notice on if credentials not setup. - * - * @action admin_notices */ public function admin_notice() { if ( ! current_user_can( 'manage_options' ) ) { @@ -632,8 +628,6 @@ public function admin_notice() { * @param array $response Data for admin ajax. * @param WP_Post $attachment Attachment object. * - * @filter wp_prepare_attachment_for_js, 10, 2 - * * @return mixed */ public function add_unsplash_author_meta( array $response, $attachment ) { @@ -663,8 +657,6 @@ public function add_unsplash_author_meta( array $response, $attachment ) { /** * Add media templates. - * - * @action print_media_templates */ public function add_media_templates() { ?> plugin->add_doc_hooks( $this ); + add_action( 'rest_api_init', [ $this, 'register_routes' ] ); } /** * Registers the routes for the Unsplash API. * * @see register_rest_route() - * - * @action rest_api_init */ public function register_routes() { register_rest_route( diff --git a/php/class-settings.php b/php/class-settings.php index 81504a26..810cf4e3 100644 --- a/php/class-settings.php +++ b/php/class-settings.php @@ -72,7 +72,9 @@ public function __construct( $plugin ) { * Initiate the class. */ public function init() { - $this->plugin->add_doc_hooks( $this ); + add_action( 'admin_menu', [ $this, 'add_admin_menu' ] ); + add_action( 'admin_init', [ $this, 'add_settings' ] ); + add_action( 'admin_init', [ $this, 'handle_auth_flow' ] ); } /** @@ -167,8 +169,6 @@ private function get_default_salt() { /** * Adds the Unsplash admin menu. - * - * @action admin_menu */ public function add_admin_menu() { add_options_page( 'Unsplash', 'Unsplash', 'manage_options', 'unsplash', [ $this, 'settings_page_render' ] ); @@ -176,8 +176,6 @@ public function add_admin_menu() { /** * Add the Unsplash settings. - * - * @action admin_init */ public function add_settings() { $args = [ @@ -345,8 +343,6 @@ public function redirect_auth( $message = '', $type = 'error' ) { /** * Handles the authentication flow for registering a dynamic client application. - * - * @action admin_init */ public function handle_auth_flow() { $code = $this->get_code(); From 70b744b44dd52204ea6afaa7910b897076ed8326 Mon Sep 17 00:00:00 2001 From: spacedmonkey Date: Thu, 23 Jul 2020 20:32:01 +0100 Subject: [PATCH 03/22] Remove unneed tests. --- tests/phpunit/php/class-test-plugin-base.php | 73 -------------------- 1 file changed, 73 deletions(-) diff --git a/tests/phpunit/php/class-test-plugin-base.php b/tests/phpunit/php/class-test-plugin-base.php index 39c49a2f..660be8ea 100644 --- a/tests/phpunit/php/class-test-plugin-base.php +++ b/tests/phpunit/php/class-test-plugin-base.php @@ -158,79 +158,6 @@ public function test_is_debug() { public function test_is_script_debug() { $this->assertEquals( \SCRIPT_DEBUG, $this->plugin->is_script_debug() ); } - - /** - * Test add_doc_hooks(). - * - * @see Plugin_Base::add_doc_hooks() - */ - public function test_add_doc_hooks() { - $object = new Test_Doc_Hooks(); - - $this->assertEquals( 10, has_action( 'init', array( $object, 'init_action' ) ) ); - $this->assertEquals( 10, has_action( 'the_content', array( $object, 'the_content_filter' ) ) ); - $object->remove_doc_hooks( $object ); - } - - /** - * Test add_doc_hooks(). - * - * @see Plugin_Base::add_doc_hooks() - */ - public function test_add_doc_hooks_error() { - $mock = $this->getMockBuilder( 'Unsplash\Plugin' ) - ->setMethods( array( 'is_wpcom_vip_prod' ) ) - ->getMock(); - - $mock->method( 'is_wpcom_vip_prod' ) - ->willReturn( false ); - - $this->assertFalse( $mock->is_wpcom_vip_prod() ); - - $obj = $this; - // phpcs:disable - set_error_handler( - function( $errno, $errstr ) use ( $obj, $mock ) { - $obj->assertEquals( sprintf( 'The add_doc_hooks method was already called on %s. Note that the Plugin_Base constructor automatically calls this method.', get_class( $mock ) ), $errstr ); - $obj->assertEquals( \E_USER_NOTICE, $errno ); - } - ); - // phpcs:enable - $mock->add_doc_hooks(); - restore_error_handler(); - - $mock->remove_doc_hooks(); - } - - /** - * Test remove_doc_hooks(). - * - * @see Plugin_Base::remove_doc_hooks() - */ - public function test_remove_doc_hooks() { - $object = new Test_Doc_Hooks(); - $this->assertEquals( 10, has_action( 'init', array( $object, 'init_action' ) ) ); - $this->assertEquals( 10, has_action( 'the_content', array( $object, 'the_content_filter' ) ) ); - - $object->remove_doc_hooks( $object ); - $this->assertFalse( has_action( 'init', array( $object, 'init_action' ) ) ); - $this->assertFalse( has_action( 'the_content', array( $object, 'the_content_filter' ) ) ); - } - - /** - * Test __destruct(). - * - * @see Plugin_Base::__destruct() - */ - public function test___destruct() { - $object = new Test_Doc_Hooks(); - $this->assertEquals( 10, has_action( 'init', array( $object, 'init_action' ) ) ); - $this->assertEquals( 10, has_action( 'the_content', array( $object, 'the_content_filter' ) ) ); - - $object->__destruct( $object ); - $this->assertFalse( has_action( 'init', array( $object, 'init_action' ) ) ); - $this->assertFalse( has_action( 'the_content', array( $object, 'the_content_filter' ) ) ); - } } // phpcs:disable From 91fc3138a3a480e9c0f8ed755b135c0f5aa7433c Mon Sep 17 00:00:00 2001 From: spacedmonkey Date: Thu, 23 Jul 2020 23:43:08 +0100 Subject: [PATCH 04/22] Feedback --- instance.php | 2 - php/class-plugin-base.php | 140 +++++++++++++++++++ tests/phpunit/php/class-test-plugin-base.php | 73 ++++++++++ tests/phpunit/php/class-test-plugin.php | 2 +- unsplash.php | 10 ++ 5 files changed, 224 insertions(+), 3 deletions(-) diff --git a/instance.php b/instance.php index 1e6d64d6..382c9ba2 100644 --- a/instance.php +++ b/instance.php @@ -19,8 +19,6 @@ $unsplash_plugin = new Plugin(); -add_action( 'plugins_loaded', [ $unsplash_plugin, 'init' ] ); - /** * Unsplash Plugin Instance * diff --git a/php/class-plugin-base.php b/php/class-plugin-base.php index 52f4e2dc..8a3448c6 100644 --- a/php/class-plugin-base.php +++ b/php/class-plugin-base.php @@ -61,6 +61,14 @@ abstract class Plugin_Base { */ protected $autoload_matches_cache = []; + /** + * Required instead of a static variable inside the add_doc_hooks method + * for the sake of unit testing. + * + * @var array + */ + protected $_called_doc_hooks = []; + /** * Plugin_Base constructor. */ @@ -72,6 +80,14 @@ public function __construct() { $this->file = trailingslashit( basename( $this->dir_path ) ) . 'unsplash.php'; spl_autoload_register( [ $this, 'autoload' ] ); + $this->add_doc_hooks(); + } + + /** + * Plugin_Base destructor. + */ + public function __destruct() { + $this->remove_doc_hooks(); } /** @@ -269,4 +285,128 @@ public function asset_version() { return $this->version(); } + + /** + * Hooks a function on to a specific filter. + * + * @param string $name The hook name. + * @param array $callback The class object and method. + * @param array $args An array with priority and arg_count. + * + * @return mixed + */ + public function add_filter( + $name, + $callback, + $args = [ + 'priority' => 10, + 'arg_count' => PHP_INT_MAX, + ] + ) { + return $this->add_hook( 'filter', $name, $callback, $args ); + } + + /** + * Hooks a function on to a specific action. + * + * @param string $name The hook name. + * @param array $callback The class object and method. + * @param array $args An array with priority and arg_count. + * + * @return mixed + */ + public function add_action( + $name, + $callback, + $args = [ + 'priority' => 10, + 'arg_count' => PHP_INT_MAX, + ] + ) { + return $this->add_hook( 'action', $name, $callback, $args ); + } + + /** + * Hooks a function on to a specific action/filter. + * + * @param string $type The hook type. Options are action/filter. + * @param string $name The hook name. + * @param array $callback The class object and method. + * @param array $args An array with priority and arg_count. + * + * @return mixed + */ + protected function add_hook( $type, $name, $callback, $args = [] ) { + $priority = isset( $args['priority'] ) ? $args['priority'] : 10; + $arg_count = isset( $args['arg_count'] ) ? $args['arg_count'] : PHP_INT_MAX; + $fn = sprintf( '\add_%s', $type ); + $retval = \call_user_func( $fn, $name, $callback, $priority, $arg_count ); + + return $retval; + } + + /** + * Add actions/filters from the methods of a class based on DocBlocks. + * + * @param object $object The class object. + */ + public function add_doc_hooks( $object = null ) { + if ( is_null( $object ) ) { + $object = $this; + } + $class_name = get_class( $object ); + if ( isset( $this->_called_doc_hooks[ $class_name ] ) ) { + $notice = sprintf( 'The add_doc_hooks method was already called on %s. Note that the Plugin_Base constructor automatically calls this method.', $class_name ); + if ( ! $this->is_wpcom_vip_prod() ) { + // phpcs:disable + trigger_error( esc_html( $notice ), \E_USER_NOTICE ); + // phpcs:enable + } + + return; + } + $this->_called_doc_hooks[ $class_name ] = true; + + $reflector = new \ReflectionObject( $object ); + foreach ( $reflector->getMethods() as $method ) { + $doc = $method->getDocComment(); + $arg_count = $method->getNumberOfParameters(); + if ( preg_match_all( '#\* @(?Pfilter|action)\s+(?P[a-z0-9\-\._/=]+)(?:,\s+(?P\-?[0-9]+))?#', $doc, $matches, PREG_SET_ORDER ) ) { + foreach ( $matches as $match ) { + $type = $match['type']; + $name = $match['name']; + $priority = empty( $match['priority'] ) ? 10 : intval( $match['priority'] ); + $callback = [ $object, $method->getName() ]; + call_user_func( [ $this, "add_{$type}" ], $name, $callback, compact( 'priority', 'arg_count' ) ); + } + } + } + } + + /** + * Removes the added DocBlock hooks. + * + * @param object $object The class object. + */ + public function remove_doc_hooks( $object = null ) { + if ( is_null( $object ) ) { + $object = $this; + } + $class_name = get_class( $object ); + + $reflector = new \ReflectionObject( $object ); + foreach ( $reflector->getMethods() as $method ) { + $doc = $method->getDocComment(); + if ( preg_match_all( '#\* @(?Pfilter|action)\s+(?P[a-z0-9\-\._/=]+)(?:,\s+(?P\-?[0-9]+))?#', $doc, $matches, PREG_SET_ORDER ) ) { + foreach ( $matches as $match ) { + $type = $match['type']; + $name = $match['name']; + $priority = empty( $match['priority'] ) ? 10 : intval( $match['priority'] ); + $callback = [ $object, $method->getName() ]; + call_user_func( "remove_{$type}", $name, $callback, $priority ); + } + } + } + unset( $this->_called_doc_hooks[ $class_name ] ); + } } diff --git a/tests/phpunit/php/class-test-plugin-base.php b/tests/phpunit/php/class-test-plugin-base.php index 660be8ea..39c49a2f 100644 --- a/tests/phpunit/php/class-test-plugin-base.php +++ b/tests/phpunit/php/class-test-plugin-base.php @@ -158,6 +158,79 @@ public function test_is_debug() { public function test_is_script_debug() { $this->assertEquals( \SCRIPT_DEBUG, $this->plugin->is_script_debug() ); } + + /** + * Test add_doc_hooks(). + * + * @see Plugin_Base::add_doc_hooks() + */ + public function test_add_doc_hooks() { + $object = new Test_Doc_Hooks(); + + $this->assertEquals( 10, has_action( 'init', array( $object, 'init_action' ) ) ); + $this->assertEquals( 10, has_action( 'the_content', array( $object, 'the_content_filter' ) ) ); + $object->remove_doc_hooks( $object ); + } + + /** + * Test add_doc_hooks(). + * + * @see Plugin_Base::add_doc_hooks() + */ + public function test_add_doc_hooks_error() { + $mock = $this->getMockBuilder( 'Unsplash\Plugin' ) + ->setMethods( array( 'is_wpcom_vip_prod' ) ) + ->getMock(); + + $mock->method( 'is_wpcom_vip_prod' ) + ->willReturn( false ); + + $this->assertFalse( $mock->is_wpcom_vip_prod() ); + + $obj = $this; + // phpcs:disable + set_error_handler( + function( $errno, $errstr ) use ( $obj, $mock ) { + $obj->assertEquals( sprintf( 'The add_doc_hooks method was already called on %s. Note that the Plugin_Base constructor automatically calls this method.', get_class( $mock ) ), $errstr ); + $obj->assertEquals( \E_USER_NOTICE, $errno ); + } + ); + // phpcs:enable + $mock->add_doc_hooks(); + restore_error_handler(); + + $mock->remove_doc_hooks(); + } + + /** + * Test remove_doc_hooks(). + * + * @see Plugin_Base::remove_doc_hooks() + */ + public function test_remove_doc_hooks() { + $object = new Test_Doc_Hooks(); + $this->assertEquals( 10, has_action( 'init', array( $object, 'init_action' ) ) ); + $this->assertEquals( 10, has_action( 'the_content', array( $object, 'the_content_filter' ) ) ); + + $object->remove_doc_hooks( $object ); + $this->assertFalse( has_action( 'init', array( $object, 'init_action' ) ) ); + $this->assertFalse( has_action( 'the_content', array( $object, 'the_content_filter' ) ) ); + } + + /** + * Test __destruct(). + * + * @see Plugin_Base::__destruct() + */ + public function test___destruct() { + $object = new Test_Doc_Hooks(); + $this->assertEquals( 10, has_action( 'init', array( $object, 'init_action' ) ) ); + $this->assertEquals( 10, has_action( 'the_content', array( $object, 'the_content_filter' ) ) ); + + $object->__destruct( $object ); + $this->assertFalse( has_action( 'init', array( $object, 'init_action' ) ) ); + $this->assertFalse( has_action( 'the_content', array( $object, 'the_content_filter' ) ) ); + } } // phpcs:disable diff --git a/tests/phpunit/php/class-test-plugin.php b/tests/phpunit/php/class-test-plugin.php index b69ad85a..034d534f 100644 --- a/tests/phpunit/php/class-test-plugin.php +++ b/tests/phpunit/php/class-test-plugin.php @@ -107,7 +107,7 @@ public static function wpSetUpBeforeClass( $factory ) { * @see Plugin::__construct() */ public function test_construct() { - $plugin = new Plugin(); + $plugin = get_plugin_instance(); $this->assertEquals( 10, has_action( 'plugins_loaded', [ $plugin, 'init' ] ) ); $this->assertEquals( 10, has_action( 'wp_default_scripts', [ $plugin, 'register_polyfill_scripts' ] ) ); $this->assertEquals( 10, has_action( 'wp_enqueue_media', [ $plugin, 'enqueue_media_scripts' ] ) ); diff --git a/unsplash.php b/unsplash.php index e7b4153c..878bb029 100644 --- a/unsplash.php +++ b/unsplash.php @@ -57,3 +57,13 @@ function _unsplash_php_version_error() { function _unsplash_php_version_text() { return esc_html__( 'Unsplash plugin error: Your version of PHP is too old to run this plugin. You must be running PHP 5.6.20 or higher.', 'unsplash' ); } +/** + * Setup Unsplash plugin. + * + * @return void + */ +function _unsplash_load_plugin() { + $unsplash_plugin = \Unsplash\get_plugin_instance(); + $unsplash_plugin->init(); +} +add_action( 'plugins_loaded', '_unsplash_load_plugin' ); From ed55b6a190fd301829a13f3ce9d1b1ab1a9d20b2 Mon Sep 17 00:00:00 2001 From: spacedmonkey Date: Thu, 23 Jul 2020 23:46:59 +0100 Subject: [PATCH 05/22] Move inside php check. --- unsplash.php | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/unsplash.php b/unsplash.php index 878bb029..a65ba86b 100644 --- a/unsplash.php +++ b/unsplash.php @@ -34,6 +34,17 @@ if ( version_compare( phpversion(), '5.6.20', '>=' ) ) { require_once __DIR__ . '/instance.php'; + + /** + * Setup Unsplash plugin. + * + * @return void + */ + function _unsplash_load_plugin() { + $unsplash_plugin = \Unsplash\get_plugin_instance(); + $unsplash_plugin->init(); + } + add_action( 'plugins_loaded', '_unsplash_load_plugin' ); } else { if ( defined( 'WP_CLI' ) ) { WP_CLI::warning( _unsplash_php_version_text() ); @@ -57,13 +68,3 @@ function _unsplash_php_version_error() { function _unsplash_php_version_text() { return esc_html__( 'Unsplash plugin error: Your version of PHP is too old to run this plugin. You must be running PHP 5.6.20 or higher.', 'unsplash' ); } -/** - * Setup Unsplash plugin. - * - * @return void - */ -function _unsplash_load_plugin() { - $unsplash_plugin = \Unsplash\get_plugin_instance(); - $unsplash_plugin->init(); -} -add_action( 'plugins_loaded', '_unsplash_load_plugin' ); From 52430183a8354e668607c0e0f5fcdcf34ac5e863 Mon Sep 17 00:00:00 2001 From: spacedmonkey Date: Thu, 23 Jul 2020 23:54:51 +0100 Subject: [PATCH 06/22] Fix test. --- tests/phpunit/php/class-test-plugin.php | 4 ++-- unsplash.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/phpunit/php/class-test-plugin.php b/tests/phpunit/php/class-test-plugin.php index 034d534f..e7fa006d 100644 --- a/tests/phpunit/php/class-test-plugin.php +++ b/tests/phpunit/php/class-test-plugin.php @@ -107,8 +107,8 @@ public static function wpSetUpBeforeClass( $factory ) { * @see Plugin::__construct() */ public function test_construct() { - $plugin = get_plugin_instance(); - $this->assertEquals( 10, has_action( 'plugins_loaded', [ $plugin, 'init' ] ) ); + $plugin = new Plugin(); + $this->assertEquals( 10, has_action( 'wp_default_scripts', [ $plugin, 'register_polyfill_scripts' ] ) ); $this->assertEquals( 10, has_action( 'wp_enqueue_media', [ $plugin, 'enqueue_media_scripts' ] ) ); $this->assertEquals( 10, has_action( 'admin_enqueue_scripts', [ $plugin, 'enqueue_admin_scripts' ] ) ); diff --git a/unsplash.php b/unsplash.php index a65ba86b..c57ccdab 100644 --- a/unsplash.php +++ b/unsplash.php @@ -34,7 +34,7 @@ if ( version_compare( phpversion(), '5.6.20', '>=' ) ) { require_once __DIR__ . '/instance.php'; - + /** * Setup Unsplash plugin. * From 3dce76b2ff1797fe976152a42a4b3ada11db5f3e Mon Sep 17 00:00:00 2001 From: spacedmonkey Date: Fri, 24 Jul 2020 00:07:57 +0100 Subject: [PATCH 07/22] Fix test again. --- tests/phpunit/php/class-test-plugin.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/phpunit/php/class-test-plugin.php b/tests/phpunit/php/class-test-plugin.php index e7fa006d..18ead972 100644 --- a/tests/phpunit/php/class-test-plugin.php +++ b/tests/phpunit/php/class-test-plugin.php @@ -108,6 +108,7 @@ public static function wpSetUpBeforeClass( $factory ) { */ public function test_construct() { $plugin = new Plugin(); + $plugin->init(); $this->assertEquals( 10, has_action( 'wp_default_scripts', [ $plugin, 'register_polyfill_scripts' ] ) ); $this->assertEquals( 10, has_action( 'wp_enqueue_media', [ $plugin, 'enqueue_media_scripts' ] ) ); From 3852281176d1f7ab1d1ed0ab231111a9c6a30a98 Mon Sep 17 00:00:00 2001 From: spacedmonkey Date: Fri, 24 Jul 2020 08:11:57 +0100 Subject: [PATCH 08/22] Ignore function. --- unsplash.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/unsplash.php b/unsplash.php index c57ccdab..a9eef262 100644 --- a/unsplash.php +++ b/unsplash.php @@ -38,6 +38,8 @@ /** * Setup Unsplash plugin. * + * @codeCoverageIgnore + * * @return void */ function _unsplash_load_plugin() { From 29d9783eb3bd38920cddffd9d7c7cfd58627894a Mon Sep 17 00:00:00 2001 From: Ravi Chandra Date: Wed, 29 Jul 2020 14:18:16 +0530 Subject: [PATCH 09/22] Fix image import parent post attach issue --- .../src/media-selector/helpers/get-post-id.js | 20 +++++++++++++++++++ .../media-selector/helpers/import-images.js | 3 ++- assets/src/media-selector/helpers/index.js | 1 + php/class-rest-controller.php | 2 +- 4 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 assets/src/media-selector/helpers/get-post-id.js diff --git a/assets/src/media-selector/helpers/get-post-id.js b/assets/src/media-selector/helpers/get-post-id.js new file mode 100644 index 00000000..32959f41 --- /dev/null +++ b/assets/src/media-selector/helpers/get-post-id.js @@ -0,0 +1,20 @@ +import { select } from '@wordpress/data'; + +/** + * Get current post ID. + * + * @return {string|undefined} Current post ID. + */ +export default () => { + if ( select && select( 'core/editor' ) ) { + return select( 'core/editor' ).getCurrentPostId(); + } + + const postId = document.getElementById( 'post_ID' ); + + if ( postId ) { + return postId.value; + } + + return 0; +}; diff --git a/assets/src/media-selector/helpers/import-images.js b/assets/src/media-selector/helpers/import-images.js index afb3d699..6f5dfecf 100644 --- a/assets/src/media-selector/helpers/import-images.js +++ b/assets/src/media-selector/helpers/import-images.js @@ -6,7 +6,7 @@ import { addQueryArgs } from '@wordpress/url'; /** * Internal dependencies */ -import { getConfig, isUnsplashImage, preloadImage } from './'; +import { getConfig, getPostId, isUnsplashImage, preloadImage } from './'; const { getUserSetting } = window; @@ -48,6 +48,7 @@ const importImage = image => { title, description, caption, + parent: getPostId(), }; return wp diff --git a/assets/src/media-selector/helpers/index.js b/assets/src/media-selector/helpers/index.js index d5af02a6..1b625a6d 100644 --- a/assets/src/media-selector/helpers/index.js +++ b/assets/src/media-selector/helpers/index.js @@ -1,4 +1,5 @@ export { default as getConfig } from './get-config'; +export { default as getPostId } from './get-post-id'; export { default as importImages } from './import-images'; export { default as isApplicableLibraries } from './is-applicable-libraries'; export { default as isImageIncluded } from './is-image-included'; diff --git a/php/class-rest-controller.php b/php/class-rest-controller.php index 5d842fbe..84ac1b5f 100755 --- a/php/class-rest-controller.php +++ b/php/class-rest-controller.php @@ -234,7 +234,7 @@ public function get_import( $request ) { $image->set_field( 'description', $request->get_param( 'description' ) ); $image->set_field( 'caption', $request->get_param( 'caption' ) ); - $importer = new Import( $id, $image ); + $importer = new Import( $id, $image, $request->get_param( 'parent' ) ); $attachment_id = $importer->process(); // @codeCoverageIgnoreStart if ( is_wp_error( $attachment_id ) ) { From 146a5ab3e3e099769a755d1761329b27d744ea65 Mon Sep 17 00:00:00 2001 From: Ravi Chandra Date: Wed, 29 Jul 2020 21:25:11 +0530 Subject: [PATCH 10/22] Fix MediaUpload override issue --- .../media-selector/featured-image-selector.js | 2 +- php/class-plugin.php | 51 ++++++++++--------- tests/phpunit/php/class-test-plugin.php | 15 +++++- 3 files changed, 42 insertions(+), 26 deletions(-) diff --git a/assets/src/media-selector/featured-image-selector.js b/assets/src/media-selector/featured-image-selector.js index 5a8422d2..185a1027 100644 --- a/assets/src/media-selector/featured-image-selector.js +++ b/assets/src/media-selector/featured-image-selector.js @@ -17,5 +17,5 @@ addFilter( 'editor.MediaUpload', 'unsplash/extend-featured-image', () => UnsplashMediaUpload, - 20 + 10 ); diff --git a/php/class-plugin.php b/php/class-plugin.php index 24167889..e72136a3 100644 --- a/php/class-plugin.php +++ b/php/class-plugin.php @@ -185,29 +185,6 @@ public function enqueue_media_scripts() { ] ); - /* - * If the block editor is available, the featured image selector in the editor will need to be overridden. This - * is an extension of the media selector enqueued above and is separated from it because the required dependencies - * are not available in WP < 5.0. It would not make sense to polyfill these dependencies anyways since the block - * editor is not officially compatible with WP < 5.0. - */ - if ( has_action( 'enqueue_block_assets' ) ) { - $asset_file = $this->dir_path . '/assets/js/featured-image-selector.asset.php'; - $asset = is_readable( $asset_file ) ? require $asset_file : []; - $version = isset( $asset['version'] ) ? $asset['version'] : $this->asset_version(); - - $dependencies = isset( $asset['dependencies'] ) ? $asset['dependencies'] : []; - $dependencies[] = 'unsplash-media-selector'; - - wp_enqueue_script( - 'unsplash-featured-image-selector', - $this->asset_url( 'assets/js/featured-image-selector.js' ), - $dependencies, - $version, - true - ); - } - // Enqueue media selector CSS. wp_enqueue_style( 'unsplash-media-selector-style', @@ -221,6 +198,34 @@ public function enqueue_media_scripts() { return true; } + /** + * Enqueue block editor assets. + * + * @action enqueue_block_assets, 100 + */ + public function enqueue_block_assets() { + /* + * If the block editor is available, the featured image selector in the editor will need to be overridden. This + * is an extension of the media selector enqueued above and is separated from it because the required dependencies + * are not available in WP < 5.0. It would not make sense to polyfill these dependencies anyways since the block + * editor is not officially compatible with WP < 5.0. + */ + $asset_file = $this->dir_path . '/assets/js/featured-image-selector.asset.php'; + $asset = is_readable( $asset_file ) ? require $asset_file : []; + $version = isset( $asset['version'] ) ? $asset['version'] : $this->asset_version(); + + $dependencies = isset( $asset['dependencies'] ) ? $asset['dependencies'] : []; + $dependencies[] = 'unsplash-media-selector'; + + wp_enqueue_script( + 'unsplash-featured-image-selector', + $this->asset_url( 'assets/js/featured-image-selector.js' ), + $dependencies, + $version, + true + ); + } + /** * Load our admin assets. * diff --git a/tests/phpunit/php/class-test-plugin.php b/tests/phpunit/php/class-test-plugin.php index b69ad85a..31004023 100644 --- a/tests/phpunit/php/class-test-plugin.php +++ b/tests/phpunit/php/class-test-plugin.php @@ -167,9 +167,20 @@ public function test_enqueue_media_scripts() { $plugin = get_plugin_instance(); $plugin->enqueue_media_scripts(); $this->assertTrue( wp_script_is( 'unsplash-media-selector', 'enqueued' ) ); + } + + /** + * Test for enqueue_block_assets() method. + * + * @see Plugin::enqueue_block_assets() + */ + public function test_enqueue_block_assets() { + wp_set_current_user( self::$admin_id ); + set_current_screen( 'post.php' ); + $plugin = get_plugin_instance(); + $plugin->enqueue_block_assets(); - $featured_image_script_loads = version_compare( '5.0', get_bloginfo( 'version' ), '<=' ); - $this->assertEquals( $featured_image_script_loads, wp_script_is( 'unsplash-featured-image-selector', 'enqueued' ) ); + $this->assertTrue( wp_script_is( 'unsplash-featured-image-selector', 'enqueued' ) ); } /** From 4f6965f20920a98b700a5ab604e8aaf69e10209b Mon Sep 17 00:00:00 2001 From: Ravi Chandra Date: Thu, 30 Jul 2020 01:25:13 +0530 Subject: [PATCH 11/22] Fix directory separator issue in Windows --- php/class-plugin-base.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/php/class-plugin-base.php b/php/class-plugin-base.php index 8a3448c6..42bafd55 100644 --- a/php/class-plugin-base.php +++ b/php/class-plugin-base.php @@ -173,7 +173,7 @@ public function locate_plugin() { } $plugin_dir = dirname( dirname( $file_name ) ); - $plugin_path = $this->relative_path( $plugin_dir, basename( content_url() ), \DIRECTORY_SEPARATOR ); + $plugin_path = $this->relative_path( $plugin_dir, basename( content_url() ), '/' ); $dir_url = content_url( trailingslashit( $plugin_path ) ); $dir_path = $plugin_dir; From 911643799059decc83dd7e72d28307926c87a7fa Mon Sep 17 00:00:00 2001 From: Ravi Chandra Date: Mon, 3 Aug 2020 14:38:57 +0530 Subject: [PATCH 12/22] Move hook to init method from docblock --- php/class-plugin.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/php/class-plugin.php b/php/class-plugin.php index 8629d45c..47e01d2c 100644 --- a/php/class-plugin.php +++ b/php/class-plugin.php @@ -72,6 +72,7 @@ public function init() { add_action( 'wp_default_scripts', [ $this, 'register_polyfill_scripts' ] ); add_action( 'wp_enqueue_media', [ $this, 'enqueue_media_scripts' ] ); + add_action( 'enqueue_block_assets', [ $this, 'enqueue_block_assets' ], 100 ); add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_admin_scripts' ] ); add_action( 'init', [ $this, 'register_meta' ] ); add_action( 'init', [ $this, 'register_taxonomy' ] ); @@ -204,8 +205,6 @@ public function enqueue_media_scripts() { /** * Enqueue block editor assets. - * - * @action enqueue_block_assets, 100 */ public function enqueue_block_assets() { /* From fb72abf487063f146598bfb3cdda86c01ed6ac81 Mon Sep 17 00:00:00 2001 From: spacedmonkey Date: Mon, 3 Aug 2020 10:12:38 +0100 Subject: [PATCH 13/22] Fix cropping in WordPress 5.5. --- php/class-hotlink.php | 26 +++++++++++++++++++ tests/phpunit/php/class-test-hotlink.php | 32 ++++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/php/class-hotlink.php b/php/class-hotlink.php index f3d1c44b..71e77e6d 100644 --- a/php/class-hotlink.php +++ b/php/class-hotlink.php @@ -48,6 +48,7 @@ public function init() { add_filter( 'wp_get_attachment_caption', [ $this, 'wp_get_attachment_caption' ], 10, 2 ); add_filter( 'render_block', [ $this, 'render_block' ], 10, 2 ); add_filter( 'wp_edited_image_metadata', [ $this, 'add_edited_attachment_metadata' ], 10, 3 ); + add_filter( 'wp_image_file_matches_image_meta', [ $this, 'make_unsplash_images_cropable' ], 10, 4 ); } /** @@ -717,4 +718,29 @@ public function add_edited_attachment_metadata( $data, $new_attachment_id, $atta return $data; } + + /** + * Filter whether an image path or URI matches image meta. + * + * @param bool $match Whether the image relative path from the image meta + * matches the end of the URI or path to the image file. + * @param string $image_location Full path or URI to the tested image file. + * @param array $image_meta (Unused) The image meta data as returned by 'wp_get_attachment_metadata()'. + * @param int $attachment_id The image attachment ID or 0 if not supplied. + * + * @return bool Can an image cropable. + */ + public function make_unsplash_images_cropable( $match, $image_location, $image_meta, $attachment_id ) { + $unsplash_url = $this->get_unsplash_url( $attachment_id ); + $cropped = $this->is_cropped_image( $attachment_id ); + if ( ! $unsplash_url || $cropped ) { + return $match; + } + + if ( ! strpos( $image_location, 'images.unsplash.com' ) ) { + return $match; + } + + return true; + } } diff --git a/tests/phpunit/php/class-test-hotlink.php b/tests/phpunit/php/class-test-hotlink.php index 2dafdb73..31aae913 100644 --- a/tests/phpunit/php/class-test-hotlink.php +++ b/tests/phpunit/php/class-test-hotlink.php @@ -842,4 +842,36 @@ public function test_add_edited_attachment_metadata() { $this->assertEquals( 'UNSPLASH_ID', get_post_meta( $second_id, 'original_id', true ) ); $this->assertEquals( 'https://www.unsplash.com/foo', get_post_meta( $second_id, 'original_link', true ) ); } + + /** + * Test make_unsplash_images_cropable. + * + * @covers ::make_unsplash_images_cropable() + */ + public function test_make_unsplash_images_cropable() { + $image_location = 'https://images.unsplash.com/photo-1593642703055-4b72c180d9b5?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjE0NzAzNH0&fm=jpg&q=85&fit=crop&w=1024&h=683'; + $result = $this->hotlink->make_unsplash_images_cropable( false, $image_location, [], self::$attachment_id ); + $this->assertTrue( $result ); + } + + /** + * Test make_unsplash_images_cropable. + * + * @covers ::make_unsplash_images_cropable() + */ + public function test_make_unsplash_images_cropable_invalid() { + $first_id = $this->factory->attachment->create_object( + '/tmp/banana.jpg', + 0, + [ + 'post_mime_type' => 'image/jpeg', + 'post_excerpt' => 'A sample caption 1', + ] + ); + + $image_location = wp_get_attachment_url( $first_id ); + + $result = $this->hotlink->make_unsplash_images_cropable( false, $image_location, [], $first_id ); + $this->assertFalse( $result ); + } } From 4af2ad356a8d41e461086d32883be92bfdb60e1a Mon Sep 17 00:00:00 2001 From: spacedmonkey Date: Mon, 3 Aug 2020 10:17:45 +0100 Subject: [PATCH 14/22] Invert logic. --- php/class-hotlink.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/php/class-hotlink.php b/php/class-hotlink.php index 71e77e6d..0044b7cd 100644 --- a/php/class-hotlink.php +++ b/php/class-hotlink.php @@ -737,10 +737,10 @@ public function make_unsplash_images_cropable( $match, $image_location, $image_m return $match; } - if ( ! strpos( $image_location, 'images.unsplash.com' ) ) { - return $match; + if ( strpos( $image_location, 'images.unsplash.com' ) ) { + return true; } - return true; + return $match; } } From 28d1b46dddfc751738caddaa00cbb8e4b2a4f253 Mon Sep 17 00:00:00 2001 From: Ravi Chandra Date: Mon, 3 Aug 2020 14:52:21 +0530 Subject: [PATCH 15/22] Add import endpoint params --- php/class-rest-controller.php | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/php/class-rest-controller.php b/php/class-rest-controller.php index 127759d7..d73d3ef9 100755 --- a/php/class-rest-controller.php +++ b/php/class-rest-controller.php @@ -98,10 +98,30 @@ public function register_routes() { '/' . $this->rest_base . '/import/(?P[\w-]+)', [ 'args' => [ - 'id' => [ + 'id' => [ 'description' => esc_html__( 'Unsplash image ID.', 'unsplash' ), 'type' => 'string', ], + 'parent' => [ + 'description' => esc_html__( 'Parent post ID.', 'unsplash' ), + 'type' => 'integer', + ], + 'alt' => [ + 'description' => esc_html__( 'Image alt text.', 'unsplash' ), + 'type' => 'string', + ], + 'title' => [ + 'description' => esc_html__( 'Image title.', 'unsplash' ), + 'type' => 'string', + ], + 'description' => [ + 'description' => esc_html__( 'Image description.', 'unsplash' ), + 'type' => 'string', + ], + 'caption' => [ + 'description' => esc_html__( 'Image caption.', 'unsplash' ), + 'type' => 'string', + ], ], [ 'methods' => WP_REST_Server::CREATABLE, From fb371d116f5e7da3aef7cce616b8c532502eb78e Mon Sep 17 00:00:00 2001 From: Ravi Chandra Date: Mon, 3 Aug 2020 15:30:58 +0530 Subject: [PATCH 16/22] CR feedback --- .../src/media-selector/helpers/get-post-id.js | 20 ------------------- .../media-selector/helpers/import-images.js | 4 ++-- assets/src/media-selector/helpers/index.js | 1 - php/class-plugin.php | 3 ++- php/class-rest-controller.php | 1 + 5 files changed, 5 insertions(+), 24 deletions(-) delete mode 100644 assets/src/media-selector/helpers/get-post-id.js diff --git a/assets/src/media-selector/helpers/get-post-id.js b/assets/src/media-selector/helpers/get-post-id.js deleted file mode 100644 index 32959f41..00000000 --- a/assets/src/media-selector/helpers/get-post-id.js +++ /dev/null @@ -1,20 +0,0 @@ -import { select } from '@wordpress/data'; - -/** - * Get current post ID. - * - * @return {string|undefined} Current post ID. - */ -export default () => { - if ( select && select( 'core/editor' ) ) { - return select( 'core/editor' ).getCurrentPostId(); - } - - const postId = document.getElementById( 'post_ID' ); - - if ( postId ) { - return postId.value; - } - - return 0; -}; diff --git a/assets/src/media-selector/helpers/import-images.js b/assets/src/media-selector/helpers/import-images.js index 6f5dfecf..935877bd 100644 --- a/assets/src/media-selector/helpers/import-images.js +++ b/assets/src/media-selector/helpers/import-images.js @@ -6,7 +6,7 @@ import { addQueryArgs } from '@wordpress/url'; /** * Internal dependencies */ -import { getConfig, getPostId, isUnsplashImage, preloadImage } from './'; +import { getConfig, isUnsplashImage, preloadImage } from './'; const { getUserSetting } = window; @@ -48,7 +48,7 @@ const importImage = image => { title, description, caption, - parent: getPostId(), + parent: getConfig( 'postId' ), }; return wp diff --git a/assets/src/media-selector/helpers/index.js b/assets/src/media-selector/helpers/index.js index 1b625a6d..d5af02a6 100644 --- a/assets/src/media-selector/helpers/index.js +++ b/assets/src/media-selector/helpers/index.js @@ -1,5 +1,4 @@ export { default as getConfig } from './get-config'; -export { default as getPostId } from './get-post-id'; export { default as importImages } from './import-images'; export { default as isApplicableLibraries } from './is-applicable-libraries'; export { default as isImageIncluded } from './is-image-included'; diff --git a/php/class-plugin.php b/php/class-plugin.php index 840ee33a..29edadc7 100644 --- a/php/class-plugin.php +++ b/php/class-plugin.php @@ -165,6 +165,7 @@ public function enqueue_media_scripts() { true ); + $post = get_post(); wp_localize_script( 'unsplash-media-selector', 'unsplash', @@ -185,7 +186,7 @@ public function enqueue_media_scripts() { 'errors' => [ 'generic' => esc_html__( 'The file was unable to be imported into the Media Library. Please try again', 'unsplash' ), ], - + 'postId' => ( $post ) ? $post->ID : null, ] ); diff --git a/php/class-rest-controller.php b/php/class-rest-controller.php index d73d3ef9..ff045518 100755 --- a/php/class-rest-controller.php +++ b/php/class-rest-controller.php @@ -105,6 +105,7 @@ public function register_routes() { 'parent' => [ 'description' => esc_html__( 'Parent post ID.', 'unsplash' ), 'type' => 'integer', + 'default' => 0, ], 'alt' => [ 'description' => esc_html__( 'Image alt text.', 'unsplash' ), From c2a977f66d635d15697b26ad5d078881b7520340 Mon Sep 17 00:00:00 2001 From: Jonny Harris Date: Mon, 3 Aug 2020 12:09:28 +0100 Subject: [PATCH 17/22] Add a better check Co-authored-by: Ravi Chandra --- php/class-hotlink.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/php/class-hotlink.php b/php/class-hotlink.php index 0044b7cd..a08cf87f 100644 --- a/php/class-hotlink.php +++ b/php/class-hotlink.php @@ -737,7 +737,7 @@ public function make_unsplash_images_cropable( $match, $image_location, $image_m return $match; } - if ( strpos( $image_location, 'images.unsplash.com' ) ) { + if ( false !== strpos( $image_location, 'images.unsplash.com' ) ) { return true; } From 95bddf32032fdd93f30f18b4101bc69c5332b64e Mon Sep 17 00:00:00 2001 From: spacedmonkey Date: Mon, 3 Aug 2020 12:26:30 +0100 Subject: [PATCH 18/22] Remove logic. --- php/class-hotlink.php | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/php/class-hotlink.php b/php/class-hotlink.php index a08cf87f..f78413ac 100644 --- a/php/class-hotlink.php +++ b/php/class-hotlink.php @@ -733,14 +733,11 @@ public function add_edited_attachment_metadata( $data, $new_attachment_id, $atta public function make_unsplash_images_cropable( $match, $image_location, $image_meta, $attachment_id ) { $unsplash_url = $this->get_unsplash_url( $attachment_id ); $cropped = $this->is_cropped_image( $attachment_id ); - if ( ! $unsplash_url || $cropped ) { + $is_unsplash = strpos( $image_location, 'images.unsplash.com' ); + if ( ! $unsplash_url || $cropped || ( false === $is_unsplash ) ) { return $match; } - if ( false !== strpos( $image_location, 'images.unsplash.com' ) ) { - return true; - } - - return $match; + return true; } } From fe07cd64232f89ab22924b99fed939b080f43e62 Mon Sep 17 00:00:00 2001 From: spacedmonkey Date: Mon, 3 Aug 2020 15:13:12 +0100 Subject: [PATCH 19/22] Change version number to 1.0.1 --- readme.md | 18 +++++++++--------- readme.txt | 2 +- unsplash.php | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/readme.md b/readme.md index db35145a..52234500 100644 --- a/readme.md +++ b/readme.md @@ -4,15 +4,15 @@ ![Banner](wp-assets/banner-1544x500.png) Search and use the internet’s largest library of freely usable images from [Unsplash](https://unsplash.com) right from the WordPress editor. -**Contributors:** [unsplash](https://profiles.wordpress.org/unsplash), [xwp](https://profiles.wordpress.org/xwp) -**Tags:** [unsplash](https://wordpress.org/plugins/tags/unsplash), [images](https://wordpress.org/plugins/tags/images), [media](https://wordpress.org/plugins/tags/media), [free](https://wordpress.org/plugins/tags/free), [photographs](https://wordpress.org/plugins/tags/photographs), [photos](https://wordpress.org/plugins/tags/photos) -**Requires at least:** 4.9 -**Tested up to:** 5.4.2 -**Stable tag:** 1.0.0 -**License:** [GPLv2 or later](https://www.gnu.org/licenses/gpl-2.0.html) -**Requires PHP:** 5.6.20 - -[![Build Status](https://travis-ci.com/xwp/unsplash-wp.svg?token=DzyA3Sey2BLS5sL6HDJq&branch=develop)](https://travis-ci.com/xwp/unsplash-wp) [![Coverage Status](https://coveralls.io/repos/xwp/unsplash-wp/badge.svg?branch=develop&service=github&t=mLvdmf)](https://coveralls.io/github/xwp/unsplash-wp) [![Built with Grunt](https://gruntjs.com/cdn/builtwith.svg)](http://gruntjs.com) +**Contributors:** [unsplash](https://profiles.wordpress.org/unsplash), [xwp](https://profiles.wordpress.org/xwp) +**Tags:** [unsplash](https://wordpress.org/plugins/tags/unsplash), [images](https://wordpress.org/plugins/tags/images), [media](https://wordpress.org/plugins/tags/media), [free](https://wordpress.org/plugins/tags/free), [photographs](https://wordpress.org/plugins/tags/photographs), [photos](https://wordpress.org/plugins/tags/photos) +**Requires at least:** 4.9 +**Tested up to:** 5.4.2 +**Stable tag:** 1.0.1 +**License:** [GPLv2 or later](https://www.gnu.org/licenses/gpl-2.0.html) +**Requires PHP:** 5.6.20 + +[![Build Status](https://travis-ci.com/xwp/unsplash-wp.svg?token=DzyA3Sey2BLS5sL6HDJq&branch=develop)](https://travis-ci.com/xwp/unsplash-wp) [![Coverage Status](https://coveralls.io/repos/xwp/unsplash-wp/badge.svg?branch=develop&service=github&t=mLvdmf)](https://coveralls.io/github/xwp/unsplash-wp) [![Built with Grunt](https://gruntjs.com/cdn/builtwith.svg)](http://gruntjs.com) ## Description ## diff --git a/readme.txt b/readme.txt index 475d03a2..39f44303 100644 --- a/readme.txt +++ b/readme.txt @@ -2,7 +2,7 @@ Contributors: unsplash, xwp Tags: unsplash, images, media, free, photographs, photos, block Requires at least: 4.9 -Stable tag: 1.0.0 +Stable tag: 1.0.1 Tested up to: 5.4.2 Requires PHP: 5.6.20 License: GPLv2 or later diff --git a/unsplash.php b/unsplash.php index a9eef262..1810488f 100644 --- a/unsplash.php +++ b/unsplash.php @@ -3,7 +3,7 @@ * Plugin Name: Unsplash * Plugin URI: https://github.com/xwp/unsplash-wp * Description: Unsplash for WordPress. - * Version: 1.0.0 + * Version: 1.0.1 * Author: Unsplash * Author URI: https://unsplash.com/ * License: GPLv2+ From 7cb29e8724560f210e722fc6cc01c4f0d6d5e803 Mon Sep 17 00:00:00 2001 From: spacedmonkey Date: Thu, 6 Aug 2020 16:52:06 +0100 Subject: [PATCH 20/22] Name sure site name isn't empty. --- php/class-settings.php | 30 ++++++++++++++++++----- tests/phpunit/php/class-test-settings.php | 1 + 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/php/class-settings.php b/php/class-settings.php index 810cf4e3..979eeeac 100644 --- a/php/class-settings.php +++ b/php/class-settings.php @@ -444,14 +444,13 @@ public function get_access_token( $code ) { * @return mixed */ public function get_client_id( $access_token ) { - $url = get_home_url( null, '/' ); - $name = get_bloginfo( 'name' ); - $response = wp_remote_post( + $site_data = $this->get_site_data(); + $response = wp_remote_post( 'https://api.unsplash.com/clients', [ 'body' => [ - 'name' => $name, - 'description' => sprintf( 'Wordpress Oauth Client application for: %1$s - %2$s', $name, $url ), + 'name' => $site_data['name'], + 'description' => sprintf( 'Wordpress Oauth Client application for: %1$s - %2$s', $site_data['name'], $site_data['url'] ), ], 'headers' => [ 'Authorization' => 'Bearer ' . $access_token, @@ -504,7 +503,8 @@ public function access_key_render() { */ public function get_credentials() { $options = get_option( 'unsplash_settings' ); - $site_name_slug = sanitize_title_with_dashes( get_bloginfo( 'name' ) ); + $site_data = $this->get_site_data(); + $site_name_slug = sanitize_title_with_dashes( $site_data['name'] ); $credentials = [ 'applicationId' => ! empty( $options['access_key'] ) ? $this->decrypt( $options['access_key'] ) : getenv( 'UNSPLASH_ACCESS_KEY' ), @@ -521,4 +521,22 @@ public function get_credentials() { return $credentials; } + + /** + * Get site name and url. If site name is empty, fallback to domain with dashes. + * + * @return array + */ + public function get_site_data() { + $data = [ + 'url' => get_home_url( null, '/' ), + 'name' => get_bloginfo( 'name' ), + ]; + if ( ! $data['name'] && $data['url'] ) { + $url = wp_parse_url( $data['url'] ); + $data['name'] = sanitize_title_with_dashes( $url['host'] ); + } + + return $data; + } } diff --git a/tests/phpunit/php/class-test-settings.php b/tests/phpunit/php/class-test-settings.php index 74e5aba1..a9d52402 100644 --- a/tests/phpunit/php/class-test-settings.php +++ b/tests/phpunit/php/class-test-settings.php @@ -704,6 +704,7 @@ public function test_access_key_render() { * Test get_credentials. * * @covers ::get_credentials() + * @covers ::get_site_data() */ public function test_get_credentials() { $credentials = $this->settings->get_credentials(); From 0a7856427da97babc987c2cee94c5e5371289c12 Mon Sep 17 00:00:00 2001 From: spacedmonkey Date: Fri, 7 Aug 2020 10:54:11 +0100 Subject: [PATCH 21/22] Add a test. --- tests/phpunit/php/class-test-settings.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/phpunit/php/class-test-settings.php b/tests/phpunit/php/class-test-settings.php index a9d52402..3c4c1ab0 100644 --- a/tests/phpunit/php/class-test-settings.php +++ b/tests/phpunit/php/class-test-settings.php @@ -718,6 +718,25 @@ public function test_get_credentials() { $this->assertEquals( $expected_utm, $actual_utm ); } + /** + * + * Test get_site_data. + * + * @covers ::get_site_data() + */ + public function test_get_site_data() { + add_filter( 'pre_option_blogname', '__return_empty_string' ); + $site_data = $this->settings->get_site_data(); + + $this->assertEquals( [ 'url', 'name' ], array_keys( $site_data ) ); + + $url = wp_parse_url( get_home_url( null, '/' ) ); + $name = sanitize_title_with_dashes( $url['host'] ); + + $this->assertEquals( $site_data['name'], $name ); + remove_filter( 'pre_option_blogname', '__return_empty_string' ); + } + /** * Fake success. * From 9e6d77778af05cd9d30f9b4294290f629d68535d Mon Sep 17 00:00:00 2001 From: spacedmonkey Date: Fri, 7 Aug 2020 11:52:59 +0100 Subject: [PATCH 22/22] Check to see host exists. --- php/class-settings.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/php/class-settings.php b/php/class-settings.php index 979eeeac..5a812f51 100644 --- a/php/class-settings.php +++ b/php/class-settings.php @@ -532,9 +532,11 @@ public function get_site_data() { 'url' => get_home_url( null, '/' ), 'name' => get_bloginfo( 'name' ), ]; - if ( ! $data['name'] && $data['url'] ) { - $url = wp_parse_url( $data['url'] ); - $data['name'] = sanitize_title_with_dashes( $url['host'] ); + if ( ! $data['name'] ) { + $url = wp_parse_url( $data['url'] ); + if ( $url && array_key_exists( 'host', $url ) ) { + $data['name'] = sanitize_title_with_dashes( $url['host'] ); + } } return $data;