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 | ... | ... |