Skip to content
Browse files

Get photos working.

  • Loading branch information...
1 parent a928c7d commit 5b5b831a7476fbe0cb2b4c75bc951ebdd70d6179 @davedash davedash committed Dec 27, 2011
View
3 .gitignore
@@ -22,4 +22,5 @@ puppet/manifests/build/
vagrantconfig_local.yaml
docs/_build
settings/*local.py
-directory
+directory
+media/uploads
View
18 apps/phonebook/forms.py
@@ -1,3 +1,4 @@
+import os
import re
import tempfile
@@ -7,6 +8,7 @@
import happyforms
import Image
from easy_thumbnails import processors
+from statsd import statsd
from tower import ugettext as _, ugettext_lazy as _lazy
from phonebook.models import Invite
@@ -121,11 +123,21 @@ def save(self, request):
user.last_name = d['last_name']
profile.bio = d['biography']
- # TODO: save/delete photo data...
- # photo
- # photo_delete
profile.ircname = d['irc_nickname']
profile.website = d['website']
+
+ if d['photo']:
+ profile.photo = True
+ with open(profile.get_photo_file(), 'w') as f:
+ f.write(d['photo'].file.read())
+
+ if d['photo_delete']:
+ profile.photo = False
+ try:
+ os.remove(profile.get_photo_file())
+ except OSError:
+ statsd.incr('errors.photo.deletion')
+
profile.save()
user.save()
View
16 apps/phonebook/helpers.py
@@ -1,6 +1,8 @@
import re
import time
+from django.conf import settings
+
import jinja2
from funfactory.urlresolvers import reverse
from funfactory.utils import absolutify
@@ -20,18 +22,16 @@ def paragraphize(value):
@register.inclusion_tag('phonebook/includes/photo.html')
@jinja2.contextfunction
-def profile_photo(context, person):
+def profile_photo(context, profile):
user = context['request'].user
- me = bool(user.username == person.username)
- url = reverse('phonebook.profile_photo', args=[person.unique_id])
-
- if me:
- url += '?%d' % int(time.time())
+ cachebust = profile.photo and bool(user.pk == profile.user_id)
- return dict(image_url=url)
+ return dict(image_url=profile.get_photo_url(cachebust=cachebust))
@register.inclusion_tag('phonebook/includes/search_result.html')
@jinja2.contextfunction
def search_result(context, profile):
- return dict(profile=profile)
+ d = dict(context.items())
+ d.update(profile=profile)
+ return d
View
6 apps/phonebook/templates/phonebook/edit_profile.html
@@ -57,7 +57,7 @@
{% if mode != 'new' %}
<div id="photo-container"
class="{{ 'error' if form.errors['photo'] }}">
- {{ profile_photo(person) }}
+ {{ profile_photo(user.get_profile()) }}
{{ form['photo'].label_tag() }}
{{ form.errors['photo'] }}
@@ -79,7 +79,7 @@
<div class="clear"></div>
</div>
{% endif %}
- {% if photo %}
+ {% if user.get_profile().photo %}
<div id="photo-delete-container" class="field">
{{ form['photo_delete'].label_tag() }}
@@ -138,7 +138,7 @@
{% if intent %} {# BrowserID Sign-up #}
<a href="{{ url('home') }}" id="cancel">
{% else %}
- <a href="{{ url('profile', user.unique_id) }}" id="cancel">
+ <a href="{{ url('profile', user.username) }}" id="cancel">
{% endif %}
{{ _('Cancel') }}{# L10n: Cancel editing #}
</a>
View
6 apps/phonebook/templates/phonebook/includes/search_result.html
@@ -1,10 +1,6 @@
-{# TODO We should reuse microformats like Domesday prototype #}
<div class="result">
<div class="avatar">
- {# TODO: FIX
- <img src="{{ url('phonebook.profile_photo', profile.user_id) }}"
- alt="{{ _('Profile photo') }}" class="profile-photo">
- #}
+ {{ profile_photo(profile) }}
</div>
<div class="details">
<h2><a href="{{url('profile', profile.user.username) }}">
View
3 apps/phonebook/templates/phonebook/profile.html
@@ -57,8 +57,7 @@
<div class="clear"></div>
<div class="blue-pastels vcard h-card">
- {# TODO: we need to get this from the filesystem #}
- {# {{ profile_photo(profile) }} #}
+ {{ profile_photo(profile) }}
{% if user.username == request.user.username %}
<a href="{{ url('phonebook.edit_profile') }}"
View
54 apps/phonebook/tests/test_views.py
@@ -2,6 +2,7 @@
from uuid import uuid4
from django import test
+from django.conf import settings
from django.contrib.auth.models import User
import test_utils
@@ -197,26 +198,43 @@ def test_profile_photo(self):
Test the upload, encoding, and removal of photo profiles. Also make
sure edge cases (from naughty user input) and HTML elements work
properly.
+
+ .. note::
+
+ This does not test that the web server is serving the files from
+ the filesystem properly.
"""
- raise SkipTest
client = self.mozillian_client
+ def assert_no_photo():
+ """This will assert that a user is in a proper no userpic state.
+
+ This means:
+ * Linking to ``unknown.jpg``.
+ * No "Remove Profile Photo" link.
+ * No file on the file system.
+ """
+ r = client.get(reverse('phonebook.edit_profile'))
+ doc = pq(r.content)
+ eq_(doc('#profile-photo').attr('src'),
+ settings.MEDIA_URL + 'img/unknown.png')
+ assert not doc('#id_photo_delete'), (
+ '"Remove Profile Photo" control should not appear.')
+
+ # make sure no file is in the file system
+ f = self.mozillian.get_profile().get_photo_file()
+ assert not os.path.exists(f)
+
# No photo exists by default, the delete photo form control shouldn't
# be present, and trying to delete a non-existant photo shouldn't
# do anything.
- r = client.get(reverse('phonebook.edit_profile'))
- doc = pq(r.content)
- image = client.get(doc('#profile-photo').attr('src'))
+ assert_no_photo()
- eq_(image.status_code, 302, (
- 'Profile image URL should redirect to "unknown" image.'))
- assert not doc('#id_photo_delete'), (
- '"Remove Profile Photo" control should not appear.')
# Try to game the form -- it shouldn't do anything.
r = client.post(reverse('phonebook.edit_profile'),
dict(last_name='foo', photo_delete=1))
- eq_(r.status_code, 302, ('Trying to delete a non-existant photo '
- "shouldn't result in an error."))
+ eq_(r.status_code, 302, 'Trying to delete a non-existant photo'
+ "shouldn't result in an error.")
# Add a profile photo
f = open(os.path.join(os.path.dirname(__file__), 'profile-photo.jpg'),
@@ -228,25 +246,20 @@ def test_profile_photo(self):
r = client.get(reverse('phonebook.edit_profile'))
doc = pq(r.content)
- image = client.get(doc('#profile-photo').attr('src'))
+ assert doc('#profile-photo').attr('src').startswith(
+ settings.USERPICS_URL + '/' + str(self.mozillian.id) + '.jpg?')
- eq_(image.status_code, 200, 'Profile image URL should not redirect.')
assert doc('#id_photo_delete'), (
'"Remove Profile Photo" control should appear.')
+ assert os.path.exists(self.mozillian.get_profile().get_photo_file())
+
# Remove a profile photo
r = client.post(reverse('phonebook.edit_profile'),
dict(last_name='foo', photo_delete='1'))
eq_(r.status_code, 302, 'Form should validate and redirect the user.')
- r = client.get(reverse('phonebook.edit_profile'))
- doc = pq(r.content)
- image = client.get(doc('#profile-photo').attr('src'))
-
- eq_(image.status_code, 302, (
- 'Profile image URL should redirect to "unknown" image.'))
- assert not doc('#id_photo_delete'), (
- '"Remove Profile Photo" control should not appear.')
+ assert_no_photo()
def test_has_website(self):
"""Verify a user's website appears in their profile (as a link)."""
@@ -280,6 +293,7 @@ def test_my_profile(self):
assert '?' in doc('#profile-photo').attr('src')
+
class TestOpensearchViews(test_utils.TestCase):
"""Tests for the OpenSearch plugin, accessible to anonymous visitors"""
def test_search_plugin(self):
View
3 apps/phonebook/urls.py
@@ -8,9 +8,6 @@
from phonebook import views
urlpatterns = patterns('',
- url('^user/photo/(?P<unique_id>.*)$', views.photo,
- name='phonebook.profile_photo'),
-
url('^user/edit/$', views.edit_profile,
name='phonebook.edit_profile'),
url('^register/edit/$', views.edit_new_profile,
View
17 apps/phonebook/views.py
@@ -97,10 +97,7 @@ def edit_profile(request, new_account=False):
d = dict(form=form,
registration_flow=new_account,
- user_groups=user_groups,
- # TODO: photo!!
-# photo=ldap.profile_photo(unique_id, use_master=True),
- )
+ user_groups=user_groups)
return render(request, 'phonebook/edit_profile.html', d)
@@ -167,18 +164,6 @@ def search_plugin(request):
@login_required
-def photo(request, unique_id):
- needs_master = (request.user.unique_id == unique_id)
-
- ldap = UserSession.connect(request)
- image = ldap.profile_photo(unique_id, use_master=needs_master)
- if image:
- return HttpResponse(image, mimetype="image/jpeg")
- else:
- return redirect('/media/img/unknown.png')
-
-
-@login_required
def invited(request, id):
invite = Invite.objects.get(pk=id)
return render(request, 'phonebook/invited.html', dict(invite=invite))
View
18 apps/users/models.py
@@ -1,3 +1,4 @@
+import time
import urllib
from django.conf import settings
@@ -76,6 +77,23 @@ def get_send_confirmation_url(self):
urllib.urlencode({'user': self.user.username}))
return url
+ def get_photo_url(self, cachebust=False):
+ """Gets a user's userpic URL. Appends cachebusting if requested."""
+
+ if self.photo:
+ url = '%s/%d.jpg' % (settings.USERPICS_URL, self.user_id)
+
+ if cachebust:
+ url += '?%d' % int(time.time())
+ else:
+ url = settings.MEDIA_URL + 'img/unknown.png'
+
+ return url
+
+ def get_photo_file(self):
+ return '%s/%d.jpg' % (settings.USERPICS_PATH, self.user_id)
+
+ # TODO: get rid of this when larper is gone ETA Apr-2012
def get_unique_id(self):
r = self.get_ldap_person()
return r[1]['uniqueIdentifier'][0]
View
3 settings_test.py
@@ -1,2 +1,5 @@
+import tempfile
+
ES_INDEXES = {'default': 'mozillians_test'}
ES_DISABLED = True
+USERPICS_PATH = tempfile.mkdtemp()

0 comments on commit 5b5b831

Please sign in to comment.
Something went wrong with that request. Please try again.