From a76488719542bbb0376ab3c515919704b8d51b39 Mon Sep 17 00:00:00 2001 From: GaryJones Date: Sun, 30 Dec 2012 14:03:44 +0000 Subject: [PATCH 1/4] Re-work of the logging / debug bar panel feature. - Main class pulled into a separate file, leaving main plugin file lighter (could still do with some work). - Debugging (or more strictly, logging) features abstracted from main class into its own class. The main class still uses a handy debug_log() method, but this is just a wrapper for a separate logger class. An instance of the logger class is injected via a set_logger() method in the main class. This leaves the main class as just being the plugin functionality, with optional logging. This class has no knowledge of what is done with the logs (i.e. used in debug bar panel class). - The logger class takes it's structure from PSR-3, with the intention that another PSR-3 compatible logger class could be used in the future with minimal changes. Note that the logger class has no interaction with WP function calls etc. so this class could be developed into a more generic Blazer_Six_Logger class (that covers all log level methods used in PSR-3) for use in other projects or WP plugins. This class has no dependencies and no knowledge of how the logs were collected or what will be done with them. - The Debug Bar panel class only deals with the logger class, and is somewhat dependent on the structure under which the logger class stores each log. It has no knowledge of how the logs were collected, or even what they apply to. This could be made into a more generic Blazer_Six_Debug_Bar_Panel class, and then extended to make it specific for this plugin (e.g. panel title, styling). Other changes: - Main class no longer uses a singleton pattern. It is instantiated within the main plugin file. - wp_embed_handler() method adds an oembed="1" attribute, so that grouping of log messages can take account of whether a raw oembedable URL was used, or the direct shortcode that the URL would have converted to. - Debug bar panel has amended styling - keeps each message as a string (as per PSR-3), groups messages by a hash of the processed shortcode attributes, includes a respresentation of the raw shortcode as group title. - Debug bar panel hidden when there are no log messages to show. - Fixes https://github.com/bradyvercher/wp-blazer-six-gist-oembed/issues/3 for correct line highlighting, and improves it by adding trim()s, so that values like " 1, 5,9 -13" will work like "1,5,9-13". - Similar trim()s added for line number attributes. - Enqueuing of style sheet changed to correct hook. - Added localization support (no .po/.mo generated yet). - Documentation improvements. - Addition of me as an author, due to significant code changes. - Version bump to 1.2.0. --- blazer-six-gist-oembed.php | 612 ++---------------- ...blazer-six-gist-oembed-debug-bar-panel.php | 92 ++- class-blazer-six-gist-oembed-log.php | 111 ++++ class-blazer-six-gist-oembed.php | 578 +++++++++++++++++ 4 files changed, 778 insertions(+), 615 deletions(-) create mode 100644 class-blazer-six-gist-oembed-log.php create mode 100644 class-blazer-six-gist-oembed.php diff --git a/blazer-six-gist-oembed.php b/blazer-six-gist-oembed.php index 26d868e..169c50f 100644 --- a/blazer-six-gist-oembed.php +++ b/blazer-six-gist-oembed.php @@ -3,587 +3,63 @@ * Plugin Name: Blazer Six Gist oEmbed * Plugin URI: https://github.com/bradyvercher/wp-blazer-six-gist-oembed * Description: Gist oEmbed and shortcode support with caching. - * Version: 1.1.0 + * Version: 1.2.0 * Author: Blazer Six, Inc. * Author URI: http://www.blazersix.com/ * License: GPLv2 or later * License URI: http://www.gnu.org/licenses/gpl-2.0.html * - * @package Blazer_Six_Gist_oEmbed + * @package BlazerSix\GistoEmbed * @author Brady Vercher + * @author Gary Jones * @copyright Copyright (c) 2012, Blazer Six, Inc. * @license GPL-2.0+ - * - * @todo Feed support: link directly to post, directly to Gist, or wrap in iframe? - * @todo Cache the stylesheet locally. */ +// Support localization +load_plugin_textdomain( 'blazersix-gist-oembed', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' ); + +// Set up main plugin and logging classes +if ( ! class_exists( 'Blazer_Six_Gist_oEmbed' ) ) + require( plugin_dir_path( __FILE__ ) . 'class-blazer-six-gist-oembed.php' ); + +if ( ! class_exists( 'Blazer_Six_Gist_oEmbed_Log' ) ) + require( plugin_dir_path( __FILE__ ) . 'class-blazer-six-gist-oembed-log.php' ); + +$gist_oembed = new Blazer_Six_Gist_oEmbed; +$gist_oembed_logger = new Blazer_Six_Gist_oEmbed_Log; +$gist_oembed->set_logger( $gist_oembed_logger ); +$gist_oembed->run(); + +/** @todo Can the following two functions be made static methods of the debug + * bar class, since there is already an inherent dependency? */ + +// Late priority to give Debug Bar plugin chance to initialise +add_action( 'plugins_loaded', 'blazer_six_gist_oembed_add_debug_bar_panel_support', 15 ); /** - * Load the plugin when plugins are loaded. + * Add optional support for Debug Bar plugin, if enabled + * + * @return null Return early if Debug Bar plugin not enabled. */ -add_action( 'plugins_loaded', array( 'Blazer_Six_Gist_oEmbed', 'instance' ) ); +function blazer_six_gist_oembed_add_debug_bar_panel_support() { + if ( ! class_exists( 'Debug_Bar' ) || is_admin() || ! is_admin_bar_showing() ) + return; + + add_filter( 'debug_bar_panels', 'blazer_six_gist_oembed_add_debug_bar_panel' ); +} /** - * The main plugin class. + * Add instance of our debug bar panel to Debug Bar plugin. * - * @since 1.0.0 + * @param array $panels + * + * @return Blazer_Six_Gist_oEmbed_Debug_Bar_Panel */ -class Blazer_Six_Gist_oEmbed { - /** - * @access private - * @var Blazer_Six_Gist_oEmbed - */ - private static $instance; - - /** - * Basic log for debug messages. - * - * @access protected - * @var array - */ - protected $debug_log = array(); - - /** - * Key for grouping debug messages by shortcode. - * - * @access protected - * @var int - */ - protected $debug_log_key = 0; - - /** - * Toggle to short-circuit shortcode output and expire its corresponding - * transient so output can be regenerated the next time it is run. - * - * @access protected - * @var bool - */ - protected $expire_transients = false; - - /** - * Main Blazer_Six_Gist_oEmbed instance. - * - * @since 1.1.0 - */ - public static function instance() { - if ( ! self::$instance ) { - self::$instance = new self(); - } - - return self::$instance; - } - - /** - * Set up the plugin. - * - * Adds a [gist] shortcode to do the bulk of the heavy lifting. An embed - * handler is registered to mimic oEmbed functionality, but it relies on - * the shortcode for processing. - * - * Old URL Format: https://gist.github.com/{{id}}#file_{{filename}} - * New URL Format: https://gist.github.com/{{id}}#file-{{file_slug}} - * - * @since 1.1.0 - */ - private function __construct() { - // File matching is maintained for backward compatibility, but won't work for the new Gist "bookmark" URLs. - wp_embed_register_handler( 'gist', '#(https://gist\.github\.com/([a-z0-9]+))(?:\#file_(.*))?#i', array( $this, 'wp_embed_handler' ) ); - add_shortcode( 'gist', array( $this, 'shortcode' ) ); - - add_action( 'init', array( $this, 'init' ) ); - add_action( 'post_updated', array( $this, 'expire_gist_transients' ), 10, 3 ); - - if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { - add_filter( 'debug_bar_panels', array( $this, 'add_debug_bar_panel' ) ); - } - } - - /** - * Register the Gist stylesheet so it can be embedded once. - * - * @since 1.0.0 - */ - public function init() { - wp_register_style( 'github-gist', get_option( 'blazersix_gist_embed_stylesheet' ) ); - } - - /** - * WP embed handler to generate a shortcode string from a Gist URL. - * - * Parses Gist URLs for oEmbed support. Returns the value as a shortcode - * string to let the shortcode method handle processing. The value - * returned also doesn't have wpautop() applied, which is a must for - * source code. - * - * @since 1.0.0 - */ - public function wp_embed_handler( $matches, $attr, $url, $rawattr ) { - $shortcode = '[gist'; - - if ( isset( $matches[2] ) && ! empty( $matches[2] ) ) { - $shortcode .= ' id="' . esc_attr( $matches[2] ) . '"'; - } - - // For backward compatibility. - if ( isset( $matches[3] ) && ! empty( $matches[3] ) ) { - $shortcode .= ' file="' . esc_attr( $matches[3] ) . '"'; - } - - $shortcode .= ']'; - - return $shortcode; - } - - /** - * Gist shortcode. - * - * Works with secret Gists, too. - * - * Shortcode attributes: - * - * - id - The Gist id (found in the URL). The only required attribute. - * - embed_stylesheet - Whether the external stylesheet should be enqueued for output in the footer. - * * If the footer is too late, set to false and enqueue the 'github-gist' style before 'wp_head'. - * * Any custom styles should be added to the theme's stylesheet. - * - file - Name of a specific file in a Gist. - * - highlight - Comma-separated list of line numbers to highlight. - * * Ranges can be specified. Ex: 2,4,6-10,12 - * - highlight_color - Background color of highlighted lines. - * * To change it globally, hook into the filter any supply a different color. - * - lines - A range of lines to limit the Gist to. - * * Suited for single file Gists or shortcodes using the 'file' attribute. - * - show_line_numbers - Whether line numbers should be displayed. - * - show_meta - Whether the trailing meta information in default Gist embeds should be displayed. - * - * @since 1.0.0 - * - * @param array $attr Attributes of the shortcode. - * @return string HTML content to display the Gist. - */ - public function shortcode( $attr ) { - global $post; - - $defaults = apply_filters( 'blazersix_gist_shortcode_defaults', array( - 'embed_stylesheet' => apply_filters( 'blazersix_gist_embed_stylesheet_default', true ), - 'file' => '', - 'highlight' => array(), - 'highlight_color' => apply_filters( 'blazersix_gist_embed_highlight_color', '#ffffcc' ), - 'id' => '', - 'lines' => '', - 'show_line_numbers' => true, - 'show_meta' => true - ) ); - - // Sanitize attributes. - $attr = shortcode_atts( $defaults, $attr ); - $attr['embed_stylesheet'] = $this->shortcode_bool( $attr['embed_stylesheet'] ); - $attr['show_line_numbers'] = $this->shortcode_bool( $attr['show_line_numbers'] ); - $attr['show_meta'] = $this->shortcode_bool( $attr['show_meta'] ); - $attr['highlight'] = $this->parse_highlight_arg( $attr['highlight'] ); - $attr['lines'] = $this->parse_line_number_arg( $attr['lines'] ); - - // Short-circuit the shortcode output and just expire the transient. - // This is set to true when posts are updated. - if ( $this->expire_transients ) { - $this->expire_gist_transient( $attr ); - return; - } - - if ( ! empty( $attr['id'] ) ) { - $url = 'https://gist.github.com/' . $attr['id']; - $json_url = $url . '.json'; - } - - // Bail if the JSON endpoint couldn't be determined. - if ( ! isset( $json_url ) ) { - return ''; - } - - if ( $json_url && isset( $post->ID ) ) { - $html = $this->get_gist_html( $json_url, $attr ); - - if ( '{{unknown}}' === $html ) { - return $wp_embed->maybe_make_link( $url ); - } - - // If there was a result, return it. - if ( $html ) { - if ( $attr['embed_stylesheet'] ) { - wp_enqueue_style( 'github-gist' ); - } - - $html = apply_filters( 'blazersix_gist_embed_html', $html, $url, $attr, $post->ID ); - - $this->debug_log( $attr ); - $this->debug_log( $html ); - $this->debug_log_key ++; - - return $html; - } - } - - return ''; - } - - /** - * Helper method to determine if a shortcode attribute is true or false. - * - * @since 1.1.0 - * - * @param string|int|bool $var Attribute value. - * @return bool - */ - public function shortcode_bool( $var ) { - $falsey = array( 'false', '0', 'no', 'n' ); - return ( ! $var || in_array( strtolower( $var ), $falsey ) ) ? false : true; - } - - /** - * Parses and expands the shortcode 'highlight' attribute and returns it - * in a usable format. - * - * @since 1.1.0 - * - * @param string $line_numbers Comma-separated list of line numbers and ranges. - * @return array List of line numbers. - */ - public function parse_highlight_arg( $line_numbers ) { - if ( empty( $line_numbers ) ) { - return null; - } - - // Determine which lines should be highlighted. - $highlight = explode( ',', $line_numbers ); - - // Convert any ranges. - foreach ( $highlight as $key => $num ) { - if ( false !== strpos( $num, '-' ) ) { - unset( $highlight[ $key ] ); - - $range = explode( '-', $num ); - $highlight += range( $range[0], $range[1] ); - } - } - - return array_unique( $highlight ); - } - - /** - * Parses the shortcode 'lines' attribute into min and max values. - * - * @since 1.1.0 - * - * @param string $line_numbers Range of line numbers separated by a dash. - * @return array Array with min and max line numbers. - */ - public function parse_line_number_arg( $line_numbers ) { - if ( empty( $line_numbers ) ) { - return array( 'min' => 0, 'max' => 0 ); - } - - if ( false === strpos( $line_numbers, '-' ) ) { - $range = array_fill_keys( array( 'min', 'max' ), absint( $line_numbers ) ); - } else { - $numbers = array_map( 'absint', explode( '-', $line_numbers ) ); - - $range = array( - 'min' => $numbers[0], - 'max' => $numbers[1] - ); - } - - return $range; - } - - /** - * Retrieve Gist HTML. - * - * Gist HTML can come from one of three different sources: - * - Remote JSON endpoint. - * - Transient. - * - Post meta cache. - * - * When a Gist is intially requested, the HTML is fetched from the JSON - * endpoint and cached in a post meta field. It is then processed to limit - * line numbers, highlight specific lines, and add a few extra classes as - * style hooks. The processed HTML is then stored in a transient using a - * hash of the shortcodes attributes for the key. - * - * On subsequent requests, the HTML is fetched from the transient until it - * expires, then it is requested from the remote URL again. - * - * In the event the HTML can't be fetched from the remote endpoint and the - * transient is expired, the HTML is retrieved from the post meta backup. - * - * This algorithm allows Gist HTML to stay in sync with any changes GitHub - * may make to their markup, while providing a local cache for faster - * retrieval and a backup in case GitHub can't be reached. - * - * @since 1.1.0 - * - * @param string $url The JSON endpoint for the Gist. - * @param array $args List of shortcode attributes. - * @return string Gist HTML or {{unknown}} if it couldn't be determined. - */ - public function get_gist_html( $url, $args ) { - global $post; - - // Add a specific file from a Gist to the URL. - if ( ! empty( $args['file'] ) ) { - $url = add_query_arg( 'file', urlencode( $args['file'] ), $url ); - } - - $post_meta_key = '_gist_embed_' . md5( $url ); - $transient_key = 'gist_embed_' . $this->shortcode_hash( 'gist', $args ); - - $html = get_transient( $transient_key ); - - // Retrieve html from Gist JSON endpoint. - if ( empty( $html ) ) { - $json = $this->fetch_gist( $url ); - - if ( ! empty( $json->div ) ) { - $html = $json->div; - } - - // Update the stylesheet reference. - if ( ! empty( $json->stylesheet ) ) { - update_option( 'blazersix_gist_embed_stylesheet', $json->stylesheet ); - } - - // Failures are cached, too. Update the post to attempt to fetch again. - $html = ( $html ) ? $html : '{{unknown}}'; - $transient_expire = 60 * 60 * 24; - - if ( '{{unknown}}' != $html ) { - // Update the post meta fallback. - // @link http://core.trac.wordpress.org/ticket/21767 - update_post_meta( $post->ID, $post_meta_key, addslashes( $html ) ); - $html = $this->process_gist_html( $html, $args ); - $this->debug_log( '

' . __( 'Output Source: Remote Request', 'blazersix-gist-oembed' ) . '

' ); - } elseif ( $fallback = get_post_meta( $post->ID, $post_meta_key, true ) ) { - // Return the fallback instead of {{unknown}} - $html = $this->process_gist_html( $fallback, $args ); - - // Cache the fallback for an hour. - $transient_expire = 60 * 60; - $this->debug_log( '

' . __( 'Output Source: Post Meta Fallback', 'blazersix-gist-oembed' ) . '

' ); - } else { - $this->debug_log( '' . __( 'Remote call and transient failed and fallback was empty.', 'blazersix-gist-oembed' ) . '' ); - } - - // Cache the processed HTML. - set_transient( $transient_key, $html, $transient_expire ); - } else { - $this->debug_log( '

' . __( 'Output Source: Transient Cache', 'blazersix-gist-oembed' ) . '

' ); - } - - $this->debug_log( '' . __( 'JSON Endpoint:', 'blazersix-gist-oembed' ) . ' ' . $url ); - $this->debug_log( '' . __( 'Post Meta Cache Key:', 'blazersix-gist-oembed' ) . ' ' . $post_meta_key ); - $this->debug_log( '' . __( 'Transient Key:', 'blazersix-gist-oembed' ) . ' ' . $transient_key ); - - return $html; - } - - /** - * Fetch Gist data from its JSON endpoint. - * - * @since 1.1.0 - * - * @param string $url Gist JSON endpoint. - * @return object|bool Gist JSON object or false. - */ - public function fetch_gist( $url ) { - $this->debug_log( '' . __( 'Doing remote request:', 'blazersix-gist-oembed' ) . '
' . $url ); - - $response = wp_remote_get( $url, array( 'sslverify' => false ) ); - - if ( 200 == wp_remote_retrieve_response_code( $response ) ) { - return json_decode( wp_remote_retrieve_body( $response ) ); - } - - return false; - } - - /** - * Process the HTML returned from a Gist's JSON endpoint based on settings - * passed through the shortcode. - * - * @since 1.1.0 - * - * @param string $html HTML from the Gist's JSON endpoint. - * @param array $args List of shortcode attributes. - * @return string Modified HTML. - */ - public function process_gist_html( $html, $args ) { - // Remove the line number cell if it has been disabled. - if ( ! $args['show_line_numbers'] ) { - $html = preg_replace( '#.*?#s', '', $html ); - } - - // Remove the meta section if it has been disabled. - if ( ! $args['show_meta'] ) { - $html = preg_replace( '#
.*?
#s', '', $html ); - } - - $lines_pattern = '#(]+>)(.+?)#s'; - preg_match( $lines_pattern, $html, $lines_matches ); - - if( ! empty( $lines_matches[2] ) ) { - // Restrict the line number display if a range has been specified. - if ( $args['show_line_numbers'] && $args['lines']['min'] && $args['lines']['max'] ) { - $html = $this->limit_gist_line_numbers( $html, $args['lines'] ); - } - - if ( ! empty( $args['highlight'] ) ) { - // Flip to use isset() when looping through the lines. - $highlight = array_flip( $args['highlight'] ); - } - - // Extract and cleanup the individual lines from the Gist HTML into an array for processing. - $lines = trim( $lines_matches[2] ); - $lines = preg_split( '#[\s]*
#', substr( $lines, 5, strlen( $lines ) - 6 ) );
-			
-			foreach ( $lines as $key => $line ) {
-				// Remove lines if they're not in the specified range and continue.
-				if ( ( $args['lines']['min'] && $key < $args['lines']['min'] - 1 ) || ( $args['lines']['max'] && $key > $args['lines']['max'] - 1 ) ) {
-					unset( $lines[ $key ] );
-					continue;
-				}
-				
-				// Add classes for styling.
-				$classes = array( 'pre-line' );
-				$classes[] = ( $key % 2 ) ? 'pre-line-odd' : 'pre-line-even';
-				$style = '';
-				
-				if ( isset( $highlight[ $key + 1 ] ) ) {
-					$classes[] = 'pre-line-highlight';
-					
-					if ( ! empty( $args['highlight_color'] ) ) {
-						$style = ' style="background-color: ' . $args['highlight_color'] . ' !important"';
-					}
-				}
-				
-				$prepend = '
';
-				
-				$lines[ $key ] = $prepend . $line . '
'; - } - - $replacement = $lines_matches[1] . join( "\n", $lines ) . ''; - $html = preg_replace( $lines_pattern, $replacement, $html, 1 ); - } - - return $html; - } - - /** - * Removes line numbers from the Gist's HTML that fall outside the - * supplied range. - * - * @since 1.1.0 - * - * @param string $html HTML from the Gist's JSON endpoint. - * @param array $range Array of min and max values. - * @return string Modified HTML. - */ - public function limit_gist_line_numbers( $html, $range ) { - // Limit the line numbers that should show. - $line_num_pattern = '#()(.*?)#s'; - - preg_match( $line_num_pattern, $html, $line_num_matches ); - - if ( $line_num_matches[2] ) { - $line_numbers = array_slice( explode( "\n", trim( $line_num_matches[2] ) ), $range['min'] - 1, $range['max'] - $range['min'] + 1 ); - - $replacement = $line_num_matches[1] . join( "\n", $line_numbers ) . ''; - $html = preg_replace( $line_num_pattern, $replacement, $html, 1 ); - } - - return $html; - } - - /** - * Removes transients associated with Gists embedded in a post. - * - * Retrieves the keys of meta data associated with a post and deletes any - * transients with a matching embed key. - * - * @since 1.1.0 - * - * @param int $post_id Post ID. - * @param WP_Post $post_after Post object after update. - * @param WP_Post $post_before Post object before update. - */ - public function expire_gist_transients( $post_id, $post_after, $post_before ) { - $this->expire_transients = true; - - // Run the shorcodes to clear associated transients. - do_shortcode( $post_after->post_content ); - do_shortcode( $post_before->post_content ); - } - - /** - * Expire the transient associated with a particular shortcode so its HTML - * will be regenerated the next time it is requested. - * - * @since 1.1.0 - * - * @param array $args List of shortcode attributes. - */ - public function expire_gist_transient( $args ) { - $key = 'gist_embed_' . $this->shortcode_hash( 'gist', $args ); - set_transient( $key, null, -1 ); - } - - /** - * Get the debug log property. - * - * @since 1.1.0 - * - * @return array - */ - public function get_debug_log() { - return $this->debug_log; - } - - /** - * Simple debug logger. - * - * @since 1.1.0 - * - * @param mixed $value A value to log for the current shortcode. - */ - protected function debug_log( $value ) { - if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { - $this->debug_log[ $this->debug_log_key ][] = $value; - } - } - - /** - * Sort a shortcode's attributes by name and hash it for use as a cache - * key. - * - * @since 1.1.0 - */ - protected function shortcode_hash( $tag, $args ) { - ksort( $args ); - return md5( $tag . '_' . serialize( $args ) ); - } - - /** - * Add a custom panel to the debug bar. - * - * @since 1.1.0 - * - * @param array $panels List of panels. - * @return array - */ - public function add_debug_bar_panel( $panels ) { - if ( ! class_exists( 'Blazer_Six_Gist_oEmbed_Debug_Bar_Panel' ) ) { - include( plugin_dir_path( __FILE__ ) . 'class-blazer-six-gist-oembed-debug-bar-panel.php' ); - $panels[] = new Blazer_Six_Gist_oEmbed_Debug_Bar_Panel(); - } - - return $panels; - } -} \ No newline at end of file +function blazer_six_gist_oembed_add_debug_bar_panel( array $panels ) { + global $gist_oembed_logger; +// wp_die('panel being added'); + if ( ! class_exists( 'Blazer_Six_Gist_oEmbed_Debug_Bar_Panel' ) ) + require( plugin_dir_path( __FILE__ ) . 'class-blazer-six-gist-oembed-debug-bar-panel.php' ); + $panels[] = new Blazer_Six_Gist_oEmbed_Debug_Bar_Panel( $gist_oembed_logger ); + return $panels; +} diff --git a/class-blazer-six-gist-oembed-debug-bar-panel.php b/class-blazer-six-gist-oembed-debug-bar-panel.php index dee7b27..e009269 100644 --- a/class-blazer-six-gist-oembed-debug-bar-panel.php +++ b/class-blazer-six-gist-oembed-debug-bar-panel.php @@ -1,86 +1,84 @@ + * @author Gary Jones + * @copyright Copyright (c) 2012, Blazer Six, Inc. + * @license GPL-2.0+ + */ + /** * Class for displaying a custom panel on the debug bar with information about * Gist shortcodes used in a post. * - * @since 1.1.0 + * @package BlazerSix\GistoEmbed + * @author Brady Vercher + * @author Gary Jones */ class Blazer_Six_Gist_oEmbed_Debug_Bar_Panel extends Debug_Bar_Panel { + /** @var object Logger object. */ + protected $logger; + + /** + * Assign properties, and call parent constructor. + * + * @since 1.2.0 + * + * @param object $logger + */ + public function __construct( $logger ) { + $this->logger = $logger; + parent::__construct(); + } + /** * Initialize the panel and set its title. * * @since 1.1.0 */ - public function init(){ + public function init() { $this->title( __( 'Gist oEmbed', 'blazersix-gist-oembed' ) ); } /** - * Make the panel visibile. + * Make the panel visible, only if there is something to display. * * @since 1.1.0 */ public function prerender() { - $this->set_visible( true ); + $logs = $this->logger->get_logs(); + $this->set_visible( ! empty( $logs ) ); } /** - * Requests the debug log from the Gist oEmbed class and displays it in - * the custom debug bar panel. + * Request the log from the logger class and display it in the custom debug + * bar panel. * * @since 1.1.0 */ public function render() { - $gists = Blazer_Six_Gist_oEmbed::instance(); - - $log = $gists->get_debug_log(); - - foreach ( $log as $gist ) { + $logs = $this->logger->get_logs(); + foreach ( $logs as $log_id => $gist ) { echo '
'; - foreach ( $gist as $entry ) { - if ( is_scalar( $entry ) ) { - echo ( false === strpos( $entry, ' - - - - - - - - - $value ) : ?> - - - - - - -
- between line number spans. + echo ( false === strpos( $entry['message'], ''; } ?> + * @author Gary Jones + * @copyright Copyright (c) 2012, Blazer Six, Inc. + * @license GPL-2.0+ + */ + +/** + * Logging class. + * + * This class handles the recording of log messages - in particular, messages at + * the debug level. + * + * It follows a subset of PSR-3 and when WordPress bumps to PHP 5.3 as + * its minimum, then it can implement Psr\Log\LoggerInterface with minimal + * changes to the interface. Note however, that code in this file doesn't + * interact directly with WordPress at all. + * + * @see https://github.com/php-fig/log + * + * @package BlazerSix\GistoEmbed + * @author Gary Jones + * @author Brady Vercher + */ +class Blazer_Six_Gist_oEmbed_Log { + /** + * Holds the log messages and contextual data. + * + * The format is something like the following: + * + * $logs = array( + * 'fookey' => array( + * array( + * 'message' => 'Some message.', + * 'qwe' => 'rty'; + * ), + * array( + * 'message' => 'Another message for the same gist shortcode.', + * 'extra' => 'data'; + * 'if' => 'needed'; + * ), + * ), + * 'barkey' => array( + * array( + * 'message' => 'Some message for gist bar.', + * 'qwe' => 'rty'; + * ), + * array( + * 'message' => 'Another message for gist bar.', + * 'extra' => 'data'; + * 'if' => 'needed'; + * ), + * ), + * ); + */ + protected $logs = array(); + + /** + * Detailed debug information. + * + * @since 1.2.0 + * + * @uses Blazer_Six_Gist_oEmbed_Log::log() + * + * @param string $message + * @param array $context + */ + public function debug( $message, array $context = array() ) { + $this->log( 'debug', $message, $context ); + } + + /** + * Log with an arbitrary level. + * + * Include a 'key' key in the $context argument to group log messages + * together. If none is provided, the message is grouped by itself under an + * md5() hash of the message itself. + * + * Note that at this point in time, we don't actually do anything with the + * $level argument. + * + * @since 1.2.0 + * + * @param mixed $level + * @param string $message + * @param array $context + */ + public function log( $level, $message, array $context = array() ) { + $context['message'] = $message; + $key = isset( $context['key'] ) ? $context['key'] : md5( $message ); + unset( $context['key'] ); + $this->logs[$key][] = $context; + } + + /** + * Return recorded logs data. + * + * Under PSR-1, this method would be called getLogs(). + * + * @since 1.2.0 + * + * @return array + */ + public function get_logs() { + return $this->logs; + } +} \ No newline at end of file diff --git a/class-blazer-six-gist-oembed.php b/class-blazer-six-gist-oembed.php new file mode 100644 index 0000000..f9cbc7c --- /dev/null +++ b/class-blazer-six-gist-oembed.php @@ -0,0 +1,578 @@ + + * @author Gary Jones + * @copyright Copyright (c) 2012, Blazer Six, Inc. + * @license GPL-2.0+ + * + * @todo Feed support: link directly to post, directly to Gist, or wrap in iframe? + * @todo Cache the style sheet locally. + */ + +/** + * The main plugin class. + * + * @package BlazerSix/GistoEmbed + * @author Brady Vercher + * @author Gary Jones + */ +class Blazer_Six_Gist_oEmbed { + /** @var object Logger object. */ + protected $logger = null; + + /** + * Toggle to short-circuit shortcode output and expire its corresponding + * transient so output can be regenerated the next time it is run. + * + * @var bool + */ + protected $expire_transients = false; + + /** + * Sets a logger instance on the object. + * + * Since logging is optional, the dependcy injection is done via this + * method, instead of being required through a constructor. + * + * Under PSR-1, this method would be called setLogger(). + * + * @see https://github.com/php-fig/log/blob/master/Psr/Log/LoggerAwareInterface.php + * + * @since 1.2.0 + * + * @param object $logger + */ + public function set_logger( $logger ) { + $this->logger = $logger; + } + + /** + * Return logger instance. + * + * Under PSR-1, this method would be called getLogger(). + * + * @since 1.2.0 + * + * @return object + */ + public function get_logger() { + return $this->$logger; + } + + /** + * Set up the plugin. + * + * Adds a [gist] shortcode to do the bulk of the heavy lifting. An embed + * handler is registered to mimic oEmbed functionality, but it relies on + * the shortcode for processing. + * + * Old URL Format: https://gist.github.com/{{id}}#file_{{filename}} + * New URL Format: https://gist.github.com/{{id}}#file-{{file_slug}} + * + * @since 1.1.0 + */ + public function run() { + // File matching is maintained for backward compatibility, but won't work for the new Gist "bookmark" URLs. + wp_embed_register_handler( 'gist', '#(https://gist\.github\.com/([a-z0-9]+))(?:\#file_(.*))?#i', array( $this, 'wp_embed_handler' ) ); + add_shortcode( 'gist', array( $this, 'shortcode' ) ); + + add_action( 'wp_enqueue_scripts', array( $this, 'style' ) ); + add_action( 'post_updated', array( $this, 'expire_gist_transients' ), 10, 3 ); + } + + /** + * Register the Gist style sheet so it can be embedded once. + * + * @since 1.0.0 + */ + public function style() { + wp_register_style( 'github-gist', get_option( 'blazersix_gist_embed_stylesheet' ) ); + } + + /** + * WP embed handler to generate a shortcode string from a Gist URL. + * + * Parses Gist URLs for oEmbed support. Returns the value as a shortcode + * string to let the shortcode method handle processing. The value + * returned also doesn't have wpautop() applied, which is a must for + * source code. + * + * @since 1.0.0 + */ + public function wp_embed_handler( $matches, $attr, $url, $rawattr ) { + $shortcode = '[gist'; + + if ( isset( $matches[2] ) && ! empty( $matches[2] ) ) { + $shortcode .= ' id="' . esc_attr( $matches[2] ) . '"'; + } + + // For backward compatibility. + if ( isset( $matches[3] ) && ! empty( $matches[3] ) ) { + $shortcode .= ' file="' . esc_attr( $matches[3] ) . '"'; + } + + // This attribute added so we can identify if a oembed URL or direct shortcode was used. + $shortcode .= ' oembed="1"]'; + + return $shortcode; + } + + /** + * Gist shortcode. + * + * Works with secret Gists, too. + * + * Shortcode attributes: + * + * - id - The Gist id (found in the URL). The only required attribute. + * - embed_stylesheet - Whether the external style sheet should be enqueued for output in the footer. + * * If the footer is too late, set to false and enqueue the 'github-gist' style before 'wp_head'. + * * Any custom styles should be added to the theme's style sheet. + * - file - Name of a specific file in a Gist. + * - highlight - Comma-separated list of line numbers to highlight. + * * Ranges can be specified. Ex: 2,4,6-10,12 + * - highlight_color - Background color of highlighted lines. + * * To change it globally, hook into the filter and supply a different color. + * - lines - A range of lines to limit the Gist to. + * * Suited for single file Gists or shortcodes using the 'file' attribute. + * - show_line_numbers - Whether line numbers should be displayed. + * - show_meta - Whether the trailing meta information in default Gist embeds should be displayed. + * + * @since 1.0.0 + * + * @param array $attr Attributes of the shortcode. + * + * @return string HTML content to display the Gist. + */ + public function shortcode( $attr ) { + global $post; + + // Rebuild the original shortcode as a string with raw attributes + $rawattr = array(); + foreach ( $attr as $key => $value ) { + if ( 'oembed' != $key ) { + $rawattr[] = $key . '="' . $value . '"'; + } + } + $shortcode = '[gist ' . implode(' ', $rawattr) . ']'; + + $defaults = apply_filters( + 'blazersix_gist_shortcode_defaults', + array( + 'embed_stylesheet' => apply_filters( 'blazersix_gist_embed_stylesheet_default', true ), + 'file' => '', + 'highlight' => array(), + 'highlight_color' => apply_filters( 'blazersix_gist_embed_highlight_color', '#ffffcc' ), + 'id' => '', + 'lines' => '', + 'show_line_numbers' => true, + 'show_meta' => true, + 'oembed' => 0, // private use only + ) + ); + + // Sanitize attributes. + $attr = shortcode_atts( $defaults, $attr ); + $attr['embed_stylesheet'] = $this->shortcode_bool( $attr['embed_stylesheet'] ); + $attr['show_line_numbers'] = $this->shortcode_bool( $attr['show_line_numbers'] ); + $attr['show_meta'] = $this->shortcode_bool( $attr['show_meta'] ); + $attr['highlight'] = $this->parse_highlight_arg( $attr['highlight'] ); + $attr['lines'] = $this->parse_line_number_arg( $attr['lines'] ); + + // Log what we're dealing with - title uses original attributes, but hashed against processed attributes. + $this->debug_log( '

' . $shortcode . '

', $this->shortcode_hash( 'gist', $attr ) ); + + // Short-circuit the shortcode output and just expire the transient. + // This is set to true when posts are updated. + if ( $this->expire_transients ) { + $this->expire_gist_transient( $attr ); + return; + } + + // Bail if the ID is not set. + if ( empty( $attr['id'] ) ) { + $this->debug_log( __( 'Shortcode did not have a required id attribute.', 'blazer_six_gist_oembed' ), $this->shortcode_hash( 'gist', $attr ) ); + return ''; + } + + $url = 'https://gist.github.com/' . $attr['id']; + $json_url = $url . '.json'; + + if ( isset( $post->ID ) ) { + $html = $this->get_gist_html( $json_url, $attr ); + + if ( '{{unknown}}' === $html ) { + return; + /** @todo Get reference to $wp_embed. Global? */ + return $wp_embed->maybe_make_link( $url ); + } + + // If there was a result, return it. + if ( $html ) { + if ( $attr['embed_stylesheet'] ) { + wp_enqueue_style( 'github-gist' ); + } + + $html = apply_filters( 'blazersix_gist_embed_html', $html, $url, $attr, $post->ID ); + + foreach ( $attr as $key => $value ) { + $message = '' . $key . __(' (shortcode attribute)', 'blazer_six_gist_oembed') . ': '; + $message .= is_scalar( $value ) ? $value : print_r( $value, true ); + $this->debug_log( $message, $this->shortcode_hash( 'gist', $attr ) ); + } + $this->debug_log( 'Gist:
' . $html, $this->shortcode_hash( 'gist', $attr ) ); + + return $html; + } + } + return ''; + } + + /** + * Helper method to determine if a shortcode attribute is true or false. + * + * @since 1.1.0 + * + * @param string|int|bool $var Attribute value. + * + * @return bool + */ + public function shortcode_bool( $var ) { + $falsey = array( 'false', '0', 'no', 'n' ); + return ( ! $var || in_array( strtolower( $var ), $falsey ) ) ? false : true; + } + + /** + * Parses and expands the shortcode 'highlight' attribute and returns it + * in a usable format. + * + * @since 1.1.0 + * + * @param string $line_numbers Comma-separated list of line numbers and ranges. + * + * @return array|null List of line numbers, or null if no line numbers given + */ + public function parse_highlight_arg( $line_numbers ) { + if ( empty( $line_numbers ) ) { + return null; + } + + // Determine which lines should be highlighted. + $highlight = array_map('trim', explode( ',', $line_numbers )); + + // Convert any ranges. + foreach ( $highlight as $index => $num ) { + if ( false !== strpos( $num, '-' ) ) { + unset( $highlight[ $index ] ); + + $range = array_map( 'trim', explode( '-', $num ) ); + foreach ( range( $range[0], $range[1] ) as $line ) + array_push($highlight, $line); + } + } + return array_unique( $highlight ); + } + + /** + * Parses the shortcode 'lines' attribute into min and max values. + * + * @since 1.1.0 + * + * @param string $line_numbers Range of line numbers separated by a dash. + * + * @return array Array with min and max line numbers. + */ + public function parse_line_number_arg( $line_numbers ) { + if ( empty( $line_numbers ) ) { + return array( 'min' => 0, 'max' => 0, ); + } + + if ( false === strpos( $line_numbers, '-' ) ) { + $range = array_fill_keys( array( 'min', 'max', ), absint( trim( $line_numbers ) ) ); + } else { + $numbers = array_map( 'absint', array_map( 'trim', explode( '-', $line_numbers ) ) ); + + $range = array( + 'min' => $numbers[0], + 'max' => $numbers[1], + ); + } + + return $range; + } + + /** + * Retrieve Gist HTML. + * + * Gist HTML can come from one of three different sources: + * - Remote JSON endpoint. + * - Transient. + * - Post meta cache. + * + * When a Gist is intially requested, the HTML is fetched from the JSON + * endpoint and cached in a post meta field. It is then processed to limit + * line numbers, highlight specific lines, and add a few extra classes as + * style hooks. The processed HTML is then stored in a transient using a + * hash of the shortcodes attributes for the key. + * + * On subsequent requests, the HTML is fetched from the transient until it + * expires, then it is requested from the remote URL again. + * + * In the event the HTML can't be fetched from the remote endpoint and the + * transient is expired, the HTML is retrieved from the post meta backup. + * + * This algorithm allows Gist HTML to stay in sync with any changes GitHub + * may make to their markup, while providing a local cache for faster + * retrieval and a backup in case GitHub can't be reached. + * + * @since 1.1.0 + * + * @param string $url The JSON endpoint for the Gist. + * @param array $args List of shortcode attributes. + * + * @return string Gist HTML or {{unknown}} if it couldn't be determined. + */ + public function get_gist_html( $url, $args ) { + global $post; + + // Add a specific file from a Gist to the URL. + if ( ! empty( $args['file'] ) ) { + $url = add_query_arg( 'file', urlencode( $args['file'] ), $url ); + } + + $post_meta_key = '_gist_embed_' . md5( $url ); + $transient_key = 'gist_embed_' . $this->shortcode_hash( 'gist', $args ); + + $html = get_transient( $transient_key ); + + // Retrieve html from Gist JSON endpoint. + if ( empty( $html ) ) { + $this->debug_log( '' . __( 'Doing remote request:', 'blazersix-gist-oembed' ) . ' ' . $url, $this->shortcode_hash( 'gist', $args ) ); + $json = $this->fetch_gist( $url ); + + if ( ! empty( $json->div ) ) { + $html = $json->div; + } + + // Update the style sheet reference. + if ( ! empty( $json->stylesheet ) ) { + update_option( 'blazersix_gist_embed_stylesheet', $json->stylesheet ); + } + + // Failures are cached, too. Update the post to attempt to fetch again. + $html = ( $html ) ? $html : '{{unknown}}'; + $transient_expire = 60 * 60 * 24; + + if ( '{{unknown}}' != $html ) { + // Update the post meta fallback. + // @link http://core.trac.wordpress.org/ticket/21767 + update_post_meta( $post->ID, $post_meta_key, addslashes( $html ) ); + $html = $this->process_gist_html( $html, $args ); + $this->debug_log( '

' . __( 'Output Source: Remote Request', 'blazersix-gist-oembed' ) . '

', $this->shortcode_hash( 'gist', $args ) ); + } elseif ( $fallback = get_post_meta( $post->ID, $post_meta_key, true ) ) { + // Return the fallback instead of {{unknown}} + $html = $this->process_gist_html( $fallback, $args ); + + // Cache the fallback for an hour. + $transient_expire = 60 * 60; + $this->debug_log( '

' . __( 'Output Source: Post Meta Fallback', 'blazersix-gist-oembed' ) . '

', $this->shortcode_hash( 'gist', $args ) ); + } else { + $this->debug_log( '' . __( 'Remote call and transient failed and fallback was empty.', 'blazersix-gist-oembed' ) . '', $this->shortcode_hash( 'gist', $args ) ); + } + + // Cache the processed HTML. + set_transient( $transient_key, $html, $transient_expire ); + } else { + $this->debug_log( '

' . __( 'Output Source: Transient Cache', 'blazersix-gist-oembed' ) . '

', $this->shortcode_hash( 'gist', $args ) ); + } + + $this->debug_log( '' . __( 'JSON Endpoint:', 'blazersix-gist-oembed' ) . ' ' . $url, $this->shortcode_hash( 'gist', $args ) ); + $this->debug_log( '' . __( 'Post Meta Cache Key:', 'blazersix-gist-oembed' ) . ' ' . $post_meta_key, $this->shortcode_hash( 'gist', $args ) ); + $this->debug_log( '' . __( 'Transient Key:', 'blazersix-gist-oembed' ) . ' ' . $transient_key, $this->shortcode_hash( 'gist', $args ) ); + + return $html; + } + + /** + * Fetch Gist data from its JSON endpoint. + * + * @since 1.1.0 + * + * @param string $url Gist JSON endpoint. + * + * @return object|bool Gist JSON object or false. + */ + public function fetch_gist( $url ) { + $response = wp_remote_get( $url, array( 'sslverify' => false ) ); + + if ( 200 == wp_remote_retrieve_response_code( $response ) ) { + return json_decode( wp_remote_retrieve_body( $response ) ); + } + + return false; + } + + /** + * Process the HTML returned from a Gist's JSON endpoint based on settings + * passed through the shortcode. + * + * @since 1.1.0 + * + * @param string $html HTML from the Gist's JSON endpoint. + * @param array $args List of shortcode attributes. + * + * @return string Modified HTML. + */ + public function process_gist_html( $html, $args ) { + // Remove the line number cell if it has been disabled. + if ( ! $args['show_line_numbers'] ) { + $html = preg_replace( '#.*?#s', '', $html ); + } + + // Remove the meta section if it has been disabled. + if ( ! $args['show_meta'] ) { + $html = preg_replace( '#
.*?
#s', '', $html ); + } + + $lines_pattern = '#(]+>)(.+?)#s'; + preg_match( $lines_pattern, $html, $lines_matches ); + + if( ! empty( $lines_matches[2] ) ) { + // Restrict the line number display if a range has been specified. + if ( $args['show_line_numbers'] && $args['lines']['min'] && $args['lines']['max'] ) { + $html = $this->limit_gist_line_numbers( $html, $args['lines'] ); + } + + if ( ! empty( $args['highlight'] ) ) { + // Flip to use isset() when looping through the lines. + $highlight = array_flip( $args['highlight'] ); + } + + // Extract and cleanup the individual lines from the Gist HTML into an array for processing. + $lines = trim( $lines_matches[2] ); + $lines = preg_split( '#
[\s]*
#', substr( $lines, 5, strlen( $lines ) - 6 ) );
+
+			foreach ( $lines as $key => $line ) {
+				// Remove lines if they're not in the specified range and continue.
+				if ( ( $args['lines']['min'] && $key < $args['lines']['min'] - 1 ) || ( $args['lines']['max'] && $key > $args['lines']['max'] - 1 ) ) {
+					unset( $lines[ $key ] );
+					continue;
+				}
+
+				// Add classes for styling.
+				$classes = array( 'pre-line' );
+				$classes[] = ( $key % 2 ) ? 'pre-line-odd' : 'pre-line-even';
+				$style = '';
+
+				if ( isset( $highlight[ $key + 1 ] ) ) {
+					$classes[] = 'pre-line-highlight';
+
+					if ( ! empty( $args['highlight_color'] ) ) {
+						$style = ' style="background-color: ' . $args['highlight_color'] . ' !important"';
+					}
+				}
+
+				$prepend = '
';
+
+				$lines[ $key ] = $prepend . $line . '
'; + } + + $replacement = $lines_matches[1] . join( "\n", $lines ) . ''; + $html = preg_replace( $lines_pattern, $replacement, $html, 1 ); + } + + return $html; + } + + /** + * Removes line numbers from the Gist's HTML that fall outside the + * supplied range. + * + * @since 1.1.0 + * + * @param string $html HTML from the Gist's JSON endpoint. + * @param array $range Array of min and max values. + * + * @return string Modified HTML. + */ + public function limit_gist_line_numbers( $html, $range ) { + // Limit the line numbers that should show. + $line_num_pattern = '#()(.*?)#s'; + + preg_match( $line_num_pattern, $html, $line_num_matches ); + + if ( $line_num_matches[2] ) { + $line_numbers = array_slice( explode( "\n", trim( $line_num_matches[2] ) ), $range['min'] - 1, $range['max'] - $range['min'] + 1 ); + + $replacement = $line_num_matches[1] . join( "\n", $line_numbers ) . ''; + $html = preg_replace( $line_num_pattern, $replacement, $html, 1 ); + } + + return $html; + } + + /** + * Removes transients associated with Gists embedded in a post. + * + * Retrieves the keys of meta data associated with a post and deletes any + * transients with a matching embed key. + * + * @since 1.1.0 + * + * @param int $post_id Post ID. + * @param WP_Post $post_after Post object after update. + * @param WP_Post $post_before Post object before update. + */ + public function expire_gist_transients( $post_id, $post_after, $post_before ) { + $this->expire_transients = true; + + // Run the shortcodes to clear associated transients. + do_shortcode( $post_after->post_content ); + do_shortcode( $post_before->post_content ); + } + + /** + * Expire the transient associated with a particular shortcode so its HTML + * will be regenerated the next time it is requested. + * + * @since 1.1.0 + * + * @param array $args List of shortcode attributes. + */ + public function expire_gist_transient( $args ) { + $key = 'gist_embed_' . $this->shortcode_hash( 'gist', $args ); + set_transient( $key, null, -1 ); + } + + /** + * Wrapper for a PSR-3 compatible logger. + * + * If no logger has been set via the set_logger() method on an instance of + * this class, or WP_DEBUG is not enabled, then log messages quietly die + * here. + * + * @since 1.1.0 + * + * @param string $message A message to log for the current shortcode. + * @param mixed $id An ID under which the message should be grouped. + */ + protected function debug_log( $message, $id = null ) { + if ( defined( 'WP_DEBUG' ) && WP_DEBUG && isset( $this->logger ) ) { + $this->logger->debug( $message, array('key' => $id ) ); + } + } + + /** + * Sort a shortcode's attributes by name and hash it for use as a cache + * key and logger message grouping. + * + * @since 1.1.0 + */ + protected function shortcode_hash( $tag, $args ) { + ksort( $args ); + return md5( $tag . '_' . serialize( $args ) ); + } +} From 6a7f87d79a9c88abab9c96e36c12cfb759235395 Mon Sep 17 00:00:00 2001 From: GaryJones Date: Sun, 30 Dec 2012 15:59:22 +0000 Subject: [PATCH 2/4] Revert version bump to 1.2.0, as 1.1.0 has not been released yet. --- blazer-six-gist-oembed.php | 2 +- class-blazer-six-gist-oembed-debug-bar-panel.php | 2 +- class-blazer-six-gist-oembed-log.php | 6 +++--- class-blazer-six-gist-oembed.php | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/blazer-six-gist-oembed.php b/blazer-six-gist-oembed.php index 169c50f..a2cface 100644 --- a/blazer-six-gist-oembed.php +++ b/blazer-six-gist-oembed.php @@ -3,7 +3,7 @@ * Plugin Name: Blazer Six Gist oEmbed * Plugin URI: https://github.com/bradyvercher/wp-blazer-six-gist-oembed * Description: Gist oEmbed and shortcode support with caching. - * Version: 1.2.0 + * Version: 1.1.0 * Author: Blazer Six, Inc. * Author URI: http://www.blazersix.com/ * License: GPLv2 or later diff --git a/class-blazer-six-gist-oembed-debug-bar-panel.php b/class-blazer-six-gist-oembed-debug-bar-panel.php index e009269..f230d2a 100644 --- a/class-blazer-six-gist-oembed-debug-bar-panel.php +++ b/class-blazer-six-gist-oembed-debug-bar-panel.php @@ -24,7 +24,7 @@ class Blazer_Six_Gist_oEmbed_Debug_Bar_Panel extends Debug_Bar_Panel { /** * Assign properties, and call parent constructor. * - * @since 1.2.0 + * @since 1.1.0 * * @param object $logger */ diff --git a/class-blazer-six-gist-oembed-log.php b/class-blazer-six-gist-oembed-log.php index 922c015..078fadf 100644 --- a/class-blazer-six-gist-oembed-log.php +++ b/class-blazer-six-gist-oembed-log.php @@ -62,7 +62,7 @@ class Blazer_Six_Gist_oEmbed_Log { /** * Detailed debug information. * - * @since 1.2.0 + * @since 1.1.0 * * @uses Blazer_Six_Gist_oEmbed_Log::log() * @@ -83,7 +83,7 @@ public function debug( $message, array $context = array() ) { * Note that at this point in time, we don't actually do anything with the * $level argument. * - * @since 1.2.0 + * @since 1.1.0 * * @param mixed $level * @param string $message @@ -101,7 +101,7 @@ public function log( $level, $message, array $context = array() ) { * * Under PSR-1, this method would be called getLogs(). * - * @since 1.2.0 + * @since 1.1.0 * * @return array */ diff --git a/class-blazer-six-gist-oembed.php b/class-blazer-six-gist-oembed.php index f9cbc7c..9182c3b 100644 --- a/class-blazer-six-gist-oembed.php +++ b/class-blazer-six-gist-oembed.php @@ -41,7 +41,7 @@ class Blazer_Six_Gist_oEmbed { * * @see https://github.com/php-fig/log/blob/master/Psr/Log/LoggerAwareInterface.php * - * @since 1.2.0 + * @since 1.1.0 * * @param object $logger */ @@ -54,7 +54,7 @@ public function set_logger( $logger ) { * * Under PSR-1, this method would be called getLogger(). * - * @since 1.2.0 + * @since 1.1.0 * * @return object */ From 03fde4d15f79382c45e373d96a3dc15c147279ff Mon Sep 17 00:00:00 2001 From: GaryJones Date: Sun, 30 Dec 2012 18:10:53 +0000 Subject: [PATCH 3/4] Refactor main plugin file. The instantiation of main and logging class is kept in the global scope, but the injection of the logging class into the main class, and the calling of the main class run() method that hooks in all of the other actions and filters, is now done within a function hooked to the init action. The loading of the plugin text domain / localization is also improved, and hooked into the init action as well (but in a different function). --- blazer-six-gist-oembed.php | 42 ++++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/blazer-six-gist-oembed.php b/blazer-six-gist-oembed.php index a2cface..79df3d8 100644 --- a/blazer-six-gist-oembed.php +++ b/blazer-six-gist-oembed.php @@ -16,20 +16,46 @@ * @license GPL-2.0+ */ -// Support localization -load_plugin_textdomain( 'blazersix-gist-oembed', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' ); - -// Set up main plugin and logging classes +// Instantiate main plugin class if ( ! class_exists( 'Blazer_Six_Gist_oEmbed' ) ) require( plugin_dir_path( __FILE__ ) . 'class-blazer-six-gist-oembed.php' ); +$gist_oembed = new Blazer_Six_Gist_oEmbed; +// Instantiate logging class if ( ! class_exists( 'Blazer_Six_Gist_oEmbed_Log' ) ) require( plugin_dir_path( __FILE__ ) . 'class-blazer-six-gist-oembed-log.php' ); - -$gist_oembed = new Blazer_Six_Gist_oEmbed; $gist_oembed_logger = new Blazer_Six_Gist_oEmbed_Log; -$gist_oembed->set_logger( $gist_oembed_logger ); -$gist_oembed->run(); + +add_action( 'init', 'blazer_six_gist_oembed_localization' ); +/** + * Support localization for plugin. + * + * @see http://www.geertdedeckere.be/article/loading-wordpress-language-files-the-right-way + * + * @since 1.1.0 + */ +function blazer_six_gist_oembed_localization() { + $domain = 'blazersix-gist-oembed'; + // The "plugin_locale" filter is also used in load_plugin_textdomain() + $locale = apply_filters( 'plugin_locale', get_locale(), $domain ); + load_textdomain( $domain, WP_LANG_DIR . '/my-plugin/' . $domain . '-' . $locale . '.mo' ); + load_plugin_textdomain( $domain, false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' ); +} + +add_action( 'init', 'blazer_six_gist_oembed_init' ); +/** + * Set plugin logger class and initialise plugin. + * + * If you want a different logger class, then unhook this function, and hook + * in your own which does what you need. + * + * @since 1.1.0 + */ +function blazer_six_gist_oembed_init() { + global $gist_oembed, $gist_oembed_logger; + $gist_oembed->set_logger( $gist_oembed_logger ); + $gist_oembed->run(); +} /** @todo Can the following two functions be made static methods of the debug * bar class, since there is already an inherent dependency? */ From e72022ce9619298f23de7ae01c105d0ce7cc747d Mon Sep 17 00:00:00 2001 From: GaryJones Date: Sun, 30 Dec 2012 18:16:04 +0000 Subject: [PATCH 4/4] Add if / foreach optional braces in, to match existing coding style (with one eye on PSR-2). --- blazer-six-gist-oembed.php | 12 ++++++++---- class-blazer-six-gist-oembed.php | 5 +++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/blazer-six-gist-oembed.php b/blazer-six-gist-oembed.php index 79df3d8..cd67ad4 100644 --- a/blazer-six-gist-oembed.php +++ b/blazer-six-gist-oembed.php @@ -17,13 +17,15 @@ */ // Instantiate main plugin class -if ( ! class_exists( 'Blazer_Six_Gist_oEmbed' ) ) +if ( ! class_exists( 'Blazer_Six_Gist_oEmbed' ) ) { require( plugin_dir_path( __FILE__ ) . 'class-blazer-six-gist-oembed.php' ); +} $gist_oembed = new Blazer_Six_Gist_oEmbed; // Instantiate logging class -if ( ! class_exists( 'Blazer_Six_Gist_oEmbed_Log' ) ) +if ( ! class_exists( 'Blazer_Six_Gist_oEmbed_Log' ) ) { require( plugin_dir_path( __FILE__ ) . 'class-blazer-six-gist-oembed-log.php' ); +} $gist_oembed_logger = new Blazer_Six_Gist_oEmbed_Log; add_action( 'init', 'blazer_six_gist_oembed_localization' ); @@ -68,8 +70,9 @@ function blazer_six_gist_oembed_init() { * @return null Return early if Debug Bar plugin not enabled. */ function blazer_six_gist_oembed_add_debug_bar_panel_support() { - if ( ! class_exists( 'Debug_Bar' ) || is_admin() || ! is_admin_bar_showing() ) + if ( ! class_exists( 'Debug_Bar' ) || is_admin() || ! is_admin_bar_showing() ) { return; + } add_filter( 'debug_bar_panels', 'blazer_six_gist_oembed_add_debug_bar_panel' ); } @@ -84,8 +87,9 @@ function blazer_six_gist_oembed_add_debug_bar_panel_support() { function blazer_six_gist_oembed_add_debug_bar_panel( array $panels ) { global $gist_oembed_logger; // wp_die('panel being added'); - if ( ! class_exists( 'Blazer_Six_Gist_oEmbed_Debug_Bar_Panel' ) ) + if ( ! class_exists( 'Blazer_Six_Gist_oEmbed_Debug_Bar_Panel' ) ) { require( plugin_dir_path( __FILE__ ) . 'class-blazer-six-gist-oembed-debug-bar-panel.php' ); + } $panels[] = new Blazer_Six_Gist_oEmbed_Debug_Bar_Panel( $gist_oembed_logger ); return $panels; } diff --git a/class-blazer-six-gist-oembed.php b/class-blazer-six-gist-oembed.php index 9182c3b..a19ba67 100644 --- a/class-blazer-six-gist-oembed.php +++ b/class-blazer-six-gist-oembed.php @@ -269,8 +269,9 @@ public function parse_highlight_arg( $line_numbers ) { unset( $highlight[ $index ] ); $range = array_map( 'trim', explode( '-', $num ) ); - foreach ( range( $range[0], $range[1] ) as $line ) + foreach ( range( $range[0], $range[1] ) as $line ) { array_push($highlight, $line); + } } } return array_unique( $highlight ); @@ -440,7 +441,7 @@ public function process_gist_html( $html, $args ) { $lines_pattern = '#(]+>)(.+?)#s'; preg_match( $lines_pattern, $html, $lines_matches ); - if( ! empty( $lines_matches[2] ) ) { + if ( ! empty( $lines_matches[2] ) ) { // Restrict the line number display if a range has been specified. if ( $args['show_line_numbers'] && $args['lines']['min'] && $args['lines']['max'] ) { $html = $this->limit_gist_line_numbers( $html, $args['lines'] );