Adding A Stats Column For Jetpack

A feature that I feel is missing from JetPack, is an easy way to view stats for each post.

The data is available there, so we only have to link to it.

Here’s an example for post ID 549:

http://example.tld/wp-admin/admin.php?page=stats&view=post&post=549

We can use the chart bar dashicon to display it:

dashicon-chart-bar

There are other icons available, like the chart line and chart area icons.

Here’s a plugin to achieve this (PHP 5.4+):

/**
 * Plugin Name: My Jetpack Stats Column
 * Plugin URI:  http://xlino.com/projects/adding-a-stats-column-for-jetpack/
 * Author:      Birgir Erlendsson (birgire)
 * Description: Adds a stats column to the posts table on the edit.php screen
 * Version:     1.0.1
 */
 
add_action( 'admin_init', function()
{
    // Check if Jetpack Stats module is active
    if( ! class_exists( 'Jetpack' ) || ! Jetpack::is_module_active( 'stats' ) )
        return;
	
    // Initialize for the 'post' and 'page' tables
    if( ! class_exists( 'MyJetPackStatsColumn' ) )
        return;		
    $o = new MyJetPackStatsColumn;	
    $o->init( [ 'post', 'page' ] );

} );

class MyJetPackStatsColumn
{	
    public function init( array $cpts )
    {
        foreach( (array) $cpts as $cpt )
        {
            $cpt = sanitize_key( $cpt );
            add_action( "manage_{$cpt}_posts_custom_column", 	[ $this, 'column' ], 10, 2 );
            add_filter( "manage_{$cpt}_posts_columns", 			[ $this, 'columns' ] ); 		 
        }
		
        if( is_array( $cpts ) && ! empty( $cpts) )
            add_action( 'admin_print_styles-edit.php', 			[ $this, 'style' ] );
    }

    public function columns( array $columns )
    {
        $columns['my_jetpack_stats'] = '';    
        return $columns;
    }
	
    public function column( $column, $post_id )
    {
        if( 'my_jetpack_stats' !== $column )
            return;
		
        printf(
            '<a href="%s%d" target="_blank" title="%s"><span class="dashicons dashicons-chart-bar"></span></a>',
            esc_url( admin_url( 'admin.php?page=stats&view=post&post=' ) ),
            (int) $post_id,
            esc_attr( __( 'Stats' ) )
        );			
    }

    public function style()
    {
        ?><style> .column-my_jetpack_stats { width: 30px; }</style><?php 
    }
		
} // end class

where we can adjust the [ ‘post’, ‘page’ ] part to our needs.

Adding a section to the Omnisearch in Jetpack with a custom posts search

Let’s say we need to add an extra section to the Omnisearch in Jetpack with a custom posts search.

Like shown in this screenshot:

my_posts_omnisearch

where I’ve moved the custom section to the top with:

/**
 * Move our 'My Posts' section to the top of the Omnisearch results page
 */
add_filter( 'omnisearch_results', function( $results, $search_term )
{
    $key = 'My Posts';
    if( isset( $results[$key] ) )
    {
        $tmp = $results[$key];
        unset( $results[$key] );		
        return [ $key => $tmp ] + $results;
    }
    return $results;
}, 99, 2 );

for better visibility.

Instead of extending the:

WP_List_Table

class, I prefer to extend the:

Jetpack_Omnisearch_Posts

class to simplify things, where we only need to override the search() method.

This is what we could start with, based on Jetpack_Omnisearch_Posts:

class My_Jetpack_Omnisearch_Posts extends Jetpack_Omnisearch_Posts 
{
    public function search( $results, $search_term ) 
    {
        if( ! post_type_exists( $this->post_type ) )
            return $results;

        parent::__construct();

        $this->post_type_obj = get_post_type_object( $this->post_type );

        // Custom posts query
        $args = [
            'post_type'        => $this->post_type,
            'posts_per_page'   => apply_filters( 'omnisearch_num_results', 5 ),
            'post_status'      => 'any',
            'suppress_filters' => false,
            'meta_query'       => [
                [ 
                    'key'       => 'some_meta_key',
                    'value'     => $search_term,
                    'compare'   => 'LIKE'
                ]
            ]
        ];
        $this->posts = get_posts( $args );
			
        $this->prepare_items();

        // Section's title
        $html = '<h2>My ' . esc_html( $this->post_type_obj->labels->name ) . '</h2>';

        // Section's table output
        ob_start();
        $this->display();
        $html .= ob_get_clean();

        $results[ $this->post_type_obj->labels->name ] = $html;

        return $results;
    }		
} // end class

But this doesn’t work for two reasons:

1) We need to take care of screen object, used in the display() method, namely this line:

$this->screen->render_screen_reader_content( 'heading_list' ); 

This screen object can be set in the constructor input, but Jetpack_Omnisearch_Posts only calls:

parent::__construct()

where the screen object is set to null by default.

One way around this is to set it as the current screen object:

$this->screen = get_current_screen();

2) Another thing is that we are overriding the post section’s results with:

$results[ $this->post_type_obj->labels->name ] = $html;

To avoid that, we need a unique custom key, like:

$results[ 'My ' . $this->post_type_obj->labels->name ] = $html;

This also generates the “jump to” link label.

 

So let’s rewrite the My_Jetpack_Omnisearch_Posts class accordingly and wrap it in a simple plugin (PHP 5.4+). We can use the omnisearch_add_providers hook to add our custom section:

<?php
/**
 * Plugin Name: My Custom Omnisearch Section For Posts
 * Description: An example how to extend the Jetpack_Omnisearch_Posts class for a custom posts query
 * Plugin URI:  http://xlino.com/projects/adding-a-section-to-the-omnisearch-in-jetpack-with-a-custom-post-search/
 * Author:      Birgir Erlendsson (birgire) 
 * Version:     1.0.1
 */

add_action( 'omnisearch_add_providers', function()
{
    if( class_exists( 'My_Jetpack_Omnisearch_Posts ' ) )
        return;

    class My_Jetpack_Omnisearch_Posts extends Jetpack_Omnisearch_Posts 
    {
        public function search( $results, $search_term ) 
        {
            if( ! post_type_exists( $this->post_type ) )
                return $results;

            parent::__construct();
            
            $this->post_type_obj = get_post_type_object( $this->post_type );

            // Custom posts query
            $args = [
                'post_type'        => $this->post_type,
                'posts_per_page'   => apply_filters( 'omnisearch_num_results', 5 ),
                'post_status'      => 'any',
                'suppress_filters' => false,
                'meta_query'       => [
                    [ 
                        'key'     => 'some_meta_key',
                        'value'   => $search_term,
                        'compare' => 'LIKE'
                    ]
                ]
            ];
            $this->posts = get_posts( $args );
			
            $this->prepare_items();

            // Set the screen object. This is needed for the display() to work
            // because display() includes $this->screen->render_screen_reader_content( 'heading_list' );
            // and Jetpack_Omnisearch_Posts only calls parent::__construct() and the  'screen' is null by default,
            $this->screen = get_current_screen();

            // Section's title
            $html = '<h2>My ' . esc_html( $this->post_type_obj->labels->name ) . '</h2>';

            // Section's table output
            ob_start();
            $this->display();
            $html .= ob_get_clean();

            // We need to change the array key to avoid overriding existing post results
            $results[ 'My ' . $this->post_type_obj->labels->name ] = $html;

            return $results;
        }		
    } // end class

    $obj = new My_Jetpack_Omnisearch_Posts ( 'post' );
} );

where you must adjust the custom query arguments $args to your needs.

If the Jetpack_Omnisearch_Posts class wouldn’t add a filter in the class constructor, it would make it easier to work with.

How to allow audio files with GET parameters in the audio shortcode?

There’s an interesting question on WordPress Stackexchange regarding the audio shortcode .

It looks like it doesn’t support sources with GET parameters.

Here are two examples:

1) Without GET parameters the audio player displays:

where the shortcode is:

[audio src="http://s.w.org/images/core/3.9/JellyRollMorton-BuddyBoldensBlues.mp3"]

2) With GET parameters it only shows the audio link:

http://s.w.org/images/core/3.9/JellyRollMorton-BuddyBoldensBlues.mp3?play=1

where the shortcode is:

[audio src="http://s.w.org/images/core/3.9/JellyRollMorton-BuddyBoldensBlues.mp3?play=1"]

I managed to work around it with the following code snippet:

/**
 * Allow unrecognize audio sources hosted on 's.w.org'.
 *
 * @see http://wordpress.stackexchange.com/a/152352/26350
 */

add_filter( 'wp_audio_shortcode_override',
    function( $html, $atts )
    {
        if( 's.w.org' === parse_url( $atts['src'], PHP_URL_HOST ) )
        {
            add_filter( 'wp_audio_extensions', 'wpse_152316_wp_audio_extensions' );
        }
        return $html;
    }
, PHP_INT_MAX, 2 );

function wpse_152316_wp_audio_extensions( $ext )
{
    remove_filter( current_filter(), __FUNCTION__ );
    $ext[] = '';
    return $ext;
}

A simple plugin to run JetPack locally

We don’t need to modify the wp-config.php file as before with:

define ('JETPACK_DEV_DEBUG', true);

to run JetPack in the development mode.

By using the jetpack_development_mode filter described here, we can construct this simple plugin:

<?php
/**
 * Plugin Name: Run JetPack locally
 * Plugin URI:  https://github.com/birgire/run-jetpack-locally
 * Author:      birgire
 * Version:     0.0.3
 */

// Activate the development mode:
add_filter( 
    'jetpack_development_mode', 
    '__return_true', 
    PHP_INT_MAX 
);

to make your life easier 😉 and it’s available on GitHub.

This class method of the JetPack class contains the filter:

 
	/**
	 * Is Jetpack in development (offline) mode?
	 */
	public static function is_development_mode() {
		$development_mode = false;

		if ( defined( 'JETPACK_DEV_DEBUG' ) ) {
			$development_mode = JETPACK_DEV_DEBUG;
		}

		elseif ( site_url() && false === strpos( site_url(), '.' ) ) {
			$development_mode = true;
		}

		return apply_filters( 'jetpack_development_mode', $development_mode );
	}

WordPress: Playlist shortcode with external audio or video files

The new [playlist] shortcode  in WordPress 3.9 is great, but we can’t use it for external files.

Here’s a solution I wrote on WPSE to use it for external files.

The plugin is hosted on GitHub.

Here’s version 0.05b for those who use PHP 5.1.x or PHP 5.2.x.

WordPress 4.0 support:

Please update to version 0.0.5+ of the plugin due to some small playlist changes in WordPress 4.0. Thanks.

Version 0.0.6, now supports with autoplay and the GitHub Updater support. It works on WordPress 4.0+.

Version 0.0.7 introduced the new shortcodes [_playlist] and [_track] and the support for the shortcodes [wpse_playlist] and [wpse_trac] will phase out.

The current version is 0.0.9.

Example 1a

This audio playlist:

is generated by the following shortcode:

[_playlist type="audio" tracklist="true" tracknumbers="true" images="true" artist="true"]
    [_track title="Ain't Misbehavin'" src="//s.w.org/images/core/3.9/AintMisbehavin.mp3" type="audio/mpeg" caption="" description="" image="" meta_artist="Louis Armstrong and His Orchestra" meta_album="78 RPMs and Cylinder Recordings" meta_genre="" meta_length_formatted="3:21" image_src="//s.w.org/images/core/3.9/louis.jpg" image_width="308" image_height="240" thumb_src="//s.w.org/images/core/3.9/louis.jpg" thumb_width="308" thumb_height="240"]
    [_track title="Buddy Bolden's Blues" src="//s.w.org/images/core/3.9/JellyRollMorton-BuddyBoldensBlues.mp3" type="audio/mpeg" caption="" description="" image="" meta_artist="Jelly Roll Morten" meta_album="78 RPMs and Cylinder Recordings" meta_genre="Jazz" meta_length_formatted="2:09"]
[/_playlist]

Example 1b

Here’s the playlist from example 1a, but with

tracknumbers="false" images="false"

Example 2

Here’s the vanilla version of the above audio playlist:

generated from the shortcode:

[_playlist type="audio" current="no" tracklist="yes" tracknumbers="no" images="no" artist="no"]
    [_track title="Davenport Blues" src="//s.w.org/images/core/3.9/DavenportBlues.mp3"]
    [_track title="Dixie Blues" src="//s.w.org/images/core/3.9/Louisiana_Five-Dixie_Blues-1919.mp3"]
[/_playlist]

Example 3

Here’s a video playlist:

from

[_playlist type="video"]
    [_track caption="Live widgets previews in WordPress 3.9" src="//s.w.org/images/core/3.9/widgets.mp4" image_src="/projects/wp-content/uploads/2014/04/widgets_screen.gif"]
[/_playlist]

Happy listening!

Changelog:

0.0.8 (2015-08-02)
– Fixed: Issue #7 by using true/false instead of 1/0 (Props: @X-PRESSIVE)

0.0.7 (2015-03-08)
– Added: New shortcodes [_playlist] and [_track].
– Deprecated: The support for the shortcodes [wpse_playlist] and [wpse_trac] will phase out.

0.0.6 (2015-03-07)
– Added: Support for the autoplay attribute.
– Added: Support for the GitHub Updater. (Probs: @BlaineMoore)
– Added: More info in the README.md file.
– Changed: Replaced filter_var() with wp_validate_boolean(), so we need WordPress 4.0+.
– Changed: Refactoring regarding late escaping.
– Fixed: Some minor adjustments.

0.0.5
– Fixed: added the missing script class to support WordPress 4.0 (Props: @ruLait)
 

WordPress developer links

Here are some useful links:

WordPress Codex:
http://codex.wordpress.org/Main_Page

Plugin API/Action Reference:
http://codex.wordpress.org/Plugin_API/Action_Reference

WordPress.org Forums:
http://wordpress.org/support/

WordPress Planet:
http://planet.wordpress.org/

Plugin Directory:
http://wordpress.org/extend/plugins/

Make WordPress:
http://make.wordpress.org/

WordPress TV:
http://wordpress.tv/

WordPress Answers:
http://wordpress.stackexchange.com/

Why is my WordPress so slow? How to identify the problems

Image from  http://en.wikipedia.org/wiki/SnailIt’s difficult to give an answer to this kind of question, so I will list general things that have helped me to identify problems of this kind:

Profile everything you can:

The P3 Profiler plugin will profile your plugins:

http://wordpress.org/extend/plugins/p3-profiler/

I also recommend the `Debug bar` (and it’s extensions)

http://wordpress.org/extend/plugins/debug-bar/

and the `Blackbox` debug bar to profile your *problematic?* queries:

http://wordpress.org/extend/plugins/blackbox-debug-bar/

Memory viewer for your hooks:

http://wordpress.org/extend/plugins/memory-viewer/

If you have lot’s of comment’s that are constantly refreshing the cache,
you might also consider using comment services like Facebook / Intense Debate / Disqus / LiveFyre

Use WordPress cache plugins, many good out there.

Test your site with online performance services:

Here is one:

http://www.webpagetest.org/

New Relic:

http://newrelic.com/

View the log files:

Check all log files you have (php, apache, nginx, mysql, …)

If you have ssh access:

Also consider using `Nginx` instead of `Apache`

You should also check what process is using your resources via ps/top/htop/… (i.e. is it apache or mysql or something else) if you have access to a Linux console

If it’s mysql there is a great script called tuning-primer.sh that can give you hints how to configure it (if you have access to ssh, you can check it with top or htop)

Use APC cache for php

(this article is a work in progress)

Pizza order time – content with weekly opening hours

This wordpress.org forum thread:

http://wordpress.org/support/topic/how-to-close-pizza-order-site-or-page-outside-business-hours

gave me an idea of a shortcode that can display content during given opening hours in the week and can handle shortcodes inside it.

Maybe this will grow into a plugin in the near future 😉

The shortcode is

[openinghours] ... some content only available during opening hours ... [/openinghours]

and here is an example of the supported (optional) attributes:

sun="11:00-22:30" // the opening hours every sunday
mon="09:00-23:00" 
tue="09:00-15:00" 
wed="09:00-23:00" 
thu="10:15-14:59" 
fri="09:00-23:00" 
sat="09:00-24:00" 
debug="0" // debug="1" will display debug information
sorry="Sorry, we are closed now. Try again during the opening hours." // text to display when it's closed
holidays="2012-07-04,2012-12-24"  // YYYY-MM-DD, YYYY-MM-DD,...

Here is a full working example (closed every sunday and saturday and open from 09-17 mon-fri. Holidays are 2012-07-04 and 2012-12-24):

[openinghours mon="09:00-17:00" tue="09:00-17:00" wed="09:00-17:00" thu="09:00-17:00" fri="09:00-17:00"  debug="0" holidays="2012-07-04,2012-12-24" sorry="Sorry, we are closed now. Try again during the opening hours."] please order now! [foobar]
[/openinghours]

Here is the code to paste into the functions.php file:

/**
 * Plugin Name: Opening hours - shortcode
 * Description: A [openinghours] shortcode to display content only on given weekly opening hours.
 * Author:      birgire
 * Author URI:  http://xlino.com
 * Version:     1.1
 */

function my_openinghours( $atts = array() ,$content = '' ){
        extract( shortcode_atts( array(
                'sun'      => '', // the opening hours every sunday
                'mon'      => '', // the opening hours every monday
                'tue'      => '',
                'wed'      => '',
                'thu'      => '',
                'fri'      => '',
                'sat'      => '',
                'sorry'    => '', // text to display when it's closed
                'debug'    => '0', // debug="1" will display debug information
                'holidays' => '', // fx: holidays="2012-07-04,2012-12-24", the format is YYYY-MM-DD, YYYY-MM-DD,...
        ), $atts ) );

        // Current time:
        $currT      = time();
        $currYMD    = date( "Y-m-d",       $currT );
        $currYMDHIS = date( "Y-m-d H:i:s", $currT );
        $currWD     = date( "w",           $currT );

        // Get the opening hours for today:
        switch ($currWD ){
                case 0:
                        $s = $sun;
                        break;
                case 1:
                        $s = $mon;
                        break;
                case 2:
                        $s = $tue;
                        break;
                case 3:
                        $s = $wed;
                        break;
                case 4:
                        $s = $thu;
                        break;
                case 5:
                        $s = $fri;
                        break;
                case 6:
                        $s = $sat;
                        break;
                default:
                        $s = "";
        }

        // format input
        $out      = esc_attr( $sorry );
        $holidays = esc_attr( $holidays );
        $s        = esc_attr( trim( $s ) );
        $debug    = (int) $debug;

        // do the time checks
        if( strlen( $s ) > 0 ){
                $a = explode( "-", $s );
                if( count( $a ) == 2 ){
                        $from  = $currYMD." ".$a[0].":00";
                        $to    = $currYMD." ".$a[1].":00";
                        $fromT = strtotime( $from );
                        $toT   = strtotime ($to );
                        if( $currT > $fromT && $currT < $toT && false === strpos( $holidays, $currYMD ) ){
                                $out = do_shortcode( $content );
                        }
                }
        }

        // Check if we have debug mode on:
        if( $debug ){
                $out .= "<div style='background-color:#ccc;padding:10px;border:1px solid #333;margin:10px;'>Debug: <br/>";
                $out .= sprintf( "from: %s <br/> current: %s <br/> to: %to </div>", $from, $currYMDHIS, $to );
        }
        return $out;
}

add_shortcode("openinghours","my_openinghours");

bon appetit 😉

changelog:

version 1.1
added holidays

version 1.0
inital version (Props: @lemonadejoex, @keesiemeijer)

Facebook Debug Links – wordpress plugin

Download the plugin here:
http://wordpress.org/extend/plugins/facebook-debug-links/





This plugin adds a link in the row-action section to view the page/post in the Facebook Debugger:

This plugin adds a metabox on the post/page screen:

Only published posts/pages will get Facebook Debug links.

YouList – Youtube playlist shortcode – wordpress plugin

Download the plugin here:
http://wordpress.org/extend/plugins/youlist/

Support Open Source developement:




The plugin supports both single and multi site WordPress installs.

Reviews:

How to use this plugin?
When you have installed it, you can for example start by pasting this shortcode into your WordPress post editor:

[youlist pid="PL3FF15AA7ED356D9F"]

like this:

yl

and preview the post. Then you can change the pid value to match your own playlist.

The above pid value is taken from this playlist url:

http://www.youtube.com/playlist?list=PL3FF15AA7ED356D9F

You can then try out the usage examples given below or here:

http://wordpress.org/extend/plugins/youlist/

There is a small playlist icon in the bottom right corner

Showcases
http://cjgrasso.com/world16 (responsive and with hd playback)

Few usage examples:

Example: Adele YouTube playlist with thumbnails

[youlist pid="PL3FF15AA7ED356D9F"]


Example: Adele YouTube playlist with custom styling

[youlist pid="PL3FF15AA7ED356D9F" style="width:400px;height:300px;z-index:1;border:2px solid gold;background-color:orange;padding:10px;"]


Example: Playlist from the search string “higgs boson”

[youlist search="higgs boson"]


Example: Playlist for uploaded videos by the user “mismag822”

[youlist uid="mismag822" listtype="user_uploads"]


Example: Playlist for favorite videos of the user “mismag822”

[youlist uid="mismag822" listtype="user_favorites"]


Example: Three YouTube videos with thumbnails

[youlist vid="ZF_K8D414-Y,sFHXWoawnt0,E2uOGOqIyC4"]


Example: Three YouTube videos without thumbnails

[youlist vid="ZF_K8D414-Y,sFHXWoawnt0,E2uOGOqIyC4" showinfo="0" ]