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.