Mar 30 2008

How to load JavaScript in WordPress plugins

Tag: otherAndrea Ercolino @ 21:43:39

If you want to load some JavaScript files from your WordPress plugin you have at least two options.

Head hooks

When building up a page, just before the closing tag of the head element, a theme should call actions hooked on wp-head. Those actions only need to output what they want to include into the head element, like script files.

What if a theme does not do that? Well, I’d include it into the so called “known issues”. For example, my last WordPress plugin, won’t work in this case. I think it’s better not to find a workaround, so that theme authors understand that they must follow WordPress standards.

On the other hand, there is also an admin-head hook, which is the wp-head hook counterpart when building up admin pages. For example, you could use the following function.

<?php


function load_into_head() { 
	$ADMIN_VIEW = "alert( 'Hello Admin!' );";
	$USERS_VIEW = "alert( 'Hello World!' );";
	?>
<script type="text/javascript">
	<?php

		if( is_admin() ) { echo $ADMIN_VIEW; }
		else             { echo $USERS_VIEW; } 
	?>
</script>
	<?php 
}

add_action( is_admin() ? 'admin_head' : 'wp_head', 'load_into_head' );

?>

Script API

WordPress provides also a good Script API that will let you do anything you want with script files, following WordPress standards. These are the main functions:

  1. wp_deregister_script
  2. wp_register_script
  3. wp_enqueue_script
  4. wp_print_scripts (action hook)
  5. print_scripts_array (filter hook)

The general idea is that you write a function for loading your scripts using wp_deregister_script, wp_register_script, and wp_enqueue_script, and hook it on wp_print_scripts. If you also need to fine tune the order in which files are loaded or if they have to be loaded at all, then you can write another function and hook it on print_scripts_array.

deregister, register, and enqueue

wp_register_script let’s you create an alias and define dependencies for each script file. If a script file with the same alias was already registered, then your registration is ignored. This is a policy for conflict resolution (the first registration wins) that may help you, if you know it.

wp_deregister_script let’s you remove an alias. If the alias you give doesn’t exist, nothing happens. Tipically you use this function for forcing a new registration of an already registered alias: first you deregister and then register again.

wp_enqueue_script prepares the loading of a script. If the script was registered with some dependencies, this function automatically prepares their loading too, recursively, making sure each script will be loaded only once and before any script depending on it.

For example, jQuery 1.2.3 is registered by WordPress 2.5, but let’s say that you want to always download the latest version. You could use the following function.

<?php


function load_with_api() {
	wp_deregister_script( 'jquery' );
	wp_register_script( 'jquery', 'http://code.jquery.com/jquery-latest.pack.js', false, '' );

	//keep jQuery and Prototype compatible
	$url = get_bloginfo('wpurl').'/wp-content/plugins/my-plugin';
	wp_register_script( 'jquery_no_conflict', $url . '/jquery_no_conflict.js', array( 'jquery' ), '' );	
	wp_enqueue_script( 'jquery_no_conflict' );
}

add_action( 'wp_print_scripts', 'myOwnTheme_init' );

?>

Keeping jQuery and Prototype compatible is a needed functionality, because WordPress uses both and they both use $ as a global symbol. In fact, the jQuery file packed with WordPress includes the compatibility setting.

Fine tuning

If you want your scripts to be loaded in a particular order, with one script before or after another, maybe with respect to one that was registered by WordPress itself, or by other plugins, then you can write a function for the print_scripts_array filter.

For example, to load the jquery_no_conflict file just after the jquery file, you can use the following function.

<?php


function jquery_no_conflict_follows_jquery( $js_array ) {
	if ( false === $jquery = array_search( 'jquery', $js_array ) )
		return $js_array;

	if ( false === $jquery_no_conflict = array_search( 'jquery_no_conflict', $js_array ) )
		return $js_array;

	if ( $jquery_no_conflict == $jquery + 1 )
		return $js_array;

	array_splice( $js_array, $jquery + 1, 0, 'jquery_no_conflict' );

	unset($js_array[$jquery_no_conflict + ($jquery_no_conflict < $jquery ? 0 : 1)]);

	return $js_array;
}

add_filter( 'print_scripts_array', 'jquery_no_conflict_follows_jquery' );

?>

My Own Theme

If you want to see all this in action, you can install the My Own Theme plugin.


Mar 14 2008

WP Chili Released

Tag: ChiliAndrea Ercolino @ 23:04:20

A couple of days ago I released WP Chili, a simple WordPress plugin that installs Chili into WordPress and let’s you add client-side code highlighting to your posts, with extreme simplicity.

After activating WP Chili, it’s just a matter of wrapping your snippets into code or pre-code elements, with the programming language of the snippet as a class, like in the following example:

<pre><code class="php">
echo &quot;Hello $name&quot;;
</code></pre>

When the post reaches your readers, the snippet is unescaped by their browser and highlighted by Chili 1.9 (downloaded from your server). Your readers will see the example rendered as:

echo "Hello $name";


Nov 24 2007

WordPress Rendering Troubles

Tag: FixingAndrea Ercolino @ 23:41:59

WordPress has many helpers that allow authors to write down some text and have it nicely formatted for their readers, without having to care about HTML issues. It’s a great job, but sometimes it doesn’t do the right thing.

This bug affects content and custom fields, which means that it’s more of a conceptual bug than a coding bug. I wrote an article with a workaround for the content. Now, let’s see a simple fix for custom fields.

I’ll describe both problems I detected and give a solution based on changing some WordPress code. I know it’s not a perfect solution, but it works and it’s also relatively easy. In the snippets you’ll see the code as it’s supposed to get changed to. (with 3 lines of context before and after changed lines, each preceded by a //noteslog.com comment)

Wordpress Version: 2.3.1

Problem: HTML Entities Conversion

Custom fields that have HTML entities in their key or value are treated by WordPress weirdly. Frustration will appear soon after realizing that WordPress won’t return what you put in before.

If I write &para; inside the visual editor of a post, WordPress should assume that I want to visualize &para;. In fact it will pretend to do the right thing the first time I save the post. All subsequent times it will show a where I put a &para;.

If I write &para; inside the code editor of a post, WordPress should assume that I want to visualize , and it does the right thing (sort of, because I now have a inside the code too).

If I write &para; inside a custom field’s key or value, WordPress should assume that I want to visualize for my readers, and &para; for myself when I’m authoring the custom field. In fact Wordpress does the wrong thing here again, because it’ll immediately convert &para; to .

Solution

This fix requires changing four lines in two files. We’ll use the PHP function htmlspecialchars in place of the WordPress function attribute_escape. After the fix, filters for “attribute_escape” won’t get applied to custom fields keys and values. (this shouldn’t be a problem)

Open: wp-admin/admin-ajax.php
Search: function wp_ajax_meta_row

add_action( 'shutdown', 'get_out_now', -1 );

function wp_ajax_meta_row( $pid, $mid, $key, $value ) {
//noteslog.com
	$value = htmlspecialchars( $value ); //attribute_escape($value);
	$key_js = addslashes(wp_specialchars($key, 'double'));
//noteslog.com
	$key = htmlspecialchars( $key ); //attribute_escape($key);
	$r .= "<tr id='meta-$mid'><td valign='top'>";
	$r .= "<input name='meta[$mid][key]' tabindex='6' onkeypress='return killSubmit(\"theList.ajaxUpdater(&#039;meta&#039;,&#039;meta-$mid&#039;);\",event);' type='text' size='20' value='$key' />";
	$r .= "</td><td><textarea name='meta[$mid][value]' tabindex='6' rows='2' cols='30'>$value</textarea></td><td align='center'>";

Open: wp-admin/includes/template.php
Search: function list_meta

}

		$key_js = js_escape( $entry['meta_key'] );
//noteslog.com
		$entry['meta_key']   = htmlspecialchars($entry['meta_key']);//attribute_escape($entry['meta_key']);
//noteslog.com
		$entry['meta_value'] = htmlspecialchars($entry['meta_value']);//attribute_escape($entry['meta_value']);
		$entry['meta_id'] = (int) $entry['meta_id'];
		$r .= "\n\t<tr id='meta-{$entry['meta_id']}' class='$style'>";
		$r .= "\n\t\t<td valign='top'><input name='meta[{$entry['meta_id']}][key]' tabindex='6' type='text' size='20' value='{$entry['meta_key']}' /></td>";

Problem: White Space Trimming

Custom fields that begin or end with white space in their key or value are trimmed. I can undestand that a key is more easily dealt with if it is trimmed before saving it into the database. But values should definitely retain all their white space AS IS.

Solution

This fix requires changing four lines in three files. We’ll add a $trim parameter to the wordpress function maybe_serialize, by default set to true. Wherever we do not want a $data value trimmed, we’ll call maybe_serialize with a false second argument.

Open: wp-includes/functions.php
Search: function maybe_serialize

return true;
}

//noteslog.com
function maybe_serialize($data, $trim=true) { //($data)
//noteslog.com
	if ( $trim && is_string($data) ) //( is_string($data) )
		$data = trim($data);
	elseif ( is_array($data) || is_object($data) )
		return serialize($data);

Open: wp-admin/includes/post.php
Search: function add_meta

$metakeyselect = $wpdb->escape( stripslashes( trim( $_POST['metakeyselect'] ) ) );
	$metakeyinput = $wpdb->escape( stripslashes( trim( $_POST['metakeyinput'] ) ) );
//noteslog.com
	$metavalue = maybe_serialize( stripslashes( ( $_POST['metavalue'] ) ), false ); //( stripslashes( trim( $_POST['metavalue'] ) ) );
	$metavalue = $wpdb->escape( $metavalue );

	if ( ('0' === $metavalue || !empty ( $metavalue ) ) && ((('#NONE#' != $metakeyselect) && !empty ( $metakeyselect) ) || !empty ( $metakeyinput) ) ) {
		// We have a key/value pair. If both the select and the

Open: wp-admin/includes/post.php
Search: function update_meta

if ( in_array($mkey, $protected) )
		return false;

//noteslog.com
	$mvalue = maybe_serialize( stripslashes( $mvalue ), false ); //( stripslashes( $mvalue ) );
	$mvalue = $wpdb->escape( $mvalue );
	$mid = (int) $mid;
	return $wpdb->query( "UPDATE $wpdb->postmeta SET meta_key = '$mkey', meta_value = '$mvalue' WHERE meta_id = '$mid'" );


Next Page »