X-Git-Url: https://code.grnet.gr/git/astakos/blobdiff_plain/76c68fd62d8d47b2d8d74d096b1ae35fafdb3e2f..9e5075ef0f2a2294dfa1317de45c7d957e6a36bd:/snf-astakos-app/astakos/im/forms.py diff --git a/snf-astakos-app/astakos/im/forms.py b/snf-astakos-app/astakos/im/forms.py index 136b485..1f1f5ed 100644 --- a/snf-astakos-app/astakos/im/forms.py +++ b/snf-astakos-app/astakos/im/forms.py @@ -31,33 +31,35 @@ # interpreted as representing official policies, either expressed # or implied, of GRNET S.A. from urlparse import urljoin -from datetime import datetime from django import forms from django.utils.translation import ugettext as _ -from django.contrib.auth.forms import UserCreationForm, AuthenticationForm, \ - PasswordResetForm, PasswordChangeForm +from django.contrib.auth.forms import (UserCreationForm, AuthenticationForm, + PasswordResetForm, PasswordChangeForm + ) from django.core.mail import send_mail from django.contrib.auth.tokens import default_token_generator from django.template import Context, loader from django.utils.http import int_to_base36 from django.core.urlresolvers import reverse -from django.utils.functional import lazy from django.utils.safestring import mark_safe -from django.contrib import messages from django.utils.encoding import smart_str from django.forms.extras.widgets import SelectDateWidget -from django.db.models import Q +from django.conf import settings + +from astakos.im.models import ( + AstakosUser, EmailChange, AstakosGroup, Invitation, + Membership, GroupKind, get_latest_terms +) +from astakos.im.settings import (INVITATIONS_PER_LEVEL, BASEURL, SITENAME, + RECAPTCHA_PRIVATE_KEY, RECAPTCHA_ENABLED, DEFAULT_CONTACT_EMAIL, + LOGGING_LEVEL + ) -from astakos.im.models import * -from astakos.im.settings import INVITATIONS_PER_LEVEL, DEFAULT_FROM_EMAIL, \ - BASEURL, SITENAME, RECAPTCHA_PRIVATE_KEY, DEFAULT_CONTACT_EMAIL, \ - RECAPTCHA_ENABLED, LOGGING_LEVEL from astakos.im.widgets import DummyWidget, RecaptchaWidget from astakos.im.functions import send_change_email -# since Django 1.4 use django.core.urlresolvers.reverse_lazy instead -from astakos.im.util import reverse_lazy, reserved_email, get_query +from astakos.im.util import reserved_email, get_query import logging import hashlib @@ -66,6 +68,7 @@ from random import random logger = logging.getLogger(__name__) + class LocalUserCreationForm(UserCreationForm): """ Extends the built in UserCreationForm in several ways: @@ -75,11 +78,13 @@ class LocalUserCreationForm(UserCreationForm): * User created is not active. """ recaptcha_challenge_field = forms.CharField(widget=DummyWidget) - recaptcha_response_field = forms.CharField(widget=RecaptchaWidget, label='') + recaptcha_response_field = forms.CharField( + widget=RecaptchaWidget, label='') class Meta: model = AstakosUser - fields = ("email", "first_name", "last_name", "has_signed_terms", "has_signed_terms") + fields = ("email", "first_name", "last_name", + "has_signed_terms", "has_signed_terms") def __init__(self, *args, **kwargs): """ @@ -90,23 +95,24 @@ class LocalUserCreationForm(UserCreationForm): kwargs.pop('request') self.ip = request.META.get('REMOTE_ADDR', request.META.get('HTTP_X_REAL_IP', None)) - + super(LocalUserCreationForm, self).__init__(*args, **kwargs) self.fields.keyOrder = ['email', 'first_name', 'last_name', 'password1', 'password2'] - if get_latest_terms(): - self.fields.keyOrder.append('has_signed_terms') + if RECAPTCHA_ENABLED: self.fields.keyOrder.extend(['recaptcha_challenge_field', - 'recaptcha_response_field',]) + 'recaptcha_response_field', ]) + if get_latest_terms(): + self.fields.keyOrder.append('has_signed_terms') if 'has_signed_terms' in self.fields: # Overriding field label since we need to apply a link # to the terms within the label terms_link_html = '%s' \ - % (reverse('latest_terms'), _("the terms")) + % (reverse('latest_terms'), _("the terms")) self.fields['has_signed_terms'].label = \ - mark_safe("I agree with %s" % terms_link_html) + mark_safe("I agree with %s" % terms_link_html) def clean_email(self): email = self.cleaned_data['email'] @@ -137,7 +143,8 @@ class LocalUserCreationForm(UserCreationForm): rrf = self.cleaned_data['recaptcha_response_field'] check = captcha.submit(rcf, rrf, RECAPTCHA_PRIVATE_KEY, self.ip) if not check.is_valid: - raise forms.ValidationError(_('You have not entered the correct words')) + raise forms.ValidationError( + _('You have not entered the correct words')) def save(self, commit=True): """ @@ -148,9 +155,10 @@ class LocalUserCreationForm(UserCreationForm): user.renew_token() if commit: user.save() - logger._log(LOGGING_LEVEL, 'Created user %s' % user.email, []) + logger.log(LOGGING_LEVEL, 'Created user %s' % user.email) return user + class InvitedLocalUserCreationForm(LocalUserCreationForm): """ Extends the LocalUserCreationForm: email is readonly. @@ -169,8 +177,7 @@ class InvitedLocalUserCreationForm(LocalUserCreationForm): ro = ('email', 'username',) for f in ro: self.fields[f].widget.attrs['readonly'] = True - - + def save(self, commit=True): user = super(InvitedLocalUserCreationForm, self).save(commit=False) level = user.invitation.inviter.level + 1 @@ -181,10 +188,12 @@ class InvitedLocalUserCreationForm(LocalUserCreationForm): user.save() return user + class ThirdPartyUserCreationForm(forms.ModelForm): class Meta: model = AstakosUser - fields = ("email", "first_name", "last_name", "third_party_identifier", "has_signed_terms") + fields = ("email", "first_name", "last_name", + "third_party_identifier", "has_signed_terms") def __init__(self, *args, **kwargs): """ @@ -194,34 +203,35 @@ class ThirdPartyUserCreationForm(forms.ModelForm): if self.request: kwargs.pop('request') super(ThirdPartyUserCreationForm, self).__init__(*args, **kwargs) - self.fields.keyOrder = ['email', 'first_name', 'last_name', 'third_party_identifier'] + self.fields.keyOrder = ['email', 'first_name', 'last_name', + 'third_party_identifier'] if get_latest_terms(): self.fields.keyOrder.append('has_signed_terms') #set readonly form fields ro = ["third_party_identifier"] for f in ro: self.fields[f].widget.attrs['readonly'] = True - + if 'has_signed_terms' in self.fields: # Overriding field label since we need to apply a link # to the terms within the label terms_link_html = '%s' \ - % (reverse('latest_terms'), _("the terms")) + % (reverse('latest_terms'), _("the terms")) self.fields['has_signed_terms'].label = \ - mark_safe("I agree with %s" % terms_link_html) + mark_safe("I agree with %s" % terms_link_html) def clean_email(self): email = self.cleaned_data['email'] if not email: raise forms.ValidationError(_("This field is required")) return email - + def clean_has_signed_terms(self): has_signed_terms = self.cleaned_data['has_signed_terms'] if not has_signed_terms: raise forms.ValidationError(_('You have to agree with the terms')) return has_signed_terms - + def save(self, commit=True): user = super(ThirdPartyUserCreationForm, self).save(commit=False) user.set_unusable_password() @@ -229,9 +239,10 @@ class ThirdPartyUserCreationForm(forms.ModelForm): user.provider = get_query(self.request).get('provider') if commit: user.save() - logger._log(LOGGING_LEVEL, 'Created user %s' % user.email, []) + logger.log(LOGGING_LEVEL, 'Created user %s' % user.email) return user + class InvitedThirdPartyUserCreationForm(ThirdPartyUserCreationForm): """ Extends the ThirdPartyUserCreationForm: email is readonly. @@ -240,15 +251,17 @@ class InvitedThirdPartyUserCreationForm(ThirdPartyUserCreationForm): """ Changes the order of fields, and removes the username field. """ - super(InvitedThirdPartyUserCreationForm, self).__init__(*args, **kwargs) + super( + InvitedThirdPartyUserCreationForm, self).__init__(*args, **kwargs) #set readonly form fields ro = ('email',) for f in ro: self.fields[f].widget.attrs['readonly'] = True - + def save(self, commit=True): - user = super(InvitedThirdPartyUserCreationForm, self).save(commit=False) + user = super( + InvitedThirdPartyUserCreationForm, self).save(commit=False) level = user.invitation.inviter.level + 1 user.level = level user.invitations = INVITATIONS_PER_LEVEL.get(level, 0) @@ -257,8 +270,10 @@ class InvitedThirdPartyUserCreationForm(ThirdPartyUserCreationForm): user.save() return user + class ShibbolethUserCreationForm(ThirdPartyUserCreationForm): - additional_email = forms.CharField(widget=forms.HiddenInput(), label='', required = False) + additional_email = forms.CharField( + widget=forms.HiddenInput(), label='', required=False) def __init__(self, *args, **kwargs): super(ShibbolethUserCreationForm, self).__init__(*args, **kwargs) @@ -266,11 +281,12 @@ class ShibbolethUserCreationForm(ThirdPartyUserCreationForm): # copy email value to additional_mail in case user will change it name = 'email' field = self.fields[name] - self.initial['additional_email'] = self.initial.get(name, field.initial) + self.initial['additional_email'] = self.initial.get(name, + field.initial) def clean_email(self): email = self.cleaned_data['email'] - for user in AstakosUser.objects.filter(email = email): + for user in AstakosUser.objects.filter(email=email): if user.provider == 'shibboleth': raise forms.ValidationError(_("This email is already associated with another shibboleth account.")) elif not user.is_active: @@ -279,13 +295,16 @@ class ShibbolethUserCreationForm(ThirdPartyUserCreationForm): super(ShibbolethUserCreationForm, self).clean_email() return email + class InvitedShibbolethUserCreationForm(ShibbolethUserCreationForm, InvitedThirdPartyUserCreationForm): pass - + + class LoginForm(AuthenticationForm): username = forms.EmailField(label=_("Email")) recaptcha_challenge_field = forms.CharField(widget=DummyWidget) - recaptcha_response_field = forms.CharField(widget=RecaptchaWidget, label='') + recaptcha_response_field = forms.CharField( + widget=RecaptchaWidget, label='') def __init__(self, *args, **kwargs): was_limited = kwargs.get('was_limited', False) @@ -293,17 +312,17 @@ class LoginForm(AuthenticationForm): if request: self.ip = request.META.get('REMOTE_ADDR', request.META.get('HTTP_X_REAL_IP', None)) - + t = ('request', 'was_limited') for elem in t: if elem in kwargs.keys(): kwargs.pop(elem) super(LoginForm, self).__init__(*args, **kwargs) - + self.fields.keyOrder = ['username', 'password'] if was_limited and RECAPTCHA_ENABLED: self.fields.keyOrder.extend(['recaptcha_challenge_field', - 'recaptcha_response_field',]) + 'recaptcha_response_field', ]) def clean_recaptcha_response_field(self): if 'recaptcha_challenge_field' in self.cleaned_data: @@ -320,7 +339,8 @@ class LoginForm(AuthenticationForm): rrf = self.cleaned_data['recaptcha_response_field'] check = captcha.submit(rcf, rrf, RECAPTCHA_PRIVATE_KEY, self.ip) if not check.is_valid: - raise forms.ValidationError(_('You have not entered the correct words')) + raise forms.ValidationError( + _('You have not entered the correct words')) def clean(self): super(LoginForm, self).clean() @@ -328,6 +348,7 @@ class LoginForm(AuthenticationForm): raise forms.ValidationError(_('Local login is not the current authentication method for this account.')) return self.cleaned_data + class ProfileForm(forms.ModelForm): """ Subclass of ``ModelForm`` for permiting user to edit his/her profile. @@ -340,7 +361,8 @@ class ProfileForm(forms.ModelForm): class Meta: model = AstakosUser - fields = ('email', 'first_name', 'last_name', 'auth_token', 'auth_token_expires') + fields = ('email', 'first_name', 'last_name', 'auth_token', + 'auth_token_expires') def __init__(self, *args, **kwargs): super(ProfileForm, self).__init__(*args, **kwargs) @@ -359,6 +381,7 @@ class ProfileForm(forms.ModelForm): user.save() return user + class FeedbackForm(forms.Form): """ Form for writing feedback. @@ -367,14 +390,16 @@ class FeedbackForm(forms.Form): feedback_data = forms.CharField(widget=forms.HiddenInput(), label='', required=False) + class SendInvitationForm(forms.Form): """ Form for sending an invitations """ - email = forms.EmailField(required = True, label = 'Email address') - first_name = forms.EmailField(label = 'First name') - last_name = forms.EmailField(label = 'Last name') + email = forms.EmailField(required=True, label='Email address') + first_name = forms.EmailField(label='First name') + last_name = forms.EmailField(label='Last name') + class ExtendedPasswordResetForm(PasswordResetForm): """ @@ -389,20 +414,24 @@ class ExtendedPasswordResetForm(PasswordResetForm): try: user = AstakosUser.objects.get(email=email, is_active=True) if not user.has_usable_password(): - raise forms.ValidationError(_("This account has not a usable password.")) - except AstakosUser.DoesNotExist, e: + raise forms.ValidationError( + _("This account has not a usable password.")) + except AstakosUser.DoesNotExist: raise forms.ValidationError(_('That e-mail address doesn\'t have an associated user account. Are you sure you\'ve registered?')) return email - - def save(self, domain_override=None, email_template_name='registration/password_reset_email.html', - use_https=False, token_generator=default_token_generator, request=None): + + def save( + self, domain_override=None, email_template_name='registration/password_reset_email.html', + use_https=False, token_generator=default_token_generator, request=None): """ Generates a one-use only link for resetting password and sends to the user. """ for user in self.users_cache: url = reverse('django.contrib.auth.views.password_reset_confirm', - kwargs={'uidb36':int_to_base36(user.id), - 'token':token_generator.make_token(user)}) + kwargs={'uidb36': int_to_base36(user.id), + 'token': token_generator.make_token(user) + } + ) url = urljoin(BASEURL, url) t = loader.get_template(email_template_name) c = { @@ -413,30 +442,33 @@ class ExtendedPasswordResetForm(PasswordResetForm): 'baseurl': BASEURL, 'support': DEFAULT_CONTACT_EMAIL } - from_email = DEFAULT_FROM_EMAIL + from_email = settings.SERVER_EMAIL send_mail(_("Password reset on %s alpha2 testing") % SITENAME, - t.render(Context(c)), from_email, [user.email]) + t.render(Context(c)), from_email, [user.email]) + class EmailChangeForm(forms.ModelForm): class Meta: model = EmailChange fields = ('new_email_address',) - + def clean_new_email_address(self): addr = self.cleaned_data['new_email_address'] if AstakosUser.objects.filter(email__iexact=addr): raise forms.ValidationError(_(u'This email address is already in use. Please supply a different email address.')) return addr - + def save(self, email_template_name, request, commit=True): ec = super(EmailChangeForm, self).save(commit=False) ec.user = request.user - activation_key = hashlib.sha1(str(random()) + smart_str(ec.new_email_address)) - ec.activation_key=activation_key.hexdigest() + activation_key = hashlib.sha1( + str(random()) + smart_str(ec.new_email_address)) + ec.activation_key = activation_key.hexdigest() if commit: ec.save() send_change_email(ec, request, email_template_name=email_template_name) + class SignApprovalTermsForm(forms.ModelForm): class Meta: model = AstakosUser @@ -451,35 +483,38 @@ class SignApprovalTermsForm(forms.ModelForm): raise forms.ValidationError(_('You have to agree with the terms')) return has_signed_terms + class InvitationForm(forms.ModelForm): username = forms.EmailField(label=_("Email")) - + def __init__(self, *args, **kwargs): super(InvitationForm, self).__init__(*args, **kwargs) - + class Meta: model = Invitation fields = ('username', 'realname') - + def clean_username(self): username = self.cleaned_data['username'] try: - Invitation.objects.get(username = username) - raise forms.ValidationError(_('There is already invitation for this email.')) + Invitation.objects.get(username=username) + raise forms.ValidationError( + _('There is already invitation for this email.')) except Invitation.DoesNotExist: pass return username + class ExtendedPasswordChangeForm(PasswordChangeForm): """ Extends PasswordChangeForm by enabling user to optionally renew also the token. """ renew = forms.BooleanField(label='Renew token', required=False) - + def __init__(self, user, *args, **kwargs): super(ExtendedPasswordChangeForm, self).__init__(user, *args, **kwargs) - + def save(self, commit=True): user = super(ExtendedPasswordChangeForm, self).save(commit=False) if self.cleaned_data.get('renew'): @@ -488,44 +523,78 @@ class ExtendedPasswordChangeForm(PasswordChangeForm): user.save() return user -def get_astakos_group_creation_form(request): - class AstakosGroupCreationForm(forms.ModelForm): - issue_date = forms.DateField(widget=SelectDateWidget()) - expiration_date = forms.DateField(widget=SelectDateWidget()) - kind = forms.ModelChoiceField(queryset=GroupKind.objects.all(), empty_label=None) - name = forms.URLField() - - class Meta: - model = AstakosGroup - - def __init__(self, *args, **kwargs): - super(AstakosGroupCreationForm, self).__init__(*args, **kwargs) - self.fields.keyOrder = ['kind', 'name', 'desc', 'issue_date', - 'expiration_date', 'estimated_participants', - 'moderatation_enabled'] - - def save(self, commit=True): - g = super(AstakosGroupCreationForm, self).save(commit=False) - if commit: - g.save() - g.owner = [request.user] -# g.approve_member(request.user) - return g + +class AstakosGroupCreationForm(forms.ModelForm): +# issue_date = forms.DateField(widget=SelectDateWidget()) +# expiration_date = forms.DateField(widget=SelectDateWidget()) + kind = forms.ModelChoiceField( + queryset=GroupKind.objects.all(), + label="", + widget=forms.HiddenInput() + ) + name = forms.URLField() + moderation_enabled = forms.BooleanField( + help_text="Check if you want to approve members participation manually", + required=False + ) + + class Meta: + model = AstakosGroup + + def __init__(self, *args, **kwargs): + try: + resources = kwargs.pop('resources') + except KeyError: + resources = {} + super(AstakosGroupCreationForm, self).__init__(*args, **kwargs) + self.fields.keyOrder = ['kind', 'name', 'homepage', 'desc', 'issue_date', + 'expiration_date', 'estimated_participants', + 'moderation_enabled'] + for id, r in resources.iteritems(): + self.fields['resource_%s' % id] = forms.IntegerField( + label=r, + required=False, + help_text=_('Leave it blank for no additional quota.') + ) + + def resources(self): + for name, value in self.cleaned_data.items(): + prefix, delimiter, suffix = name.partition('resource_') + if suffix: + # yield only those having a value + if not value: + continue + yield (suffix, value) + +class AstakosGroupUpdateForm(forms.ModelForm): + class Meta: + model = AstakosGroup + fields = ('homepage', 'desc') + +class AddGroupMembersForm(forms.Form): + q = forms.CharField(max_length=800, widget=forms.Textarea, label=_('Search users'), + help_text=_('Add comma separated user emails'), + required=True) - return AstakosGroupCreationForm - -def get_astakos_group_policy_creation_form(group): - class AstakosGroupPolicyCreationForm(forms.ModelForm): - choices = Resource.objects.filter(~Q(astakosgroup=group)) - resource = forms.ModelChoiceField(queryset=choices, empty_label=None) - - class Meta: - model = AstakosGroupQuota - - def __init__(self, *args, **kwargs): - if not args: - args = ({'group':group},) - super(AstakosGroupPolicyCreationForm, self).__init__(*args, **kwargs) - self.fields['group'].widget.attrs['disabled'] = True + def clean(self): + q = self.cleaned_data.get('q') or '' + users = q.split(',') + users = list(u.strip() for u in users if u) + db_entries = AstakosUser.objects.filter(email__in=users) + unknown = list(set(users) - set(u.email for u in db_entries)) + if unknown: + raise forms.ValidationError( + _('Unknown users: %s' % ','.join(unknown))) + self.valid_users = db_entries + return self.cleaned_data - return AstakosGroupPolicyCreationForm \ No newline at end of file + def get_valid_users(self): + """Should be called after form cleaning""" + try: + return self.valid_users + except: + return () + + +class AstakosGroupSearchForm(forms.Form): + q = forms.CharField(max_length=200, label='Search group')