--- /dev/null
+# 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.
+
+from django.db import IntegrityError, transaction
+from django.core.exceptions import ObjectDoesNotExist
+
+from functools import wraps
+from smtplib import SMTPException
+
+from astakos.im.models import (
+ AstakosUser, AstakosGroup, GroupKind, Resource, Service, RESOURCE_SEPARATOR
+)
+from astakos.im.api.backends.base import BaseBackend, SuccessResult, FailureResult
+from astakos.im.api.backends.errors import (
+ ItemNotExists, ItemExists, MissingIdentifier, MultipleItemsExist
+)
+from astakos.im.util import reserved_email, model_to_dict
+from astakos.im.endpoints.quotaholder import get_quota
+
+import logging
+
+logger = logging.getLogger(__name__)
+
+DEFAULT_CONTENT_TYPE = None
+
+
+def safe(func):
+ """Decorator function for views that implement an API method."""
+ @transaction.commit_manually
+ @wraps(func)
+ def wrapper(self, *args, **kwargs):
+ logger.debug('%s %s %s' % (func, args, kwargs))
+ try:
+ data = func(self, *args, **kwargs) or ()
+ except Exception, e:
+ logger.exception(e)
+ transaction.rollback()
+ return FailureResult(getattr(e, 'message', e))
+ else:
+ transaction.commit()
+ return SuccessResult(data)
+ return wrapper
+
+
+class DjangoBackend(BaseBackend):
+ def _lookup_object(self, model, **kwargs):
+ """
+ Returns an object of the specific model matching the given lookup
+ parameters.
+ """
+ if not kwargs:
+ raise MissingIdentifier
+ try:
+ return model.objects.get(**kwargs)
+ except model.DoesNotExist:
+ raise ItemNotExists(model._meta.verbose_name, **kwargs)
+ except model.MultipleObjectsReturned:
+ raise MultipleItemsExist(model._meta.verbose_name, **kwargs)
+
+ def _lookup_user(self, id):
+ """
+ Returns an AstakosUser having this id.
+ """
+ if not isinstance(id, int):
+ raise TypeError('User id should be of type int')
+ return self._lookup_object(AstakosUser, id=id)
+
+ def _lookup_service(self, id):
+ """
+ Returns an Service having this id.
+ """
+ if not isinstance(id, int):
+ raise TypeError('Service id should be of type int')
+ return self._lookup_object(Service, id=id)
+
+ def _list(self, model, filter=()):
+ q = model.objects.all()
+ if filter:
+ q = q.filter(id__in=filter)
+ return map(lambda o: model_to_dict(o, exclude=[]), q)
+
+ def _create_object(self, model, **kwargs):
+ o = model.objects.create(**kwargs)
+ o.save()
+ return o
+
+ def _update_object(self, model, id, save=True, **kwargs):
+ o = self._lookup_object(model, id=id)
+ if kwargs:
+ o.__dict__.update(kwargs)
+ if save:
+ o.save()
+ return o
+
+ @safe
+ def update_user(self, user_id, renew_token=False, **kwargs):
+ user = self._update_object(AstakosUser, user_id, save=False, **kwargs)
+ if renew_token:
+ user.renew_token()
+ if kwargs or renew_token:
+ user.save()
+
+ @safe
+ def create_user(self, **kwargs):
+ policies = kwargs.pop('policies', ())
+ permissions = kwargs.pop('permissions', ())
+ groups = kwargs.pop('groups', ())
+ password = kwargs.pop('password', None)
+
+ u = self._create_object(AstakosUser, **kwargs)
+
+ if password:
+ u.set_password(password)
+ u.permissions = permissions
+ u.policies = policies
+ u.extended_groups = groups
+ return self._list(AstakosUser, filter=(u.id,))
+
+ @safe
+ def add_policies(self, user_id, update=False, policies=()):
+ user = self._lookup_user(user_id)
+ rejected = []
+ append = rejected.append
+ for p in policies:
+ service = p.get('service')
+ resource = p.get('resource')
+ uplimit = p.get('uplimit')
+ try:
+ user.add_policy(service, resource, uplimit, update)
+ except (ObjectDoesNotExist, IntegrityError), e:
+ append((service, resource, e))
+ return rejected
+
+ @safe
+ def remove_policies(self, user_id, policies=()):
+ user = self._lookup_user(user_id)
+ if not user:
+ return user_id
+ rejected = []
+ append = rejected.append
+ for p in policies:
+ service = p.get('service')
+ resource = p.get('resource')
+ try:
+ user.delete_policy(service, resource)
+ except ObjectDoesNotExist, e:
+ append((service, resource, e))
+ return rejected
+ @safe
+ def add_permissions(self, user_id, permissions=()):
+ user = self._lookup_user(user_id)
+ rejected = []
+ append = rejected.append
+ for p in permissions:
+ try:
+ user.add_permission(p)
+ except IntegrityError, e:
+ append((p, e))
+ return rejected
+
+ @safe
+ def remove_permissions(self, user_id, permissions=()):
+ user = self._lookup_user(user_id)
+ rejected = []
+ append = rejected.append
+ for p in permissions:
+ try:
+ user.remove_permission(p)
+ except (ObjectDoesNotExist, IntegrityError), e:
+ append((p, e))
+ return rejected
+
+ @safe
+ def invite_users(self, senderid, recipients=()):
+ user = self._lookup_user(senderid)
+ rejected = []
+ append = rejected.append
+ for r in recipients:
+ try:
+ user.invite(r.get('email'), r.get('realname'))
+ except (IntegrityError, SMTPException), e:
+ append((email, e))
+ return rejected
+
+ @safe
+ def list_users(self, filter=()):
+ return self._list(AstakosUser, filter=filter)
+
+ @safe
+ def get_resource_usage(self, user_id):
+ user = self._lookup_user(user_id)
+ c, data = get_quota((user,))
+ resources = []
+ append = resources.append
+ for t in data:
+ t = (i if i else 0 for i in t)
+ (entity, name, quantity, capacity, importLimit, exportLimit,
+ imported, exported, returned, released, flags) = t
+ service, sep, resource = name.partition(RESOURCE_SEPARATOR)
+ resource = Resource.objects.select_related().get(
+ service__name=service, name=resource)
+ d = dict(name=name,
+ description=resource.desc,
+ unit=resource.unit or '',
+ maxValue=quantity + capacity,
+ currValue=quantity + imported - released - exported + returned)
+ append(d)
+ return resources
+
+ @safe
+ def list_resources(self, filter=()):
+ return self._list(Resource, filter=filter)
+
+ @safe
+ def create_service(self, **kwargs):
+ resources = kwargs.pop('resources', ())
+ s = self._create_object(Service, **kwargs)
+ s.resources = resources
+ return self._list(Service, filter=(s.id,))
+
+ @safe
+ def remove_services(self, ids=()):
+ # TODO return information for unknown ids
+ q = Service.objects.filter(id__in=ids)
+ q.delete()
+
+ @safe
+ def update_service(self, service_id, renew_token=False, **kwargs):
+ s = self._update_object(Service, service_id, save=False, **kwargs)
+ if renew_token:
+ s.renew_token()
+
+ if kwargs or renew_token:
+ s.save()
+
+ @safe
+ def add_resources(self, service_id, update=False, resources=()):
+ s = self._lookup_service(service_id)
+ rejected = []
+ append = rejected.append
+ for r in resources:
+ try:
+ rr = r.copy()
+ resource_id = rr.pop('id', None)
+ if update:
+ if not resource_id:
+ raise MissingIdentifier
+ resource = self._update_object(Resource, resource_id, **rr)
+ else:
+ resource = self._create_object(Resource, service=s, **rr)
+ except Exception, e:
+ append((r, e))
+ return rejected
+
+ @safe
+ def remove_resources(self, service_id, ids=()):
+ # TODO return information for unknown ids
+ q = Resource.objects.filter(service__id=service_id,
+ id__in=ids)
+ q.delete()
+
+ @safe
+ def create_group(self, **kwargs):
+ policies = kwargs.pop('policies', ())
+ permissions = kwargs.pop('permissions', ())
+ members = kwargs.pop('members', ())
+ owners = kwargs.pop('owners', ())
+ kwargs['kind'] = self._lookup_object(
+ GroupKind, name=kwargs.get('kind', 'course')
+ )
+
+ g = self._create_object(AstakosGroup, **kwargs)
+
+ g.permissions = permissions
+ g.policies = policies
+# g.members = members
+ g.owners = owners
+ return self._list(AstakosGroup, filter=(g.id,))
\ No newline at end of file
--- /dev/null
+// jQuery Alert Dialogs Plugin\r
+//\r
+// Version 1.1\r
+//\r
+// Cory S.N. LaViska\r
+// A Beautiful Site (http://abeautifulsite.net/)\r
+// 14 May 2009\r
+//\r
+// Visit http://abeautifulsite.net/notebook/87 for more information\r
+//\r
+// Usage:\r
+// jAlert( message, [title, callback] )\r
+// jConfirm( message, [title, callback] )\r
+// jPrompt( message, [value, title, callback] )\r
+// \r
+// History:\r
+//\r
+// 1.00 - Released (29 December 2008)\r
+//\r
+// 1.01 - Fixed bug where unbinding would destroy all resize events\r
+//\r
+// License:\r
+// \r
+// This plugin is dual-licensed under the GNU General Public License and the MIT License and\r
+// is copyright 2008 A Beautiful Site, LLC. \r
+//\r
+(function($) {\r
+ \r
+ $.alerts = {\r
+ \r
+ // These properties can be read/written by accessing $.alerts.propertyName from your scripts at any time\r
+ \r
+ verticalOffset: -75, // vertical offset of the dialog from center screen, in pixels\r
+ horizontalOffset: 0, // horizontal offset of the dialog from center screen, in pixels/\r
+ repositionOnResize: true, // re-centers the dialog on window resize\r
+ overlayOpacity: .01, // transparency level of overlay\r
+ overlayColor: '#FFF', // base color of overlay\r
+ draggable: true, // make the dialogs draggable (requires UI Draggables plugin)\r
+ okButton: ' OK ', // text for the OK button\r
+ cancelButton: ' Cancel ', // text for the Cancel button\r
+ dialogClass: null, // if specified, this class will be applied to all dialogs\r
+ \r
+ // Public methods\r
+ \r
+ alert: function(message, title, callback) {\r
+ if( title == null ) title = 'Alert';\r
+ $.alerts._show(title, message, null, 'alert', function(result) {\r
+ if( callback ) callback(result);\r
+ });\r
+ },\r
+ \r
+ confirm: function(message, title, callback) {\r
+ if( title == null ) title = 'Confirm';\r
+ $.alerts._show(title, message, null, 'confirm', function(result) {\r
+ if( callback ) callback(result);\r
+ });\r
+ },\r
+ \r
+ prompt: function(message, value, title, callback) {\r
+ if( title == null ) title = 'Prompt';\r
+ $.alerts._show(title, message, value, 'prompt', function(result) {\r
+ if( callback ) callback(result);\r
+ });\r
+ },\r
+ \r
+ // Private methods\r
+ \r
+ _show: function(title, msg, value, type, callback) {\r
+ \r
+ $.alerts._hide();\r
+ $.alerts._overlay('show');\r
+ \r
+ $("BODY").append(\r
+ '<div id="popup_container">' +\r
+ '<h1 id="popup_title"></h1>' +\r
+ '<div id="popup_content">' +\r
+ '<div id="popup_message"></div>' +\r
+ '</div>' +\r
+ '</div>');\r
+ \r
+ if( $.alerts.dialogClass ) $("#popup_container").addClass($.alerts.dialogClass);\r
+ \r
+ // IE6 Fix\r
+ var pos = ($.browser.msie && parseInt($.browser.version) <= 6 ) ? 'absolute' : 'fixed'; \r
+ \r
+ $("#popup_container").css({\r
+ position: pos,\r
+ zIndex: 99999,\r
+ padding: 0,\r
+ margin: 0\r
+ });\r
+ \r
+ $("#popup_title").text(title);\r
+ $("#popup_content").addClass(type);\r
+ $("#popup_message").text(msg);\r
+ $("#popup_message").html( $("#popup_message").text().replace(/\n/g, '<br />') );\r
+ \r
+ $("#popup_container").css({\r
+ minWidth: $("#popup_container").outerWidth(),\r
+ maxWidth: $("#popup_container").outerWidth()\r
+ });\r
+ \r
+ $.alerts._reposition();\r
+ $.alerts._maintainPosition(true);\r
+ \r
+ switch( type ) {\r
+ case 'alert':\r
+ $("#popup_message").after('<div id="popup_panel"><input type="button" value="' + $.alerts.okButton + '" id="popup_ok" /></div>');\r
+ $("#popup_ok").click( function() {\r
+ $.alerts._hide();\r
+ callback(true);\r
+ });\r
+ $("#popup_ok").focus().keypress( function(e) {\r
+ if( e.keyCode == 13 || e.keyCode == 27 ) $("#popup_ok").trigger('click');\r
+ });\r
+ break;\r
+ case 'confirm':\r
+ $("#popup_message").after('<div id="popup_panel"><input type="button" value="' + $.alerts.okButton + '" id="popup_ok" /> <input type="button" value="' + $.alerts.cancelButton + '" id="popup_cancel" /></div>');\r
+ $("#popup_ok").click( function() {\r
+ $.alerts._hide();\r
+ if( callback ) callback(true);\r
+ });\r
+ $("#popup_cancel").click( function() {\r
+ $.alerts._hide();\r
+ if( callback ) callback(false);\r
+ });\r
+ $("#popup_ok").focus();\r
+ $("#popup_ok, #popup_cancel").keypress( function(e) {\r
+ if( e.keyCode == 13 ) $("#popup_ok").trigger('click');\r
+ if( e.keyCode == 27 ) $("#popup_cancel").trigger('click');\r
+ });\r
+ break;\r
+ case 'prompt':\r
+ $("#popup_message").append('<br /><input type="text" size="30" id="popup_prompt" />').after('<div id="popup_panel"><input type="button" value="' + $.alerts.okButton + '" id="popup_ok" /> <input type="button" value="' + $.alerts.cancelButton + '" id="popup_cancel" /></div>');\r
+ $("#popup_prompt").width( $("#popup_message").width() );\r
+ $("#popup_ok").click( function() {\r
+ var val = $("#popup_prompt").val();\r
+ $.alerts._hide();\r
+ if( callback ) callback( val );\r
+ });\r
+ $("#popup_cancel").click( function() {\r
+ $.alerts._hide();\r
+ if( callback ) callback( null );\r
+ });\r
+ $("#popup_prompt, #popup_ok, #popup_cancel").keypress( function(e) {\r
+ if( e.keyCode == 13 ) $("#popup_ok").trigger('click');\r
+ if( e.keyCode == 27 ) $("#popup_cancel").trigger('click');\r
+ });\r
+ if( value ) $("#popup_prompt").val(value);\r
+ $("#popup_prompt").focus().select();\r
+ break;\r
+ }\r
+ \r
+ // Make draggable\r
+ if( $.alerts.draggable ) {\r
+ try {\r
+ $("#popup_container").draggable({ handle: $("#popup_title") });\r
+ $("#popup_title").css({ cursor: 'move' });\r
+ } catch(e) { /* requires jQuery UI draggables */ }\r
+ }\r
+ },\r
+ \r
+ _hide: function() {\r
+ $("#popup_container").remove();\r
+ $.alerts._overlay('hide');\r
+ $.alerts._maintainPosition(false);\r
+ },\r
+ \r
+ _overlay: function(status) {\r
+ switch( status ) {\r
+ case 'show':\r
+ $.alerts._overlay('hide');\r
+ $("BODY").append('<div id="popup_overlay"></div>');\r
+ $("#popup_overlay").css({\r
+ position: 'absolute',\r
+ zIndex: 99998,\r
+ top: '0px',\r
+ left: '0px',\r
+ width: '100%',\r
+ height: $(document).height(),\r
+ background: $.alerts.overlayColor,\r
+ opacity: $.alerts.overlayOpacity\r
+ });\r
+ break;\r
+ case 'hide':\r
+ $("#popup_overlay").remove();\r
+ break;\r
+ }\r
+ },\r
+ \r
+ _reposition: function() {\r
+ var top = (($(window).height() / 2) - ($("#popup_container").outerHeight() / 2)) + $.alerts.verticalOffset;\r
+ var left = (($(window).width() / 2) - ($("#popup_container").outerWidth() / 2)) + $.alerts.horizontalOffset;\r
+ if( top < 0 ) top = 0;\r
+ if( left < 0 ) left = 0;\r
+ \r
+ // IE6 fix\r
+ if( $.browser.msie && parseInt($.browser.version) <= 6 ) top = top + $(window).scrollTop();\r
+ \r
+ $("#popup_container").css({\r
+ top: top + 'px',\r
+ left: left + 'px'\r
+ });\r
+ $("#popup_overlay").height( $(document).height() );\r
+ },\r
+ \r
+ _maintainPosition: function(status) {\r
+ if( $.alerts.repositionOnResize ) {\r
+ switch(status) {\r
+ case true:\r
+ $(window).bind('resize', $.alerts._reposition);\r
+ break;\r
+ case false:\r
+ $(window).unbind('resize', $.alerts._reposition);\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ \r
+ }\r
+ \r
+ // Shortuct functions\r
+ jAlert = function(message, title, callback) {\r
+ $.alerts.alert(message, title, callback);\r
+ }\r
+ \r
+ jConfirm = function(message, title, callback) {\r
+ $.alerts.confirm(message, title, callback);\r
+ };\r
+ \r
+ jPrompt = function(message, value, title, callback) {\r
+ $.alerts.prompt(message, value, title, callback);\r
+ };\r
+ \r
+})(jQuery);
\ No newline at end of file
--- /dev/null
+{% extends "im/account_base.html" %}
+
+{% load filters %}
+{% block headjs %}
+ {{ block.super }}
+ <script src="{{ IM_STATIC_URL }}js/quotas.js"></script>
+{% endblock %}
+{% block page.body %}
+
+
+<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">
+ <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"> </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'}}_proxy" 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'}}_proxy" class="radio limited">
+ <input type="text"
+ id="{{'id_'|add:r|add:'_uplimit'}}"
+ name="{{r|add:'_uplimit'}}_proxy"
+ placeholder="{{ resource_info.placeholder}} "
+ {% if resource.unit == 'bytes' %}
+ class="dehumanize"
+ {% endif %}
+ />
+ </p>
+ <p class="msg"></p>
+ </div>
+
+ </div>
+ -->
+ <div class="form-row">
+ <p class="clearfix">
+ <label for="num_storage">
+ 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"> </span>
+ <span class="info"><em>more info</em><span>Help Text</span></span>
+ </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>
+
+<script>
+
+</script>
+
+{% endblock %}
\ No newline at end of file
--- /dev/null
+{% extends "im/account_base.html" %}
+
+{% load filters %}
+{% block headjs %}
+ {{ block.super }}
+ <script src="{{ IM_STATIC_URL }}js/quotas.js"></script>
+{% endblock %}
+{% block page.body %}
+
+
+<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"> </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>
+ {% 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
--- /dev/null
+{% extends "im/account_base.html" %}
+
+{% load filters %}
+{% block headjs %}
+ {{ block.super }}
+ <script src="{{ IM_STATIC_URL }}js/quotas.js"></script>
+{% endblock %}
+{% block page.body %}
+
+
+<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">
+ <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"> </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'}}_proxy" 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'}}_proxy" class="radio limited">
+ <input type="text"
+ id="{{'id_'|add:r|add:'_uplimit'}}"
+ name="{{r|add:'_uplimit'}}_proxy"
+ placeholder="{{ resource_info.placeholder}} "
+ {% if resource.unit == 'bytes' %}
+ class="dehumanize"
+ {% endif %}
+ />
+ </p>
+ <p class="msg"></p>
+ </div>
+
+ </div>
+ -->
+ <div class="form-row">
+ <p class="clearfix">
+ <label for="num_storage">
+ 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"> </span>
+ <span class="info"><em>more info</em><span>Help Text</span></span>
+ </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>
+
+<script>
+
+</script>
+
+{% endblock %}
\ No newline at end of file