Skip to content

Commit 9e31264

Browse files
committed
reset password
1 parent 493b0c4 commit 9e31264

18 files changed

+507
-18
lines changed

cdc.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ todo long run
1010
- button radius
1111
- flake8 compliant (pep8 + other stuff)
1212
- `git tag -a v1.4 -m 'my version 1.4'` + `git push origin --tags`
13+
- migrations
14+
* ./manage.py schemamigration app_name --initial
15+
* ./manage.py schemamigration app_name --auto
1316

1417
#########################################################
1518
# #
@@ -20,11 +23,11 @@ todo long run
2023
to do now
2124
- https://docs.djangoproject.com/en/dev/ref/contrib/sitemaps/
2225
- syndication (rss + atom) : https://docs.djangoproject.com/en/dev/ref/contrib/syndication/
26+
- media sociaux (syncro fb, twitter, google) -> https://github.com/foxmask/django-th
2327
- news / tuto / articles : export pdf (pandoc)
2428

2529
to do maybe
2630
- noscript : menu, (logout ok)
27-
- media sociaux (syncro fb, twitter, google) -> https://github.com/foxmask/django-th
2831
- calendar : list (prochain, passés, ...)
2932
- publications : tags, multi-page
3033
- better member (mail, citation, bio, ... osef ?)

iTeam/member/admin.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# @Author: Adrien Chardon
44
# @Date: 2014-07-13 18:51:48
55
# @Last Modified by: Adrien Chardon
6-
# @Last Modified time: 2014-08-22 17:02:41
6+
# @Last Modified time: 2014-11-02 17:54:01
77

88
# This file is part of iTeam.org.
99
# Copyright (C) 2014 Adrien Chardon (Nodraak).
@@ -24,10 +24,16 @@
2424

2525
from django.contrib import admin
2626

27-
from iTeam.member.models import Profile
27+
from iTeam.member.models import Profile, ForgotPasswordToken
2828

2929

3030
class ProfileAdmin(admin.ModelAdmin):
3131
search_fields = ['user__username']
3232

3333
admin.site.register(Profile, ProfileAdmin)
34+
35+
36+
class ForgotPasswordTokenAdmin(admin.ModelAdmin):
37+
search_fields = ['user__username']
38+
39+
admin.site.register(ForgotPasswordToken, ForgotPasswordTokenAdmin)

iTeam/member/forms.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# @Author: Adrien Chardon
44
# @Date: 2014-08-20 19:01:30
55
# @Last Modified by: Adrien Chardon
6-
# @Last Modified time: 2014-09-02 14:02:30
6+
# @Last Modified time: 2014-11-02 17:48:30
77

88
# This file is part of iTeam.org.
99
# Copyright (C) 2014 Adrien Chardon (Nodraak).
@@ -200,3 +200,28 @@ def clean(self):
200200
del cleaned_data['password_confirm']
201201

202202
return cleaned_data
203+
204+
205+
class LostPasswordForm(forms.Form):
206+
207+
username = forms.CharField(
208+
label='Identifiant',
209+
widget=forms.TextInput(
210+
attrs={
211+
'autofocus': '',
212+
'placeholder': 'Identifiant'
213+
}
214+
)
215+
)
216+
217+
def clean(self):
218+
cleaned_data = super(LostPasswordForm, self).clean()
219+
220+
username = cleaned_data.get('username')
221+
222+
# Check if the user exist
223+
if User.objects.filter(username=username).count() == 0:
224+
msg = u"L'utilisateur %s n'a pas été trouvé." % username
225+
self._errors['username'] = self.error_class([msg])
226+
227+
return cleaned_data
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# -*- coding: utf-8 -*-
2+
from south.utils import datetime_utils as datetime
3+
from south.db import db
4+
from south.v2 import SchemaMigration
5+
from django.db import models
6+
7+
8+
class Migration(SchemaMigration):
9+
10+
def forwards(self, orm):
11+
# Adding model 'Profile'
12+
db.create_table(u'member_profile', (
13+
(u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
14+
('user', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['auth.User'], unique=True)),
15+
('promo', self.gf('django.db.models.fields.IntegerField')(null=True, blank=True)),
16+
('avatar_url', self.gf('django.db.models.fields.CharField')(default='', max_length=256, blank=True)),
17+
('is_publisher', self.gf('django.db.models.fields.BooleanField')(default=False)),
18+
('is_admin', self.gf('django.db.models.fields.BooleanField')(default=False)),
19+
))
20+
db.send_create_signal(u'member', ['Profile'])
21+
22+
23+
def backwards(self, orm):
24+
# Deleting model 'Profile'
25+
db.delete_table(u'member_profile')
26+
27+
28+
models = {
29+
u'auth.group': {
30+
'Meta': {'object_name': 'Group'},
31+
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
32+
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
33+
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
34+
},
35+
u'auth.permission': {
36+
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
37+
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
38+
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
39+
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
40+
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
41+
},
42+
u'auth.user': {
43+
'Meta': {'object_name': 'User'},
44+
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
45+
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
46+
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
47+
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
48+
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
49+
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
50+
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
51+
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
52+
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
53+
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
54+
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
55+
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
56+
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
57+
},
58+
u'contenttypes.contenttype': {
59+
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
60+
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
61+
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
62+
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
63+
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
64+
},
65+
u'member.profile': {
66+
'Meta': {'object_name': 'Profile'},
67+
'avatar_url': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
68+
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
69+
'is_admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
70+
'is_publisher': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
71+
'promo': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
72+
'user': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.User']", 'unique': 'True'})
73+
}
74+
}
75+
76+
complete_apps = ['member']
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# -*- coding: utf-8 -*-
2+
from south.utils import datetime_utils as datetime
3+
from south.db import db
4+
from south.v2 import SchemaMigration
5+
from django.db import models
6+
7+
8+
class Migration(SchemaMigration):
9+
10+
def forwards(self, orm):
11+
# Adding model 'ForgotPasswordToken'
12+
db.create_table(u'member_forgotpasswordtoken', (
13+
(u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
14+
('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])),
15+
('token', self.gf('django.db.models.fields.CharField')(max_length=100)),
16+
('expires', self.gf('django.db.models.fields.DateTimeField')()),
17+
))
18+
db.send_create_signal(u'member', ['ForgotPasswordToken'])
19+
20+
21+
def backwards(self, orm):
22+
# Deleting model 'ForgotPasswordToken'
23+
db.delete_table(u'member_forgotpasswordtoken')
24+
25+
26+
models = {
27+
u'auth.group': {
28+
'Meta': {'object_name': 'Group'},
29+
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
30+
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
31+
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
32+
},
33+
u'auth.permission': {
34+
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
35+
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
36+
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
37+
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
38+
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
39+
},
40+
u'auth.user': {
41+
'Meta': {'object_name': 'User'},
42+
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
43+
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
44+
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
45+
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
46+
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
47+
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
48+
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
49+
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
50+
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
51+
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
52+
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
53+
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
54+
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
55+
},
56+
u'contenttypes.contenttype': {
57+
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
58+
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
59+
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
60+
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
61+
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
62+
},
63+
u'member.forgotpasswordtoken': {
64+
'Meta': {'object_name': 'ForgotPasswordToken'},
65+
'expires': ('django.db.models.fields.DateTimeField', [], {}),
66+
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
67+
'token': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
68+
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"})
69+
},
70+
u'member.profile': {
71+
'Meta': {'object_name': 'Profile'},
72+
'avatar_url': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
73+
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
74+
'is_admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
75+
'is_publisher': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
76+
'promo': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
77+
'user': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.User']", 'unique': 'True'})
78+
}
79+
}
80+
81+
complete_apps = ['member']

iTeam/member/migrations/__init__.py

Whitespace-only changes.

iTeam/member/models.py

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# @Author: Adrien Chardon
44
# @Date: 2014-08-20 14:35:58
55
# @Last Modified by: Adrien Chardon
6-
# @Last Modified time: 2014-11-01 14:33:28
6+
# @Last Modified time: 2014-11-02 22:35:10
77

88
# This file is part of iTeam.org.
99
# Copyright (C) 2014 Adrien Chardon (Nodraak).
@@ -26,6 +26,9 @@
2626

2727
from django.db import models
2828
from django.contrib.auth.models import User
29+
from django.core.urlresolvers import reverse
30+
from django.core.mail import send_mail
31+
from django.template.loader import render_to_string
2932

3033

3134
class Profile(models.Model):
@@ -43,3 +46,43 @@ def get_avatar_url(self):
4346
username_hash = md5(self.user.username).hexdigest()
4447
size = 100
4548
return 'https://secure.gravatar.com/avatar/{0}?d=identicon&s={1}'.format(username_hash, size)
49+
50+
51+
class ForgotPasswordToken(models.Model):
52+
class Meta:
53+
verbose_name = 'Token réinitialisation mot de passe'
54+
verbose_name_plural = 'Tokens réinitialisation mot de passe'
55+
56+
user = models.ForeignKey(User, verbose_name='Utilisateur')
57+
token = models.CharField(max_length=100)
58+
expires = models.DateTimeField('Expiration')
59+
60+
def __str__(self):
61+
return '<ForgotPasswordToken User={}>'.format(self.user)
62+
63+
def get_absolute_url(self):
64+
return reverse('member:password_reset_confirm', args=[self.token])
65+
66+
67+
def send_templated_mail(subject, template, context, recipients):
68+
"""Send an email based on a template.
69+
70+
Args:
71+
subject: (string) Subject of the email
72+
template: (string) Name of the template used for the message
73+
context: (dictionary) Dictionary used for the message
74+
recipients: (list) List of the recipients
75+
76+
Returns:
77+
Number of successfully delivered messages (0 or 1)
78+
79+
"""
80+
81+
message = render_to_string(template, context)
82+
83+
return send_mail(
84+
recipient_list = recipients,
85+
subject = subject,
86+
message = message,
87+
from_email = None, # use default one from settings_prod.py
88+
)

iTeam/member/urls.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# @Author: Adrien Chardon
44
# @Date: 2014-08-19 17:38:33
55
# @Last Modified by: Adrien Chardon
6-
# @Last Modified time: 2014-10-30 22:55:17
6+
# @Last Modified time: 2014-11-02 18:08:40
77

88
# This file is part of iTeam.org.
99
# Copyright (C) 2014 Adrien Chardon (Nodraak).
@@ -36,6 +36,9 @@
3636
url(r'^inscription/$', views.register_view, name='register_view'),
3737
url(r'^connexion/$', views.login_view, name='login_view'),
3838
url(r'^deconnexion/$', views.logout_view, name='logout_view'),
39+
40+
url(r'^oubli/$', views.password_reset_ask, name='password_reset_ask'),
41+
url(r'^oubli/(?P<token>.+)/$', views.password_reset_confirm, name='password_reset_confirm'),
3942
)
4043

4144
# Note : '_view' because of weird recursive function call ...

0 commit comments

Comments
 (0)