0001-WIP-support-avatar-picture-in-user-profile-26022.patch
src/authentic2/attribute_kinds.py | ||
---|---|---|
17 | 17 |
from .plugins import collect_from_plugins |
18 | 18 |
from . import app_settings |
19 | 19 |
from .forms import widgets |
20 |
from .utils import store_image |
|
20 | 21 | |
21 | 22 |
capfirst = allow_lazy(capfirst, unicode) |
22 | 23 | |
... | ... | |
151 | 152 |
'field_class': PhoneNumberField, |
152 | 153 |
'rest_framework_field_class': PhoneNumberDRFField, |
153 | 154 |
}, |
155 |
{ |
|
156 |
'label': _('image'), |
|
157 |
'name': 'image', |
|
158 |
'field_class': forms.ImageField, |
|
159 |
'serialize': store_image, |
|
160 |
'deserialize': lambda x: x, |
|
161 |
'rest_framework_field_class': serializers.ImageField, |
|
162 |
}, |
|
154 | 163 |
] |
155 | 164 | |
156 | ||
157 | 165 |
def get_attribute_kinds(): |
158 | 166 |
attribute_kinds = {} |
159 | 167 |
for attribute_kind in chain(DEFAULT_ATTRIBUTE_KINDS, app_settings.A2_ATTRIBUTE_KINDS): |
src/authentic2/custom_user/apps.py | ||
---|---|---|
10 | 10 |
from django.db.models.signals import post_migrate |
11 | 11 | |
12 | 12 |
post_migrate.connect( |
13 |
self.create_first_name_last_name_attributes,
|
|
13 |
self.create_custom_attributes,
|
|
14 | 14 |
sender=self) |
15 | 15 | |
16 |
def create_first_name_last_name_attributes(self, app_config, verbosity=2, interactive=True,
|
|
16 |
def create_custom_attributes(self, app_config, verbosity=2, interactive=True,
|
|
17 | 17 |
using=DEFAULT_DB_ALIAS, **kwargs): |
18 | 18 |
from django.utils import translation |
19 | 19 |
from django.utils.translation import ugettext_lazy as _ |
... | ... | |
50 | 50 |
'asked_on_registration': True, |
51 | 51 |
'user_editable': True, |
52 | 52 |
'user_visible': True}) |
53 |
attrs['avatar_picture'], created = Attribute.objects.get_or_create( |
|
54 |
name='avatar_picture', |
|
55 |
defaults={'kind': 'image', |
|
56 |
'label': _('Avatar picture'), |
|
57 |
'required': False, |
|
58 |
'asked_on_registration': False, |
|
59 |
'user_editable': True, |
|
60 |
'user_visible': True}) |
|
53 | 61 | |
54 |
serialize = get_kind('string').get('serialize') |
|
55 | 62 |
for user in User.objects.all(): |
56 |
for attr_name in attrs: |
|
63 |
for at in attrs: |
|
64 |
serialize = get_kind(at.kind).get('serialize') |
|
57 | 65 |
av, created = AttributeValue.objects.get_or_create( |
58 | 66 |
content_type=content_type, |
59 | 67 |
object_id=user.id, |
60 |
attribute=attrs[attr_name],
|
|
68 |
attribute=attrs[at], |
|
61 | 69 |
defaults={ |
62 | 70 |
'multiple': False, |
63 | 71 |
'verified': False, |
64 |
'content': serialize(getattr(user, attr_name, None))
|
|
72 |
'content': serialize(getattr(user, at, None)) |
|
65 | 73 |
}) |
src/authentic2/templates/authentic2/accounts_edit.html | ||
---|---|---|
12 | 12 |
{% endblock %} |
13 | 13 | |
14 | 14 |
{% block content %} |
15 |
<form method="post"> |
|
15 |
{% if form.is_multipart %} |
|
16 |
<form enctype="multipart/form-data" method="post"> |
|
17 |
{% else %} |
|
18 |
<form method="post"> |
|
19 |
{% endif %} |
|
20 | ||
16 | 21 |
{% csrf_token %} |
17 | 22 |
{{ form.as_p }} |
18 | 23 |
{% if form.instance and form.instance.id %} |
src/authentic2/utils.py | ||
---|---|---|
8 | 8 |
import uuid |
9 | 9 |
import datetime |
10 | 10 |
import copy |
11 |
import fcntl |
|
12 |
import os |
|
11 | 13 | |
12 | 14 |
from functools import wraps |
13 | 15 |
from itertools import islice, chain, count |
14 | ||
16 |
from PIL import Image |
|
15 | 17 |
from importlib import import_module |
18 |
from hashlib import md5 |
|
19 |
from base64 import b32encode |
|
16 | 20 | |
17 | 21 |
from django.conf import settings |
18 | 22 |
from django.http import HttpResponseRedirect, HttpResponse |
... | ... | |
30 | 34 |
from django.template.loader import render_to_string, TemplateDoesNotExist |
31 | 35 |
from django.core.mail import send_mail |
32 | 36 |
from django.core import signing |
37 |
from django.core.files.storage import default_storage |
|
33 | 38 |
from django.core.urlresolvers import reverse, NoReverseMatch |
34 | 39 |
from django.utils.formats import localize |
35 | 40 |
from django.contrib import messages |
... | ... | |
1073 | 1078 |
if ou_value is not None: |
1074 | 1079 |
return ou_value |
1075 | 1080 |
return default |
1081 | ||
1082 | ||
1083 |
def store_image(image, owner_uuid, *args, **kwargs): |
|
1084 |
logger = logging.getLogger(__name__) |
|
1085 | ||
1086 |
digest = md5(value.read()) |
|
1087 |
image_uuid = b32encode(digest)[0:63] |
|
1088 |
image_format = image.image.format |
|
1089 | ||
1090 |
filefolder = default_storage.path('{root}/avatars/{oid}'.format( |
|
1091 |
root=app_settings.MEDIA_ROOT, |
|
1092 |
oid=owner_uuid)) |
|
1093 | ||
1094 |
filepath = os.path.join( |
|
1095 |
filefolder, |
|
1096 |
"{uuid}.{ext}".format(uuid=image_uuid, ext=image_format)) |
|
1097 | ||
1098 |
try: |
|
1099 |
if not os.path.exists(local_folder): |
|
1100 |
os.makesdir(local_folder) |
|
1101 | ||
1102 |
with open(filepath, 'wb') as f: |
|
1103 |
try: |
|
1104 |
fcntl.lockf(f, fcntl.LOCK_EX | fcntl.LOCK_NB) |
|
1105 |
with tempfile.NamedTemporaryFile(mode='w', dir=folder, delete=False) as temp: |
|
1106 |
try: |
|
1107 |
image.seek(0) |
|
1108 |
temp.write(image.read()) |
|
1109 |
temp.flush() |
|
1110 |
os.rename(temp.name, filepath) |
|
1111 |
pass |
|
1112 |
except: |
|
1113 |
logger.error("Could'nt store fingerprint for entity ID", entity_id) |
|
1114 |
os.unlink(temp.name) |
|
1115 |
finally: |
|
1116 |
fcntl.lockf(f, fcntl.LOCK_UN) |
|
1117 |
except: |
|
1118 |
logger.error("Couldn't hold exclusive lock for file {}".format(filepath)) |
|
1119 |
finally: |
|
1120 |
fctnl.lockf(f, fcntl.LOCK_UN) |
|
1121 |
except IOError: |
|
1122 |
return |
|
1123 | ||
1124 |
return filepath |
|
1125 | ||
1126 | ||
1127 |
def retrieve_image_url(image_filename): |
|
1128 |
media_url = app_settings.MEDIA_URL |
|
1129 |
# xxx retrieve everything after MEDIA_ROOT, concatenate craft the |
|
1130 |
# appropriate media url |
|
1131 |
pass |
|
1076 |
- |