WordPress Archives - Justin Silver https://www.justinsilver.com/category/technology/wordpress/ Technology, Travel, and Pictures Wed, 05 Sep 2018 14:30:16 +0000 en-US hourly 1 https://wordpress.org/?v=6.0.1 https://www.justinsilver.com/wp-content/uploads/2013/06/cropped-apple-touch-icon-160x160.png WordPress Archives - Justin Silver https://www.justinsilver.com/category/technology/wordpress/ 32 32 Unban GoDaddy / MediaTemple WordPress Plugins https://www.justinsilver.com/technology/wordpress/unban-godaddy-mediatemple-wordpress-plugins/?utm_source=rss&utm_medium=rss&utm_campaign=unban-godaddy-mediatemple-wordpress-plugins https://www.justinsilver.com/technology/wordpress/unban-godaddy-mediatemple-wordpress-plugins/#respond Thu, 17 Mar 2016 19:22:03 +0000 http://www.justinsilver.com/?p=4007 GoDaddy managed WordPress hosting, and by ownership MediaTemple as well, prevents some plugins from being activated on website. Some of these may make some sense if the conflict with caching, etc, but I feel...

The post Unban GoDaddy / MediaTemple WordPress Plugins appeared first on Justin Silver.

]]>
AmpedSense.OptimizeAdSpot('AP'); AmpedSense.OptimizeAdSpot('IL'); AmpedSense.OptimizeAdSpot('IR');

GoDaddy managed WordPress hosting, and by ownership MediaTemple as well, prevents some plugins from being activated on website. Some of these may make some sense if the conflict with caching, etc, but I feel like including a list is good enough and if you want to install that plugin on the website you’re paying for, so be it. There are several plugins that are banned in the list that I regularly use in my website development, for example Gravity Forms.

If you try to enable this plugin on a site hosted on GoDaddy or MediaTemple rather than activating, you get an error that says:

Not Available: This plugin is not allowed on our system due to performance, security, or compatibility concerns. Please contact our support with any questions.

There is a Must-Use Plugin called gd-system-plugin that includes a class called GD_System_Plugin_Blacklist. This class hooks into the plugin installer code to hide links, show errors, and otherwise prevent some plugins from being installed and activated. The way that it does this is by calling the GD_System_Plugin_Blacklist->get_blacklist() method. This method tries to get a value for get_site_transient( 'gd_system_blacklist' ) and if it’s empty, then fetches the list from a URL and caches it.

Luckily there is a filter for pre_site_transient_* so you can short circuit the whole thing.

Unban GoDaddy Plugin Code

We can’t just return an empty array here, because there is a check for empty(), but we can return our own “list” of banned plugins instead of fetching it from a URL (or even the transient cache if it was already set). Plugin code is below, but the filter could just as easily be added to another plugin, functions.php in your theme, or another mu-plugin.

/*
Plugin Name: Unban Goddady/MediaTemple Plugins
Description: Allow plugins banned by GoDaddy to be used on your site
*/
add_filter( 'pre_site_transient_gd_system_blacklist', function(){
    return array( array( 'name'=>'godaddy', 'minVersion'=>0, 'maxVersion'=>1 ) );
} );

List of Plugins Banned on GoDaddy and MediaTemple

This is the list of plugins banned by GoDaddy as of the writing of this post.

6scan-backup
6scan-protection
adminer
adsense-click-fraud-monitoring
all-in-one-seo-pack
all-in-one-wp-migration
aspose-cloud-ebook-generator
aspose-doc-exporter
backupwordpress
backwpup
broken-link-checker
contextual-related-posts
custom-contact-forms
easy-coming-soon
ezpz-one-click-backup
fancybox-for-wordpress
favicon-by-realfavicongenerator
fuzzy-seo-booster
google-analytics-for-wordpress
google-sitemap-generator
google-xml-sitemaps-with-multisite-support
gravityforms
inboundio-marketing
iwp-client
jr-referrer
leads
liveforms
miwoftp
mp3-jplayer
newsletter
nextgen-gallery
pagelines
photo-gallery
php-event-calendar
platform
pluscaptcha
pods
portable-phpmyadmin
ptengine-real-time-web-analytics-and-heatmap
quick-cache
referrer-wp
schram-kljsdfjkl
seo-alrp
sgcachepress
similar-posts
statpress
synthesis
tdwordcount
the-codetree-backup
toolspack
ultimate-member
updraft
w3-total-cache
wordpress-beta-tester
wordpress-gzip-compression
wordpress-popular-posts
wordpress-seo
work-the-flow-file-upload
wp-all-import
wp-business-intelligence-lite
wp-cache
wp-cachecom
wp-copysafe-pdf
wp-copysafe-web
wp-engine-snapshot
wp-fast-cache
wp-fastest-cache
wp-file-cache
wp-phpmyadmin
wp-postviews
wp-power-stats
wp-slimstat
wp-super-cache
wp-ultimate-csv-importer
wpengine-common
wponlinebackup
wptouch
wysija-newsletters
yet-another-featured-posts-plugin
yet-another-related-posts-plugin

The post Unban GoDaddy / MediaTemple WordPress Plugins appeared first on Justin Silver.

]]>
https://www.justinsilver.com/technology/wordpress/unban-godaddy-mediatemple-wordpress-plugins/feed/ 0
ACF: Link to Specific Tab https://www.justinsilver.com/technology/wordpress/acf-link-to-specific-tab/?utm_source=rss&utm_medium=rss&utm_campaign=acf-link-to-specific-tab https://www.justinsilver.com/technology/wordpress/acf-link-to-specific-tab/#comments Thu, 03 Sep 2015 16:04:50 +0000 http://justinsilver.com/?p=3931 Advanced Custom Fields, or ACF, provides a great user interface for defining and laying out custom fields for Posts, Pages, Custom Post Types, Options, Users and more in WordPress. The Tab Field provides an...

The post ACF: Link to Specific Tab appeared first on Justin Silver.

]]>
AmpedSense.OptimizeAdSpot('AP'); AmpedSense.OptimizeAdSpot('IL'); AmpedSense.OptimizeAdSpot('IR');

Advanced Custom Fields, or ACF, provides a great user interface for defining and laying out custom fields for Posts, Pages, Custom Post Types, Options, Users and more in WordPress. The Tab Field provides an easy way to clean up your user interface by seperating different field inputs into different tabs, making it easier for your users to find what they are looking for.

One feature that is missing from the Tab field however is the ability to link directly to a specific tab from another page. We can work around this with some pretty straightforward JavaScript. In short when the acf “ready” event fires, we want to look at all the tab buttons, and if any match our URL hash (in lowercase with spaces replaced with hyphens) then we “click” that link, loading the appropriate tab. Subsequently we can update the hash in the address bar any time a new tab is clicked making it easy for uses to bookmark the page or send the link to another user.

If your tab name is “Options Tab 1” the link to it would be something like edit.php?post_type=my-post-type#options-tab-1.

(function($){
	// run when ACF is ready
	acf.add_action('ready', function(){
		// check if there is a hash
		if (location.hash.length>1){
			// get everything after the #
			var hash = location.hash.substring(1);
			// loop through the tab buttons and try to find a match
			$('.acf-tab-wrap .acf-tab-button').each(function(i, button){ 
				if (hash==$(button).text().toLowerCase().replace(' ', '-')){
					// if we found a match, click it then exit the each() loop
					$(button).trigger('click');
					return false;
				}
			});
		}
		// when a tab is clicked, update the hash in the URL
		$('.acf-tab-wrap .acf-tab-button').on('click', function($el){
			location.hash='#'+$(this).text().toLowerCase().replace(' ', '-');
		});
	});
})(jQuery);

The post ACF: Link to Specific Tab appeared first on Justin Silver.

]]>
https://www.justinsilver.com/technology/wordpress/acf-link-to-specific-tab/feed/ 5
Admin Columns Pro SSL Upgrade Fix https://www.justinsilver.com/technology/wordpress/admin-columns-pro-ssl-upgrade-fix/?utm_source=rss&utm_medium=rss&utm_campaign=admin-columns-pro-ssl-upgrade-fix https://www.justinsilver.com/technology/wordpress/admin-columns-pro-ssl-upgrade-fix/#comments Sun, 02 Nov 2014 19:21:28 +0000 http://justinsilver.com/?p=3801 I use the Admin Columns Pro plugin on several of my WordPress site to easily customize the layout in my admin tables. As it is a premium plugin updates to it are not hosted...

The post Admin Columns Pro SSL Upgrade Fix appeared first on Justin Silver.

]]>
AmpedSense.OptimizeAdSpot('AP'); AmpedSense.OptimizeAdSpot('IL'); AmpedSense.OptimizeAdSpot('IR');

I use the Admin Columns Pro plugin on several of my WordPress site to easily customize the layout in my admin tables. As it is a premium plugin updates to it are not hosted on the WordPress repository but rather come from their own private repository. This was working fine until a recently when I started getting errors during the upgrade process. It seems as though the SSL request was to www.admincolumns.com but for some reason the server was responding with a wildcard cert for *.wpengine.com – their hosting provider.

I opened a ticket on their support site and while helpful, unfortunately they were not able to come to a resolution. The error during the plugin update reads like the following.

Enabling Maintenance mode…

Updating Plugin Admin Columns Pro (1/1)
Downloading update from https://www.admincolumns.com?wc-api=software-licence-api&request=plugindownload&licence_key=XXXXXXXXXXX&plugin_name=admin-columns-pro…
An error occurred while updating Admin Columns Pro: Download failed. SSL: certificate subject name '*.wpengine.com' does not match target host name 'www.admincolumns.com'

Disabling Maintenance mode…

Root Issue

I was never able to determine the root issue other than it likely likes with WP Engine. The behavior is not consistent between environments, for example from my Mac running OSX Yosemite I can can use curl to load the Admin Columns Pro site via curl:

> curl -v https://www.admincolumns.com
* Adding handle: conn: 0x7fa2cc004000
* Adding handle: send: 0
* Adding handle: recv: 0
* Curl_addHandleToPipeline: length: 1
* - Conn 0 (0x7fa2cc004000) send_pipe: 1, recv_pipe: 0
* About to connect() to www.admincolumns.com port 443 (#0)
*   Trying 178.79.179.38...
* Connected to www.admincolumns.com (178.79.179.38) port 443 (#0)
* TLS 1.0 connection using TLS_RSA_WITH_AES_128_CBC_SHA
* Server certificate: www.admincolumns.com
* Server certificate: RapidSSL CA
* Server certificate: GeoTrust Global CA
> GET / HTTP/1.1
> User-Agent: curl/7.30.0
> Host: www.admincolumns.com
> Accept: */*
>
< HTTP/1.1 200 OK

But the same request did not work from my CentOS 5 servers:

[root@dev1 ~]# curl -v https://www.admincolumns.com
* About to connect() to www.admincolumns.com port 443 (#0)
*   Trying 178.79.179.38... connected
* Connected to www.admincolumns.com (178.79.179.38) port 443 (#0)
* successfully set certificate verify locations:
*   CAfile: /etc/pki/tls/certs/ca-bundle.crt
  CApath: none
* SSLv3, TLS handshake, Client hello (1):
* SSLv3, TLS handshake, Server hello (2):
* SSLv3, TLS handshake, CERT (11):
* SSLv3, TLS handshake, Server finished (14):
* SSLv3, TLS handshake, Client key exchange (16):
* SSLv3, TLS change cipher, Client hello (1):
* SSLv3, TLS handshake, Finished (20):
* SSLv3, TLS change cipher, Client hello (1):
* SSLv3, TLS handshake, Finished (20):
* SSL connection using AES256-SHA
* Server certificate:
* 	subject: serialNumber=dzc7avuEuqhZCEL82HF5aqoCQMgtwixa; OU=GT41552380; OU=See www.rapidssl.com/resources/cps (c)14; OU=Domain Control Validated - RapidSSL(R); CN=*.wpengine.com
* 	start date: 2014-04-17 12:42:18 GMT
* 	expire date: 2018-05-19 17:27:48 GMT
* 	subjectAltName does not match www.admincolumns.com
* Closing connection #0
* SSLv3, TLS alert, Client hello (1):
* SSL peer certificate or SSH remote key was not OK
curl: (51) SSL peer certificate or SSH remote key was not OK

I noticed that the CentOS machines were using SSLv3, whereas my Mac was using TLS. I was then able to recreate the issue on my Mac by forcing curl to use SSLv3.

> curl -v -sslv3 https://www.admincolumns.com
* Adding handle: conn: 0x7fae0b004000
* Adding handle: send: 0
* Adding handle: recv: 0
* Curl_addHandleToPipeline: length: 1
* - Conn 0 (0x7fae0b004000) send_pipe: 1, recv_pipe: 0
* About to connect() to www.admincolumns.com port 443 (#0)
*   Trying 178.79.179.38...
* Connected to www.admincolumns.com (178.79.179.38) port 443 (#0)
* SSL certificate problem: Invalid certificate chain
* Closing connection 0
curl: (60) SSL certificate problem: Invalid certificate chain
More details here: http://curl.haxx.se/docs/sslcerts.html

Disable SSL Certificate Verification in WP_Http

With the SSL certificate for the request being invalid and the server not being in my control, the only option is to disable the SSL certificate verification in WP_Http. This is accomplished by setting a key in its configuration array called sslverify to false. We can do this by hooking into the http_request_args filter, checking the URL that it is loading, and disabling the verification for Admin Columns Pro.

add_filter( 'http_request_args', 'fix_acp_plugin_update', 10, 2 );
function fix_acp_plugin_update( $r, $url ){
	$starts_with = 'https://www.admincolumns.com?wc-api=software-licence-api&request=plugindownload';
	// if the url starts with ^ then don't verify SSL
	if ( 0 === strpos( $url, $starts_with ) ){
		$r['sslverify'] = false;
	}
	return $r;
}

Successfully Updated!

Et voila! The plugin is now able to update successfully.

Enabling Maintenance mode…

Updating Plugin Admin Columns Pro (1/1)
Admin Columns Pro updated successfully. Show Details.
Disabling Maintenance mode…

All updates have been completed.

The post Admin Columns Pro SSL Upgrade Fix appeared first on Justin Silver.

]]>
https://www.justinsilver.com/technology/wordpress/admin-columns-pro-ssl-upgrade-fix/feed/ 2
Fix Backslashes in Advanced Custom Fields https://www.justinsilver.com/technology/wordpress/fix-backslashes-advanced-custom-fields/?utm_source=rss&utm_medium=rss&utm_campaign=fix-backslashes-advanced-custom-fields https://www.justinsilver.com/technology/wordpress/fix-backslashes-advanced-custom-fields/#respond Sat, 01 Nov 2014 21:53:44 +0000 http://justinsilver.com/?p=3798 There is a somewhat serious bug in Advanced Custom Fields 5.0 and its handling of backslashes, at least on my WordPress installs. The problem is that the slashes are stripped, but after the data...

The post Fix Backslashes in Advanced Custom Fields appeared first on Justin Silver.

]]>
AmpedSense.OptimizeAdSpot('AP'); AmpedSense.OptimizeAdSpot('IL'); AmpedSense.OptimizeAdSpot('IR');

There is a somewhat serious bug in Advanced Custom Fields 5.0 and its handling of backslashes, at least on my WordPress installs. The problem is that the slashes are stripped, but after the data has been serialized resulting in a mismatch between the declared string length in the serialized data, and it’s actual value length.

Imagine for example a Validated Field that uses a regular expression to check that the value is a digit. The validation pattern would look something like \d for our example. When this is serialized, it’s length is 2 and the data looks like s:2:"\d". The problem is that when this is represented as a string to insert into the database, the backslash just escapes the d and what gets saved to the database is s:2:"d".

Now we have a much bigger problem than a missing backslash – because the declared length is 2 but the length of the string "d" is 1, the serialized data can no longer be loaded from the database and all the field configuration values are lost.

This occurs during the upgrade process from ACF 4 to 5, as well as when saving a field with a backslash as part of its configuration. The field will default back to the “Text” field type in the UI, and the settings will be unloadable in the database. It is possible to manually fix this data by adding the missing \ characters back in, but this is going to be tedious work depending on how many fields are affected and the complexity. Even when fixed manually the field will break again the next time it is saved.

A bug has been listed on the ACF support site.

Fix for Backslashes in Field Configuration

The following code attempts to correct this behavior, the following code attempts to use the filters content_save_pre and acf/get_valid_field to double slash any single slashes found in the configuration so that they are saved correctly to the database.

class ACF5_Slash_Fix {
	function __construct(){

		// bug fix for acf with backslashes in the content.
		add_filter( 'content_save_pre', array( $this, 'fix_post_content' ) );
		add_filter( 'acf/get_valid_field', array( $this, 'fix_upgrade' ) );
	}

	function fix_upgrade( $field ){

		// the $_POST will tell us if this is an upgrade
		$is_5_upgrade = 
			isset( $_POST['action'] ) && $_POST['action'] == 'acf/admin/data_upgrade' && 
			isset( $_POST['version'] ) && $_POST['version'] == '5.0.0';

		// if it is an upgrade recursively fix the field values
		if ( $is_5_upgrade ){
			$field = $this->do_recursive_slash_fix( $field );
		}

		return $field;
	}

	function fix_post_content( $content ){
		global $post;

		// are we saving a field group?
		$is_field_group = get_post_type() == 'acf-field-group';

		// are we upgrading to ACF 5?
		$is_5_upgrade = 
			isset( $_POST['action'] ) && $_POST['action'] == 'acf/admin/data_upgrade' && 
			isset( $_POST['version'] ) && $_POST['version'] == '5.0.0';

		// if we are, we need to check the values for single, but not double, backslashes and make them double
		if ( $is_field_group || $is_5_upgrade ){
			$content = $this->do_slash_fix( $content );
		}
		
		return $content;
	}

	function do_slash_fix( $string ){
		return preg_match( '~(?<!\\\\)\\\\(?!\\\\)~', $string )?
			str_replace('\\', '\\\\', $string ) :
			$string;
	}

	function do_recursive_slash_fix( $array ){

		// loop through all levels of the array
		foreach( $array as $key => &$value ){
			if ( is_array( $value ) ){
				// div deeper
				$value = $this->do_recursive_slash_fix( $value );
			} elseif ( is_string( $value ) ){
				// fix single backslashes to double
				$value = $this->do_slash_fix( $value );
			}
		}

		return $array;
	}
}
new ACF5_Slash_Fix();

Included in Validated Field

This fix is also included in the Validated Field plugin available in the WordPress repository. It offers additional enhancements as well to support field validation, read-only values, unique key, and more.

The post Fix Backslashes in Advanced Custom Fields appeared first on Justin Silver.

]]>
https://www.justinsilver.com/technology/wordpress/fix-backslashes-advanced-custom-fields/feed/ 0
W3 Total Cache Fragment Caching in WordPress https://www.justinsilver.com/technology/wordpress/w3-total-cache-fragment-caching-wordpress/?utm_source=rss&utm_medium=rss&utm_campaign=w3-total-cache-fragment-caching-wordpress https://www.justinsilver.com/technology/wordpress/w3-total-cache-fragment-caching-wordpress/#comments Fri, 31 Oct 2014 19:20:14 +0000 http://justinsilver.com/?p=3796 W3 Total Cache, also known as W3TC, is a very powerful caching plugin for WordPress which is notoriously slow without tuning. For guest users much of your content will typically be static which makes...

The post W3 Total Cache Fragment Caching in WordPress appeared first on Justin Silver.

]]>
AmpedSense.OptimizeAdSpot('AP'); AmpedSense.OptimizeAdSpot('IL'); AmpedSense.OptimizeAdSpot('IR');

W3 Total Cache, also known as W3TC, is a very powerful caching plugin for WordPress which is notoriously slow without tuning. For guest users much of your content will typically be static which makes serving up cached content to these users a pretty good idea. But what if you want part of the page to be be dynamic while most of the page is cached? Fragment caching to the rescue.

Configure W3 Total Cache

First you will need to make sure W3TC is properly configured for fragment caching. To enable it, from your WordPress Admin, visit Performance > General Settings and look under the Page Cache section.  Make sure that the Page cache method is set to Disk: Basic and save your settings.

Next visit Performance > Page Cache and scroll down to the Advanced section. Here you will need to check the box next to Late initialization to enable it. Notice the help text under it:

Enables support for WordPress functionality in fragment caching for the page caching engine. Use of this feature may increase response times.

Set W3TC_DYNAMIC_SECURITY Constant

For security reasons W3TC requires that you create a constant named W3TC_DYNAMIC_SECURITY and pass it to the sections of code you would like it to ignore (and execute!). An obvious choice for this is wp-config.php, but you might also have success with /wp-content/mu-plugins, a custom plugin, your theme’s functions.php, or even inline before your fragment – though the latter isn’t really recommended. You can set this to a hardcoded value, or use something random for each request.

define( 'W3TC_DYNAMIC_SECURITY', 'SOME_SECURE_STRING_YOU_CREATE' );

Update your template

The final step is to update your template files to indicate where you want W3TC to *not* cache the page. This is done by using an HTML comment called `mfunc`. You pass it your W3TC_DYNAMIC_SECURITY and include any PHP you want to execute (without <?php> tags!).

<!--mfunc <?php echo W3TC_DYNAMIC_SECURITY; ?> -->
echo 'The time is '.date( 'H:i:s', time() );
<!--/mfunc <?php echo W3TC_DYNAMIC_SECURITY; ?> -->

The post W3 Total Cache Fragment Caching in WordPress appeared first on Justin Silver.

]]>
https://www.justinsilver.com/technology/wordpress/w3-total-cache-fragment-caching-wordpress/feed/ 44
ACF validate_value Filter With post_ID https://www.justinsilver.com/technology/wordpress/advanced-custom-fields-validate_value-filter-with-post_id/?utm_source=rss&utm_medium=rss&utm_campaign=advanced-custom-fields-validate_value-filter-with-post_id https://www.justinsilver.com/technology/wordpress/advanced-custom-fields-validate_value-filter-with-post_id/#comments Sun, 19 Oct 2014 21:22:18 +0000 http://justinsilver.com/?p=3769 Advanced Custom Forms Pro 5.0 is out, and it contains a major overhaul to the way that custom fields are handled. It also had a major impact on the way that third-party add-ons were...

The post ACF validate_value Filter With post_ID appeared first on Justin Silver.

]]>
AmpedSense.OptimizeAdSpot('AP'); AmpedSense.OptimizeAdSpot('IL'); AmpedSense.OptimizeAdSpot('IR');

Advanced Custom Forms Pro 5.0 is out, and it contains a major overhaul to the way that custom fields are handled. It also had a major impact on the way that third-party add-ons were written to extend ACF functionality which meant that it was considerable work to refactor my Validated Fields plugin to support the new architecture. The new architecture is definitely superior in my opinion and has some great new filters that we can leverage such as acf/validate_value and its siblings acf/validate_value/type={$field_type}acf/validate_value/name={$field_name}acf/validate_value/key={$field_key}.

Unfortunately this filter does not have the post_ID available to it, which greatly limits the range of things we can do with it. To work around this I found that by inserting a field named acf[post_ID] into the editor form – and the “acf” part is critical – it would be picked up and submitted with the rest of the form values. This meant that it would be available in the $_POST, really opening up the possibilities.

Sample Code

// use a unique value to prevent conflicts with other ACF fields
define( 'MY_ACF_FORM_VALUES', 'MY_ACF_FORM_VALUES' );

// add the post_ID to the acf[] form
function my_edit_form_after_editor( $post ){
	print( "<input type='hidden' name='acf[%1$s][post_ID]' value='%2$d'/>", 
		MY_ACF_FORM_VALUES, 
		$post->ID 
	);
}
add_action( 'edit_form_after_editor', 'my_edit_form_after_editor' );

// use the post_ID in your validation function
function my_validate_value( $valid, $value, $field, $input ) {
	$post_id = $_POST['acf'][MY_ACF_FORM_VALUES]['post_ID'];
	// more code!
	return $valid;
}
add_filter( "acf/validate_value", 'my_validate_value', 10, 4 );

The rest of the my_validate_value() will be up to you.

Shameless Plug.

Why reinvent the wheel? The above code is already included in Validated Field for ACF available in the WordPress repository.

The post ACF validate_value Filter With post_ID appeared first on Justin Silver.

]]>
https://www.justinsilver.com/technology/wordpress/advanced-custom-fields-validate_value-filter-with-post_id/feed/ 8
Sort by Featured Image in WordPress https://www.justinsilver.com/technology/wordpress/sort-featured-image-wordpress/?utm_source=rss&utm_medium=rss&utm_campaign=sort-featured-image-wordpress https://www.justinsilver.com/technology/wordpress/sort-featured-image-wordpress/#comments Fri, 15 Aug 2014 02:10:52 +0000 http://justin.ag/?p=3703 Featured Images on a WordPress page or post can make it easy to use a single image to represent the content of your page. Given that they look better, why not feature them at...

The post Sort by Featured Image in WordPress appeared first on Justin Silver.

]]>
AmpedSense.OptimizeAdSpot('AP'); AmpedSense.OptimizeAdSpot('IL'); AmpedSense.OptimizeAdSpot('IR');

Featured Images on a WordPress page or post can make it easy to use a single image to represent the content of your page. Given that they look better, why not feature them at the top of your content, while still preserving the rest of your ORDER BY? The answer is that it’s not terribly straightforward but you do have a couple of options. We can make use of the “_thumbnail_id” meta_key that is stores the attachment ID of the featured image, but as it is missing for pages and posts that don’t have a featured image they are omitted from The Loop entirely, which is not what we want.

Filters on WP_Query

The most robust way to sort your posts by featured image while not interfering with your existing ORDER BY is to plug into the filters for posts_fields_requestposts_join_request, and posts_orderby_request. The SQL that is going to be run passes through these filters in pieces, allowing us to modify it – and make sure that pages and posts without featured images are last! Since we don’t want to modify every WP_Query, presumably just some of them, we can add another argument to handle this for us – we’ll call it featured_image_sort.

class Featured_Image_Sort {
	public static function init(){
		add_filter( 'pre_get_posts', array( __CLASS__, 'pre_get_posts' ) );
	}

	public static function pre_get_posts( $query ){
		if ( $query->query_vars['featured_image_sort'] ){
			add_filter( 'posts_fields_request', array( __CLASS__, 'posts_fields_request' ) );
			add_filter( 'posts_join_request', array( __CLASS__, 'posts_join_request' ) );
			add_filter( 'posts_orderby_request', array( __CLASS__, 'posts_orderby_request' ) );
		}
		return $query;
	}

	public static function posts_fields_request( $select ){
		global $wpdb;
		return "{$select}, IF ( _has_featured_image.meta_value IS NULL, 0, 1 ) AS has_featured_image ";
	}
	public static function posts_join_request( $join ){
		global $wpdb;
		return "{$join}\tLEFT JOIN {$wpdb->postmeta} AS _has_featured_image ON _has_featured_image.post_id = {$wpdb->posts}.ID and _has_featured_image.meta_key = '_thumbnail_id'";
	}
	public static function posts_orderby_request( $orderby ){
		return "has_featured_image DESC, {$orderby}";
	}
}
Featured_Image_Sort::init();

Using the new WP_Query argument

I like to keep things flexible, so if you want to add this to every query I would use a hook to pre_get_posts higher than a priority 10 to set the featured_image_sort flag. This is also an easy way to quickly test your code.

add_filter( 'pre_get_posts', 'sort_featured', 1 );
function sort_featured( $query ){
	$query->query_vars['featured_image_sort'] = true;
	return $query;
}

If you want to apply the sort to specific queries you would pass this flag as an argument to WP_Query as you would any other argument.

$args = array( 
	'orderby' => 'meta_value', 
	'meta_key' => 'some_meta_key', 
	'featured_image_sort' => true 
);
$query = new WP_Query( $args );

The post Sort by Featured Image in WordPress appeared first on Justin Silver.

]]>
https://www.justinsilver.com/technology/wordpress/sort-featured-image-wordpress/feed/ 3
Using WP Better Emails with WooCommerce Email Templates https://www.justinsilver.com/technology/wordpress/using-wp-better-emails-woocommerce-email-templates/?utm_source=rss&utm_medium=rss&utm_campaign=using-wp-better-emails-woocommerce-email-templates https://www.justinsilver.com/technology/wordpress/using-wp-better-emails-woocommerce-email-templates/#comments Thu, 24 Jul 2014 22:35:00 +0000 http://justin.ag/?p=3673 On one site that I run there are several different functions, primarily provided by disparate plugins with custom actions/filters, that send emails both to site administrators and customers. For most emails that are sent...

The post Using WP Better Emails with WooCommerce Email Templates appeared first on Justin Silver.

]]>
AmpedSense.OptimizeAdSpot('AP'); AmpedSense.OptimizeAdSpot('IL'); AmpedSense.OptimizeAdSpot('IR');

On one site that I run there are several different functions, primarily provided by disparate plugins with custom actions/filters, that send emails both to site administrators and customers. For most emails that are sent from WordPress itself and several of the plugins, the content type is set to plain/text allowing me to use WP Better Emails to wrap content in an easily edited header and footer. WooCommerce on the other hand proved to be a little more tricky – there is the option to send emails via plain/text, but they definitely lack valuable formatting when displaying order information to a customer. This is further compounded if you insert any custom HTML into the content.

Conceptually all that needs to be done is to first remove the WooCommerce email header and footer, and second have WP Better Emails process it even though it has a content type of text/html. After checking out the code for both of the plugins in question, I devised a way to make it work.

Remove WooCommerce Email Headers and Footers

If you view the source of any of the WooCommerce email templates (at least the default ones) you will see a line for <?php do_action( 'woocommerce_email_header', $email_heading ); ?> and <?php do_action( 'woocommerce_email_footer' ); ?> which are the hooks that WooCommerce itself uses to insert the header and footer contents. By setting our own functions to these hooks earlier and later than the WooCommerce priorities, we can capture all of the content and hide it with ob_start() and ob_get_clean() to capture and clean the output buffer contents. We are also setting a filter to return true any time the woocommerce_email_header action is run.

Common sense says that we could just unhook all functions from these actions, however in my testing I found that some other, non-UI, logic was being performed in some of the attached functions. This method allows the code to run, but prevents the output from being inserted into the generated email.

Apply WP Better Emails Template

Next we need to apply the email template that WP Better Emails did not, because the content type was wrong – text/html and not text/plain. This is actually a good thing because we also want to avoid some of the formatting that WP Better Emails applies to the typical plain text email content. We can do this using the global $wp_better_emails and hooking into phpmailer_init after most of the work has been done – priority 20 works fine.

This example uses anonymous function which require PHP 5.3+, however you could also use create_function().

// Determine if it's an email using the WooCommerce email header
add_action( 'woocommerce_email_header', function(){ add_filter( "better_wc_email", "__return_true" ); } );

// Hide the WooCommerce Email header and footer
add_action( 'woocommerce_email_header', function(){ ob_start(); }, 1 );
add_action( 'woocommerce_email_header', function(){ ob_get_clean(); }, 100 );
add_action( 'woocommerce_email_footer', function(){ ob_start(); }, 1 );
add_action( 'woocommerce_email_footer', function(){ ob_get_clean(); }, 100 );

// Selectively apply WPBE template if it's a WooCommerce email
add_action( 'phpmailer_init', 'better_phpmailer_init', 20 );
function better_phpmailer_init( $phpmailer ){
	// this filter will return true if the woocommerce_email_header action has run
	if ( apply_filters( 'better_wc_email', false ) ){
		global $wp_better_emails;

		// Add template to message
		$phpmailer->Body = $wp_better_emails->set_email_template( $phpmailer->Body );

		// Replace variables in email
		$phpmailer->Body = apply_filters( 'wpbe_html_body', $wp_better_emails->template_vars_replacement( $phpmailer->Body ) );
	}
}

Success!

Your WP Better Emails templates should now be applied to your WooCommerce emails.

The post Using WP Better Emails with WooCommerce Email Templates appeared first on Justin Silver.

]]>
https://www.justinsilver.com/technology/wordpress/using-wp-better-emails-woocommerce-email-templates/feed/ 5
Prepare IN and NOT IN Statements in WordPress https://www.justinsilver.com/technology/wordpress/prepare-statements-wordpress/?utm_source=rss&utm_medium=rss&utm_campaign=prepare-statements-wordpress https://www.justinsilver.com/technology/wordpress/prepare-statements-wordpress/#comments Mon, 30 Jun 2014 19:11:00 +0000 http://justin.ag/?p=3637 Why should I use prepared statements? Using some type of prepared statement protects you against SQL injection attacks when you need to interact with the database using parameters passed in from the client side. Depending...

The post Prepare IN and NOT IN Statements in WordPress appeared first on Justin Silver.

]]>
AmpedSense.OptimizeAdSpot('AP'); AmpedSense.OptimizeAdSpot('IL'); AmpedSense.OptimizeAdSpot('IR');

Why should I use prepared statements?

Using some type of prepared statement protects you against SQL injection attacks when you need to interact with the database using parameters passed in from the client side. Depending on the library you are using, how you do this will vary slightly, however when working directly with the database in WordPress it’s best to use the $wpdb global object to abstract the actual interface. The deprecated mysql_* extensions are still officially being used, however as of release of WordPress 3.9 when used in conjunction with PHP 5.5, you bumped over to the newer and improved mysqli. Either way, you can use $wpdb->prepare( $statement, $arg1, $arg2... ) and the WordPress API will handle the details of the implementation.

DO NOT DO THIS AT HOME (OR ANYWHERE)

This is an example of code that is susceptible to a SQL injection.

global $wpdb;
// is this really a numeric ID?
$id = $_POST['id'];
$sql = "SELECT post_name FROM {$wpdb->posts} WHERE ID = $id;";
$name = $wpdb->get_var( $sql );

Imagine what would happen if the value of $_POST['id'] was not a number like we expect, and is instead the string 0; DELETE FROM wp_posts;. The resulting $sql value that is executed would look like the following.

SELECT post_name
FROM wp_posts
WHERE ID = 0;
DELETE FROM wp_posts;

The right way, using prepare()

The correct way to write the above code so that it is not susceptible to SQL injection attacks would be to use $wpdb->prepare(), and it would look like this:

global $wpdb;
// is this really a numeric ID?
$id = $_POST['id'];
// Use %d for digits, or %s for strings when calling prepare()
$sql = $wpdb->prepare( "SELECT post_name FROM {$wpdb->posts} WHERE ID = %d", $id );
$name = $wpdb->get_var( $sql );

Using Prepared Statements with IN and NOT IN

Things get a bit more complicated when you need to pass an array of values into an IN or NOT IN clause however. The best way to deal with this situation is to use call_user_func_array() to pass an array as a list of arguments to $wpdb->prepare(). Since there may be other values we want to escape as well, I usually define a new function to handle this special case and end up calling prepare() twice. This particular function assumes you want to pass in a list of post ID’s, potentially having the list twice (an OR statement for example) – so you may need to adjust for your needs.

function my_function(){
	global $wpdb;
	$id = $_POST['id'];
	$id_array = $_POST['id_array'];

	$sql = "SELECT post_name FROM {$wpdb->posts} WHERE ID = %d or ID IN ([IN])";
	$sql = $wpdb->prepare( $sql, $id );
	$sql = prepare_in( $sql, $id_array );

	// SELECT post_name FROM wp_posts WHERE ID = 9 or ID IN ( 10, 11, 12 )
}

function prepare_in( $sql, $vals ){
	global $wpdb;
	$not_in_count = substr_count( $sql, '[IN]' );
	if ( $not_in_count > 0 ){
		$args = array( str_replace( '[IN]', implode( ', ', array_fill( 0, count( $vals ), '%d' ) ), str_replace( '%', '%%', $sql ) ) );
		// This will populate ALL the [IN]'s with the $vals, assuming you have more than one [IN] in the sql
		for ( $i=0; $i < substr_count( $sql, '[IN]' ); $i++ ) {
			$args = array_merge( $args, $vals );
		}
		$sql = call_user_func_array( array( $wpdb, 'prepare' ), array_merge( $args ) );
	}
	return $sql;
}

The post Prepare IN and NOT IN Statements in WordPress appeared first on Justin Silver.

]]>
https://www.justinsilver.com/technology/wordpress/prepare-statements-wordpress/feed/ 2
Disable WP-Cron in WordPress https://www.justinsilver.com/technology/wordpress/disable-wp-cron-wordpress/?utm_source=rss&utm_medium=rss&utm_campaign=disable-wp-cron-wordpress https://www.justinsilver.com/technology/wordpress/disable-wp-cron-wordpress/#comments Wed, 14 May 2014 02:49:28 +0000 http://justin.ag/?p=3519 What is WP-Cron? WP-Cron is the way that WordPress executes scheduled jobs that are added via wp_schedule_event() or one of its associated functions. First it’s a bit of a hack given the single threaded-ness...

The post Disable WP-Cron in WordPress appeared first on Justin Silver.

]]>
AmpedSense.OptimizeAdSpot('AP'); AmpedSense.OptimizeAdSpot('IL'); AmpedSense.OptimizeAdSpot('IR');

What is WP-Cron?

WP-Cron is the way that WordPress executes scheduled jobs that are added via wp_schedule_event() or one of its associated functions. First it’s a bit of a hack given the single threaded-ness of PHP. It’s simply not possible to spin up a Thread like you would in Java to do the work asynchronously, and we don’t want the users to have to wait while we run a large job. To get around this WordPress checks to see if there are any scheduled jobs with an execution time in the past (more on this in a bit), and will then make a request to itself calling wp-cron.php with a querystring of doing_wp_cron. The job can now run in this new request (which is a different PHP process) and the page HTML is returned to the users without ever trying to fetch the results.

What’s good about WP-Cron?

WP-Cron lets you do things like check for plugin updates or other arbitrary tasks at set intervals – once or twice a day, longer, or more frequently if you desire. In many ways this mimics the functionality of cron on your Linux box.

There are lots of useful things you can do with  a scheduled task – send emails, update derived database values, really anything that you want to happen regularly, but not on every request. If you’re ok with some user’s seeing slightly stale data in exchange for better performance, you can use a WP-Cron job to indicate that the cache needs to be primed.

What’s bad about WP-Cron?

While WP-Cron can be a very useful tool, there are  some downsides to it as well. As mentioned earlier it can only check for jobs that should have already run, meaning that the scheduled $execution_time < time(), but this can be an issue on sites with low traffic, especially if you actually care what time the task will be run. Even if your site does decent traffic during the day, maybe it drops way off at night and you want to run something as close to 1:00AM as possible… which might or might not happen.

There is also the performance hit you take making an HTTP request, even though it’s to yourself. Why check the database and WP-Cron locks to see if the HTTP request is even necessary? Speaking of locks – while much better about locking WP-Cron, sites with heavy traffic can occasionally end up with multiple WP-Cron requests (if user requests come in at the exact same time).

How to disable WP-Cron?

The way that I handle WP-Cron on my sites is to disable it entirely by setting the DISABLE_WP_CRON constant to true in wp-config.php. This will prevent WordPress from checking for WP-Cron tasks at all when a user makes a request.

The next step is to set cron on the system to make the request to wp-cron.php based on whatever schedule works best for you. You can have it run as frequently as once per minute, or up to… forever. Probably whatever the smallest resolution of your recurring tasks are is best, unless you are using wp_schedule_single_event() to run something asynchronously – then a minute might be best.

Disable WP-Cron in wp-config.php

define( 'DISABLE_WP_CRON', true );

Schedule WP-Cron using crontab

# Run WP-Cron every 5 minutes
*/5 * * * * /usr/bin/curl --silent http://example.com/wp-cron.php?doing_wp_cron >/dev/null

The post Disable WP-Cron in WordPress appeared first on Justin Silver.

]]>
https://www.justinsilver.com/technology/wordpress/disable-wp-cron-wordpress/feed/ 4