Category: PHP

Add additional links on the plugins overview page

You know the usual plugin overview page. You have a small description of the plugins, the version, the author, a link to the plugins page and so on. On the left side, you will find the activation and deactivation links as well as the edit links. But from time to time, you will find plugins, which added a settings link or something similar to this line.

This is sometimes useful. Especially for plugins which do not need a lot of attention an additional list point in the admin menu is rather disturbing. In such a case a settings link on the plugin overview page is rather useful. But, how do you add such a link?

I searched a bit, how such plugins add these links and I found the filter hook “plugin_action_links“. This filter contains an array of all action links, which are shown on the overview page. Since these links differ from plugin to plugin, you have to adjust the filter for your own plugin by extending the filter name with the basename of your plugin:

add_filter( 'plugin_action_links_' . plugin_basename(__FILE__), 'function_to_execute' );

This filter function accepts now the array of links and you can alter the links as you wish. In my example code, I add a link to my web page:

But how to create a admin page which doesn’t appear in the menue? You can use add_submenu_page() and define the parent slug as ‘null’:

add_submenu_page( null, 'Title', 'Title', 'manage_options', 'invisible', 'render_function' );

This page can be reached via the URL wp-admin/?page=invisible, so by simply adding the GET-Parameter page with the page slug.

How to create a help menu in WordPress

Create your own help tab

In this post, I want to address the question, how you can use and extend the help menu, you find in the admin dashboard of WordPress. Usually, this help menu can be found in the top right corner and it gives the user some hints and tips, how to use the current page.

Create help tab
Create help tab

So, lets start with the basics: We have to register a new menu page:

As you know, add_menu_page() returns the ID of the newly created page, which we will need, since we want to hook into the specific load event for this page. We do so by using

add_action( 'load-' . $page_with_help, 'pwh_add_help_tab' );

In this action, we will be able to register our help tab and give it some contents.

Adding the help tab

In the first step, we need to acquire the current screen. This is be done by get_current_screen(). This function returns the current WP_Screen object, which contains – beside others – the help tabs.

In a second step, we will now add our own help tabs, by using WP_Screen::add_help_tab(). A help tab contains an ID, a title and the actual content.

With these two simple steps, we can create our own help tabs, which blend perfectly into WordPress. We can also name a callback function, which does the rendering:

With such a callback function, we have more possibilities to render the output. The callback function can receive two parameters: The current WP_Screen object and the current help tab array:

Creating Sidebars

Once you have registered a help tab, you can also register a sidebar for the help. This is done quite simple by set_help_sidebar(). Once, you open the help section, you can see the sidebar on the right side of it:

It is very simple to add a little bit more help for the users of your plugins. So, just do it 🙂

SSL Connection Reset By Peer in PayPal IPN

It looks like some time ago, Paypal made some changes to its Instant Payment Notification System (IPN). With IPN Paypal notifies shop systems about successful payments. In the documentation, you will find an example script, how this works.

The only problem with this script is, it doesn’t work right now. You will find the reason in the headers, you send to Paypal. The example script states:

  $header  = "POST /cgi-bin/webscr HTTP/1.1\r\n";                    // HTTP POST request
  $header .= "Content-Type: application/x-www-form-urlencoded\r\n";
  $header .= "Content-Length: " . strlen($req) . "\r\n\r\n";

If you just use this headers, Paypal will reset the connection and the error message you get is SSL Connection Reset By Peer.

What to do? You have to extend the headers and tell Paypal explicitly to close the connection and which host you want to reach:

	$header = "POST /cgi-bin/webscr HTTP/1.1\r\n";
	$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
	$header .= "Content-Length: " . strlen($req) . "\r\n";
	$header .= "Connection: Close\r\n";
	$header .= "Host: www.paypal.com\r\n\r\n";

If you are using the sandbox, you have to use www.sandbox.paypal.com as host.

With this script IPN runs again and gives you your desired feedback.

WordPress Order by Price with two currencies

A client of mine runs a WordPress site where user can add products. The prices are stored as a postmeta value. In a second postmeta it is saved, in which currency the price is saved. So the user cann add the price as US-Dollar or Euro.

The client now asked if it is possible to order the list by price ascending and price descending. So usually, we would alter our WP_Query like this:

$args = array(
	'post_type'	=> 'products',
	'meta_key'	=> 'price',
	'orderby'	=> 'meta_value_num',
	'order'		=> 'ASC'
);
$query = new WP_Query( $args );

But in this particular case, the meta_value field was not consistent. This approach would lead to problems. Lets say, you have the following products and prices:

Product Price Converted
A 10,00 EUR 13,51 USD
B 12,00 USD 8,88 EUR

So, product B would be listed under product A also it is cheaper!

This site already converts one currency in the other to display it. We store the exchange rate in the option ‘currency_rate’ which is updated on a daily basis. What we need now is a different approach to order our search results in a proper way. For this, we need to use some filter hooks.

In the PHP-file which will execute the WP_Query we added these lines just before we execute the WP_Query:

if( isset( $_GET['order'] ) && ( $_GET['order'] == 'asc' || $_GET['order'] == 'desc' ) ):		
	add_filter('posts_join_paged','orderbyprice_posts_join_paged');
	add_filter('posts_where_request','orderbyprice_posts_where_request');		
	add_filter('posts_orderby','orderbyprice');
endif;

In the query.php of WordPress (wp-includes/query.php), we will find these filters in the Lines
2860 – 2866. With these filters, we can alter the actual MySQL-Query. We can add new tables, WHERE-conditions and modify the actual ORDER BY statement.

So, we created this solution:

prefix . "postmeta ON ( " . $wpdb->prefix . "postmeta.post_id = " . $wpdb->prefix . "posts.ID) ";
	$join_paged_statement .= " LEFT JOIN " . $wpdb->prefix . "postmeta as currency ON (currency.post_id = " . $wpdb->prefix . "posts.ID) ";
	return $join_paged_statement;	
}

function orderbyprice_posts_where_request( $where ){
	global $wpdb;
	$where .= " AND ( " . $wpdb->prefix . "postmeta.meta_key = 'price' ) AND ( currency.meta_key = 'currency' ) ";
	return $where;
}

function orderbyprice( $orderby ){
	global $wpdb;
	$rate = get_option( 'currency_rate' );
	if( $_SESSION['currency'] == '$' ):
		$sql = "IF ( currency.meta_value != '$', ( CAST(" . $wpdb->prefix . "postmeta.meta_value as DECIMAL(10,2)) / " . $rate . ") , CAST(" . $wpdb->prefix . "postmeta.meta_value as DECIMAL(10,2)) ) " . $_GET['order']  . " ";
	else:
		$sql = "IF ( currency.meta_value = '$', ( CAST(" . $wpdb->prefix . "postmeta.meta_value as DECIMAL(10,2)) * " . $rate . ") , CAST(" . $wpdb->prefix . "postmeta.meta_value as DECIMAL(10,2)) ) " . $_GET['order']  . " ";
	endif;
	return $sql;
}
?>

So, first we add two postmeta-tables in orderbyprice_posts_join_paged() and define them as containing the currency and the price in orderbyprice_posts_where_request()

In our function orderbyprice() we first get the exchange rate. Depending on the currency of the session we will return two different SQL statements. Both statements contain an IF-clause, which you can use in MySQL.

So: If the current session is $ and the currency-postmeta is not $ we return the price/rate. If the currency-postmeta is $, we just return the price. We have to convert the meta_value field with CAST, so MySQL uses the value as an float and not as a string. And thats it!

Photo Credit: Chobist cc

An Example of How To Remove Empty HTML Tags with PHP

In his latest blogpost Tom McFarlin gives us “An Example of How To Remove Empty HTML Tags“. Empty tags can be a real nightmare, since they can really destroy the layout of an article. Take for example empty<p>-Tags, which easliy produce huge spaces between paragraphs. There is no solution in CSS for such a problem and to call the designer is a wasted call. Especially WYSIWYG editors produce these problems easily and probably a lot of WordPress users know, what I am talking about.

So I was quite curious about this topic and read his blogpost. I was a bit disappointed, when I saw, he was facing the problem of empty HTML Tags with an Javascript solution:

( function ( $) {
	'use strict';
	$( '.comment code' ).each(function() {
		if ( '' === $.trim( $( this ).text() ) ) {
			$( this ).remove();
		}
	});
}( jQuery ) );

This solution is neat, no question: for Javascript enabled browsers. But for sure, this solution has some disadvantages:

  • the browser has to keep care of the problem, which basically costs time. First you retrieve data you don’t need and in a second step, you need to remove this data.
  • Empty tags are something like silent conversations. What does <em></em> mean? I emphasize nothing?
  • Your browser needs to have Javascript enabled

So, a server side solution would be my preferred way. I was searching a bit, if there might be such a solution, and I found this blogpost by CodeSnap:


 * @version    1.0
 * @param    string    $str    String to remove tags.
 * @param    string    $repto    Replace empty string with.
 * @return    string    Cleaned string.
 */
function remove_empty_tags_recursive ($str, $repto = NULL)
{
    //** Return if string not given or empty.
    if (!is_string ($str)
        || trim ($str) == '')
            return $str;

    //** Recursive empty HTML tags.
    return preg_replace (

        //** Pattern written by Junaid Atari.
        '/<([^<\/>]*)>([\s]*?|(?R))<\/\1>/imsU',

        //** Replace with nothing if string empty.
        !is_string ($repto) ? '' : $repto,

        //** Source string
        $str
    );
}
/*
+=====================================
| EXAMPLE
+=====================================
*/
$str=<<

Hello User,
Welcome to our domain.

EOF; echo remove_empty_tags_recursive ($str); /* +===================================== | OUTPUT: +===================================== */ /*
Hello User,
Welcome to our domain.
*/

Code Snap is using Regular Expressions to identify empty HTML Tags. The advantage of this solution over DOM-solutions is obvious: It doesn’t need to be a valid DOM-path. But for now, this solution only removes for example <a></a> but not <a href=”” class=”external link”></a>.

So its time to play with the regular expression. In order to fix this, I would suggest this solution:

'/<([^<\/>]*)([^<\/>]*)>([\s]*?|(?R))<\/\1>/imsU'

This removes properly <p> as well as <p class=”entry”></p>

For me, the proposed Javascript solution is like: First you smash a glas and then you present the broom to fix it. A server side solution doesn’t smash the glas in the first place.

photo credit: Eldkvast cc