file = __FILE__; $self->url = plugin_dir_url( $self->file ); $self->path = plugin_dir_path( $self->file ); $self->basename = plugin_basename( $self->file ); $self->includes(); $self->hooks(); //$self->registerJavaScripts(); //$self->registerCSS(); } return self::$instance; } /** * Include the dependencies. * * @since 1.0 */ private function includes() { require_once( 'includes/class.wp-rest-post.php'); require_once( 'includes/class.wp-rest-add-featured-image.php'); } /** * Register the plugins actions/filters. * * @since 1.0 */ private function hooks() { add_shortcode( 'display-posts-remote', array( __CLASS__, 'shortcode' ) ); } /** * Get the plugin's bas URL. * * @since 1.0 */ public function getURL() { return $this->url; } /** * Register the plugin's JavaScript. * * @since 1.0 */ private function registerJavaScripts() { } /** * Enqueue the plugin's JavaScript. * * @since 1.0 */ public static function enqueueJS() { } /** * Register the plugin's CSS. * * @since 1.0 */ private function registerCSS() { } /** * Enqueue the plugin's JavaScript. * * @since 1.0 */ public static function enqueueCSS() { } /** * Cache the REST response. * * @since 1.0 * * @param string $url * @param array $response * @param float|int $timeout */ protected function setCache( $url, $response, $timeout = DAY_IN_SECONDS ) { set_transient( $this->cacheKey( $url ), $response, $timeout ); } /** * Get cached REST response. * * @since 1.0 * * @param string $url * * @return array|false */ protected function getCache( $url ) { if ( is_array( $response = get_transient( $this->cacheKey( $url ) ) ) ) { return $response; } return FALSE; } /** * Clear cache. * * @since 1.0 * * @param string $url */ public function clearCache( $url ){ delete_transient( $this->cacheKey( $url ) ); } /** * Create cache key based on URL. * * @since 1.0 * * @param string $url * * @return string */ protected function cacheKey( $url ) { return md5( preg_replace( '(^https?://)', '', $url ) ); } /** * Query a remote site's posts. * * @since 1.0 * * @param array $untrusted * * @return array|WP_Error */ public function getPosts( $untrusted ) { $defaults = array( 'url' => '', 'category_id' => 0, 'cache_timeout' => DAY_IN_SECONDS, ); $atts = shortcode_atts( $defaults, $untrusted ); $atts['url'] = esc_url( filter_var( $atts['url'], FILTER_SANITIZE_URL ) ); if ( 0 >= strlen( $atts['url'] ) ) { return new WP_Error( 'invalid_url', __( 'Remote site URL must be provided.', 'display-posts-shortcode-remote' ), $atts['url'] ); } $url = trailingslashit( $atts['url'] ) . 'wp-json/wp/v2/posts'; $url = add_query_arg( '_embed' , '', $url ); if ( ! empty( $atts['category_id'] ) ) { $url = add_query_arg( 'categories', $atts['category_id'], $url ); } if ( 0 >= $atts['cache_timeout'] ) { $this->clearCache( $url ); } if ( FALSE === $response = $this->getCache( $url ) ) { $response = wp_safe_remote_get( $url ); if ( ! is_wp_error( $response ) && 0 < $atts['cache_timeout'] ) { /* * NOTE: cache will be saved during Gutenberg autosaves via the REST API. */ $this->setCache( $url, $response, $atts['cache_timeout'] ); } } if ( is_wp_error( $response ) ) { return $response; } $posts = json_decode( wp_remote_retrieve_body( $response ) ); if ( JSON_ERROR_NONE !== json_last_error() ) { return new WP_Error( 'invalid_response', json_last_error_msg(), $posts ); } return $posts; } /** * The shortcode default options/values. * * @since 1.0 * * @return array */ public function getDefaults() { return array( 'category_id' => 0, 'content_class' => 'content', 'date_format' => '(n/j/Y)', 'include_content' => FALSE, 'include_date' => FALSE, 'include_date_modified' => FALSE, 'include_link' => TRUE, 'include_title' => TRUE, 'image_size' => 'thumbnail', 'url' => '', 'wrapper' => 'ul', 'cache_timeout' => DAY_IN_SECONDS, ); } /** * Parse and sanitize the user supplied shortcode values. * * @since 1.0 * * @param array $untrusted The user defined shortcode attributes. * * @return array */ public function parseShortcodeAtts( $untrusted ) { $defaults = Display_Posts_Remote()->getDefaults(); $atts = shortcode_atts( $defaults, $untrusted, 'display-posts-remote' ); $atts['category_id'] = absint( $atts['category_id'] ); $atts['content_class'] = array_map( 'sanitize_html_class', ( explode( ' ', $atts['content_class'] ) ) ); $atts['date_format'] = sanitize_text_field( $atts['date_format'] ); $atts['include_content'] = self::toBoolean( $atts['include_content'] ); $atts['include_date'] = self::toBoolean( $atts['include_date'] ); $atts['include_date_modified'] = self::toBoolean( $atts['include_date_modified'] ); $atts['include_link'] = self::toBoolean( $atts['include_link'] ); $atts['include_title'] = self::toBoolean( $atts['include_title'] ); $atts['image_size'] = sanitize_key( $atts['image_size'] ); $atts['url'] = filter_var( $atts['url'], FILTER_SANITIZE_URL ); $atts['wrapper'] = sanitize_text_field( $atts['wrapper'] ); $atts['cache_timeout'] = absint( $atts['cache_timeout'] ); return $atts; } /** * Callback for the `display-posts-remote` shortcode. * * @since 1.0 * * @param array $untrusted * @param string $content * @param string $tag * * @return string */ public static function shortcode( $untrusted, $content, $tag = 'display-posts-remote' ) { $self = Display_Posts_Remote(); $html = ''; $atts = $self->parseShortcodeAtts( $untrusted ); $result = $self->getPosts( $atts ); if ( is_wp_error( $result ) ) { return '
' . $result->get_error_message() . '
'; } if ( empty( $result ) || ! is_array( $result ) ) { return '
' . __( 'No posts to display.', 'display-posts-shortcode-remote' ) . '
'; } // Set up html elements used to wrap the posts. // Default is ul/li, but can also be ol/li and div/div. if ( ! in_array( $atts['wrapper'], array( 'ul', 'ol', 'div' ) ) ) { $atts['wrapper'] = 'ul'; } $itemElement = 'div' === $atts['wrapper'] ? 'div' : 'li'; foreach ( $result as $data ) { $image = $date = $postContent = ''; $post = new Display_Posts_Remote_Post( $data ); if ( $atts['include_title'] && $atts['include_link'] ) { $title = '' . esc_attr( strip_tags( $post->get_the_title() ) ) . ''; } elseif ( $atts['include_title'] ) { $title = '' . esc_attr( strip_tags( $post->get_the_title() ) ) . ''; } else { $title = ''; } $imageAttributes = $self->getImageAttributes( $atts ); if ( $atts['image_size'] && $post->has_post_thumbnail() && $atts['include_link'] ) { $image = '' . $post->get_the_post_thumbnail( $atts['image_size'], $imageAttributes ) . ' '; } elseif ( $atts['image_size'] && $post->has_post_thumbnail() ) { $image = '' . $post->get_the_post_thumbnail( $atts['image_size'], $imageAttributes ) . ' '; } elseif ( $post->has_featured_media() && $atts['include_link'] ) { $image = '' . $post->get_the_post_thumbnail( 'full', $imageAttributes ) . ' '; } elseif ( $post->has_featured_media() ) { $image = '' . $post->get_the_post_thumbnail( 'full', $imageAttributes ) . ' '; } if ( $atts['include_date'] ) { $date = ' ' . $post->get_the_date( $atts['date_format'] ) . ''; } elseif ( $atts['include_date_modified'] ) { $date = ' ' . $post->get_the_modified_date( $atts['date_format'] ) . ''; } if ( $atts['include_content'] ) { $postContent = '
' . $post->get_the_content() . '
'; } /** * Filter the HTML markup for output via the shortcode. * * Use the same filter name and pass the same values so existing filters work on DPS Remote. * * @since 1.0 * * @param string $html The shortcode's HTML output. * @param array $original_atts Original attributes passed to the shortcode. * @param string $image HTML markup for the post's featured image element. * @param string $title HTML markup for the post's title element. * @param string $date HTML markup for the post's date element. * @param string $excerpt HTML markup for the post's excerpt element. * @param string $inner_wrapper Type of container to use for the post's inner wrapper element. * @param string $content The post's content. * @param string $class Space-separated list of post classes to supply to the $inner_wrapper element. * @param string $author HTML markup for the post's author. * @param string $category_display_text */ $html .= apply_filters( 'display_posts_shortcode_output', "<{$itemElement} class=\"listing-item\">{$image}{$title}{$date}{$postContent}" . PHP_EOL, array(), // $original_atts $image, $title, $date, '', // $excerpt $itemElement, // $inner_wrapper $postContent, // $content array( 'listing-item' ), // $class '', // $author '' // $category_display_text ); } $open = "<{$atts['wrapper']} class=\"display-posts-listing\">" . PHP_EOL; $close = "" . PHP_EOL; return $open . $html . $close; } /** * Return the image attributes that should be applied ti the image tags. * * This is primarily to support DPS Pinch Zoomer add on. * * @since 1.0 * * @param array $atts * * @return array */ public function getImageAttributes( $atts = array() ) { $attributes = array(); /* * Support DPS Pinch Zoomer. */ if ( function_exists( 'Display_Posts_Pinch_Zoomer' ) ) { $options = Display_Posts_Pinch_Zoomer()->shortcodeAtts( $atts ); $attributes = Display_Posts_Pinch_Zoomer()->postImageAttributes( $options ); } return $attributes; } /** * Converts the following strings: yes/no; true/false and 0/1 to boolean values. * If the supplied string does not match one of those values the method will return NULL. * * @since 1.0 * * @param string|int|bool $value * * @return bool */ public static function toBoolean( &$value ) { // Already a bool, return it. if ( is_bool( $value ) ) return $value; $value = filter_var( strtolower( $value ), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE ); if ( is_null( $value ) ) { $value = FALSE; } return $value; } } /** * @since 1.0 * * @return Display_Posts_Remote */ function Display_Posts_Remote() { return Display_Posts_Remote::instance(); } Display_Posts_Remote(); }