=== $data ) { return false; } /** * Filters the HTML returned by the oEmbed provider. * * @since 2.9.0 * * @param string|false $data The returned oEmbed HTML (false if unsafe). * @param string $url URL of the content to be embedded. * @param string|array $args Optional. Additional arguments for retrieving embed HTML. * See wp_oembed_get() for accepted arguments. Default empty. */ return apply_filters( 'oembed_result', $this->data2html( $data, $url ), $url, $args ); } /** * Attempts to discover link tags at the given URL for an oEmbed provider. * * @since 2.9.0 * * @param string $url The URL that should be inspected for discovery `` tags. * @return string|false The oEmbed provider URL on success, false on failure. */ public function discover( $url ) { $providers = array(); $args = array( 'limit_response_size' => 153600, // 150 KB ); /** * Filters oEmbed remote get arguments. * * @since 4.0.0 * * @see WP_Http::request() * * @param array $args oEmbed remote get arguments. * @param string $url URL to be inspected. */ $args = apply_filters( 'oembed_remote_get_args', $args, $url ); // Fetch URL content. $request = wp_safe_remote_get( $url, $args ); $html = wp_remote_retrieve_body( $request ); if ( $html ) { /** * Filters the link types that contain oEmbed provider URLs. * * @since 2.9.0 * * @param string[] $format Array of oEmbed link types. Accepts 'application/json+oembed', * 'text/xml+oembed', and 'application/xml+oembed' (incorrect, * used by at least Vimeo). */ $linktypes = apply_filters( 'oembed_linktypes', array( 'application/json+oembed' => 'json', 'text/xml+oembed' => 'xml', 'application/xml+oembed' => 'xml', ) ); // Strip
. $html_head_end = stripos( $html, '' ); if ( $html_head_end ) { $html = substr( $html, 0, $html_head_end ); } // Do a quick check. $tagfound = false; foreach ( $linktypes as $linktype => $format ) { if ( stripos( $html, $linktype ) ) { $tagfound = true; break; } } if ( $tagfound && preg_match_all( '#]+)/?>#iU', $html, $links ) ) { foreach ( $links[1] as $link ) { $atts = shortcode_parse_atts( $link ); if ( ! empty( $atts['type'] ) && ! empty( $linktypes[ $atts['type'] ] ) && ! empty( $atts['href'] ) ) { $providers[ $linktypes[ $atts['type'] ] ] = htmlspecialchars_decode( $atts['href'] ); // Stop here if it's JSON (that's all we need). if ( 'json' === $linktypes[ $atts['type'] ] ) { break; } } } } } // JSON is preferred to XML. if ( ! empty( $providers['json'] ) ) { return $providers['json']; } elseif ( ! empty( $providers['xml'] ) ) { return $providers['xml']; } else { return false; } } /** * Connects to an oEmbed provider and returns the result. * * @since 2.9.0 * * @param string $provider The URL to the oEmbed provider. * @param string $url The URL to the content that is desired to be embedded. * @param string|array $args Optional. Additional arguments for retrieving embed HTML. * See wp_oembed_get() for accepted arguments. Default empty. * @return object|false The result in the form of an object on success, false on failure. */ public function fetch( $provider, $url, $args = '' ) { $args = wp_parse_args( $args, wp_embed_defaults( $url ) ); $provider = add_query_arg( 'maxwidth', (int) $args['width'], $provider ); $provider = add_query_arg( 'maxheight', (int) $args['height'], $provider ); $provider = add_query_arg( 'url', urlencode( $url ), $provider ); $provider = add_query_arg( 'dnt', 1, $provider ); /** * Filters the oEmbed URL to be fetched. * * @since 2.9.0 * @since 4.9.0 The `dnt` (Do Not Track) query parameter was added to all oEmbed provider URLs. * * @param string $provider URL of the oEmbed provider. * @param string $url URL of the content to be embedded. * @param array $args Optional. Additional arguments for retrieving embed HTML. * See wp_oembed_get() for accepted arguments. Default empty. */ $provider = apply_filters( 'oembed_fetch_url', $provider, $url, $args ); foreach ( array( 'json', 'xml' ) as $format ) { $result = $this->_fetch_with_format( $provider, $format ); if ( is_wp_error( $result ) && 'not-implemented' === $result->get_error_code() ) { continue; } return ( $result && ! is_wp_error( $result ) ) ? $result : false; } return false; } /** * Fetches result from an oEmbed provider for a specific format and complete provider URL * * @since 3.0.0 * * @param string $provider_url_with_args URL to the provider with full arguments list (url, maxheight, etc.) * @param string $format Format to use. * @return object|false|WP_Error The result in the form of an object on success, false on failure. */ private function _fetch_with_format( $provider_url_with_args, $format ) { $provider_url_with_args = add_query_arg( 'format', $format, $provider_url_with_args ); /** This filter is documented in wp-includes/class-wp-oembed.php */ $args = apply_filters( 'oembed_remote_get_args', array(), $provider_url_with_args ); $response = wp_safe_remote_get( $provider_url_with_args, $args ); if ( 501 === wp_remote_retrieve_response_code( $response ) ) { return new WP_Error( 'not-implemented' ); } $body = wp_remote_retrieve_body( $response ); if ( ! $body ) { return false; } $parse_method = "_parse_$format"; return $this->$parse_method( $body ); } /** * Parses a json response body. * * @since 3.0.0 * * @param string $response_body * @return object|false */ private function _parse_json( $response_body ) { $data = json_decode( trim( $response_body ) ); return ( $data && is_object( $data ) ) ? $data : false; } /** * Parses an XML response body. * * @since 3.0.0 * * @param string $response_body * @return object|false */ private function _parse_xml( $response_body ) { if ( ! function_exists( 'libxml_disable_entity_loader' ) ) { return false; } if ( PHP_VERSION_ID < 80000 ) { /* * This function has been deprecated in PHP 8.0 because in libxml 2.9.0, external entity loading * is disabled by default, so this function is no longer needed to protect against XXE attacks. */ $loader = libxml_disable_entity_loader( true ); } $errors = libxml_use_internal_errors( true ); $return = $this->_parse_xml_body( $response_body ); libxml_use_internal_errors( $errors ); if ( PHP_VERSION_ID < 80000 && isset( $loader ) ) { // phpcs:ignore PHPCompatibility.FunctionUse.RemovedFunctions.libxml_disable_entity_loaderDeprecated libxml_disable_entity_loader( $loader ); } return $return; } /** * Serves as a helper function for parsing an XML response body. * * @since 3.6.0 * * @param string $response_body * @return stdClass|false */ private function _parse_xml_body( $response_body ) { if ( ! function_exists( 'simplexml_import_dom' ) || ! class_exists( 'DOMDocument', false ) ) { return false; } $dom = new DOMDocument(); $success = $dom->loadXML( $response_body ); if ( ! $success ) { return false; } if ( isset( $dom->doctype ) ) { return false; } foreach ( $dom->childNodes as $child ) { if ( XML_DOCUMENT_TYPE_NODE === $child->nodeType ) { return false; } } $xml = simplexml_import_dom( $dom ); if ( ! $xml ) { return false; } $return = new stdClass(); foreach ( $xml as $key => $value ) { $return->$key = (string) $value; } return $return; } /** * Converts a data object from WP_oEmbed::fetch() and returns the HTML. * * @since 2.9.0 * * @param object $data A data object result from an oEmbed provider. * @param string $url The URL to the content that is desired to be embedded. * @return string|false The HTML needed to embed on success, false on failure. */ public function data2html( $data, $url ) { if ( ! is_object( $data ) || empty( $data->type ) ) { return false; } $return = false; switch ( $data->type ) { case 'photo': if ( empty( $data->url ) || empty( $data->width ) || empty( $data->height ) ) { break; } if ( ! is_string( $data->url ) || ! is_numeric( $data->width ) || ! is_numeric( $data->height ) ) { break; } $title = ! empty( $data->title ) && is_string( $data->title ) ? $data->title : ''; $return = ']*>.+?)#i', $tokenized, $matches, PREG_SET_ORDER ); foreach ( $matches as $i => $match ) { $tag_html = str_replace( $replace, $search, $match[0] ); $tag_token = $token . $i; $found[ $tag_token ] = $tag_html; $html = str_replace( $tag_html, $tag_token, $html, $count ); } $replaced = str_replace( $replace, $search, $html ); $stripped = str_replace( array( "\r\n", "\n" ), '', $replaced ); $pre = array_values( $found ); $tokens = array_keys( $found ); return str_replace( $tokens, $pre, $stripped ); } } plugin slugs, for example. * * @since 6.5.0 * * @param string $slug The slug. */ $slug = apply_filters( 'wp_plugin_dependencies_slug', $slug ); // Match to WordPress.org slug format. if ( preg_match( '/^[a-z0-9]+(-[a-z0-9]+)*$/mu', $slug ) ) { $sanitized_slugs[] = $slug; } } $sanitized_slugs = array_unique( $sanitized_slugs ); sort( $sanitized_slugs ); return $sanitized_slugs; } /** * Gets the filepath of installed dependencies. * If a dependency is not installed, the filepath defaults to false. * * @since 6.5.0 * * @return array An array of install dependencies filepaths, relative to the plugins directory. */ protected static function get_dependency_filepaths() { if ( is_array( self::$dependency_filepaths ) ) { return self::$dependency_filepaths; } if ( null === self::$dependency_slugs ) { return array(); } self::$dependency_filepaths = array(); $plugin_dirnames = self::get_plugin_dirnames(); foreach ( self::$dependency_slugs as $slug ) { if ( isset( $plugin_dirnames[ $slug ] ) ) { self::$dependency_filepaths[ $slug ] = $plugin_dirnames[ $slug ]; continue; } self::$dependency_filepaths[ $slug ] = false; } return self::$dependency_filepaths; } /** * Retrieves and stores dependency plugin data from the WordPress.org Plugin API. * * @since 6.5.0 * * @global string $pagenow The filename of the current screen. * * @return array|void An array of dependency API data, or void on early exit. */ protected static function get_dependency_api_data() { global $pagenow; if ( ! is_admin() || ( 'plugins.php' !== $pagenow && 'plugin-install.php' !== $pagenow ) ) { return; } if ( is_array( self::$dependency_api_data ) ) { return self::$dependency_api_data; } $plugins = self::get_plugins(); self::$dependency_api_data = (array) get_site_transient( 'wp_plugin_dependencies_plugin_data' ); foreach ( self::$dependency_slugs as $slug ) { // Set transient for individual data, remove from self::$dependency_api_data if transient expired. if ( ! get_site_transient( "wp_plugin_dependencies_plugin_timeout_{$slug}" ) ) { unset( self::$dependency_api_data[ $slug ] ); set_site_transient( "wp_plugin_dependencies_plugin_timeout_{$slug}", true, 12 * HOUR_IN_SECONDS ); } if ( isset( self::$dependency_api_data[ $slug ] ) ) { if ( false === self::$dependency_api_data[ $slug ] ) { $dependency_file = self::get_dependency_filepath( $slug ); if ( false === $dependency_file ) { self::$dependency_api_data[ $slug ] = array( 'Name' => $slug ); } else { self::$dependency_api_data[ $slug ] = array( 'Name' => $plugins[ $dependency_file ]['Name'] ); } continue; } // Don't hit the Plugin API if data exists. if ( ! empty( self::$dependency_api_data[ $slug ]['last_updated'] ) ) { continue; } } if ( ! function_exists( 'plugins_api' ) ) { require_once ABSPATH . 'wp-admin/includes/plugin-install.php'; } $information = plugins_api( 'plugin_information', array( 'slug' => $slug, 'fields' => array( 'short_description' => true, 'icons' => true, ), ) ); if ( is_wp_error( $information ) ) { continue; } self::$dependency_api_data[ $slug ] = (array) $information; // plugins_api() returns 'name' not 'Name'. self::$dependency_api_data[ $slug ]['Name'] = self::$dependency_api_data[ $slug ]['name']; set_site_transient( 'wp_plugin_dependencies_plugin_data', self::$dependency_api_data, 0 ); } // Remove from self::$dependency_api_data if slug no longer a dependency. $differences = array_diff( array_keys( self::$dependency_api_data ), self::$dependency_slugs ); foreach ( $differences as $difference ) { unset( self::$dependency_api_data[ $difference ] ); } ksort( self::$dependency_api_data ); // Remove empty elements. self::$dependency_api_data = array_filter( self::$dependency_api_data ); set_site_transient( 'wp_plugin_dependencies_plugin_data', self::$dependency_api_data, 0 ); return self::$dependency_api_data; } /** * Gets plugin directory names. * * @since 6.5.0 * * @return array An array of plugin directory names. */ protected static function get_plugin_dirnames() { if ( is_array( self::$plugin_dirnames ) ) { return self::$plugin_dirnames; } self::$plugin_dirnames = array(); $plugin_files = array_keys( self::get_plugins() ); foreach ( $plugin_files as $plugin_file ) { $slug = self::convert_to_slug( $plugin_file ); self::$plugin_dirnames[ $slug ] = $plugin_file; } return self::$plugin_dirnames; } /** * Gets circular dependency data. * * @since 6.5.0 * * @return array[] An array of circular dependency pairings. */ protected static function get_circular_dependencies() { if ( is_array( self::$circular_dependencies_pairs ) ) { return self::$circular_dependencies_pairs; } if ( null === self::$dependencies ) { return array(); } self::$circular_dependencies_slugs = array(); self::$circular_dependencies_pairs = array(); foreach ( self::$dependencies as $dependent => $dependencies ) { /* * $dependent is in 'a/a.php' format. Dependencies are stored as slugs, i.e. 'a'. * * Convert $dependent to slug format for checking. */ $dependent_slug = self::convert_to_slug( $dependent ); self::$circular_dependencies_pairs = array_merge( self::$circular_dependencies_pairs, self::check_for_circular_dependencies( array( $dependent_slug ), $dependencies ) ); } return self::$circular_dependencies_pairs; } /** * Checks for circular dependencies. * * @since 6.5.0 * * @param array $dependents Array of dependent plugins. * @param array $dependencies Array of plugins dependencies. * @return array A circular dependency pairing, or an empty array if none exists. */ protected static function check_for_circular_dependencies( $dependents, $dependencies ) { $circular_dependencies_pairs = array(); // Check for a self-dependency. $dependents_location_in_its_own_dependencies = array_intersect( $dependents, $dependencies ); if ( ! empty( $dependents_location_in_its_own_dependencies ) ) { foreach ( $dependents_location_in_its_own_dependencies as $self_dependency ) { self::$circular_dependencies_slugs[] = $self_dependency; $circular_dependencies_pairs[] = array( $self_dependency, $self_dependency ); // No need to check for itself again. unset( $dependencies[ array_search( $self_dependency, $dependencies, true ) ] ); } } /* * Check each dependency to see: * 1. If it has dependencies. * 2. If its list of dependencies includes one of its own dependents. */ foreach ( $dependencies as $dependency ) { // Check if the dependency is also a dependent. $dependency_location_in_dependents = array_search( $dependency, self::$dependent_slugs, true ); if ( false !== $dependency_location_in_dependents ) { $dependencies_of_the_dependency = self::$dependencies[ $dependency_location_in_dependents ]; foreach ( $dependents as $dependent ) { // Check if its dependencies includes one of its own dependents. $dependent_location_in_dependency_dependencies = array_search( $dependent, $dependencies_of_the_dependency, true ); if ( false !== $dependent_location_in_dependency_dependencies ) { self::$circular_dependencies_slugs[] = $dependent; self::$circular_dependencies_slugs[] = $dependency; $circular_dependencies_pairs[] = array( $dependent, $dependency ); // Remove the dependent from its dependency's dependencies. unset( $dependencies_of_the_dependency[ $dependent_location_in_dependency_dependencies ] ); } } $dependents[] = $dependency; /* * Now check the dependencies of the dependency's dependencies for the dependent. * * Yes, that does make sense. */ $circular_dependencies_pairs = array_merge( $circular_dependencies_pairs, self::check_for_circular_dependencies( $dependents, array_unique( $dependencies_of_the_dependency ) ) ); } } return $circular_dependencies_pairs; } /** * Converts a plugin filepath to a slug. * * @since 6.5.0 * * @param string $plugin_file The plugin's filepath, relative to the plugins directory. * @return string The plugin's slug. */ protected static function convert_to_slug( $plugin_file ) { if ( 'hello.php' === $plugin_file ) { return 'hello-dolly'; } return str_contains( $plugin_file, '/' ) ? dirname( $plugin_file ) : str_replace( '.php', '', $plugin_file ); } } ]; } $response = Controller::get_instance()->get_info( $args ); if ( empty( $response ) ) { $data = array(); } else { $data = $this->prepare_item_for_response( $response, $request ); } $objects = $this->prepare_response_for_collection( $data ); return rest_ensure_response( $objects ); } /** * Send data directly to CookieYes web app. * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|WP_REST_Response */ public function send_items( $request ) { $response = Controller::get_instance()->sync(); if ( empty( $response ) ) { $data = array(); } else { $data = $this->prepare_item_for_response( $response, $request ); } $objects = $this->prepare_response_for_collection( $data ); return rest_ensure_response( $objects ); } /** * Clear cache of all the modules * * @return WP_Error|WP_REST_Response */ public function clear_cache() { $banner_controller = new \CookieYes\Lite\Admin\Modules\Banners\Includes\Controller(); $category_controller = new \CookieYes\Lite\Admin\Modules\Cookies\Includes\Category_Controller(); $cookie_controller = new \CookieYes\Lite\Admin\Modules\Cookies\Includes\Cookie_Controller(); $banner_controller->delete_cache(); $category_controller->delete_cache(); $cookie_controller->delete_cache(); wp_cache_flush(); $data = array( 'status' => true ); return rest_ensure_response( $data ); } /** * Initiate disconnect request. * * @return WP_Error|WP_REST_Response */ public function disconnect() { $response = Controller::get_instance()->disconnect(); return rest_ensure_response( $response ); } /** * Update the status of admin notices. * * @param object $request Request. * @return WP_Error|WP_REST_Response */ public function update_notice( $request ) { $response = array( 'status' => false ); $notice = isset( $request['notice'] ) ? $request['notice'] : false; $expiry = isset( $request['expiry'] ) ? intval( $request['expiry'] ) : 0; if ( $notice ) { Notice::get_instance()->dismiss( $notice, $expiry ); $response['status'] = true; } return rest_ensure_response( $response ); } /** * Update the status of admin notices. * * @param object $request Request. * @return WP_Error|WP_REST_Response */ public function install_missing_tables( $request ) { $missing_tables = cky_missing_tables(); if ( count( $missing_tables ) > 0 ) { do_action( 'cky_reinstall_tables' ); do_action( 'cky_clear_cache' ); } return rest_ensure_response( array( 'success' => true ) ); } /** * Format data to provide output to API * * @param object $object Object of the corresponding item Cookie or Cookie_Categories. * @param array $request Request params. * @return array */ public function prepare_item_for_response( $object, $request ) { $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; $data = $this->add_additional_fields_to_object( $object, $request ); $data = $this->filter_response_by_context( $data, $context ); return rest_ensure_response( $data ); } /** * Prepare a single item for create or update. * * @param WP_REST_Request $request Request object. * @return stdClass */ public function prepare_item_for_database( $request ) { $object = new Settings(); $data = $object->get(); $schema = $this->get_item_schema(); $properties = isset( $schema['properties'] ) && is_array( $schema['properties'] ) ? $schema['properties'] : array(); if ( ! empty( $properties ) ) { $properties_keys = array_keys( array_filter( $properties, function( $property ) { return isset( $property['readonly'] ) && true === $property['readonly'] ? false : true; } ) ); foreach ( $properties_keys as $key ) { $value = isset( $request[ $key ] ) ? $request[ $key ] : ''; $data[ $key ] = $value; } } $object->update( $data ); return $object->get(); } /** * Get the query params for collections. * * @return array */ public function get_collection_params() { return array( 'context' => $this->get_context_param( array( 'default' => 'view' ) ), 'paged' => array( 'description' => __( 'Current page of the collection.', 'cookie-law-info' ), 'type' => 'integer', 'default' => 1, 'sanitize_callback' => 'absint', 'validate_callback' => 'rest_validate_request_arg', 'minimum' => 1, ), 'per_page' => array( 'description' => __( 'Maximum number of items to be returned in result set.', 'cookie-law-info' ), 'type' => 'integer', 'default' => 10, 'minimum' => 1, 'maximum' => 100, 'sanitize_callback' => 'absint', 'validate_callback' => 'rest_validate_request_arg', ), 'search' => array( 'description' => __( 'Limit results to those matching a string.', 'cookie-law-info' ), 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', 'validate_callback' => 'rest_validate_request_arg', ), 'force' => array( 'type' => 'boolean', 'description' => __( 'Force fetch data', 'cookie-law-info' ), ), ); } /** * Get the Consent logs's schema, conforming to JSON Schema. * * @return array */ public function get_item_schema() { $schema = array( '$schema' => 'http://json-schema.org/draft-04/schema#', 'title' => 'consentlogs', 'type' => 'object', 'properties' => array( 'id' => array( 'description' => __( 'Unique identifier for the resource.', 'cookie-law-info' ), 'type' => 'integer', 'context' => array( 'view' ), 'readonly' => true, ), 'site' => array( 'description' => __( 'Unique identifier for the resource.', 'cookie-law-info' ), 'type' => 'object', 'context' => array( 'view', 'edit' ), ), 'api' => array( 'description' => __( 'Language.', 'cookie-law-info' ), 'type' => 'object', 'context' => array( 'view', 'edit' ), ), 'account' => array( 'description' => __( 'Language.', 'cookie-law-info' ), 'type' => 'object', 'context' => array( 'view', 'edit' ), ), 'consent_logs' => array( 'description' => __( 'Language.', 'cookie-law-info' ), 'type' => 'object', 'context' => array( 'view', 'edit' ), ), 'languages' => array( 'description' => __( 'Language.', 'cookie-law-info' ), 'type' => 'object', 'context' => array( 'view', 'edit' ), ), 'onboarding' => array( 'description' => __( 'Language.', 'cookie-law-info' ), 'type' => 'object', 'context' => array( 'view', 'edit' ), ), ), ); return $this->add_additional_fields_schema( $schema ); } } // End the class.