Commit 9b5fb5e9 authored by Olga Brani's avatar Olga Brani

- New styles for select

- new Class ResourcePresentation
- Group add form javascript validation
parent 633e98ae
......@@ -539,7 +539,7 @@ class AstakosGroupCreationForm(forms.ModelForm):
initial=True
)
max_participants = forms.IntegerField(
required=False, min_value=1
required=True, min_value=1
)
class Meta:
......@@ -661,9 +661,11 @@ class AstakosGroupCreationSummaryForm(forms.ModelForm):
super(AstakosGroupCreationSummaryForm, self).clean()
self.cleaned_data['policies'] = []
append = self.cleaned_data['policies'].append
print '#', self.cleaned_data
#tbd = [f for f in self.fields if (f.startswith('is_selected_') and (not f.endswith('_proxy')))]
tbd = [f for f in self.fields if f.startswith('is_selected_')]
for name, uplimit in self.cleaned_data.iteritems():
print '####', name, uplimit
subs = name.split('_uplimit')
if len(subs) == 2:
tbd.append(name)
......@@ -671,9 +673,10 @@ class AstakosGroupCreationSummaryForm(forms.ModelForm):
s, sep, r = prefix.partition(RESOURCE_SEPARATOR)
resource = Resource.objects.get(service__name=s, name=r)
print '#### ####', resource
# keep only resource limits for selected resource groups
if self.cleaned_data.get(
'is_selected_%s' % resource.group, True
'is_selected_%s' % resource.group, False
):
append(dict(service=s, resource=r, uplimit=uplimit))
for name in tbd:
......
......@@ -36,7 +36,7 @@ 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.'
INVITATION_SENT = 'Invitation sent to %(email)s.'
PROFILE_UPDATED = 'Profile has been updated successfully.'
FEEDBACK_SENT = 'Feedback successfully sent.'
EMAIL_CHANGED = 'Account email has been changed successfully.'
......@@ -44,8 +44,8 @@ EMAIL_CHANGE_REGISTERED = 'Change email request has been regis
You are going to receive a verification email in the new address.'
OBJECT_CREATED = 'The %(verbose_name)s was created successfully.'
MEMBER_JOINED_GROUP = '%(realnames has been successfully joined the group.'
MEMBER_REMOVED = '%(realnames has been successfully removed from the group.'
MEMBER_JOINED_GROUP = '%(realname)s has been successfully joined the group.'
MEMBER_REMOVED = '%(realname)s has been successfully removed from the group.'
BILLING_ERROR = 'Service response status: %(status)d'
LOGOUT_SUCCESS = 'You have successfully logged out.'
......@@ -56,7 +56,7 @@ 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.'
OBJECT_CREATED_FAILED = 'The %(verbose_name)s creation failed: %(reason)s.'
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.'
......@@ -105,7 +105,7 @@ 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. \
To change this account to a shibboleth one follow the link in the verification email sent to %(email)s. \
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. \
......
......@@ -186,3 +186,68 @@ PAGINATE_BY = getattr(settings, 'ASTAKOS_PAGINATE_BY', 8)
# Enforce token renewal on password change/reset
NEWPASSWD_INVALIDATE_TOKEN = getattr(
settings, 'ASTAKOS_NEWPASSWD_INVALIDATE_TOKEN', True)
RESOURCES_PRESENTATION_DATA = getattr(
settings, 'ASTAKOS_RESOURCES_PRESENTATION_DATA', {
'groups': {
'compute': {
'help_text':'group compute help text',
'is_abbreviation':False,
'report_desc':'',
'verbose_name':'compute',
},
'storage': {
'help_text':'group storage help text',
'is_abbreviation':False,
'report_desc':'',
'verbose_name':'storage',
},
},
'resources': {
'pithos+.diskspace': {
'help_text':'resource pithos+.diskspace help text',
'is_abbreviation':False,
'report_desc':'Pithos+ Diskspace',
'placeholder':'eg. 10GB',
'verbose_name':'diskspace',
},
'cyclades.vm': {
'help_text':'resource cyclades.vm help text resource cyclades.vm help text resource cyclades.vm help text resource cyclades.vm help text',
'is_abbreviation':True,
'report_desc':'Virtual Machines',
'placeholder':'eg. 2',
'verbose_name':'vm',
},
'cyclades.disk': {
'help_text':'resource cyclades.disk help text',
'is_abbreviation':False,
'report_desc':'Disk',
'placeholder':'eg. 5GB, 2GB etc',
'verbose_name':'disk'
},
'cyclades.ram': {
'help_text':'resource cyclades.ram help text',
'is_abbreviation':True,
'report_desc':'RAM',
'placeholder':'eg. 4GB',
'verbose_name':'ram'
},
'cyclades.cpu': {
'help_text':'resource cyclades.cpu help text',
'is_abbreviation':True,
'report_desc':'CPUs',
'placeholder':'eg. 1',
'verbose_name':'cpu'
},
'cyclades.network.private': {
'help_text':'resource cyclades.network.private help text',
'is_abbreviation':False,
'report_desc':'Network',
'placeholder':'eg. 1',
'verbose_name':'private network'
}
}
})
......@@ -14,7 +14,7 @@
/* dropkick select extra styles */
.form-row .dk_container { border-radius:0; margin-bottom:0; border: 1px solid #ccc; height: 21px; letter-spacing: 1px; line-height: 22px; margin-bottom: -1px; width:240px; padding:5px 0; font-weight:normal; font-family: 'Didact Gothic', Verdana, sans-serif; font-size:1em; background:transparent; color:#808080;}
.form-row .dk_toggle { border-radius:0; padding:0; border:0 none; text-indent:1.5em; text-decoration:none;background-image:url(../images/arrow-down_black.png); background-position:90% 5px;}
.form-row .dk_toggle { border-radius:0; padding:0; border:0 none; text-indent:1.5em; text-decoration:none;background-image:url(../images/arrow-down_grey.png); background-position:90% 5px;}
.form-row .dk_toggle:hover { text-decoration:none; }
.form-row .dk_open { background:transparent; box-shadow: none; }
.form-row .dk_open .dk_toggle { background-color:transparent; border:0 none; color:#000; box-shadow: none;}
......
......@@ -115,6 +115,7 @@ $(document).ready(function() {
$('select.dropkicked').dropkick({
change: function (value, label) {
$(this).parents('form').submit();
}
});
......
......@@ -4,7 +4,7 @@ function group_form_show_resources(el){
var id = el.attr('id');
$('.quotas-form .group').each(function() {
if( $(this).hasClass(id) ) {
$(this).appendTo('.foo');
$(this).appendTo('.visible');
$(this).show('slow');
$(this).find('input')[0].focus()
}
......@@ -68,7 +68,7 @@ $(document).ready(function() {
// hide relevant fieldset
$(this).parents('.group').hide('slow', function() {
$(this).appendTo('.not-foo');
$(this).appendTo('.not-visible');
});
group_class = $(this).parents('.group').attr('class').replace('group ', '');
......@@ -114,49 +114,68 @@ $(document).ready(function() {
if ($(this).hasClass('dehumanize')){
var flag = 0;
// check if the value is not float
var num_float = parseFloat(value);
num_float= String(num_float);
if (num_float.indexOf(".") == 1){
flag = 1 ;
msg="Please enter an integer";
} else {
var num = parseInt(value);
if ( num == '0' ) {
flag = 1 ; msg="zero"
} else {
if ( value && !num ) { flag = 1 ; msg="Invalid format"}
var bytes = num;
// remove any numbers and get suffix
var suffix = value.replace( num, '');
// validate suffix. 'i' renders it case insensitive
var suf = suffix.match( new RegExp('^(GB|KB|MB|TB|bytes|G|K|M|T|byte)$', 'i'));
if (suf){
suf = suf[0].toLowerCase();
suf = suf.substr(0,1);
// transform to bytes
switch (suf){
case 'b':
bytes = num*Math.pow(1024,0);
break;
case 'k':
bytes = num*Math.pow(1024,1);
break;
case 'm':
bytes = num*Math.pow(1024,2);
break;
case 'g':
bytes = num*Math.pow(1024,3);
break;
case 't':
bytes = num*Math.pow(1024,4);
break;
default:
bytes = num;
}
} else {
if (num) {
flag = 1;
msg ="You must specify correct units"
}
}
}
}
// replace , with . and get number
value = value.replace(",",".");
var num = parseFloat(value);
if ( value && !num ) { flag = 1 ; msg="Invalid format"}
var bytes = num;
// get suffix. 'i' renders it case insensitive
var suf = value.match( new RegExp('GB|KB|MB|TB|bytes|G|K|M|T|byte', 'i'));
if (suf){
suf = suf[0].toLowerCase();
suf = suf.substr(0,1);
// transform to bytes
switch (suf){
case 'b':
bytes = num*Math.pow(1024,0);
break;
case 'k':
bytes = num*Math.pow(1024,1);
break;
case 'm':
bytes = num*Math.pow(1024,2);
break;
case 'g':
bytes = num*Math.pow(1024,3);
break;
case 't':
bytes = num*Math.pow(1024,4);
break;
default:
bytes = num;
}
} else {
if (num) {
flag = 1;
msg ="You must specify correct units"
}
}
if ( flag == '1' ){
$(this).parents('.form-row').addClass('with-errors');
......@@ -171,14 +190,15 @@ $(document).ready(function() {
hidden_input.val(bytes);
}
// actions for int fields
// validation actions for int fields
else {
var is_int = value.match (new RegExp('^[0-9]*$'));
if ( !is_int ){
$(this).parents('.form-row').find('.error-msg').html('Enter a positive intiger');
$(this).parents('.form-row').find('.error-msg').html('Enter a positive integer');
$(this).parents('.form-row').addClass('with-errors');
} else {
......@@ -198,6 +218,7 @@ $(document).ready(function() {
} else {
hidden_input.removeAttr('value');
}
$('#icons span.info').removeClass('error-msg');
});
......
......@@ -4,9 +4,9 @@
{% block page.body %}
{% with object.owners as owners %}
{% with object.quota as quota %}
<div class="projects">
<h2>
{% if object.is_member %}
<em>
......@@ -79,46 +79,38 @@
<dd>{% if object.max_participants%}{{object.max_participants}}{% else %}&nbsp;{% endif %}</dd>
</dl>
</div>
<div class="full-dotted">
<div class="full-dotted">
<h3>RESOURCES:</h3>
{% if quota %}
<dl class="alt-style">
{% for k in quota|dkeys %}
{% with resource_catalog|lookup:'resources' as resources %}
{% with resources|lookup_uni:k as info %}
{% with resource_presentation|lookup_uni:k as resource_info %}
<dt>
Max {% if resource_info.is_abbreviation %}{{ info.name|upper }}{% else %}{{ info.name }}{% endif %}{% if not info.unit %}s {% endif %} per user
</dt>
<dd>
{% with quota|lookup:k as uplimit%}
{% if uplimit %}
{% if info.unit %}
{{ uplimit|sizeof_fmt }}
{% else %}
{{ uplimit|isinf }}
{% endif %}
{% endif %}
{% endwith%}
</dd>
{% endwith%}
{% endwith %}
{% endwith %}
{% endfor %}
</dl>
<dl class="alt-style">
{% for q in quota %}
<dt>
Max {% if q.is_abbreviation %}{{ q.verbose_name|upper }}{% else %}{{ q.verbose_name }}{% endif %}{% if not q.unit %}s {% endif %} per user
</dt>
<dd>
{% if q.value %}
{% if q.unit %}
{{ q.value|sizeof_fmt }}
{% else %}
{{ q.value|isinf }}
{% endif %}
{% else %}
Unlimited
{% endif %}
</dd>
{% endfor %}
</dl>
{% else %}
<p>No policies</p>
<p>No resources</p>
{% endif %}
</div>
<div class="full-dotted">
{% with page|concat:sorting as args %}
{% with object.membership_set.select_related.all|paginate:args as membership %}
{% if membership %}
<form method="GET" class="minimal" action="">
<form method="GET" class="minimal" action="#members-table">
<div class="form-row">
<select name="sorting" onchange="this.form.submit();" class="dropkicked">
<option value="">Sort by</option>
......@@ -128,7 +120,7 @@
</select>
</div>
</form>
<table class="alt-style">
<table class="alt-style" id="members-table">
<caption>MEMBERS:</caption>
<thead>
<tr>
......@@ -199,6 +191,6 @@
</div>
{% endwith %}
{% endwith %}
{% endblock %}
......@@ -6,10 +6,8 @@
<script src="{{ IM_STATIC_URL }}js/quotas.js"></script>
{% endblock %}
{% block page.body %}
<form action="#top" method="post" class="withlabels quotas-form" id="group_create_form">{% csrf_token %}
<fieldset class="with-info" id="top">
<legend>
1. CREATE GROUP
......@@ -31,93 +29,68 @@
<span>You need to specify at least one resource</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="proxy_{{ 'is_selected_'|add:g }}" id="proxy_{{ 'id_is_selected_'|add:g }}">
<p class="msg">{{ group_info.help_text }}</p>
</li>
{% endwith %}
{% for g, group_info in resource_catalog.groups.items %}
{% if g %}
<li>
<a href="#{{ g }}" id="{{'group_'|add:g}}"><img src="/static/im/images/create-{{ g }}.png" alt="vm"/></a>
<input type="hidden" name="proxy_{{ 'is_selected_'|add:g }}" id="proxy_{{ 'id_is_selected_'|add:g }}">
<p class="msg">{{ group_info.help_text }}</p>
</li>
{% endif %}
{% endfor %}
</ul>
</fieldset>
<div class="foo">
<div class="visible">
</div>
<div class="not-visible">
{% for gname, resources in resource_catalog.get_groups_resources %}
<div class="group {{'group_'|add:gname}}" id="{{ g }}">
<a href="#icons" class="delete">X remove resource</a>
{% for rname, rdata in resources.items %}
<fieldset class="quota">
<legend>
{% if rdata.is_abbreviation %}
{{ rdata.verbose_name|upper }}
{% else %}
{{ rdata.verbose_name|capfirst }}
{% endif %}
<span class="info">
<em>more info</em>
<span>{{ rdata.help_text }}</span>
</span>
</legend>
<div class="form-row">
<p class="clearfix">
<label for="{{'id_'|add:rname|add:'_uplimit'}}_proxy" >
Max {% if rdata.is_abbreviation %}{{ rdata.verbose_name|upper }}{% else %}{{ rdata.verbose_name }}{% endif %}{% if not rdata.unit %}s {% endif %} per user
</label>
<input type="text"
id="{{'id_'|add:rname|add:'_uplimit'}}_proxy"
name="{{rname|add:'_uplimit'}}_proxy"
placeholder="{{ rdata.placeholder}} "
{% if rdata.unit == 'bytes' %}
class="dehumanize"
{% endif %}
/>
<span class="extra-img">&nbsp;</span>
<span class="info"><em>more info</em><span>Leave this field blank if you don't want to specify this resource</span></span>
<p class="error-msg">Invalid format</p>
</p>
<p class="msg"></p>
</div>
</fieldset>
{% endfor %}
</div>
{% endfor %}
</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">
<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="form-row">
<p class="clearfix">
<label for="{{'id_'|add:r|add:'_uplimit'}}_proxy" >
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
</label>
<input type="text"
id="{{'id_'|add:r|add:'_uplimit'}}_proxy"
name="{{r|add:'_uplimit'}}_proxy"
placeholder="{{ resource_info.placeholder}} "
{% if resource.unit == 'bytes' %}
class="dehumanize"
{% endif %}
/>
<span class="extra-img">&nbsp;</span>
<span class="info"><em>more info</em><span>Leave this field blank if you don't want to specify this resource</span></span>
<p class="error-msg">Invalid format</p>
</p>
<p class="msg"></p>
</div>
</fieldset>
{% endwith %}
{% endwith %}
{% endfor %}
</div>
{% endfor %}
</div>
{% endwith %}
{% endwith %}
<div class="form-row submit">
<input type="submit" value="CONTINUE" class="submit altcol" autocomplete="off">
</div>
</form>
......
......@@ -6,7 +6,6 @@
{% with form.data as data %}
<div class="projects summary">
<form action="{% url group_add_complete %}" method="post" class="quotas-form">{% csrf_token %}
<legend>3. CONFIRM YOUR REQUEST</legend>
<!--
......@@ -39,41 +38,35 @@
<dd>{% if data.max_participants %}{{ data.max_participants }}{% else %}Unlimited{% endif %}</dd>
</dl>
</div>
<div class="full-dotted">
<h3>RESOURCES:</h3>
<dl class="alt-style">
{% for r in policies %}
{% with r.service|add:'.'|add:r.resource as t %}
{% with resource_catalog|lookup:'resources' as resources %}
{% with resources|lookup:t as info %}
{% with resource_presentation|lookup:t as resource_info %}
<dt>
Max {% if resource_info.is_abbreviation %}{{ r.resource|upper }}{% else %}{{ r.resource }}{% endif %}{% if not info.unit %}s {% endif %} per user
</dt>
<dd>
{% if r.uplimit %}
{% if info.unit %}
{{ r.uplimit|sizeof_fmt }}
{% else %}