bug 558508, add/remove userpics.

This commit is contained in:
Dave Dash 2010-08-20 13:45:53 -07:00
Родитель 366efe5ef5
Коммит a68f534fb2
8 изменённых файлов: 152 добавлений и 30 удалений

Просмотреть файл

@ -8,9 +8,10 @@ from django.forms.util import ErrorList
import captcha.fields
import commonware.log
import happyforms
from tower import ugettext as _
from tower import ugettext as _, ugettext_lazy as _lazy
from .models import UserProfile, BlacklistedUsername
import tasks
log = commonware.log.getLogger('z.users')
@ -125,6 +126,8 @@ class UserEditForm(UserRegisterForm):
password2 = forms.CharField(max_length=255, required=False,
widget=forms.PasswordInput(render_value=False))
photo = forms.FileField(label=_lazy('Profile Photo'), required=False)
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
super(UserEditForm, self).__init__(*args, **kwargs)
@ -155,18 +158,49 @@ class UserEditForm(UserRegisterForm):
super(UserEditForm, self).clean()
return data
def clean_photo(self):
photo = self.cleaned_data['photo']
if not photo:
return
if photo.content_type not in ('image/png', 'image/jpeg'):
raise forms.ValidationError(
_('Images must be either PNG or JPG.'))
if photo.size > settings.MAX_PHOTO_UPLOAD_SIZE:
raise forms.ValidationError(
_('Please use images smaller than %dMB.' %
(settings.MAX_PHOTO_UPLOAD_SIZE / 1024 / 1024 - 1)))
return photo
def save(self):
super(UserEditForm, self).save()
u = super(UserEditForm, self).save(commit=False)
data = self.cleaned_data
amouser = self.request.user.get_profile()
photo = data['photo']
if photo:
u.picture_type = 'image/png'
tmp_destination = u.picture_path + '__unconverted'
if not os.path.exists(u.picture_dir):
os.mkdir(u.picture_dir)
fh = open(tmp_destination, 'w')
for chunk in photo.chunks():
fh.write(chunk)
fh.close()
tasks.resize_photo.delay(tmp_destination, u.picture_path)
if data['password']:
amouser.set_password(data['password'])
log.info(u'User (%s) changed their password' % amouser)
u.set_password(data['password'])
log.info(u'User (%s) changed their password' % u)
log.debug(u'User (%s) updated their profile' % amouser)
log.debug(u'User (%s) updated their profile' % u)
amouser.save()
u.save()
return u
class BlacklistedUsernameAddForm(forms.Form):

Просмотреть файл

@ -1,5 +1,6 @@
from datetime import datetime
import hashlib
import os
import random
import re
import string
@ -104,12 +105,22 @@ class UserProfile(amo.models.ModelBase):
return self.addons.valid().filter(addonuser__listed=True).distinct()
@property
def picture_url(self):
def picture_dir(self):
split_id = re.match(r'((\d*?)(\d{0,3}?))\d{1,3}$', str(self.id))
return os.path.join(settings.USERPICS_PATH, split_id.group(2) or 0,
split_id.group(1) or 0)
@property
def picture_path(self):
return os.path.join(self.picture_dir, str(self.id) + '.png')
@property
def picture_url(self):
if not self.picture_type:
return settings.MEDIA_URL + '/img/zamboni/anon_user.png'
else:
return settings.USER_PIC_URL % (
split_id = re.match(r'((\d*?)(\d{0,3}?))\d{1,3}$', str(self.id))
return settings.USERPICS_URL % (
split_id.group(2) or 0, split_id.group(1) or 0, self.id,
int(time.mktime(self.modified.timetuple())))

Просмотреть файл

@ -1,10 +1,14 @@
import os
import random
from django.conf import settings
from django.contrib.auth.models import User as DjangoUser
from django.db import IntegrityError
import commonware.log
from celery.decorators import task
from easy_thumbnails import processors
from PIL import Image
from . import cron
from amo.utils import slugify
@ -32,19 +36,19 @@ def add_usernames(data, **kw):
try:
UserProfile.objects.filter(id=user[0]).update(username=name_slug,
display_name=name)
except IntegrityError, e:
except IntegrityError:
try:
name_slug = "%s%s" % (name_slug, user[0])
if not len(name_slug) > 10:
# This can happen if they have a blank name_slug and then
# there is already a username in the system that corresponds
# to their user id. It's a total edge case.
name_slug = "%s%s" % (random.randint(1000,100000),
# there is already a username in the system that
# corresponds to their user id. It's a total edge case.
name_slug = "%s%s" % (random.randint(1000, 100000),
name_slug)
UserProfile.objects.filter(id=user[0]).update(username=name_slug,
display_name=name)
except IntegrityError, e:
UserProfile.objects.filter(id=user[0]).update(
username=name_slug, display_name=name)
except IntegrityError:
task_log.error(u"""F-F-Fail! I tried setting a user's (id:%s)
username to to %s and it was already taken. This should never
happen.""" % (user[0], name_slug))
@ -60,3 +64,32 @@ def _delete_users(data, **kw):
UserProfile.objects.filter(pk__in=data).delete()
DjangoUser.objects.filter(pk__in=data).delete()
@task
def delete_photo(dst):
task_log.info('[%s@%s] Deleting photo.' % (dst, delete_photo.rate_limit))
if not dst.startswith(settings.USERPICS_PATH):
task_log.error("Someone tried deleting something they shouldn't: %s"
% dst)
return
try:
os.remove(dst)
except Exception, e:
task_log.error("Error deleting userpic: %s" % e)
@task
def resize_photo(src, dst):
"""Resizes userpics to 200x200"""
task_log.info('[%s@%s] Resizing photo.' % (dst, resize_photo.rate_limit))
try:
im = Image.open(src)
im = processors.scale_and_crop(im, (200, 200))
im.save(dst)
os.remove(src)
except Exception, e:
task_log.error("Error saving userpic: %s" % e)

Просмотреть файл

@ -0,0 +1,21 @@
{% extends "base.html" %}
{% block title %}{{ page_title(_('Delete User Photo')) }}{% endblock %}
{% block content %}
<div class="primary" role="main">
<div class="primary">
<h2>{{ _('Delete User Photo') }}</h2>
<form method="post" action="{{ url('users.delete_photo') }}"
class="featured-inner object-lead user-input">
{{ csrf() }}
<div class="fm-control">
<button type="submit">{{ _('Delete my user picture now') }}</button>
</div>
</form>
</div>
</div>{# .primary #}
{% endblock content %}

Просмотреть файл

@ -17,7 +17,8 @@
{% endfor %}
{% endif %}
<h1>{{ _('Account Settings') }}</h1>
<form method="post" action="" class="user-input">
<form method="post" action="" class="user-input"
enctype="multipart/form-data">
{{ csrf() }}
<div id="user-edit" class="tab-wrapper">
<ul class="tab-nav main">
@ -73,8 +74,9 @@
<legend>{{ _('Notifications') }}</legend>
<p>
{% trans %}
From time to time, Mozilla may send you email about upcoming releases
and add-on events. Please select the topics you are interested in below:
From time to time, Mozilla may send you email about upcoming
releases and add-on events. Please select the topics you are
interested in below:
{% endtrans %}
</p>
<ul>
@ -95,8 +97,8 @@
</ul>
<p class="note">
{% trans %}
Mozilla reserves the right to contact you individually about specific
concerns with your hosted add-ons.
Mozilla reserves the right to contact you individually about
specific concerns with your hosted add-ons.
{% endtrans %}
</p>
</fieldset>
@ -126,12 +128,15 @@
{{ form.homepage.errors|safe }}
</li>
<li class="profile-photo">
{# TODO XXX: Change these fields out for whatever we need to do notifications #}
{# TODO XXX: Add L10n #}
<label for="id_photo">Profile Photo</label>
<input type="file" id="id_photo" name="photo">
<img src="../media/img/amo2009/site-images/avatar-developer-200x200.jpg" alt="" width="200" height="200" class="avatar photo-large photo" />
<a href="#">Delete current photo</a>
<label for="id_photo">{{ _('Profile Photo') }}</label>
<input type="file" id="id_photo" name="photo">
{{ form.photo.errors|safe }}
<img src="{{ amouser.picture_url }}" alt=""
class="avatar photo-large photo" />
{% if amouser.picture_type %}
<a href="{{ url('users.delete_photo') }}">{{ _('Delete current photo') }}</a>
{% endif %}
</li>
</ol>
</fieldset>

Просмотреть файл

@ -21,6 +21,7 @@ detail_patterns = patterns('',
users_patterns = patterns('',
url('^ajax$', views.ajax, name='users.ajax'),
url('^delete$', views.delete, name='users.delete'),
url('^delete_photo$', views.delete_photo, name='users.delete_photo'),
url('^edit$', views.edit, name='users.edit'),
url('^login', views.login, name='users.login'),
url('^logout', views.logout, name='users.logout'),

Просмотреть файл

@ -21,6 +21,7 @@ from .models import UserProfile
from .signals import logged_out
from .users import forms
from .utils import EmailResetCode
import tasks
log = commonware.log.getLogger('z.users')
@ -76,7 +77,7 @@ def confirm_resend(request, user_id):
@login_required
def delete(request):
amouser = request.user.get_profile()
amouser = request.amo_user
if request.method == 'POST':
form = forms.UserDeleteForm(request.POST, request=request)
if form.is_valid():
@ -92,6 +93,21 @@ def delete(request):
{'form': form, 'amouser': amouser})
@login_required
def delete_photo(request):
u = request.amo_user
if request.method == 'POST':
u.picture_type = ''
u.save()
tasks.delete_photo.delay(u.picture_path)
messages.success(request, _('Photo Deleted'))
return http.HttpResponseRedirect(reverse('users.edit') +
'#user-profile')
return jingo.render(request, 'users/delete_photo.html', dict(user=u))
@login_required
def edit(request):
amouser = request.user.get_profile()
@ -99,7 +115,7 @@ def edit(request):
# ModelForm alters the instance you pass in. We need to keep a copy
# around in case we need to use it below (to email the user)
original_email = amouser.email
form = forms.UserEditForm(request.POST, request=request,
form = forms.UserEditForm(request.POST, request.FILES, request=request,
instance=amouser)
if form.is_valid():
messages.success(request, _('Profile Updated'))

Просмотреть файл

@ -403,7 +403,7 @@ PREVIEW_THUMBNAIL_URL = (STATIC_URL +
'/img/uploads/previews/thumbs/%s/%d.png?modified=%d')
PREVIEW_FULL_URL = (STATIC_URL +
'/img/uploads/previews/full/%s/%d.png?modified=%d')
USER_PIC_URL = STATIC_URL + '/img/uploads/userpics/%s/%s/%s.png?modified=%d'
USERPICS_URL = STATIC_URL + '/img/uploads/userpics/%s/%s/%s.png?modified=%d'
# paths for uploaded extensions
FILES_URL = STATIC_URL + "/%s/%s/downloads/file/%d/%s?src=%s"
COLLECTION_ICON_URL = ('%s/%s/%s/images/collection_icon/%%s/%%s' %
@ -536,6 +536,7 @@ def read_only_mode(env):
# Uploaded file limits
MAX_ICON_UPLOAD_SIZE = 4 * 1024 * 1024
MAX_PHOTO_UPLOAD_SIZE = MAX_ICON_UPLOAD_SIZE
## Feature switches
# Use this to keep collections compatible with remora before we're ready to