Back up file
authorOlga Brani <olgabrani@grnet.gr>
Tue, 13 Nov 2012 15:45:26 +0000 (17:45 +0200)
committerOlga Brani <olgabrani@grnet.gr>
Tue, 13 Nov 2012 15:45:26 +0000 (17:45 +0200)
15 files changed:
snf-astakos-app/astakos/im/activation_backends.py
snf-astakos-app/astakos/im/endpoints/quotaholder.py
snf-astakos-app/astakos/im/forms.py
snf-astakos-app/astakos/im/functions.py
snf-astakos-app/astakos/im/management/commands/user-update.py [moved from snf-astakos-app/astakos/im/management/commands/user-modify.py with 100% similarity]
snf-astakos-app/astakos/im/messages.py [new file with mode: 0644]
snf-astakos-app/astakos/im/models.py
snf-astakos-app/astakos/im/target/local.py
snf-astakos-app/astakos/im/target/redirect.py
snf-astakos-app/astakos/im/target/shibboleth.py
snf-astakos-app/astakos/im/templates/im/astakosgroup_detail.html
snf-astakos-app/astakos/im/templates/im/astakosgroup_form.html
snf-astakos-app/astakos/im/templates/im/astakosgroup_form_demo_bak.html [moved from snf-astakos-app/astakos/im/templates/im/astakosgroup_form_demo.html with 100% similarity]
snf-astakos-app/astakos/im/util.py
snf-astakos-app/astakos/im/views.py

index 584928f..890d3db 100644 (file)
@@ -41,7 +41,10 @@ from astakos.im.util import get_invitation
 from astakos.im.functions import (send_verification, send_activation,
                                   send_account_creation_notification,
                                   send_group_creation_notification, activate)
-from astakos.im.settings import INVITATIONS_ENABLED, MODERATION_ENABLED, SITENAME, RE_USER_EMAIL_PATTERNS
+from astakos.im.settings import (INVITATIONS_ENABLED, MODERATION_ENABLED,
+    SITENAME, RE_USER_EMAIL_PATTERNS
+)
+from astakos.im.messages import as astakos_messages
 
 import logging
 import re
@@ -226,28 +229,23 @@ class ActivationResult(object):
 
 class VerificationSent(ActivationResult):
     def __init__(self):
-        message = _('Verification sent.')
+        message = _(astakos_messages.VERIFICATION_SENT)
         super(VerificationSent, self).__init__(message)
 
 
 class SwitchAccountsVerificationSent(ActivationResult):
     def __init__(self, email):
-        message = _('This email is already associated with another \
-                    local account. To change this account to a shibboleth \
-                    one follow the link in the verification email sent \
-                    to %s. Otherwise just ignore it.' % email)
+        message = _(astakos_messages.SWITCH_ACCOUNT_LINK_SENT)
         super(SwitchAccountsVerificationSent, self).__init__(message)
 
 
 class NotificationSent(ActivationResult):
     def __init__(self):
-        message = _('Your request for an account was successfully received and is now pending \
-                    approval. You will be notified by email in the next few days. Thanks for \
-                    your interest in ~okeanos! The GRNET team.')
+        message = _(astakos_messages.NOTIFACATION_SENT)
         super(NotificationSent, self).__init__(message)
 
 
 class RegistationCompleted(ActivationResult):
     def __init__(self):
-        message = _('Registration completed. You can now login.')
+        message = _(astakos_messages.REGISTRATION_COMPLETED)
         super(RegistationCompleted, self).__init__(message)
index 68e7fd7..e908f39 100644 (file)
@@ -47,8 +47,11 @@ if QUOTA_HOLDER_URL:
 
 ENTITY_KEY = '1'
 
+inf = float('inf')
+
 logger = logging.getLogger(__name__)
 
+inf = float('inf')
 
 def call(func_name):
     """Decorator function for QuotaholderHTTP client calls."""
@@ -88,7 +91,7 @@ def send_quota(users, client=None):
         for resource, uplimit in user.quota.iteritems():
             key = ENTITY_KEY
             quantity = None
-            capacity = uplimit
+            capacity = uplimit if uplimit != inf else None
             import_limit = None
             export_limit = None
             flags = 0
index e39b8bb..9a573a2 100644 (file)
@@ -60,6 +60,8 @@ from astakos.im.functions import send_change_email
 
 from astakos.im.util import reserved_email, get_query
 
+import astakos.im.messages as astakos_messages
+
 import logging
 import hashlib
 import recaptcha.client.captcha as captcha
@@ -116,15 +118,15 @@ class LocalUserCreationForm(UserCreationForm):
     def clean_email(self):
         email = self.cleaned_data['email']
         if not email:
-            raise forms.ValidationError(_("This field is required"))
+            raise forms.ValidationError(_(astakos_messages.REQUIRED_FIELD))
         if reserved_email(email):
-            raise forms.ValidationError(_("This email is already used"))
+            raise forms.ValidationError(_(astakos_messages.EMAIL_USED))
         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'))
+            raise forms.ValidationError(_(astakos_messages.SIGN_TERMS))
         return has_signed_terms
 
     def clean_recaptcha_response_field(self):
@@ -142,8 +144,7 @@ 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(_(astakos_messages.CAPTCHA_VALIDATION_ERR))
 
     def save(self, commit=True):
         """
@@ -222,13 +223,13 @@ class ThirdPartyUserCreationForm(forms.ModelForm):
     def clean_email(self):
         email = self.cleaned_data['email']
         if not email:
-            raise forms.ValidationError(_("This field is required"))
+            raise forms.ValidationError(_(astakos_messages.REQUIRED_FIELD))
         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'))
+            raise forms.ValidationError(_(astakos_messages.SIGN_TERMS))
         return has_signed_terms
 
     def save(self, commit=True):
@@ -287,10 +288,9 @@ class ShibbolethUserCreationForm(ThirdPartyUserCreationForm):
         email = self.cleaned_data['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."))
+                raise forms.ValidationError(_(astakos_messages.SHIBBOLETH_EMAIL_USED))
             elif not user.is_active:
-                raise forms.ValidationError(_("This email is already associated with an inactive account. \
-                                              You need to wait to be activated before being able to switch to a shibboleth account."))
+                raise forms.ValidationError(_(astakos_messages.SHIBBOLETH_INACTIVE_ACC))
         super(ShibbolethUserCreationForm, self).clean_email()
         return email
 
@@ -343,13 +343,12 @@ 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(_(astakos_messages.CAPTCHA_VALIDATION_ERR))
 
     def clean(self):
         super(LoginForm, self).clean()
         if self.user_cache and self.user_cache.provider not in ('local', ''):
-            raise forms.ValidationError(_('Local login is not the current authentication method for this account.'))
+            raise forms.ValidationError(_(astakos_messages.SUSPENDED_LOCAL_ACC))
         return self.cleaned_data
 
 
@@ -419,10 +418,9 @@ 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."))
+                raise forms.ValidationError(_(astakos_messages.UNUSABLE_PASSWORD))
         except AstakosUser.DoesNotExist:
-            raise forms.ValidationError(_('That e-mail address doesn\'t have an associated user account. Are you sure you\'ve registered?'))
+            raise forms.ValidationError(_(astakos_messages.EMAIL_UNKNOWN))
         return email
 
     def save(
@@ -460,7 +458,7 @@ class EmailChangeForm(forms.ModelForm):
     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.'))
+            raise forms.ValidationError(_(astakos_messages.EMAIL_USED))
         return addr
 
     def save(self, email_template_name, request, commit=True):
@@ -485,7 +483,7 @@ class SignApprovalTermsForm(forms.ModelForm):
     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'))
+            raise forms.ValidationError(_(astakos_messages.SIGN_TERMS))
         return has_signed_terms
 
 
@@ -503,8 +501,7 @@ class InvitationForm(forms.ModelForm):
         username = self.cleaned_data['username']
         try:
             Invitation.objects.get(username=username)
-            raise forms.ValidationError(
-                _('There is already invitation for this email.'))
+            raise forms.ValidationError(_(astakos_messages.INVITATION_EMAIL_EXISTS))
         except Invitation.DoesNotExist:
             pass
         return username
@@ -690,7 +687,7 @@ class AstakosGroupUpdateForm(forms.ModelForm):
 class AddGroupMembersForm(forms.Form):
     q = forms.CharField(
         max_length=800, widget=forms.Textarea, label=_('Add users'),
-        help_text=_('Add comma separated user emails, eg. user1@user.com, user2@user.com'),
+        help_text=_(astakos_messages.ADD_GROUP_MEMBERS_Q_HELP),
         required=True)
 
     def clean(self):
@@ -700,8 +697,7 @@ class AddGroupMembersForm(forms.Form):
         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)))
+            raise forms.ValidationError(_(astakos_messages.UNKNOWN_USERS) % ','.join(unknown))
         self.valid_users = db_entries
         return self.cleaned_data
 
index 459a37a..589730d 100644 (file)
@@ -61,6 +61,7 @@ from astakos.im.settings import (DEFAULT_CONTACT_EMAIL, SITENAME, BASEURL,
                                  FEEDBACK_EMAIL_SUBJECT,
                                  EMAIL_CHANGE_EMAIL_SUBJECT)
 import astakos.im.models
+import astakos.im.messages as astakos_messages
 
 logger = logging.getLogger(__name__)
 
@@ -303,41 +304,41 @@ class SendMailError(Exception):
 
 class SendAdminNotificationError(SendMailError):
     def __init__(self):
-        self.message = _('Failed to send notification')
+        self.message = _(astakos_messages.ADMIN_NOTIFICATION_SEND_ERR)
         super(SendAdminNotificationError, self).__init__()
 
 
 class SendVerificationError(SendMailError):
     def __init__(self):
-        self.message = _('Failed to send verification')
+        self.message = _(astakos_messages.VERIFICATION_SEND_ERR)
         super(SendVerificationError, self).__init__()
 
 
 class SendInvitationError(SendMailError):
     def __init__(self):
-        self.message = _('Failed to send invitation')
+        self.message = _(astakos_messages.INVITATION_SEND_ERR)
         super(SendInvitationError, self).__init__()
 
 
 class SendGreetingError(SendMailError):
     def __init__(self):
-        self.message = _('Failed to send greeting')
+        self.message = _(astakos_messages.GREETING_SEND_ERR)
         super(SendGreetingError, self).__init__()
 
 
 class SendFeedbackError(SendMailError):
     def __init__(self):
-        self.message = _('Failed to send feedback')
+        self.message = _(astakos_messages.FEEDBACK_SEND_ERR)
         super(SendFeedbackError, self).__init__()
 
 
 class ChangeEmailError(SendMailError):
     def __init__(self):
-        self.message = _('Failed to send change email')
+        self.message = self.message = _(astakos_messages.CHANGE_EMAIL_SEND_ERR)
         super(ChangeEmailError, self).__init__()
 
 
 class SendNotificationError(SendMailError):
     def __init__(self):
-        self.message = _('Failed to send notification email')
+        self.message = _(astakos_messages.NOTIFICATION_SEND_ERR)
         super(SendNotificationError, self).__init__()
diff --git a/snf-astakos-app/astakos/im/messages.py b/snf-astakos-app/astakos/im/messages.py
new file mode 100644 (file)
index 0000000..0cc056d
--- /dev/null
@@ -0,0 +1,113 @@
+# Copyright 2011-2012 GRNET S.A. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or
+# without modification, are permitted provided that the following
+# conditions are met:
+#
+#   1. Redistributions of source code must retain the above
+#      copyright notice, this list of conditions and the following
+#      disclaimer.
+#
+#   2. Redistributions in binary form must reproduce the above
+#      copyright notice, this list of conditions and the following
+#      disclaimer in the documentation and/or other materials
+#      provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
+# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION HOWEVER CAUSED
+# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+# The views and conclusions contained in the software and
+# documentation are those of the authors and should not be
+# interpreted as representing official policies, either expressed
+# or implied, of GRNET S.A.
+
+ACCOUNT_AUTHENTICATION_FAILED           =   'Cannot authenticate account.'
+ACCOUNT_INACTIVE                        =   'Inactive account.'
+ACCOUNT_ALREADY_ACTIVE                  =   'Account is already active.'
+TOKEN_UNKNOWN                           =   'There is no user matching this token.'
+
+INVITATION_SENT                         =   'Invitation sent to %(emails.'
+PROFILE_UPDATED                         =   'Profile has been updated successfully.'
+FEEDBACK_SENT                           =   'Feedback successfully sent.'
+EMAIL_CHANGED                           =   'Account email has been changed successfully.'
+EMAIL_CHANGE_REGISTERED                 =   'Change email request has been registered succefully. \
+                                               You are going to receive a verification email in the new address.'
+
+OBJECT_CREATED                          =   'The %(verbose_names was created successfully.'
+MEMBER_JOINED_GROUP                     =   '%(realnames has been successfully joined the group.'
+MEMBER_REMOVED                          =   '%(realnames has been successfully removed from the group.'
+BILLING_ERROR                           =   'Service response status: %(status)d' 
+LOGOUT_SUCCESS                          =   'You have successfully logged out.'
+
+GENERIC_ERROR                           =   'Something wrong has happened. \
+                                               Please contact the administrators for more details.'
+
+MAX_INVITATION_NUMBER_REACHED   =           'There are no invitations left.'
+GROUP_MAX_PARTICIPANT_NUMBER_REACHED    =   'Group maximum participant number has been reached.'
+NO_APPROVAL_TERMS                       =   'There are no approval terms.'
+PENDING_EMAIL_CHANGE_REQUEST            =   'There is already a pending change email request.'
+OBJECT_CREATED_FAILED                   =   'The %(verbose_names creation failed: %(reasons.'
+GROUP_JOIN_FAILURE                      =   'Failed to join group.'
+GROUPKIND_UNKNOWN                       =   'There is no such a group kind'
+NOT_MEMBER                              =   'User is not member of the group.'
+NOT_OWNER                               =   'User is not a group owner.'
+OWNER_CANNOT_LEAVE_GROUP                =   'Owner cannot leave the group.'
+
+# Field validation fields
+REQUIRED_FIELD                          =   'This field is required.'
+EMAIL_USED                              =   'This email address is already in use. Please supply a different email address.'
+SHIBBOLETH_EMAIL_USED                   =   'This email is already associated with another shibboleth account.'
+SHIBBOLETH_INACTIVE_ACC                 =   'This email is already associated with an inactive account. \
+                                               You need to wait to be activated before being able to switch to a shibboleth account.'   
+
+SIGN_TERMS                              =   'You have to agree with the terms.'
+CAPTCHA_VALIDATION_ERR                  =   'You have not entered the correct words.'
+SUSPENDED_LOCAL_ACC                     =   'Local login is not the current authentication method for this account.'
+UNUSABLE_PASSWORD                       =   'This account has not a usable password.'
+EMAIL_UNKNOWN                           =   'That e-mail address doesn\'t have an associated user account. \
+                                               Are you sure you\'ve registered?'
+INVITATION_EMAIL_EXISTS                 =   'There is already invitation for this email.'
+INVITATION_CONSUMED_ERR                 =   'Invitation is used.'
+UNKNOWN_USERS                           =   'Unknown users: %s'
+UNIQUE_EMAIL_IS_ACTIVE_CONSTRAIN_ERR    =   'Another account with the same email & is_active combination found.'
+INVALID_ACTIVATION_KEY                  =   'Invalid activation key.'
+NEW_EMAIL_ADDR_RESERVED                 =   'The new email address is reserved.'
+EMAIL_RESERVED                          =   'Email: %(email)s is reserved'
+
+# Field help text
+ADD_GROUP_MEMBERS_Q_HELP                =   'Add comma separated user emails, eg. user1@user.com, user2@user.com'
+ASTAKOSUSER_GROUPS_HELP                 =   'In addition to the permissions manually assigned, \
+                                               this user will also get all permissions granted to each group he/she is in.'
+EMAIL_CHANGE_NEW_ADDR_HELP              =   'Your old email address will be used until you verify your new one.'
+
+EMAIL_SEND_ERR                          =   'Failed to send %s.'
+ADMIN_NOTIFICATION_SEND_ERR             =   EMAIL_SEND_ERR % 'admin notification'
+VERIFICATION_SEND_ERR                   =   EMAIL_SEND_ERR % 'verification'
+INVITATION_SEND_ERR                     =   EMAIL_SEND_ERR % 'invitation'
+GREETING_SEND_ERR                       =   EMAIL_SEND_ERR % 'greeting'
+FEEDBACK_SEND_ERR                       =   EMAIL_SEND_ERR % 'feedback'
+CHANGE_EMAIL_SEND_ERR                   =   EMAIL_SEND_ERR % 'feedback'
+NOTIFICATION_SEND_ERR                   =   EMAIL_SEND_ERR % 'notification'
+
+
+MISSING_NEXT_PARAMETER                  =   'No next parameter'
+
+VERIFICATION_SENT                       =   'Verification sent.'
+
+SWITCH_ACCOUNT_LINK_SENT                =   'This email is already associated with another local account. \
+                                               To change this account to a shibboleth one follow the link in the verification email sent to %(emails. \
+                                               Otherwise just ignore it.'
+NOTIFACATION_SENT                       =   'Your request for an account was successfully received and is now pending approval. \
+                                               You will be notified by email in the next few days. \
+                                               Thanks for your interest in ~okeanos! The GRNET team.'
+REGISTRATION_COMPLETED                  =   'Registration completed. You can now login.'
\ No newline at end of file
index deeb29c..2b6e6f3 100644 (file)
@@ -44,6 +44,7 @@ from collections import defaultdict
 from django.db import models, IntegrityError
 from django.contrib.auth.models import User, UserManager, Group, Permission
 from django.utils.translation import ugettext as _
+from django.db import transaction
 from django.core.exceptions import ValidationError
 from django.db import transaction
 from django.db.models.signals import (pre_save, post_save, post_syncdb,
@@ -63,6 +64,8 @@ from astakos.im.functions import send_invitation
 from astakos.im.tasks import propagate_groupmembers_quota
 from astakos.im.functions import send_invitation
 
+import astakos.im.messages as astakos_messages
+
 logger = logging.getLogger(__name__)
 
 DEFAULT_CONTENT_TYPE = None
@@ -228,14 +231,20 @@ class AstakosGroup(Group):
         self.save()
         quota_disturbed.send(sender=self, users=self.approved_members)
 
+    @transaction.commit_manually
     def approve_member(self, person):
         m, created = self.membership_set.get_or_create(person=person)
         # update date_joined in any case
-        m.date_joined = datetime.now()
-        m.save()
+        try:
+            m.approve()
+        except:
+            transaction.rollback()
+            raise
+        else:
+            transaction.commit()
 
-    def disapprove_member(self, person):
-        self.membership_set.remove(person=person)
+#     def disapprove_member(self, person):
+#         self.membership_set.remove(person=person)
 
     @property
     def members(self):
@@ -340,9 +349,7 @@ class AstakosUser(User):
 
     astakos_groups = models.ManyToManyField(
         AstakosGroup, verbose_name=_('agroups'), blank=True,
-        help_text=_("""In addition to the permissions manually assigned, this
-                    user will also get all permissions granted to each group
-                    he/she is in."""),
+        help_text=_(astakos_messages.ASTAKOSUSER_GROUPS_HELP),
         through='Membership')
 
     __has_signed_terms = False
@@ -519,7 +526,7 @@ class AstakosUser(User):
         q = q.filter(email=self.email)
         q = q.filter(is_active=self.is_active)
         if q.count() != 0:
-            raise ValidationError({'__all__': [_('Another account with the same email & is_active combination found.')]})
+            raise ValidationError({'__all__': [_(astakos_messages.UNIQUE_EMAIL_IS_ACTIVE_CONSTRAIN_ERR)]})
 
     @property
     def signed_terms(self):
@@ -564,6 +571,8 @@ class Membership(models.Model):
         return False
 
     def approve(self):
+        if self.group.max_participants:
+            assert len(self.group.approved_members) + 1 <= self.group.max_participants
         self.date_joined = datetime.now()
         self.save()
         quota_disturbed.send(sender=self, users=(self.person,))
@@ -690,7 +699,7 @@ class EmailChangeManager(models.Manager):
             except AstakosUser.DoesNotExist:
                 pass
             else:
-                raise ValueError(_('The new email address is reserved.'))
+                raise ValueError(_(astakos_messages.NEW_EMAIL_ADDR_RESERVED))
             # update user
             user = AstakosUser.objects.get(pk=email_change.user_id)
             user.email = email_change.new_email_address
@@ -698,12 +707,12 @@ class EmailChangeManager(models.Manager):
             email_change.delete()
             return user
         except EmailChange.DoesNotExist:
-            raise ValueError(_('Invalid activation key'))
+            raise ValueError(_(astakos_messages.INVALID_ACTIVATION_KEY))
 
 
 class EmailChange(models.Model):
     new_email_address = models.EmailField(_(u'new e-mail address'),
-                                          help_text=_(u'Your old email address will be used until you verify your new one.'))
+                                          help_text=_(astakos_messages.EMAIL_CHANGE_NEW_ADDR_HELP))
     user = models.ForeignKey(
         AstakosUser, unique=True, related_name='emailchange_user')
     requested_at = models.DateTimeField(default=datetime.now())
@@ -851,4 +860,4 @@ post_delete.connect(send_quota_disturbed, sender=Membership)
 post_save.connect(send_quota_disturbed, sender=AstakosUserQuota)
 post_delete.connect(send_quota_disturbed, sender=AstakosUserQuota)
 post_save.connect(send_quota_disturbed, sender=AstakosGroupQuota)
-post_delete.connect(send_quota_disturbed, sender=AstakosGroupQuota)
+post_delete.connect(send_quota_disturbed, sender=AstakosGroupQuota)
\ No newline at end of file
index df2a092..954a020 100644 (file)
@@ -43,6 +43,8 @@ from astakos.im.views import requires_anonymous
 from astakos.im.forms import LoginForm
 from astakos.im.settings import RATELIMIT_RETRIES_ALLOWED
 
+import astakos.im.messages as astakos_messages
+
 from ratelimit.decorators import ratelimit
 
 retries = RATELIMIT_RETRIES_ALLOWED - 1
@@ -72,9 +74,9 @@ def login(request, on_failure='im/login.html'):
 
     message = None
     if not user:
-        message = _('Cannot authenticate account')
+        message = _(astakos_messages.ACCOUNT_AUTHENTICATION_FAILED)
     elif not user.is_active:
-        message = _('Inactive account')
+        message = _(astakos_messages.ACCOUNT_INACTIVE)
     if message:
         messages.error(request, message)
         return render_to_response(on_failure,
index 8e278bc..7bed1cb 100644 (file)
@@ -45,6 +45,8 @@ from astakos.im.settings import COOKIE_NAME, COOKIE_DOMAIN
 from astakos.im.util import set_cookie
 from astakos.im.functions import login as auth_login, logout
 
+import astakos.im.messages as astakos_messages
+
 import logging
 
 logger = logging.getLogger(__name__)
@@ -62,7 +64,7 @@ def login(request):
     """
     next = request.GET.get('next')
     if not next:
-        return HttpResponseBadRequest(_('No next parameter'))
+        return HttpResponseBadRequest(_(astakos_messages.MISSING_NEXT_PARAMETER))
     force = request.GET.get('force', None)
     response = HttpResponse()
     if force == '':
index 94c18bf..c4ad702 100644 (file)
@@ -43,6 +43,7 @@ from astakos.im.models import AstakosUser
 from astakos.im.forms import LoginForm
 from astakos.im.activation_backends import get_backend, SimpleBackend
 
+import astakos.im.messages as astakos_messages
 
 class Tokens:
     # these are mapped by the Shibboleth SP software
@@ -89,7 +90,7 @@ def login(request, backend=None, on_login_template='im/login.html',
                                     request.GET.get('next'),
                                     'renew' in request.GET)
         else:
-            message = _('Inactive account')
+            message = _(astakos_messages.ACCOUNT_INACTIVE)
             messages.error(request, message)
             return render_response(on_login_template,
                                    login_form=LoginForm(request=request),
index ea5b7ce..5fb0ea7 100644 (file)
@@ -75,6 +75,8 @@
                     {% endif %}
                 {% endfor %}&nbsp;
             </dd>
+            <dt>Max participants</dt>
+                       <dd>{% if object.max_participants%}{{object.max_participants}}{% else %}&nbsp;{% endif %}</dd>
                 </dl>
         </div>
         
index 2c61330..e06a35e 100644 (file)
 {% extends "im/account_base.html" %}
 
+{% load filters %}
+{% block headjs %}
+       {{ block.super }}        
+       <script src="{{ IM_STATIC_URL }}js/quotas.js"></script> 
+{% endblock %} 
 {% block page.body %}
-<div class="projects">
-       <div class="maincol {% block innerpage.class %}{% endblock %}">
-           <form action="" method="post"
-                   class="withlabels">{% csrf_token %}
-                 <h2><span>CREATE {{ kind|upper }}</span></h2>
-                   {% include "im/form_render.html" %}
-                   <div class="form-row submit">
-                       <input type="submit" class="submit altcol" value="SUBMIT" />
-                   </div>
-           </form>
+       
+<form action="" method="post" class="withlabels quotas-form">{% csrf_token %}
+        
+    <fieldset class="with-info">
+       <legend>
+               1. CREATE GROUP
+                       <span class="info"> 
+                       <em>more info</em>
+                       <span>Help Text Help Text Help Text Help Text Help Text Help Text Help Text Help Text Help Text Help Text Help Text Help Text Help Text Help Text</span>
+               </span>                 
+       </legend>
+        
+        {% include "im/form_render.html" %}
+               
+<!-- 
+               <div class="double-checks">
+               <label>Max users per group</label>
+               <div class="form-row">
+                       <p class="clearfix">
+                               <label for="members_unlimited">Unlimited</label>
+                               <input type="checkbox" id="members_unlimited" name="members_unlimited" class="unlimited" checked="checked">
+                               <span class="info"> 
+                                       <em>more info</em>
+                                       <span>Help Text Help Text Help Text Text Help Text Help Text</span>
+                               </span>  
+                       </p>
+               </div>
+               <div class="form-row">
+                       <p class="clearfix">
+                               <label for="members_limited">Limited</label>
+                               <input type="checkbox" id="members_limited" name="members_limited" class="limited">
+                                       <input type="text" id="members_uplimit" name="members_uplimit" />
+                                                
+                       </p>
+                       
+               </div>
+               
+       </div> 
+ -->
+    </fieldset>     
+    
+    <fieldset id="icons">
+       <legend>
+               2. CHOOSE RESOURCES
+               <span class="info"> 
+                       <em>more info</em>
+                       <span>Help Text Help Text Help Text Help Text Help Text Help Text Help Text Help Text Help Text Help Text Help Text Help Text Help Text Help Text</span>
+               </span>    
+       </legend>
+
+    {% with resource_catalog|lookup:'resources' as resources %}
+    {% with resource_catalog|lookup:'groups' as groups %}      
+       <ul class="clearfix">
+            {% for g, rs in groups.items %}   
+            {% with resource_presentation|lookup:g as group_info %}            
+               <li>
+                       <a href="#{{ g }}" id="{{'group_'|add:g}}"><img src="/static/im/images/create-{{ g }}.png" alt="vm"/></a>
+                       <input type="hidden" name="{{ 'is_selected_'|add:g }}"  id="{{ 'id_is_selected_'|add:g }}" value="0">
+                       <p class="msg">{{ group_info.help_text }}</p>
+               </li>
+               {% endwith %}
+            {% endfor %}
+       </ul>
+       
+    </fieldset>
+   
+    <div class="foo">
+    
+    </div>
+    <div class="not-foo">
+        {% for g, rs in groups.items %}
+        
+        <div class="group {{'group_'|add:g}}" id="{{ g }}">
+                       <a href="#icons" class="delete">X remove resource</a>   
+                   {% for r in rs %}
+                   {% with resource_presentation|lookup:r as resource_info %}
+                   {% with resources|lookup:r as resource%}
+                   <fieldset class="quota storage">
+                       <legend>
+                               {% if resource_info.is_abbreviation %}
+                                       {{ r|get_value_after_dot|upper }}
+                               {% else %}
+                                       {{ r|get_value_after_dot|capfirst }}
+                               {% endif %}
+                               <span class="info"> 
+                                       <em>more info</em>
+                                       <span>{{ resource_info.help_text }}</span>
+                               </span>  
+                       </legend>
+<!--           <div class="form-row">
+                               <p class="clearfix">
+                                       <label for="num_storage">Total storage</label>
+                                       <input type="text" name="num_storage">
+                                       <span class="extra-img">&nbsp;</span>
+                                       <span class="info"><em>more info</em><span>Help Text</span></span>
+                               </p>
+                       </div>-->
+                               <div class="double-checks">
+                               <label>
+                                       Max {% if resource_info.is_abbreviation %}{{ r|get_value_after_dot|upper }}{% else %}{{ r|get_value_after_dot }}{% endif %}{% if not resource.unit %}s {% endif  %} per user
+                                               {% if resource.unit %} 
+                                                       ({{ resource.unit }})
+                                               {% endif  %}
+                                       </label>
+                               <div class="form-row">
+                                       <p class="clearfix">
+                                               <label for="{{r|add:'_unlimited'}}">Unlimited</label>
+                                               <input type="checkbox" id="{{'id_'|add:r|add:'_unlimited'}}" name="{{r|add:'_unlimited'}}" class="unlimited radio" checked="checked">
+                                       </p>
+                               </div>
+                               <div class="form-row">
+                                       <p class="clearfix">
+                                               <label for="{{r|add:'_limited'}}">Limited</label>
+                                               <input type="checkbox" id="id_storage_per_user_limited" name="{{r|add:'_limited'}}" class="radio limited">
+                                               <input type="text" id="{{'id_'|add:r|add:'_uplimit'}}" name="{{r|add:'_uplimit'}}"/> 
+                                       </p>
+                               </div>
+                               
+                       </div> 
+                   </fieldset>
+                   {% endwith %}
+                   {% endwith %}
+                   {% endfor %}
+           </div>
+           
+       {% endfor %}
        </div>
-</div>
-{% endblock %}
+    {% endwith %}
+    {% endwith %}
+        
+    <div class="form-row submit">
+               <input type="submit" value="SUBMIT" class="submit altcol" autocomplete="off">
+       </div>     
+</form>
+        
+<script>
+       
+</script>       
+{% endblock %}
\ No newline at end of file
index d778e30..5d591ab 100644 (file)
@@ -45,11 +45,15 @@ from django.contrib.auth import authenticate
 from django.core.urlresolvers import reverse
 from django.core.exceptions import ValidationError, ObjectDoesNotExist
 from django.db.models.fields import Field
+from django.utils.translation import ugettext as _
+
 from astakos.im.models import AstakosUser, Invitation
 from astakos.im.settings import COOKIE_NAME, \
     COOKIE_DOMAIN, COOKIE_SECURE, FORCE_PROFILE_UPDATE, LOGGING_LEVEL
 from astakos.im.functions import login
 
+import astakos.im.messages as astakos_messages
+
 logger = logging.getLogger(__name__)
 
 
@@ -94,9 +98,10 @@ def get_invitation(request):
         return
     invitation = Invitation.objects.get(code=code)
     if invitation.is_consumed:
-        raise ValueError(_('Invitation is used'))
+        raise ValueError(_(astakos_messages.INVITATION_CONSUMED_ERR))
     if reserved_email(invitation.username):
-        raise ValueError(_('Email: %s is reserved' % invitation.username))
+        email = invitation.username
+        raise ValueError(_(astakos_messages.EMAIL_RESRVED) % locals()))
     return invitation
 
 
index 15337a9..c99366a 100644 (file)
@@ -90,6 +90,8 @@ from astakos.im.settings import (COOKIE_NAME, COOKIE_DOMAIN, LOGOUT_NEXT,
 from astakos.im.tasks import request_billing
 from astakos.im.api.callpoint import AstakosCallpoint
 
+import astakos.im.messages as astakos_messages
+
 logger = logging.getLogger(__name__)
 
 
@@ -229,21 +231,21 @@ def invite(request, template_name='im/invitations.html', extra_context=None):
                     email = form.cleaned_data.get('username')
                     realname = form.cleaned_data.get('realname')
                     inviter.invite(email, realname)
-                    message = _('Invitation sent to %s' % email)
+                    message = _(astakos_messages.INVITATION_SENT) % locals()
                     messages.success(request, message)
                 except SendMailError, e:
                     message = e.message
                     messages.error(request, message)
                     transaction.rollback()
                 except BaseException, e:
-                    message = _('Something went wrong.')
+                    message = _(astakos_messages.GENERIC_ERROR)
                     messages.error(request, message)
                     logger.exception(e)
                     transaction.rollback()
                 else:
                     transaction.commit()
         else:
-            message = _('No invitations left')
+            message = _(astakos_messages.MAX_INVITATION_NUMBER_REACHED)
             messages.error(request, message)
 
     sent = [{'email': inv.username,
@@ -305,7 +307,7 @@ def edit_profile(request, template_name='im/profile.html', extra_context=None):
                 next = request.POST.get('next')
                 if next:
                     return redirect(next)
-                msg = _('Profile has been updated successfully')
+                msg = _(astakos_messages.PROFILE_UPDATED)
                 messages.success(request, msg)
             except ValueError, ve:
                 messages.success(request, ve)
@@ -397,7 +399,7 @@ def signup(request, template_name='im/signup.html', on_success='im/signup_comple
                 messages.error(request, message)
                 transaction.rollback()
             except BaseException, e:
-                message = _('Something went wrong.')
+                message = _(astakos_messages.GENERIC_ERROR)
                 messages.error(request, message)
                 logger.exception(e)
                 transaction.rollback()
@@ -452,7 +454,7 @@ def feedback(request, template_name='im/feedback.html', email_template_name='im/
             except SendMailError, e:
                 messages.error(request, message)
             else:
-                message = _('Feedback successfully sent')
+                message = _(astakos_messages.FEEDBACK_SENT)
                 messages.success(request, message)
     return render_response(template_name,
                            feedback_form=form,
@@ -481,7 +483,7 @@ def logout(request, template='registration/logged_out.html', extra_context=None)
         response['Location'] = LOGOUT_NEXT
         response.status_code = 301
         return response
-    messages.success(request, _('You have successfully logged out.'))
+    messages.success(request, _(astakos_messages.LOGOUT_SUCCESS))
     context = get_context(request, extra_context)
     response.write(
         template_loader.render_to_string(template, context_instance=context))
@@ -504,10 +506,10 @@ def activate(request, greeting_email_template_name='im/welcome_email.txt',
     try:
         user = AstakosUser.objects.get(auth_token=token)
     except AstakosUser.DoesNotExist:
-        return HttpResponseBadRequest(_('No such user'))
+        return HttpResponseBadRequest(_(astakos_messages.ACCOUNT_UNKNOWN))
 
     if user.is_active:
-        message = _('Account already active.')
+        message = _(astakos_messages.ACCOUNT_ALREADY_ACTIVE)
         messages.error(request, message)
         return index(request)
 
@@ -534,7 +536,7 @@ def activate(request, greeting_email_template_name='im/welcome_email.txt',
             transaction.rollback()
             return index(request)
         except BaseException, e:
-            message = _('Something went wrong.')
+            message = _(astakos_messages.GENERIC_ERROR)
             messages.error(request, message)
             logger.exception(e)
             transaction.rollback()
@@ -555,7 +557,7 @@ def activate(request, greeting_email_template_name='im/welcome_email.txt',
             transaction.rollback()
             return index(request)
         except BaseException, e:
-            message = _('Something went wrong.')
+            message = _(astakos_messages.GENERIC_ERROR)
             messages.error(request, message)
             logger.exception(e)
             transaction.rollback()
@@ -578,7 +580,7 @@ def approval_terms(request, term_id=None, template_name='im/approval_terms.html'
             pass
 
     if not term:
-        messages.error(request, 'There are no approval terms.')
+        messages.error(request, _(astakos_messages.NO_APPROVAL_TERMS))
         return HttpResponseRedirect(reverse('index'))
     f = open(term.location, 'r')
     terms = f.read()
@@ -626,7 +628,7 @@ def change_email(request, activation_key=None,
         try:
             user = EmailChange.objects.change_email(activation_key)
             if request.user.is_authenticated() and request.user == user:
-                msg = _('Email changed successfully.')
+                msg = _(astakos_messages.EMAIL_CHANGED)
                 messages.success(request, msg)
                 auth_logout(request)
                 response = prepare_response(request, user)
@@ -653,11 +655,10 @@ def change_email(request, activation_key=None,
             messages.error(request, msg)
             transaction.rollback()
         except IntegrityError, e:
-            msg = _('There is already a pending change email request.')
+            msg = _(astakos_messages.PENDING_EMAIL_CHANGE_REQUEST)
             messages.error(request, msg)
         else:
-            msg = _('Change email request has been registered succefully.\
-                    You are going to receive a verification email in the new address.')
+            msg = _(astakos_messages.EMAIL_CHANGE_REGISTERED)
             messages.success(request, msg)
             transaction.commit()
     return render_response(form_template_name,
@@ -751,7 +752,7 @@ def group_add(request, kind_name='default'):
     try:
         kind = GroupKind.objects.get(name=kind_name)
     except:
-        return HttpResponseBadRequest(_('No such group kind'))
+        return HttpResponseBadRequest(_(astakos_messages.GROUPKIND_UNKNOWN))
     
     
 
@@ -781,7 +782,7 @@ def group_add(request, kind_name='default'):
         form = form_class(data)
 
     # Create the template, context, response
-    template_name = "%s/%s_form_demo.html" % (
+    template_name = "%s/%s_form.html" % (
         model._meta.app_label,
         model._meta.object_name.lower()
     )
@@ -795,7 +796,8 @@ def group_add(request, kind_name='default'):
     return HttpResponse(t.render(c))
 
 
-@require_http_methods(["POST"])
+#@require_http_methods(["POST"])
+@require_http_methods(["GET", "POST"])
 @signed_terms_required
 @login_required
 def group_add_complete(request):
@@ -807,7 +809,7 @@ def group_add_complete(request):
         result = callpoint.create_groups((d,)).next()
         if result.is_success:
             new_object = result.data[0]
-            msg = _("The %(verbose_name)s was created successfully.") %\
+            msg = _(astakos_messages.OBJECT_CREATED) %\
                 {"verbose_name": model._meta.verbose_name}
             messages.success(request, msg, fail_silently=True)
             
@@ -826,7 +828,7 @@ def group_add_complete(request):
             post_save_redirect = '/im/group/%(id)s/'
             return HttpResponseRedirect(post_save_redirect % new_object)
         else:
-            msg = _("The %(verbose_name)s creation failed: %(reason)s.") %\
+            msg = _(astakos_messages.OBJECT_CREATED_FAILED) %\
                 {"verbose_name": model._meta.verbose_name,
                  "reason":result.reason}
             messages.error(request, msg, fail_silently=True)
@@ -836,7 +838,8 @@ def group_add_complete(request):
         form=form)
 
 
-@require_http_methods(["GET"])
+#@require_http_methods(["GET"])
+@require_http_methods(["GET", "POST"])
 @signed_terms_required
 @login_required
 def group_list(request):
@@ -944,7 +947,11 @@ def group_detail(request, group_id):
         if update_form.is_valid():
             update_form.save()
         if addmembers_form.is_valid():
-            map(obj.approve_member, addmembers_form.valid_users)
+            try:
+                map(obj.approve_member, addmembers_form.valid_users)
+            except AssertionError:
+                msg = _(astakos_messages.GROUP_MAX_PARTICIPANT_NUMBER_REACHED)
+                messages.error(request, msg)
             addmembers_form = AddGroupMembersForm()
 
     template_name = "%s/%s_detail.html" % (
@@ -1032,7 +1039,7 @@ def group_search(request, extra_context=None, **kwargs):
                            sorting=sorting))
 
 
-@require_http_methods(["POST"])
+@require_http_methods(["GET", "POST"])
 @signed_terms_required
 @login_required
 def group_all(request, extra_context=None, **kwargs):
@@ -1070,7 +1077,8 @@ def group_all(request, extra_context=None, **kwargs):
                            sorting=sorting))
 
 
-@require_http_methods(["POST"])
+#@require_http_methods(["POST"])
+@require_http_methods(["POST", "GET"])
 @signed_terms_required
 @login_required
 def group_join(request, group_id):
@@ -1085,7 +1093,7 @@ def group_join(request, group_id):
         return HttpResponseRedirect(post_save_redirect)
     except IntegrityError, e:
         logger.exception(e)
-        msg = _('Failed to join group.')
+        msg = _(astakos_messages.GROUP_JOIN_FAILURE)
         messages.error(request, msg)
         return group_search(request)
 
@@ -1099,9 +1107,9 @@ def group_leave(request, group_id):
             group__id=group_id,
             person=request.user)
     except Membership.DoesNotExist:
-        return HttpResponseBadRequest(_('Invalid membership.'))
+        return HttpResponseBadRequest(_(astakos_messages.NOT_A_MEMBER))
     if request.user in m.group.owner.all():
-        return HttpResponseForbidden(_('Owner can not leave the group.'))
+        return HttpResponseForbidden(_(astakos_messages.OWNER_CANNOT_LEAVE_GROUP))
     return delete_object(
         request,
         model=Membership,
@@ -1120,16 +1128,17 @@ def handle_membership(func):
                 group__id=group_id,
                 person__id=user_id)
         except Membership.DoesNotExist:
-            return HttpResponseBadRequest(_('Invalid membership.'))
+            return HttpResponseBadRequest(_(astakos_messages.NOT_MEMBER))
         else:
             if request.user not in m.group.owner.all():
-                return HttpResponseForbidden(_('User is not a group owner.'))
+                return HttpResponseForbidden(_(astakos_messages.NOT_OWNER))
             func(request, m)
             return group_detail(request, group_id)
     return wrapper
 
 
-@require_http_methods(["POST"])
+#@require_http_methods(["POST"])
+@require_http_methods(["POST", "GET"])
 @signed_terms_required
 @login_required
 @handle_membership
@@ -1137,12 +1146,15 @@ def approve_member(request, membership):
     try:
         membership.approve()
         realname = membership.person.realname
-        msg = _('%s has been successfully joined the group.' % realname)
+        msg = _(astakos_messages.MEMBER_JOINED_GROUP) % locals()
         messages.success(request, msg)
+    except AssertionError:
+        msg = _(astakos_messages.GROUP_MAX_PARTICIPANT_NUMBER_REACHED)
+        messages.error(request, msg)
     except BaseException, e:
         logger.exception(e)
         realname = membership.person.realname
-        msg = _('Something went wrong during %s\'s approval.' % realname)
+        msg = _(astakos_messages.GENERIC_ERROR)
         messages.error(request, msg)
 
 
@@ -1153,15 +1165,16 @@ def disapprove_member(request, membership):
     try:
         membership.disapprove()
         realname = membership.person.realname
-        msg = _('%s has been successfully removed from the group.' % realname)
+        msg = MEMBER_REMOVED % realname
         messages.success(request, msg)
     except BaseException, e:
         logger.exception(e)
-        msg = _('Something went wrong during %s\'s disapproval.' % realname)
+        msg = _(astakos_messages.GENERIC_ERROR)
         messages.error(request, msg)
 
 
-@require_http_methods(["GET"])
+#@require_http_methods(["GET"])
+@require_http_methods(["POST", "GET"])
 @signed_terms_required
 @login_required
 def resource_list(request):
@@ -1221,7 +1234,8 @@ def group_create_list(request):
         context_instance=get_context(request),)
 
 
-@require_http_methods(["GET"])
+#@require_http_methods(["GET"])
+@require_http_methods(["POST", "GET"])
 @signed_terms_required
 @login_required
 def billing(request):
@@ -1245,7 +1259,7 @@ def billing(request):
         status, data = r.result
         data = _clear_billing_data(data)
         if status != 200:
-            messages.error(request, _('Service response status: %d' % status))
+            messages.error(request, _(astakos_messages.BILLING_ERROR) % status)
     except:
         messages.error(request, r.result)
 
@@ -1283,7 +1297,8 @@ def _clear_billing_data(data):
     return data
      
      
-@require_http_methods(["GET"])
+#@require_http_methods(["GET"])
+@require_http_methods(["POST", "GET"])
 @signed_terms_required
 @login_required
 def timeline(request):