Improved Newsletter Creation

The site has a custom post type called ‘newsletter’. This is for the weekly newsletters which will be uploaded to the site. The format for this is very specific. The title will be the title of the newsletter, the content will contain a download instruction, followed by the link to the actual PDF file, and two custom fields will hold information about the memory verse of the week.

By using a custom meta box for the memory verse, the only item left that might lead to inconsistent output would be to allow the user to key in whatever they want for the download instruction. Also, the default media upload would require the user to key in the newsletter title again for the attachment to be appropriately titled. All this leads to lots of room for error.

My solution is to disable the default editor and replace it with a meta box which only allows file upload.

Making these changes required quite a few edits to the functions.php file.

Disable Editor

To disable the editor, add the key-value pair 'supports' => array('title') to the arguments array in the register_post_type() function.

Define the Meta Box

This is based on previous posts about creating meta boxes. The original code was taken from
The above link actually has a download link to a version of the code where file and image upload has been implemented. Although it’s not described in the blog post, it has actually been done.

$meta_boxes[] = array(
	'id' => 'newsletter-upload',
	'title' => 'Newsletter Upload',
	'pages' => array('newsletter'),
	'context' => 'normal',
	'priority' => 'high',
	'fields' => array(
			'name' => 'Newsletter Upload',
			'desc' => 'Select the MM to upload',
			'id' => $prefix . 'newsletter_file',
			'type' => 'file',
			'std' => ''

This declares a meta box with just a file upload field.

Change Form Type

To support file uploads, the form encoding must be declared as ‘multipart/form-data’. To do this without hacking core, jQuery is used to add the attribute and the function is hooked to ‘admin_head’.

add_action('admin_head', array(&$this, 'add_post_enctype'));

	function add_post_enctype() {
		echo '
					jQuery("#post").attr("enctype", "multipart/form-data");
					jQuery("#post").attr("encoding", "multipart/form-data");

This code is actually placed inside the My_meta_box class, as explained in the tutorial. The action add is done in the constructor.

Show the Field

The show() function must be updated to include the input ‘file’ type. The code here has been customized so it shows the link to the PDF file which is currently attached to the newsletter.

case 'file':
			//find any attachments assigned to the post
			$children = get_children(array('post_parent' => $post->ID, 'post_type' => 'attachment'));
			if($children) {
				echo 'Currently attached: 
'; foreach ($children as $child) { //there should only be 1 child anyway echo 'guid . '\'>' . $child->guid . '
'; } echo '
'; } echo $meta ? "$meta
" : '', '', '
', $field['desc']; break;

Save the Data

This code is run when the ‘Publish’ button is clicked. The original code had a strange foreach loop in it. I’m not sure what it does, but taking it out allowed my code to work when there’s only 1 file input box.

The code here as some specialized functions. Given a filename in the format prefix-date.pdf, it will rename it to my desired prefix and change the date format to yyyy-mm-dd. This supposes that the original date format is either yyyy-mm-dd or yyyy-MMM-dd. Either spaces or dashes can be used as separators in the filename. Thus the code here will accept the naming convention currently used by the church, and change it to the one used on the site.

I also want the uploads to go into the folder depending on when the file is dated and not when it is uploaded. Thus the year and month information is also used to tell WordPress where to upload the file to. To do this, a parameter $time in the format yyyy-mm must be passed to the wp_handle_upload() function.

Next, if a new attachment is added when is already attached to the post, I want the old one to be unattached. Thus each newsletter post can only have one attached file. Unattaching the old ones allows them to show up as unattached in the media library. Attachments should also take on the title of the parent post. When unattached, their title reverts to one based on their actual filename.

Finally, the content of the newsletter post must be in a specific format. This is dynamically generated with the path to the attached file and the title of the post. The post is then updated with wp_update_post()

if ($field['type'] == 'file' || $field['type'] == 'image') {
			if (!empty($_FILES[$name])) {
				//get the name of the uploaded file, without the extension
				$uploaded_file = $_FILES[$name]['name'];
				$uploaded_file = basename($uploaded_file);
				//replace spaces with '-' to facilitate year/mth extraction
				$uploaded_file = str_replace(' ', '-', $uploaded_file);		

				$name_parts = explode('-', $uploaded_file);
				if( !is_numeric( $name_parts[2] ) ) {	//assume 3 letter month, replace with 2 digit number
					$month_names = array("JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP",
											"OCT", "NOV", "DEC");
					$name_parts[2] = strtoupper($name_parts[2]);
					$name_parts[2] = array_search($name_parts[2], $month_names) + 1;
					$name_parts[2] = sprintf('%02u', $name_parts[2]);
				$time = $name_parts[1] . '-' . $name_parts[2];		//time is in yyyy-mm format

				if(strtoupper($name_parts[0]) == 'WEEKLY') {		//rename weekly to mm
					$name_parts[0] = 'mm';

				if(preg_match('/[0-9]{4}-[0-1][0-9]/', $time) == 0) {		//time does not match format
					$time = null;
				$_FILES[$name]['name'] = implode('-', $name_parts);			//rename file

				$file = wp_handle_upload($_FILES[$name], array('test_form' => false), $time );
				$filename = $file['url'];
				if (!empty($filename)) {
					$currPost = get_post($post_id);

					//unattach current attachment, if any
					$children = get_children(array('post_parent' => $post_id, 'post_type' => 'attachment'));
					if($children) {
						foreach($children as $child) {
							$path_parts = pathinfo($child->guid);
							wp_update_post(array('ID' => $child->ID, 'post_parent' => 0, 
									'post_name' => $path_parts['filename'], 
									'post_title' => $path_parts['filename']));

					//attach attachment post to the main parent post
					$wp_filetype = wp_check_filetype(basename($filename), null);
					$attachment = array(
							'post_mime_type' => $wp_filetype['type'],
							//	'post_title' => preg_replace('/\.[^.]+$/', '', basename($filename)),
							'post_status' => 'inherit',
							'guid' => $filename,
							'post_title' => $currPost -> post_title
					$attach_id = wp_insert_attachment($attachment, $filename, $post_id);
					// you must first include the image.php file
					// for the function wp_generate_attachment_metadata() to work
					require_once(ABSPATH . 'wp-admin/includes/image.php');
					$attach_data = wp_generate_attachment_metadata($attach_id, $filename);
					wp_update_attachment_metadata($attach_id, $attach_data);

					//update post content with the download text and link to the file
					$post_content = 'Download MM: ' . $currPost -> post_title . '';
					wp_update_post(array('ID' => $post_id, 'post_content' => $post_content));

Doing this was a lot of work and took up the whole of Saturday just to get right, but having this functionality in place finally makes it possible to pass on some of the updating work to other people.


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s