WordPress Weekly Patch – Escaped Apostrophe – Trac #14996

October 1st, 2010  |  Published in Mind

Glenn Ansley started this “WordPress Weekly Patch” idea. Where you submit one patch a week to WordPress core. I’ve been doing it, but have definitely missed the “weekly” part of “weekly” :). I’ve just been way too busy.

Regardless, this is my patch for the week – http://core.trac.wordpress.org/ticket/14996

If you insert a Video/Audio/Media link and have an apostrophe in the title, the link will have tried to escape the apostrophe with a backslash. My patch removes the unnecessary backslash. Pretty simple to fix.

Tags:

Using WordPress’ Built-in Media Upload

September 27th, 2010  |  Published in Mind

If you found this post, useful, please consider donating 1$ to help fund my and my wife’s adoption… click here for details.

I was working with a client a few weeks ago. They wanted to create a customer portal that allowed their customers to upload files to their website. Once their clients have uploaded the files, they needed to be able to download and delete the files from the server. There were a few plugins in the WordPress repository that had some form of media uploading, but none of them seemed to use WordPress’ built-in functions. So, I dug in a bit and figured out how WordPress does it so I could easily replicate that functionality.

The main function that to know about is media_handle_upload(). It is the bread and butter of WordPress’ media upload. It takes up to 4 arguments and returns one variable.

From the media.php file:

@param string $file_id Index into the {@link $_FILES} array of the upload
@param int $post_id The post ID the media is associated with
@param array $post_data allows you to overwrite some of the attachment
@param array $overrides allows you to override the {@link wp_handle_upload()} behavior
@return int the ID of the attachment

For my basic needs, I only needed to worry about the first two arguments, $file_id and $post_id … and really the the $post_id is optional unless you want to associate the file as an attachment to a specific post (which I did).

The media_handle_upload() function is is part of wp-admin, so for good measure I included wp-admin/includes/admin.php. This probably isn’t necessary if you’re in the backend — but if you have a front-end page that your logged in clients will use, then you will need to do the same.

What The Client Needed

A membership portal that allowed clients to log in, go to the “upload” section, and upload a file. Then to be able to go to a “files” section to retrieve and delete the files. To do this I created two theme templates, “Client Files” and “Client File Upload”.

File Upload

First a simple form with a file input…

	<form id="file-form" enctype="multipart/form-data" action="<?php echo $_SERVER['REQUEST_URI']; ?>" method="POST">
	<p id="async-upload-wrap">
	<label for="async-upload">Upload</label>
	<input type="file" id="async-upload" name="async-upload"> <input type="submit" value="Upload" name="html-upload">
	</p>

	<p>
	<input type="hidden" name="post_id" id="post_id" value="1199" />
	<?php wp_nonce_field('client-file-upload'); ?>
	<input type="hidden" name="redirect_to" value="<?php echo $_SERVER['REQUEST_URI']; ?>" />
	</p>

	<p>
	<input type="submit" value="Save all changes" name="save" style="display: none;">
	</p>
	</form>

The form calls itself, so I needed a function to check if the form has been submitted and calls the media_handle_upload() function…

if ( isset( $_POST['html-upload'] ) && !empty( $_FILES ) ) {
	require_once(ABSPATH . 'wp-admin/includes/admin.php');
	$id = media_handle_upload('async-upload', 1199); //post id of Client Files page
	unset($_FILES);
	if ( is_wp_error($id) ) {
		$errors['upload_error'] = $id;
		$id = false;
	}

	if ($errors) {
		echo "<p>There was an error uploading your file.</p>";
	} else {
		echo "<p>Your file has been uploaded.</p>";
	}
}

I chose to hard code the post ID 1199 in this code. Not the best solution, but it serves its purpose. 1199 is the page ID of the page I created to have all the attachments associated with. This solves the next problem, how to display all the uploaded files. This way my client can download and delete them the files from an easy to access page.

Client Files

The client files page is another template, I actually set it as a private page and added some permissions so only the administrator can see the page. Since all the attachments uploaded should be associated with this page, I needed to create a special query that got all those attachments and display them in a neatly organized table…

$args = array(
	'post_type' => 'attachment',
	'numberposts' => null,
	'post_status' => null,
	'post_parent' => 1199 //Post ID of Client Files page
);
$attachments = get_posts($args);

if ($attachments) { ?>
<form id="file-form" action="<?php echo $_SERVER['REQUEST_URI']; ?>" method="POST">
	<table style="width: 100%;">
	<tr>
		<td style="width: 50px; text-align:center;">Delete?</td>
		<td style="width: 150px;">Attachment</td>
		<td style="width: 50px;">Username</td>
		<td style="width: 150px;">Name</td>
	</tr>

	<?php
	foreach ($attachments as $attachment) {
	?>
	<tr>
		<td style="text-align:center;"><input type="checkbox" id="attachment_id" name="attachment_ids[]" value="<?php echo $attachment->ID ?>" /></td>
		<td><a href="<?php get_the_attachment_link($attachment->ID); ?>"><?php echo apply_filters('the_title', $attachment->post_title); ?></a></td>
		<td><?php the_author_meta( 'user_login', $attachment->post_author ); ?></td>
		<td><?php the_author_meta( 'first_name', $attachment->post_author ); ?> <?php the_author_meta( 'last_name', $attachment->post_author ); ?></td>
	</tr>
<?php
}
?>

	<tr>
		<td colspan="4">
		<input type="submit" value="Delete" name="html-delete">
		</td>
	</tr>
</table>

Just like the previous template, this form calls itself whenever the client tries to delete a file. So I needed to add a test to see if the delete function was called with the attachment ID(s) to delete. The form is using an array, so the client can delete multiple files at once.

if ( isset( $_POST['html-delete'] ) ) {
	if ( isset( $_POST['attachment_ids'] ) ) {
		foreach ($_POST['attachment_ids'] as $id) {
			wp_delete_attachment($id, true);
		}
	}
}

And that’s it… it is actually quite simple. Using the media_handle_upload() function reduces a lot of extra work. There is no sense in re-inventing the wheel and usually with WordPress you don’t have to.

I’ve stripped down this code a bit for this tutorial. You should be able to use it as a framework for creating your own pages. Obviously you should keep security in mind, making sure people have the right permissions to see these pages, etc.

Let me know if you have any questions, I’d love to hear your feedback!

If you found this post, useful, please consider donating 1$ to help fund my and my wife’s adoption… click here for details.

Tags: ,

Allowing Hyperlinks in Your WordPress Excerpts

September 22nd, 2010  |  Published in Mind

By default, WordPress strips out all the HTML tags from your post excerpts. I needed to allow hyperlinks, but there is a problem when WordPress tries to truncate the post’s content. The wp_trim_excerpt function is what WordPress uses to do all the trimming work, I simply copied the code, modified it, and stuck my new version in the mu-plugins directory (or you could add it to your functions.php).

The first line I needed to change was the PHP function call strip_tags(). I need to set it to allow the <a> tag… very simple to do:

Original Line:

$text = strip_tags($text);

New Line:

$text = strip_tags($text, '<a>');

The second line I needed to change was where WordPress splits the excerpt up into separate words. This was a bit tricky because you run into an issue where you could be cutting of the </a>, which could screw up your entire page. I decided the best thing to do would be to treat the entire hyperlink as a single word. So, <a href=”http://lewayotte.com/”>My Website</a>, would count as a single word. By default, WordPress creates a 55 word excerpt. That is pretty easy to change as well.

Original Line:

$words = preg_split('/[\n\r\t ]', $text, $excerpt_length + 1, PREG_SPLIT_NO_EMPTY );

New Line:

$words = preg_split('/(<a.*?a>)|\n|\r|\t|\s/', $text, $excerpt_length + 1, PREG_SPLIT_NO_EMPTY|PREG_SPLIT_DELIM_CAPTURE );

Next, you simple need to remove the current wp_trim_excerpt filter and add a new one that points to your new function. Here is what the entire block of code looks like for me:

<?php
function new_wp_trim_excerpt($text) {
	$raw_excerpt = $text;
	if ( '' == $text ) {
		$text = get_the_content('');

		$text = strip_shortcodes( $text );

		$text = apply_filters('the_content', $text);
		$text = str_replace(']]>', ']]>', $text);
		$text = strip_tags($text, '<a>');
		$excerpt_length = apply_filters('excerpt_length', 55);

		$excerpt_more = apply_filters('excerpt_more', ' ' . '[...]');
		$words = preg_split('/(<a.*?a>)|\n|\r|\t|\s/', $text, $excerpt_length + 1, PREG_SPLIT_NO_EMPTY|PREG_SPLIT_DELIM_CAPTURE );
		if ( count($words) > $excerpt_length ) {
			array_pop($words);
			$text = implode(' ', $words);
			$text = $text . $excerpt_more;
		} else {
			$text = implode(' ', $words);
		}
	}
	return apply_filters('new_wp_trim_excerpt', $text, $raw_excerpt);

}
remove_filter('get_the_excerpt', 'wp_trim_excerpt');
add_filter('get_the_excerpt', 'new_wp_trim_excerpt');

Regular Expression Explained

I changed /[\n\r\t\s ]/ to /(<a.*?a>)|\n|\r|\t|\s/ because I needed to capture everything in the <a> HTML tags and count it as a single word. \n, \r, \t, \s are all pretty basic regex characters that preg_split() is using to break up the content. (<a.*?a>) is what captures everything in the <a> tag. The .* means all “characters” adding the ? to .* makes it “non-greedy.” This for the case where there are multiple hyperlinks in the content. It prevents the regex from thinking <a href=”link1″>link 1</a>, <a href=”link2″>link 2</a> is a single word. The parenthesis simply group the <a.*?a> together, so it doesn’t try to split those items separately and then I needed to add PREG_SPLIT_DELIM_CAPTURE to preg_split, or preg_split would have just removed the link as garbage.

You should be able to use this as a pretty solid base for adding other HTML tags.

Tags: , , , , , , ,

Active Directory (LDAP) Authentication in WordPress Multi-Site

September 9th, 2010  |  Published in Mind

As many of you know, I led the team that launched the College of Education at UGA’s new website (http://www.coe.uga.edu/) which is driven by WordPress Multi-Site. The first phase is complete, and the second phase has started up. Part of their second phase is to allow a custodian from each department to edit content on their own departmental site. UGA uses Active Directory campus wide, so I thought it would be best to incorporate the WordPress authentication mechanism into UGA’s current AD implementation.

When I first researched this there weren’t many plugins for WordPress Multi-Site that handled AD/LDAP authentication. I was planning on writing one myself, but I found one yesterday by Clifton Griffin called Simple LDAP Login. I installed in on a development website, put in the necessary information and BAM it worked… but it wasn’t really designed for WordPress Multi-Site. What I needed was every site to use the Active Directory authentication, without having to set each site up individually. So I paired down the code a bit (to just what I needed) and stuck the files in the mu-plugins dir.

Clifton used the opensource PHP LDAP Class in his plugin, which works great. So, in my version, I stripped out everything I didn’t need/want like auto account creation, non-AD functionality, etc. Basically, I just needed to authenticate users via Active Directory.

So I stuck this code into a file in my mu-plugins folder. Now whenever anyone tries to authenticate it, uses Active Directory instead of WordPress’ user table:

<?php
require_once( WPMU_PLUGIN_DIR . '/simple-ldap-login/adLDAP.php' );

define( 'DOMAIN_CONTROLLERS', 	'active.directory.controller.domain' );
define( 'SECURITY_MODE', 		'high' );
define( 'ACCOUNT_SUFFIX', 		'@account.edu');
define( 'BASE_DN', 				'OU=OUOU,DC=DCDC,DC=EDU' );
define( 'USE_TLS',				false );
define( 'USE_SSL', 				true );

//Authenticate function
function sll_authenticate( $user, $username, $password ) {
	if ( is_a( $user, 'WP_User' ) ) { return $user; }

	//Failed, should we let it continue to lower priority authenticate methods?
	if( "high" === SECURITY_MODE ) {
		remove_filter('authenticate', 'wp_authenticate_username_password', 20, 3);
	}

	if ( empty( $username ) || empty( $password ) ) {
		$error = new WP_Error();

		if ( empty( $username ) )
			$error->add( 'empty_username', __( 'ERROR: The username field is empty.' ) );

		if ( empty($password) )
			$error->add( 'empty_password', __( 'ERROR: The password field is empty.' ) );

		return $error;
	}

	if( sll_can_authenticate( $username, $password ) ) {
			$user = get_userdatabylogin( $username );

			if ( !$user || ( strtolower( $user->user_login ) != strtolower( $username ) ) ) {
				do_action( 'wp_login_failed', $username );
				return new WP_Error( 'invalid_username', __( 'Login Error An error occurred while attempting to log in. If this continues please contact coeweb@uga.edu for support.' ) );
			} else {
				//we're ready to return the user
				return new WP_User( $user->ID );
			}
	} else {
		return new WP_Error( 'invalid_username', __( 'Login Error An error occurred while attempting to log in. If this continues please contact coeweb@uga.edu for support.' ) );
	}
}
add_filter( 'authenticate', 'sll_authenticate', 1, 3 );

function sll_can_authenticate( $username, $password ) {
	$sll_options = array(
		'account_suffix'		=>	ACCOUNT_SUFFIX,
		'base_dn'				=>	BASE_DN,
		'use_tls'				=>	USE_TLS,
		'use_ssl'				=>	USE_SSL,
		'domain_controllers'	=>	explode( ';', DOMAIN_CONTROLLERS )
	);

	$adldap = new adLDAP( $sll_options );

	$result = false;
	$result = $adldap->authenticate( $username, $password );

	return $result;
}

Tags: , , , , , ,

Certified as one of the Three Most Important People in WordPress

August 24th, 2010  |  Published in Life, Mind

I have been certified as one of the three most important people in WordPress!

From Matt Mullenweg’s hands, to your eyes; read’em and weep!

Tags: