HOWTO: Liberating the User Picture Upload Interface
Navigation : Back to Blog
Tags :
One of the things that makes Drupal such a powerful platform for building community websites is its robust and scalable user system. Its got everything you need to start popin' and lockin' built right into the core framework: unlimited user roles, granular permissions, easily extensible profiles and more.
Still, as hookable and mashup-friendly as it is, there are a number of things that Drupal core has a stubborn hold on. One of these is the user-picture (aka avatar) upload interface. The picture system is very well implemented -- it will resize uploaded images, display them in posts and comments automagically, etc -- but the user-interface for you to actually put up your picture is locked onto the catch-all "account settings" part of the user/edit interface, which can get pretty cluttered if you've got a lot of modules throwing their settings in there.
In future (5.0+) revisions, I'm sure that the already-awesome profile system will grow and allow site admins to easily reposition all user-account elements, but yesterday I had a need to move that user-picture upload field into it's own page, and so I did. Less than 50 lines of code, too. Here's how:
Step 1: Create a new section in the user/edit interface
For my application, I wanted to put the user-picture upload off on it's own section of the user/edit interface, on it's own "category" in Drupal's user/edit lingo. This would separate it from the "account settings" clutter, and also give me a place to expand if I want to add other user-picture features down the line.
The first order of business is making a new category. For this, I used hook_user and returned some data when the $op was looking for "categories":
<span style="color: #000000"><span style="color: #0000BB"><?php<br /></span><span style="color: #007700">function </span><span style="color: #0000BB">avatar_user</span><span style="color: #007700">(</span><span style="color: #0000BB">$op</span><span style="color: #007700">, &</span><span style="color: #0000BB">$edit</span><span style="color: #007700">, &</span><span style="color: #0000BB">$account</span><span style="color: #007700">, </span><span style="color: #0000BB">$category </span><span style="color: #007700">= </span><span style="color: #0000BB">NULL</span><span style="color: #007700">) {<br /> if (</span><span style="color: #0000BB">$op </span><span style="color: #007700">== </span><span style="color: #DD0000">'categories'</span><span style="color: #007700">) {<br /> </span><span style="color: #0000BB">$categories </span><span style="color: #007700">= array(array(</span><span style="color: #DD0000">'name' </span><span style="color: #007700">=> </span><span style="color: #DD0000">'avatar'</span><span style="color: #007700">, </span><span style="color: #DD0000">'title' </span><span style="color: #007700">=> </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'picture'</span><span style="color: #007700">), </span><span style="color: #DD0000">'weight' </span><span style="color: #007700">=> </span><span style="color: #0000BB">10</span><span style="color: #007700">));<br /> return </span><span style="color: #0000BB">$categories</span><span style="color: #007700">;<br /> }<br />}<br /></span><span style="color: #0000BB">?></span></span>The data structure you return is a little complex there. You return an array of associative arrays, with each associative array defining a new category. You set an internal name, a human readable title, a weight to help set the order when there are multiple categories. Since I only want one new category, I have an array of one arrays, as you can see.
This sets me up to now tell drupal to display a form when the user clicks on this category.
Step 2: Define your form
Within the same instance of hook_user, I added the following code:
<span style="color: #000000"><span style="color: #0000BB"><?php<br /></span><span style="color: #007700">if (</span><span style="color: #0000BB">$op </span><span style="color: #007700">== </span><span style="color: #DD0000">'form' </span><span style="color: #007700">&& </span><span style="color: #0000BB">$category </span><span style="color: #007700">== </span><span style="color: #DD0000">'avatar'</span><span style="color: #007700">) {<br /> </span><span style="color: #0000BB">$form </span><span style="color: #007700">= array();<br /> </span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'avatar'</span><span style="color: #007700">] = array(<br /> </span><span style="color: #DD0000">'#type' </span><span style="color: #007700">=> </span><span style="color: #DD0000">'fieldset'</span><span style="color: #007700">,<br /> );<br /> </span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'avatar'</span><span style="color: #007700">][</span><span style="color: #DD0000">'preview'</span><span style="color: #007700">] = array(<br /> </span><span style="color: #DD0000">'#type' </span><span style="color: #007700">=> </span><span style="color: #DD0000">'markup'</span><span style="color: #007700">,<br /> </span><span style="color: #DD0000">'#value' </span><span style="color: #007700">=> </span><span style="color: #0000BB">theme</span><span style="color: #007700">(</span><span style="color: #DD0000">'user_picture'</span><span style="color: #007700">, </span><span style="color: #0000BB">$account</span><span style="color: #007700">),<br /> </span><span style="color: #DD0000">'#prefix' </span><span style="color: #007700">=> </span><span style="color: #DD0000">'<h2>'</span><span style="color: #007700">.</span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Your current picture'</span><span style="color: #007700">).</span><span style="color: #DD0000">'</h2>'</span><span style="color: #007700">,<br /> );<br /> </span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'avatar'</span><span style="color: #007700">][</span><span style="color: #DD0000">'picture_upload'</span><span style="color: #007700">] = array(<br /> </span><span style="color: #DD0000">'#type' </span><span style="color: #007700">=> </span><span style="color: #DD0000">'file'</span><span style="color: #007700">,<br /> </span><span style="color: #DD0000">'#title' </span><span style="color: #007700">=> </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Upload a new picture'</span><span style="color: #007700">),<br /> </span><span style="color: #DD0000">'#size' </span><span style="color: #007700">=> </span><span style="color: #0000BB">20</span><span style="color: #007700">,<br /> );<br /> return </span><span style="color: #0000BB">$form</span><span style="color: #007700">;<br /> }<br /></span><span style="color: #0000BB">?></span></span>When the $op is "form," Drupal listens for a Form API-style array to render on that category page of the user/edit interface. You can also use this to extend the form on other category pages, including the default "account settings" page.
What I've defined here tells the "avatar" category to display the current user-picture via a "markup" form element, and then defines a file field for uploading a new one. I've borrowed heavily from the core user.module and kept it simple, but this could someday expand to include other picture-related options, such as multiple images, a caption, etc.
With this in place, can now move on to handling the submitted form values.
Step 3: Handling form values
Again, we're utilizing hook_user, this time to deal with a submitted form by means of the "validate" $op value:
<span style="color: #000000"><span style="color: #0000BB"><?php<br /></span><span style="color: #007700">if (</span><span style="color: #0000BB">$op </span><span style="color: #007700">== </span><span style="color: #DD0000">'validate' </span><span style="color: #007700">&& </span><span style="color: #0000BB">$category </span><span style="color: #007700">== </span><span style="color: #DD0000">'avatar'</span><span style="color: #007700">) {<br /> if (</span><span style="color: #0000BB">$file </span><span style="color: #007700">= </span><span style="color: #0000BB">file_check_upload</span><span style="color: #007700">(</span><span style="color: #DD0000">'picture_upload'</span><span style="color: #007700">)) {<br /> </span><span style="color: #0000BB">user_validate_picture</span><span style="color: #007700">(</span><span style="color: #0000BB">$file</span><span style="color: #007700">, </span><span style="color: #0000BB">$edit</span><span style="color: #007700">, </span><span style="color: #0000BB">$account</span><span style="color: #007700">);<br /> }<br /> }<br /></span><span style="color: #0000BB">?></span></span>This seems too easy to be true, but it isn't. Since I've stuck with the established style for Drupal's default user-picture upload, and I don't want to change how the stored pictures are handled (just where the upload interface lives), I can reuse the function in the core user.module to handle the form values.
If I wanted to handle multiple images or additional data, this would have to be re-written and expanded, but for now we can take advantage of the functionality Drupal already offers to finish up our user/edit category.
Step 4: Hiding the old form elements
One last detail is to get rid of the old user-picture upload interface. It will still work fine, but it would be confusing to users to have this interface in two places. Luckily, there's no need to hack at Drupal core's user.module thanks again to Form API and the form_alter hook:
<span style="color: #000000"><span style="color: #0000BB"><?php<br /></span><span style="color: #007700">function </span><span style="color: #0000BB">avatar_form_alter</span><span style="color: #007700">(</span><span style="color: #0000BB">$form_id</span><span style="color: #007700">, &</span><span style="color: #0000BB">$form</span><span style="color: #007700">) {<br /> if(</span><span style="color: #0000BB">$form_id </span><span style="color: #007700">== </span><span style="color: #DD0000">'user_edit' </span><span style="color: #007700">&& </span><span style="color: #0000BB">arg</span><span style="color: #007700">(</span><span style="color: #0000BB">3</span><span style="color: #007700">) == </span><span style="color: #0000BB">NULL</span><span style="color: #007700">) {<br /> </span><span style="color: #FF8000">// only fire this if it's user/<uid>/edit, not any other category<br /> </span><span style="color: #007700">unset(</span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'picture'</span><span style="color: #007700">]);<br /> }<br />}<br /></span><span style="color: #0000BB">?></span></span>This clears out the old user-picture upload interface, preventing user confusion.
Guess what? You're done! You can download the complete module code below. Share and enjoy!
Comments
Post a comment
Presentations
What we've coded
Videos
Upcoming workshops
-
San Francisco, CAFebruary 28, 2012
-
Denver, COMarch 19, 2012








http://www.chapterthreellc.com/files/avatar.module
Forbidden
You don't have permission to access /files/avatar.module on this server.
:)
Yes, thats set in drupal's .htaccess file
Cool stuff! Do you intend to always host the module here, or can we look forward to a Drupal.org project for it? Also, I missed whether this is a 4.7 or 5.0 module? Great work =)
Haha... Drupal's being smart. It thinks you're trying to get at my code. I've replaced that with a gzip version.
And I do intend to check this into cvs, but first I'm going to add a few features.
Great tutorial - works a treat and solved a number of other problems too.
What i'd like to do is put the picture upload tab somewhere else completely, i'm using bio module and would like to put it as a sub tab of that module - any idea how to do that.
I want an anonymous user to be able to create a page and have there pic added to the post if they want.
First, I'm so sorry for acting on such an old post. However since Drupal 6, it is still being difficult to move the user picture out of the settings tab. I've tried for hours to get this working for Drupal 6. The validation has changed as well as user_edit_form has gone through some minor changes.
Could anyway please help modify this code for Drupal 6 as the settings page is being cramped by other modules making the user picture upload form very inaccessible.
Regards,
Marius
I'm on Drupal 5.19
I inserted these into my theme's template.php , but nothing happened.
I googled thoroughly for a solution to this (because this is THE main reason why people don't upload their pictures), but I can't find any tutorial apart from this one.
I don't want to hack the core, because with every upgrade, all hacks disappear. A custom module for 5.x and 6.x would be most appreciated!!!
Please help!!!
This post is almost three years old, so I can't vouch for any literal copy/paste usability. However, the basic technique is still valid in 5.x and 6.x, just slightly different.
ホームページ製作
ホームページ制作
ホームページ作成
ホームページ作成ソフト
ホームページセミナー
CMS
ビジネスブログ
There are so many custom t-shirt companies online that it can be hard to find one that offers fair pricing and reputable services. moncler AtCost1.com is one of the best custom t-shirt printers out there as we can handle just about every custom t-shirt job or custom sweatshirt job you have. moncler Boutique We provide you with higher quality products that are made to make your company stand out and to help your easily brand your company in a hurry. moncler pas cher AtCost1.com offers screen printing and embroidery work, both of which are far superior to making a digital t-shirt with an inkjet printer. moncler prix We only provide you with the best quality materials to work with as well in order to make your t-shirts last. moncler soldes From t-shirts that are heavy duty stain resistant to cheap promotional t-shirts, AtCost1.com has the t-shirt options you need to make your custom t-shirts marketable and one of a kind. moncler paris Printing custom t-shirts should not cost you an arm and a leg. Atcost1.com has affordable pricing for your custom t-shirt needs as we want you to be able to proudly display them and to actually afford them. moncler polo Our employees review every single t-shirt order before they are sent to our clients to ensure they are perfect. moncler veste We will never ship out an order to a client if we would not feel comfortable wearing the custom t-shirts. moncler doudoune If you need more than custom t-shirts, Atcost1.com has many other options to choose from. We also specialize in providing customers with custom sweatshirts, blankets, hats, polo shirts, and many other options. moncler homme To get an idea of the different thingss we can do for your company, take a look at our online inventory to view additional custom t-shirts and products.
Hi Josh,
I've been trying to have the user picture in a different section, away from user settings just like you are doing for previous versions of Drupal. Have you figure out the way of doing this in D6 ? Thanks!