Merge in photo_import

This commit is contained in:
Oliver Falk
2018-07-11 11:07:30 +02:00
10 changed files with 195 additions and 49 deletions

View File

@@ -24,6 +24,8 @@ from openid.association import Association as OIDAssociation
from openid.store import nonce as oidnonce from openid.store import nonce as oidnonce
from openid.store.interface import OpenIDStore from openid.store.interface import OpenIDStore
from libravatar import libravatar_url
from ivatar.settings import MAX_LENGTH_EMAIL, logger from ivatar.settings import MAX_LENGTH_EMAIL, logger
from ivatar.settings import MAX_PIXELS, AVATAR_MAX_SIZE, JPEG_QUALITY from ivatar.settings import MAX_PIXELS, AVATAR_MAX_SIZE, JPEG_QUALITY
from ivatar.settings import MAX_LENGTH_URL from ivatar.settings import MAX_LENGTH_URL
@@ -128,6 +130,9 @@ class Photo(BaseAccountModel):
if gravatar: if gravatar:
image_url = gravatar['image_url'] image_url = gravatar['image_url']
if service_name == 'Libravatar':
image_url = libravatar_url(email_address)
if not image_url: if not image_url:
return False # pragma: no cover return False # pragma: no cover
try: try:

View File

@@ -0,0 +1,37 @@
{% load i18n %}
{% load bootstrap4 %}
{% if not user.is_anonymous %}
{% if photos %}
<p>{% trans 'Would you like to import some of these externally hosted photos?' %}</p>
{% if email_id %}
<form action="{% url 'import_photo' email_id %}" method="post">{% csrf_token %}
<input type="hidden" name="email_id" value="{{ email_id }}">
{% else %}
<form action="{% url 'import_photo' %}" method="post">{% csrf_token %}
<input type="hidden" name="email_addr" value="{{ email_addr }}">
{% endif %}
<ul class="horizontal-list imported-list centered">
{% for photo in photos %}
<li><input type="checkbox" name="photo_{{photo.service_name}}" id="photo_{{photo.service_name}}" checked="checked">
<br/>
<label for="photo_{{photo.service_name}}">
<img src="{{ photo.thumbnail_url }}" class="thumbnail" alt="{{ photo.service_name }} image">
</label>
<br/>
{% if photo.service_url %}
<a href="{{ photo.service_url }}">{{ photo.service_name }}</a>
{% else %} {# pragma: no cover #}
{{ photo.service_name }} {# pragma: no cover #}
{% endif %} {# pragma: no cover #}
</li>
{% endfor %}
</ul>
<p>
{% buttons %}
<button type="submit" class="btn btn-primary">{% trans 'Import' %}</button>
{% endbuttons %}
</p>
</form>
{% endif %}
{% endif %}

View File

@@ -37,7 +37,12 @@
</form></li> </form></li>
</ul> </ul>
<p><a href="{% url 'upload_photo' %}">{% blocktrans %}upload a new one{% endblocktrans %}</a></p> <p>
<br/>
<a href="{% url 'upload_photo' %}">{% blocktrans %}Upload a new one{% endblocktrans %}</a>
<br/>
<a href="{% url 'import_photo' email.pk %}">{% blocktrans %}Import from other services{% endblocktrans %}</a>
</p>
{% endif %} {% endif %}

View File

@@ -9,31 +9,8 @@
<p><b>{% trans 'Your email address was successfully confirmed!' %}</b></p> <p><b>{% trans 'Your email address was successfully confirmed!' %}</b></p>
{% if not user.is_anonymous %} {% include '_import_photo_form.html' %}
{% if photos %}
<p>{% trans 'Would you like to import some of these externally hosted photos?' %}</p>
<form action="{% url 'import_photo' email_id %}" method="post">{% csrf_token %}
<input type="hidden" name="email_id" value="{{ email_id }}">
<ul class="horizontal-list imported-list centered">
{% for photo in photos %}
<li><input type="checkbox" name="photo_{{photo.service_name}}" id="photo_{{photo.service_name}}" checked="checked">
<br/><label for="photo_{{photo.service_name}}"><img src="{{ photo.thumbnail_url }}" class="thumbnail" alt="{{photo.service_name}} image"></label>
<br/>
{% if photo.service_url %}
<a href="{{ photo.service_url }}">{{ photo.service_name }}</a>
{% else %} {# pragma: no cover #}
{{ photo.service_name }} {# pragma: no cover #}
{% endif %} {# pragma: no cover #}
</li>
{% endfor %}
</ul>
<p><input type="submit" value="{% trans 'Import' %}"></p>
</form>
{% endif %}
<p><a href="{% url 'profile' %}">{% trans 'Back to your profile' %}</a></p> <p><a href="{% url 'profile' %}">{% trans 'Back to your profile' %}</a></p>
{% endif %}
{% endblock content %} {% endblock content %}

View File

@@ -0,0 +1,33 @@
{% extends 'base.html' %}
{% load i18n %}
{% load bootstrap4 %}
{% block title %}{% trans 'Import photo' %} - Libravatar{% endblock title %}
{% block content %}
<h1>{% trans 'Import photo' %}</h1>
{% include '_import_photo_form.html' %}
{% if not email_id %}
<form action="{% url 'import_photo' %}" method="get" id="check_mail_form">
<label for="check_email_addr">{% trans 'Email Address' %}</label>
<input type="text" name="check_email_addr" value="{{ email_addr }}">
{% buttons %}
<button type="submit" class="btn btn-primary">{% trans 'Check' %}</button>
{% endbuttons %}
</form>
<script>
document.getElementById('check_mail_form').onsubmit =
function(self) {
window.location.href = "{% url 'import_photo' %}" + document.getElementsByName('check_email_addr')[0].value;
return false;
};
</script>
{% endif %}
<p><a href="{% url 'profile' %}">{% trans 'Back to your profile' %}</a></p>
{% endblock content %}

View File

@@ -79,7 +79,11 @@
{% endif %} {% endif %}
{% if not max_photos %} {% if not max_photos %}
<p><a href="{% url 'upload_photo' %}">{% trans 'Upload a new photo' %}</a></p> <p>
<br/>
<a href="{% url 'upload_photo' %}">{% trans 'Upload a new photo' %}</a><br/>
<a href="{% url 'import_photo' %}">{% trans 'Import photo from other services' %}</a>
</p>
{% endif %} {% endif %}
<h2>{% trans 'Account settings' %}</h2> <h2>{% trans 'Account settings' %}</h2>

View File

@@ -333,7 +333,7 @@ class Tester(TestCase): # pylint: disable=too-many-public-methods
'unable to import photo from Gravatar?') 'unable to import photo from Gravatar?')
self.assertEqual( self.assertEqual(
str(list(response.context[0]['messages'])[0]), str(list(response.context[0]['messages'])[0]),
'Image successfully imported', 'Gravatar image successfully imported',
'Importing gravatar photo did not work?') 'Importing gravatar photo did not work?')
self.assertIsInstance( self.assertIsInstance(
self.user.photo_set.first(), self.user.photo_set.first(),
@@ -1162,8 +1162,6 @@ class Tester(TestCase): # pylint: disable=too-many-public-methods
) )
url = '%s?%s' % (urlobj.path, urlobj.query) url = '%s?%s' % (urlobj.path, urlobj.query)
response = self.client.get(url, follow=False) response = self.client.get(url, follow=False)
print(response)
print(response.content)
self.assertRedirects( self.assertRedirects(
response=response, response=response,
expected_url=default, expected_url=default,

View File

@@ -69,6 +69,12 @@ urlpatterns = [ # pylint: disable=invalid-name
url( url(
r'assign_photo_openid/(?P<openid_id>\d+)', r'assign_photo_openid/(?P<openid_id>\d+)',
AssignPhotoOpenIDView.as_view(), name='assign_photo_openid'), AssignPhotoOpenIDView.as_view(), name='assign_photo_openid'),
url(
r'import_photo/$',
ImportPhotoView.as_view(), name='import_photo'),
url(
r'import_photo/(?P<email_addr>[\w.]+@[\w.]+.[\w.]+)',
ImportPhotoView.as_view(), name='import_photo'),
url( url(
r'import_photo/(?P<email_id>\d+)', r'import_photo/(?P<email_id>\d+)',
ImportPhotoView.as_view(), name='import_photo'), ImportPhotoView.as_view(), name='import_photo'),

View File

@@ -2,6 +2,7 @@
View classes for ivatar/ivataraccount/ View classes for ivatar/ivataraccount/
''' '''
import io import io
from urllib.request import urlopen
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
@@ -16,6 +17,7 @@ from django.utils.translation import ugettext_lazy as _
from django.http import HttpResponseRedirect, HttpResponse from django.http import HttpResponseRedirect, HttpResponse
from django.urls import reverse_lazy, reverse from django.urls import reverse_lazy, reverse
from django.shortcuts import render from django.shortcuts import render
from django_openid_auth.models import UserOpenID
from django_openid_auth.models import UserOpenID from django_openid_auth.models import UserOpenID
from openid import oidutil from openid import oidutil
@@ -23,6 +25,9 @@ from openid.consumer import consumer
from ipware import get_client_ip from ipware import get_client_ip
from libravatar import libravatar_url
from .gravatar import get_photo as get_gravatar_photo
from ivatar.settings import MAX_NUM_PHOTOS, MAX_PHOTO_SIZE from ivatar.settings import MAX_NUM_PHOTOS, MAX_PHOTO_SIZE
from .forms import AddEmailForm, UploadPhotoForm, AddOpenIDForm from .forms import AddEmailForm, UploadPhotoForm, AddOpenIDForm
@@ -272,21 +277,78 @@ class AssignPhotoOpenIDView(SuccessMessageMixin, TemplateView):
@method_decorator(login_required, name='dispatch') @method_decorator(login_required, name='dispatch')
class ImportPhotoView(SuccessMessageMixin, View): class ImportPhotoView(SuccessMessageMixin, TemplateView):
''' '''
View class to import a photo from another service View class to import a photo from another service
Currently only Gravatar is supported Currently only Gravatar is supported
''' '''
template_name = 'import_photo.html'
@staticmethod def get_context_data(self, **kwargs):
def post(request, *args, **kwargs): # pylint: disable=unused-argument context = super().get_context_data(**kwargs)
''' context['photos'] = []
Handle post - import photo addr = None
''' if 'email_id' in kwargs:
try: try:
email = ConfirmedEmail.objects.get( addr = ConfirmedEmail.objects.get(pk=kwargs['email_id']).email
id=kwargs['email_id'], user=request.user) except:
except Exception: # pylint: disable=broad-except messages.error(
self.request,
_('Address does not exist'))
return context
if 'email_addr' in kwargs:
addr = kwargs['email_addr']
if addr:
gravatar = get_gravatar_photo(addr)
if gravatar:
context['photos'].append(gravatar)
libravatar_service_url = libravatar_url(
email=addr,
default=404,
)
try:
if libravatar_service_url:
response = urlopen(libravatar_service_url)
context['photos'].append({
'service_url': libravatar_service_url,
'thumbnail_url': libravatar_service_url + '?s=80',
'image_url': libravatar_service_url + '?s=512',
'width': 80,
'height': 80,
'service_name': 'Libravatar',
})
except Exception:
pass
return context
def post(self, request, *args, **kwargs): # pylint: disable=no-self-use,unused-argument
'''
Handle post to photo import
'''
addr = None
email_id = None
imported = None
if 'email_id' in kwargs:
email_id = kwargs['email_id']
if 'email_id' in request.POST:
email_id = request.POST['email_id']
if 'email_addr' in kwargs:
addr = kwargs['email_addr']
if 'email_addr' in request.POST:
addr = request.POST['email_addr']
if email_id:
email = ConfirmedEmail.objects.filter(
id=email_id, user=request.user)
if email.count() > 0:
addr = email.first().email
else:
messages.error( messages.error(
request, request,
_('Address does not exist')) _('Address does not exist'))
@@ -295,16 +357,31 @@ class ImportPhotoView(SuccessMessageMixin, View):
if 'photo_Gravatar' in request.POST: if 'photo_Gravatar' in request.POST:
photo = Photo() photo = Photo()
photo.user = request.user photo.user = request.user
photo.ip_address = get_client_ip(request)[0] photo.ip_address = get_client_ip(request)
if photo.import_image('Gravatar', email.email): if photo.import_image('Gravatar', addr):
messages.success(request, messages.success(request,
_('Image successfully imported')) _('Gravatar image successfully imported'))
else: else:
# Honestly, I'm not sure how to test this... # Honestly, I'm not sure how to test this...
messages.error( messages.error(
request, request,
_('Image import not successful')) # pragma: no cover _('Gravatar image import not successful')) # pragma: no cover
imported = True
if 'photo_Libravatar' in request.POST:
photo = Photo()
photo.user = request.user
photo.ip_address = get_client_ip(request)
if photo.import_image('Libravatar', addr):
messages.success(request,
_('Libravatar image successfully imported'))
else: else:
# Honestly, I'm not sure how to test this...
messages.error(
request,
_('Libravatar image import not successful')) # pragma: no cover
imported = True
if not imported:
messages.warning(request, _('Nothing importable')) messages.warning(request, _('Nothing importable'))
return HttpResponseRedirect(reverse_lazy('profile')) return HttpResponseRedirect(reverse_lazy('profile'))

View File

@@ -5,8 +5,10 @@ from io import BytesIO
from os import path from os import path
from PIL import Image from PIL import Image
from django.views.generic.base import TemplateView from django.views.generic.base import TemplateView
from django.http import HttpResponse, HttpResponseRedirect from django.http import HttpResponse, HttpResponseRedirect, HttpResponseNotFound
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.utils.translation import ugettext_lazy as _
from ivatar.settings import AVATAR_MAX_SIZE, JPEG_QUALITY from ivatar.settings import AVATAR_MAX_SIZE, JPEG_QUALITY
from . ivataraccount.models import ConfirmedEmail, ConfirmedOpenId from . ivataraccount.models import ConfirmedEmail, ConfirmedOpenId
from . ivataraccount.models import pil_format from . ivataraccount.models import pil_format
@@ -58,10 +60,12 @@ class AvatarImageView(TemplateView):
# If that mail/openid doesn't exist, or has no photo linked to it # If that mail/openid doesn't exist, or has no photo linked to it
if not obj or not obj.photo: if not obj or not obj.photo:
# Return the default URL, as specified # Return the default URL, as specified, or 404 Not Found, if default=404
if default: if default:
if str(default) == str(404):
return HttpResponseNotFound(_('<h1>Image not found</h1>'))
return HttpResponseRedirect(default) return HttpResponseRedirect(default)
# Return our default URl # Return our default URL
else: else:
static_img = path.join('static', 'img', 'mm', '%s%s' % (str(size), '.png')) static_img = path.join('static', 'img', 'mm', '%s%s' % (str(size), '.png'))
if not path.isfile(static_img): if not path.isfile(static_img):