Commit 08d8d2ed3e8269028ee23806e33dd35ce84fc60a
Exists in
master
and in
39 other branches
Merge branch 'master' of github.com:TracyWebTech/colab
Showing
6 changed files
with
127 additions
and
7 deletions
Show diff stats
src/accounts/forms.py
... | ... | @@ -5,11 +5,24 @@ from django.contrib.auth import get_user_model |
5 | 5 | from django.utils.translation import ugettext_lazy as _ |
6 | 6 | |
7 | 7 | from super_archives.models import MailingList |
8 | - | |
8 | +from .utils.validators import validate_social_account | |
9 | 9 | |
10 | 10 | User = get_user_model() |
11 | 11 | |
12 | 12 | |
13 | +class SocialAccountField(forms.Field): | |
14 | + def __init__(self, *args, **kwargs): | |
15 | + self.url = kwargs.pop('url', None) | |
16 | + super(SocialAccountField, self).__init__(*args, **kwargs) | |
17 | + | |
18 | + def validate(self, value): | |
19 | + super(SocialAccountField, self).validate(value) | |
20 | + | |
21 | + if value and not validate_social_account(value, self.url): | |
22 | + raise forms.ValidationError(_('Social account does not exist'), | |
23 | + code='social-account-doesnot-exist') | |
24 | + | |
25 | + | |
13 | 26 | class UserForm(forms.ModelForm): |
14 | 27 | required = ('first_name', 'last_name', 'email', 'username') |
15 | 28 | |
... | ... | @@ -34,12 +47,16 @@ class UserCreationForm(UserForm): |
34 | 47 | |
35 | 48 | |
36 | 49 | class UserUpdateForm(UserForm): |
50 | + | |
37 | 51 | class Meta: |
38 | 52 | model = User |
39 | 53 | fields = ('username', 'first_name', 'last_name', |
40 | 54 | 'institution', 'role', 'twitter', 'facebook', |
41 | 55 | 'google_talk', 'webpage') |
42 | 56 | |
57 | + twitter = SocialAccountField(url='https://twitter.com/', required=False) | |
58 | + facebook = SocialAccountField(url='https://graph.facebook.com/', required=False) | |
59 | + | |
43 | 60 | |
44 | 61 | class ListsForm(forms.Form): |
45 | 62 | LISTS_NAMES = ((list.name, list.name) for list in MailingList.objects.all()) | ... | ... |
src/accounts/migrations/0005_remove_host_from_social_accounts.py
0 → 100644
... | ... | @@ -0,0 +1,67 @@ |
1 | +# -*- coding: utf-8 -*- | |
2 | +import datetime | |
3 | +from south.db import db | |
4 | +from south.v2 import DataMigration | |
5 | +from django.db import models | |
6 | + | |
7 | +class Migration(DataMigration): | |
8 | + | |
9 | + def forwards(self, orm): | |
10 | + for user in orm.User.objects.iterator(): | |
11 | + if user.twitter: | |
12 | + user.twitter = user.twitter.split('/')[-1] | |
13 | + if user.facebook: | |
14 | + user.facebook = user.facebook.split('/')[-1] | |
15 | + user.save() | |
16 | + | |
17 | + def backwards(self, orm): | |
18 | + "Write your backwards methods here." | |
19 | + | |
20 | + models = { | |
21 | + u'accounts.user': { | |
22 | + 'Meta': {'object_name': 'User'}, | |
23 | + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), | |
24 | + 'email': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75', 'blank': 'True'}), | |
25 | + 'facebook': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}), | |
26 | + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), | |
27 | + 'google_talk': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}), | |
28 | + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}), | |
29 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | |
30 | + 'institution': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}), | |
31 | + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), | |
32 | + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), | |
33 | + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), | |
34 | + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), | |
35 | + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), | |
36 | + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), | |
37 | + 'role': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}), | |
38 | + 'twitter': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}), | |
39 | + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}), | |
40 | + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}), | |
41 | + 'verification_hash': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}), | |
42 | + 'webpage': ('django.db.models.fields.CharField', [], {'max_length': '256', 'null': 'True', 'blank': 'True'}) | |
43 | + }, | |
44 | + u'auth.group': { | |
45 | + 'Meta': {'object_name': 'Group'}, | |
46 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | |
47 | + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), | |
48 | + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) | |
49 | + }, | |
50 | + u'auth.permission': { | |
51 | + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, | |
52 | + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), | |
53 | + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), | |
54 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | |
55 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) | |
56 | + }, | |
57 | + u'contenttypes.contenttype': { | |
58 | + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, | |
59 | + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), | |
60 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | |
61 | + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), | |
62 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) | |
63 | + } | |
64 | + } | |
65 | + | |
66 | + complete_apps = ['accounts'] | |
67 | + symmetrical = True | ... | ... |
src/accounts/models.py
1 | 1 | |
2 | +import urlparse | |
3 | + | |
2 | 4 | from django.db import models |
3 | 5 | from django.contrib.auth.models import AbstractUser |
4 | - | |
5 | 6 | from django.core.urlresolvers import reverse |
6 | 7 | |
7 | 8 | |
... | ... | @@ -17,6 +18,12 @@ class User(AbstractUser): |
17 | 18 | def get_absolute_url(self): |
18 | 19 | return reverse('user_profile', kwargs={'username': self.username}) |
19 | 20 | |
21 | + def twitter_link(self): | |
22 | + return urlparse.urljoin('https://twitter.com', self.twitter) | |
23 | + | |
24 | + def facebook_link(self): | |
25 | + return urlparse.urljoin('https://www.facebook.com', self.facebook) | |
26 | + | |
20 | 27 | # We need to have `email` field set as unique but Django does not |
21 | 28 | # support field overriding (at least not until 1.6). |
22 | 29 | # The following workaroud allows to change email field to unique | ... | ... |
src/accounts/templates/accounts/user_detail.html
... | ... | @@ -9,7 +9,7 @@ |
9 | 9 | {% block main-content %} |
10 | 10 | |
11 | 11 | <div id="user-profile" class="row"> |
12 | - <div class="vcard col-lg-3 col-md-3 col-sm-4"> | |
12 | + <div class="vcard col-lg-4 col-md-4 col-sm-4"> | |
13 | 13 | <div class="thumbnail"> |
14 | 14 | {% gravatar user_.email 200 %} |
15 | 15 | </div> |
... | ... | @@ -43,23 +43,26 @@ |
43 | 43 | </li> |
44 | 44 | {% endif %} |
45 | 45 | {% if request.user.is_active %} |
46 | + <li> | |
46 | 47 | {% if user_.twitter %} |
47 | - <li><span class="icon-twitter icon-fixed-width"></span> {{ user_.twitter }}</li> | |
48 | + <span class="icon-twitter icon-fixed-width"></span> <a target="_blank" href="{{ user_.twitter_link }}" title="{% trans 'Twitter account' %}">{{ user_.twitter }}</a> | |
48 | 49 | {% endif %} |
49 | 50 | {% if user_.facebook %} |
50 | - <li><span class="icon-facebook icon-fixed-width"></span> {{ user_.facebook }}</li> | |
51 | + <span class="icon-facebook icon-fixed-width"></span> <a target="_blank" href="{{ user_.facebook_link }}" title="{% trans 'Facebook account' %}">{{ user_.facebook }}</a> | |
51 | 52 | {% endif %} |
53 | + </li> | |
54 | + | |
52 | 55 | {% if user_.google_talk %} |
53 | 56 | <li><span class="icon-google-plus icon-fixed-width"></span> {{ user_.google_talk }}</li> |
54 | 57 | {% endif %} |
55 | 58 | {% if user_.webpage %} |
56 | - <li><span class="icon-link icon-fixed-width"></span> {{ user_.webpage }}</li> | |
59 | + <li><span class="icon-link icon-fixed-width"></span> <a target="_blank" href="{{ user_.webpage }}" title="{% trans 'Personal webpage' %}">{{ user_.webpage }}</a></li> | |
57 | 60 | {% endif %} |
58 | 61 | {% endif %} |
59 | 62 | </ul> |
60 | 63 | </div> |
61 | 64 | |
62 | - <div class="col-lg-5 col-md-5 col-sm-8"> | |
65 | + <div class="col-lg-4 col-md-4 col-sm-8"> | |
63 | 66 | <div class="panel panel-default"> |
64 | 67 | <div class="panel-heading"> |
65 | 68 | <h3 class="panel-title">{% trans "Contributions by Area" %}</h3> | ... | ... |
... | ... | @@ -0,0 +1,26 @@ |
1 | + | |
2 | +import urllib2 | |
3 | +import urlparse | |
4 | + | |
5 | + | |
6 | +def validate_social_account(account, url): | |
7 | + """Verifies if a social account is valid. | |
8 | + | |
9 | + Examples: | |
10 | + | |
11 | + >>> validate_social_account('seocam', 'http://twitter.com') | |
12 | + True | |
13 | + | |
14 | + >>> validate_social_account('seocam-fake-should-fail', 'http://twitter.com') | |
15 | + False | |
16 | + """ | |
17 | + | |
18 | + request = urllib2.Request(urlparse.urljoin(url, account)) | |
19 | + request.get_method = lambda: 'HEAD' | |
20 | + | |
21 | + try: | |
22 | + response = urllib2.urlopen(request) | |
23 | + except urllib2.HTTPError: | |
24 | + return False | |
25 | + | |
26 | + return response.code == 200 | ... | ... |