.project
.pydevproject
snf-astakos-app/astakos/version.py
+snf-astakos-app/distribute-0.6.10-py2.6.egg
+snf-astakos-app/distribute-0.6.10.tar.gz
\ No newline at end of file
--- /dev/null
+#Mon Nov 05 16:04:44 EET 2012
+eclipse.preferences.version=1
+encoding//astakos/im/migrations/0010_auto__add_field_astakosuser_activation_sent__chg_field_service_url.py=utf-8
+encoding//astakos/im/migrations/0011_set_old_activation_sent.py=utf-8
+encoding//astakos/im/migrations/0017_populate_resource_data.py=utf-8
+encoding//astakos/im/migrations/0030_populate_resource_data.py=utf-8
Changelog
---------
+next
+^^^^
+
+- Setting ASTAKOS_DEFAULT_ADMIN_EMAIL has been deprecated. Use ADMINS django setting instead.
+- Setting ASTAKOS_DEFAULT_FROM_EMAIL has been deprecated. Use SERVER_EMAIL django setting instead.
v0.7.5
^^^^^^
ASTAKOS_GREETING_EMAIL_SUBJECT 'Welcome to %s alpha2 testing' % SITENAME Welcome email subject
ASTAKOS_FEEDBACK_EMAIL_SUBJECT 'Feedback from %s alpha2 testing' % SITENAME Feedback email subject
ASTAKOS_VERIFICATION_EMAIL_SUBJECT '%s alpha2 testing account activation is needed' % SITENAME Account activation email subject
-ASTAKOS_ADMIN_NOTIFICATION_EMAIL_SUBJECT '%s alpha2 testing account created (%%(user)s)' % SITENAME Account creation admin notification email subject
+ASTAKOS_ACCOUNT_CREATION_SUBJECT '%s alpha2 testing account created (%%(user)s)' % SITENAME Account creation email subject
+ASTAKOS_GROUP_CREATION_SUBJECT '%s alpha2 testing group created (%%(group)s)' % SITENAME Group creation email subject
ASTAKOS_HELPDESK_NOTIFICATION_EMAIL_SUBJECT '%s alpha2 testing account activated (%%(user)s)' % SITENAME Account activation helpdesk notification email subject
ASTAKOS_EMAIL_CHANGE_EMAIL_SUBJECT 'Email change on %s alpha2 testing' % SITENAME Email change subject
ASTAKOS_PASSWORD_RESET_EMAIL_SUBJECT 'Password reset on %s alpha2 testing' % SITENAME Password change email subject
+
+ASTAKOS_QUOTA_HOLDER_URL '' The quota holder URI
+ e.g. ``http://localhost:8080/api/quotaholder/v``
+ASTAKOS_SERVICES {'cyclades': {'resources': [{'desc': 'Number of virtual machines', Default cloud service information
+ 'group': 'compute',
+ 'name': 'vm',
+ 'uplimit': 2},
+ {'desc': 'Virtual machine disk size',
+ 'group': 'compute',
+ 'name': 'diskspace',
+ 'unit': 'GB',
+ 'uplimit': 5},
+ {'desc': 'Number of virtual machine processors',
+ 'group': 'compute',
+ 'name': 'cpu',
+ 'uplimit': 1},
+ {'desc': 'Virtual machines',
+ 'group': 'compute',
+ 'name': 'ram',
+ 'unit': 'MB',
+ 'uplimit': 1024}],
+ 'url': 'https://node1.example.com/ui/'},
+ 'pithos+': {'resources': [{'desc': 'Pithos account diskspace',
+ 'group': 'storage',
+ 'name': 'diskspace',
+ 'unit': 'bytes',
+ 'uplimit': 5368709120}],
+ 'url': 'https://node2.example.com/ui/'}}
+ASTAKOS_AQUARIUM_URL '' The billing (aquarium) URI
+ e.g. ``http://localhost:8888/user``
+ASTAKOS_PAGINATE_BY 10 Number of object to be displayed per page
+
ASTAKOS_NEWPASSWD_INVALIDATE_TOKEN True Enforce token renewal on password change/reset. If set to False, user can optionally decide
whether to renew the token or not.
ASTAKOS_ENABLE_LOCAL_ACCOUNT_MIGRATION True Permit local account migration to third party account
To update user credibility from the billing system (Aquarium), enable the queue, install snf-pithos-tools and use ``pithos-dispatcher``::
- pithos-dispatcher --exchange=aquarium --callback=astakos.im.queue.listener.on_creditevent
-
-Load groups:
-------------
-
-To set the initial user groups load the followind fixture:
-
- snf-manage loaddata groups
+ pithos-dispatcher --exchange=aquarium --callback=astakos.im.endpoints.aquarium.consumer.on_creditevent
# Copyright (c) Django Software Foundation and individual contributors.
# 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,
+#
+# 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
+#
+# 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.
-#
+#
# 3. Neither the name of Django nor the names of its contributors may be used
# to endorse or promote products derived from this software without
# specific prior written permission.
-#
+#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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
VERSION = (0, 3, 0, 'alpha', 0)
+
def get_version():
version = '%s.%s' % (VERSION[0], VERSION[1])
if VERSION[2]:
from django.utils.importlib import import_module
from django.core.exceptions import ImproperlyConfigured
-from django.core.mail import send_mail
-from django.template.loader import render_to_string
-from django.contrib import messages
-from django.core.urlresolvers import reverse
from django.utils.translation import ugettext as _
-from django.db import transaction
-from urlparse import urljoin
-
-from astakos.im.models import AstakosUser, Invitation
-from astakos.im.forms import *
+from astakos.im.models import AstakosUser
from astakos.im.util import get_invitation
-from astakos.im.functions import send_verification, send_activation, \
- send_admin_notification, activate, SendMailError
-from astakos.im.settings import INVITATIONS_ENABLED, DEFAULT_CONTACT_EMAIL, \
- DEFAULT_FROM_EMAIL, MODERATION_ENABLED, SITENAME, DEFAULT_ADMIN_EMAIL, RE_USER_EMAIL_PATTERNS
+from astakos.im.functions import (
+ send_activation, send_account_creation_notification, activate
+)
+from astakos.im.settings import (
+ INVITATIONS_ENABLED, MODERATION_ENABLED, RE_USER_EMAIL_PATTERNS
+)
+
+import astakos.im.messages as astakos_messages
-import socket
import logging
import re
logger = logging.getLogger(__name__)
+
def get_backend(request):
"""
Returns an instance of an activation backend,
"""
module = 'astakos.im.activation_backends'
prefix = 'Invitations' if INVITATIONS_ENABLED else 'Simple'
- backend_class_name = '%sBackend' %prefix
+ backend_class_name = '%sBackend' % prefix
try:
mod = import_module(module)
except ImportError, e:
- raise ImproperlyConfigured('Error loading activation backend %s: "%s"' % (module, e))
+ raise ImproperlyConfigured(
+ 'Error loading activation backend %s: "%s"' % (module, e))
try:
backend_class = getattr(mod, backend_class_name)
except AttributeError:
- raise ImproperlyConfigured('Module "%s" does not define a activation backend named "%s"' % (module, attr))
+ raise ImproperlyConfigured('Module "%s" does not define a activation backend named "%s"' % (module, backend_class_name))
return backend_class(request)
+
class ActivationBackend(object):
+ def __init__(self, request):
+ self.request = request
+
def _is_preaccepted(self, user):
# return True if user email matches specific patterns
for pattern in RE_USER_EMAIL_PATTERNS:
if re.match(pattern, user.email):
return True
return False
-
+
def get_signup_form(self, provider='local', instance=None):
"""
Returns a form instance of the relevant class
"""
main = provider.capitalize() if provider == 'local' else 'ThirdParty'
- suffix = 'UserCreationForm'
+ suffix = 'UserCreationForm'
formclass = '%s%s' % (main, suffix)
request = self.request
initial_data = None
send_activation(user, activation_template_name)
return VerificationSent()
else:
- send_admin_notification(user, admin_email_template_name)
+ send_account_creation_notification(
+ template_name=admin_email_template_name,
+ dictionary={'user': user.__dict__, 'group_creation': True}
+ )
return NotificationSent()
except BaseException, e:
logger.exception(e)
raise e
+
class InvitationsBackend(ActivationBackend):
"""
A activation backend which implements the following workflow: a user
account is created and the user is going to receive an email as soon as an
administrator activates his/her account.
"""
- def __init__(self, request):
- self.request = request
- super(InvitationsBackend, self).__init__()
def get_signup_form(self, provider='local', instance=None):
"""
Returns a form instance of the relevant class
-
+
raises Invitation.DoesNotExist and ValueError if invitation is consumed
or invitation username is reserved.
"""
initial_data = self.get_signup_initial_data(provider)
prefix = 'Invited' if invitation else ''
main = provider.capitalize()
- suffix = 'UserCreationForm'
+ suffix = 'UserCreationForm'
formclass = '%s%s%s' % (prefix, main, suffix)
return globals()[formclass](initial_data, instance=instance, request=self.request)
if invitation:
# create a tmp user with the invitation realname
# to extract first and last name
- u = AstakosUser(realname = invitation.realname)
- initial_data = {'email':invitation.username,
- 'inviter':invitation.inviter.realname,
- 'first_name':u.first_name,
- 'last_name':u.last_name,
- 'provider':provider}
+ u = AstakosUser(realname=invitation.realname)
+ initial_data = {'email': invitation.username,
+ 'inviter': invitation.inviter.realname,
+ 'first_name': u.first_name,
+ 'last_name': u.last_name,
+ 'provider': provider}
else:
if provider == request.POST.get('provider', ''):
initial_data = request.POST
return True
return False
+
class SimpleBackend(ActivationBackend):
"""
A activation backend which implements the following workflow: a user
supplies the necessary registation information, an incative user account is
created and receives an email in order to activate his/her account.
"""
- def __init__(self, request):
- self.request = request
- super(SimpleBackend, self).__init__()
-
def _is_preaccepted(self, user):
if super(SimpleBackend, self)._is_preaccepted(user):
return True
return False
return True
+
class ActivationResult(object):
def __init__(self, message):
self.message = message
+
class VerificationSent(ActivationResult):
def __init__(self):
- message = _('Verification sent.')
+ message = _(astakos_messages.VERIFICATION_SENT)
super(VerificationSent, 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.NOTIFICATION_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)
\ No newline at end of file
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.
+from functools import wraps
+from traceback import format_exc
+from urllib import quote, unquote
+
from django.http import HttpResponse
from django.utils import simplejson as json
+from django.conf import settings
+from django.core.urlresolvers import reverse
+
+from astakos.im.models import AstakosUser, GroupKind, Service, Resource
+from astakos.im.api.faults import Fault, ItemNotFound, InternalServerError, BadRequest
+from astakos.im.settings import INVITATIONS_ENABLED, COOKIE_NAME, EMAILCHANGE_ENABLED
-from astakos.im.models import AstakosUser
-from astakos.im.api.faults import ItemNotFound
+import logging
+logger = logging.getLogger(__name__)
format = ('%a, %d %b %Y %H:%M:%S GMT')
+absolute = lambda request, url: request.build_absolute_uri(url)
+
+
+def render_fault(request, fault):
+ if isinstance(fault, InternalServerError) and settings.DEBUG:
+ fault.details = format_exc(fault)
+
+ request.serialization = 'text'
+ data = fault.message + '\n'
+ if fault.details:
+ data += '\n' + fault.details
+ response = HttpResponse(data, status=fault.code)
+ response['Content-Length'] = len(response.content)
+ return response
+
+
+def api_method(http_method=None):
+ """Decorator function for views that implement an API method."""
+ def decorator(func):
+ @wraps(func)
+ def wrapper(request, *args, **kwargs):
+ try:
+ if http_method and request.method != http_method:
+ raise BadRequest('Method not allowed.')
+ response = func(request, *args, **kwargs)
+ return response
+ except Fault, fault:
+ return render_fault(request, fault)
+ except BaseException, e:
+ logger.exception('Unexpected error: %s' % e)
+ fault = InternalServerError('Unexpected error')
+ return render_fault(request, fault)
+ return wrapper
+ return decorator
+
+
def _get_user_by_username(user_id):
try:
- user = AstakosUser.objects.get(username = user_id)
- except AstakosUser.DoesNotExist, e:
+ user = AstakosUser.objects.get(username=user_id)
+ except AstakosUser.DoesNotExist:
raise ItemNotFound('Invalid userid')
else:
response = HttpResponse()
- response.status=200
- user_info = {'id':user.id,
- 'username':user.username,
- 'email':[user.email],
- 'name':user.realname,
- 'auth_token_created':user.auth_token_created.strftime(format),
- 'auth_token_expires':user.auth_token_expires.strftime(format),
- 'has_credits':user.has_credits,
- 'enabled':user.is_active,
- 'groups':[g.name for g in user.groups.all()]}
+ response.status = 200
+ user_info = {'id': user.id,
+ 'username': user.username,
+ 'email': [user.email],
+ 'name': user.realname,
+ 'auth_token_created': user.auth_token_created.strftime(format),
+ 'auth_token_expires': user.auth_token_expires.strftime(format),
+ 'has_credits': user.has_credits,
+ 'enabled': user.is_active,
+ 'groups': [g.name for g in user.groups.all()]}
response.content = json.dumps(user_info)
response['Content-Type'] = 'application/json; charset=UTF-8'
response['Content-Length'] = len(response.content)
return response
+
def _get_user_by_email(email):
if not email:
raise BadRequest('Email missing')
try:
- user = AstakosUser.objects.get(email = email)
- except AstakosUser.DoesNotExist, e:
+ user = AstakosUser.objects.get(email=email)
+ except AstakosUser.DoesNotExist:
raise ItemNotFound('Invalid email')
-
+
if not user.is_active:
raise ItemNotFound('Inactive user')
else:
response = HttpResponse()
- response.status=200
- user_info = {'id':user.id,
- 'username':user.username,
- 'email':[user.email],
- 'enabled':user.is_active,
- 'name':user.realname,
- 'auth_token_created':user.auth_token_created.strftime(format),
- 'auth_token_expires':user.auth_token_expires.strftime(format),
- 'has_credits':user.has_credits,
- 'groups':[g.name for g in user.groups.all()],
- 'user_permissions':[p.codename for p in user.user_permissions.all()]}
+ response.status = 200
+ user_info = {'id': user.id,
+ 'username': user.username,
+ 'email': [user.email],
+ 'enabled': user.is_active,
+ 'name': user.realname,
+ 'auth_token_created': user.auth_token_created.strftime(format),
+ 'auth_token_expires': user.auth_token_expires.strftime(format),
+ 'has_credits': user.has_credits,
+ 'groups': [g.name for g in user.groups.all()],
+ 'user_permissions': [p.codename for p in user.user_permissions.all()]}
response.content = json.dumps(user_info)
response['Content-Type'] = 'application/json; charset=UTF-8'
response['Content-Length'] = len(response.content)
- return response
\ No newline at end of file
+ return response
+
+
+@api_method(http_method='GET')
+def get_services(request):
+ callback = request.GET.get('callback', None)
+ services = Service.objects.all()
+ data = tuple({'id': s.pk, 'name': s.name, 'url': s.url, 'icon':
+ s.icon} for s in services)
+ data = json.dumps(data)
+ mimetype = 'application/json'
+
+ if callback:
+ mimetype = 'application/javascript'
+ data = '%s(%s)' % (callback, data)
+
+ return HttpResponse(content=data, mimetype=mimetype)
+
+
+@api_method()
+def get_menu(request, with_extra_links=False, with_signout=True):
+ user = request.user
+ index_url = reverse('index')
+ l = [{'url': absolute(request, index_url), 'name': "Sign in"}]
+ if user.is_authenticated():
+ l = []
+ append = l.append
+ item = MenuItem
+ item.current_path = absolute(request, request.path)
+ append(item(
+ url=absolute(request, reverse('index')),
+ name=user.email))
+ append(item(url=absolute(request, reverse('edit_profile')),
+ name="My account"))
+ if with_extra_links:
+ if user.has_usable_password() and user.provider in ('local', ''):
+ append(item(
+ url=absolute(request, reverse('password_change')),
+ name="Change password"))
+ if EMAILCHANGE_ENABLED:
+ append(item(
+ url=absolute(request, reverse('email_change')),
+ name="Change email"))
+ if INVITATIONS_ENABLED:
+ append(item(
+ url=absolute(request, reverse('invite')),
+ name="Invitations"))
+
+ append(item(
+ url=absolute(request, reverse('group_list')),
+ name="Groups",
+ submenu=(item(
+ url=absolute(request,
+ reverse('group_list')),
+ name="Overview"),
+ item(
+ url=absolute(request,
+ reverse('group_create_list')),
+ name="Create"),
+ item(
+ url=absolute(request,
+ reverse('group_search')),
+ name="Join"),)))
+ append(item(
+ url=absolute(request, reverse('resource_list')),
+ name="Report"))
+ append(item(
+ url=absolute(request, reverse('feedback')),
+ name="Feedback"))
+ append(item(
+ url=absolute(request, reverse('billing')),
+ name="Billing"))
+ append(item(
+ url=absolute(request, reverse('timeline')),
+ name="Timeline"))
+ if with_signout:
+ append(item(
+ url=absolute(request, reverse('logout')),
+ name="Sign out"))
+
+ callback = request.GET.get('callback', None)
+ data = json.dumps(tuple(l))
+ mimetype = 'application/json'
+
+ if callback:
+ mimetype = 'application/javascript'
+ data = '%s(%s)' % (callback, data)
+
+ return HttpResponse(content=data, mimetype=mimetype)
+
+
+class MenuItem(dict):
+ current_path = ''
+
+ def __init__(self, *args, **kwargs):
+ super(MenuItem, self).__init__(*args, **kwargs)
+ if kwargs.get('url') or kwargs.get('submenu'):
+ self.__set_is_active__()
+
+ def __setitem__(self, key, value):
+ super(MenuItem, self).__setitem__(key, value)
+ if key in ('url', 'submenu'):
+ self.__set_is_active__()
+
+ def __set_is_active__(self):
+ if self.get('is_active'):
+ return
+ if self.current_path == self.get('url'):
+ self.__setitem__('is_active', True)
+ else:
+ submenu = self.get('submenu', ())
+ current = (i for i in submenu if i.get('url') == self.current_path)
+ try:
+ current_node = current.next()
+ if not current_node.get('is_active'):
+ current_node.__setitem__('is_active', True)
+ self.__setitem__('is_active', True)
+ except StopIteration:
+ return
+
+ def __setattribute__(self, name, value):
+ super(MenuItem, self).__setattribute__(name, value)
+ if name == 'current_path':
+ self.__set_is_active__()
# or implied, of GRNET S.A.
import logging
-import urllib
from functools import wraps
-from traceback import format_exc
from time import time, mktime
-from urllib import quote
-from urlparse import urlparse
-from collections import defaultdict
-from django.conf import settings
from django.http import HttpResponse
from django.utils import simplejson as json
-from django.core.urlresolvers import reverse
-from astakos.im.api.faults import *
-from astakos.im.models import AstakosUser, Service
-from astakos.im.settings import INVITATIONS_ENABLED, EMAILCHANGE_ENABLED
+from astakos.im.api.faults import (
+ Fault, Unauthorized, InternalServerError, BadRequest,
+ Forbidden
+)
+from astakos.im.api import render_fault, _get_user_by_email, _get_user_by_username
+from astakos.im.models import AstakosUser
from astakos.im.util import epoch
-from astakos.im.api import _get_user_by_email, _get_user_by_username
logger = logging.getLogger(__name__)
format = ('%a, %d %b %Y %H:%M:%S GMT')
-def render_fault(request, fault):
- if isinstance(fault, InternalServerError) and settings.DEBUG:
- fault.details = format_exc(fault)
-
- request.serialization = 'text'
- data = fault.message + '\n'
- if fault.details:
- data += '\n' + fault.details
- response = HttpResponse(data, status=fault.code)
- response['Content-Length'] = len(response.content)
- return response
def api_method(http_method=None, token_required=False, perms=None):
"""Decorator function for views that implement an API method."""
return wrapper
return decorator
+
@api_method(http_method='GET', token_required=True)
def authenticate_old(request, user=None):
# Normal Response Codes: 204
if (time() - mktime(user.auth_token_expires.timetuple())) > 0:
raise Unauthorized('Authentication expired')
- if not user.signed_terms():
+ if not user.signed_terms:
raise Unauthorized('Pending approval terms')
response = HttpResponse()
- response.status=204
- user_info = {'username':user.username,
- 'uniq':user.email,
- 'auth_token':user.auth_token,
- 'auth_token_created':user.auth_token_created.isoformat(),
- 'auth_token_expires':user.auth_token_expires.isoformat(),
- 'has_credits':user.has_credits,
- 'has_signed_terms':user.signed_terms(),
- 'groups':[g.name for g in user.groups.all()]}
+ response.status = 204
+ user_info = {'username': user.username,
+ 'uniq': user.email,
+ 'auth_token': user.auth_token,
+ 'auth_token_created': user.auth_token_created.isoformat(),
+ 'auth_token_expires': user.auth_token_expires.isoformat(),
+ 'has_credits': user.has_credits,
+ 'has_signed_terms': user.signed_terms,
+ 'groups': [g.name for g in user.groups.all()]}
response.content = json.dumps(user_info)
response['Content-Type'] = 'application/json; charset=UTF-8'
response['Content-Length'] = len(response.content)
return response
+
@api_method(http_method='GET', token_required=True)
def authenticate(request, user=None):
# Normal Response Codes: 204
if (time() - mktime(user.auth_token_expires.timetuple())) > 0:
raise Unauthorized('Authentication expired')
- if not user.signed_terms():
+ if not user.signed_terms:
raise Unauthorized('Pending approval terms')
response = HttpResponse()
- response.status=204
- user_info = {'userid':user.username,
- 'email':[user.email],
- 'name':user.realname,
- 'auth_token':user.auth_token,
- 'auth_token_created':epoch(user.auth_token_created),
- 'auth_token_expires':epoch(user.auth_token_expires),
- 'has_credits':user.has_credits,
- 'is_active':user.is_active,
- 'groups':[g.name for g in user.groups.all()]}
+ response.status = 204
+ user_info = {'userid': user.username,
+ 'email': [user.email],
+ 'name': user.realname,
+ 'auth_token': user.auth_token,
+ 'auth_token_created': epoch(user.auth_token_created),
+ 'auth_token_expires': epoch(user.auth_token_expires),
+ 'has_credits': user.has_credits,
+ 'is_active': user.is_active,
+ 'groups': [g.name for g in user.groups.all()]}
response.content = json.dumps(user_info)
response['Content-Type'] = 'application/json; charset=UTF-8'
response['Content-Length'] = len(response.content)
return response
-@api_method(http_method='GET')
-def get_services(request):
- callback = request.GET.get('callback', None)
- services = Service.objects.all()
- data = tuple({'id':s.pk, 'name':s.name, 'url':s.url, 'icon':s.icon} for s in services)
- data = json.dumps(data)
- mimetype = 'application/json'
-
- if callback:
- mimetype = 'application/javascript'
- data = '%s(%s)' % (callback, data)
-
- return HttpResponse(content=data, mimetype=mimetype)
-
-@api_method()
-def get_menu(request, with_extra_links=False, with_signout=True):
- index_url = reverse('index')
- absolute = lambda (url): request.build_absolute_uri(url)
- l = [{ 'url': absolute(index_url), 'name': "Sign in"}]
- user = request.user
- if user.is_authenticated():
- l = []
- l.append({ 'url': absolute(reverse('astakos.im.views.index')),
- 'name': user.email})
- l.append({ 'url': absolute(reverse('astakos.im.views.edit_profile')),
- 'name': "My account" })
- if with_extra_links:
- if user.has_usable_password() and user.provider == 'local':
- l.append({ 'url': absolute(reverse('password_change')),
- 'name': "Change password" })
- if EMAILCHANGE_ENABLED:
- l.append({'url':absolute(reverse('email_change')),
- 'name': "Change email"})
- if INVITATIONS_ENABLED:
- l.append({ 'url': absolute(reverse('astakos.im.views.invite')),
- 'name': "Invitations" })
- l.append({ 'url': absolute(reverse('astakos.im.views.feedback')),
- 'name': "Feedback" })
- if with_signout:
- l.append({ 'url': absolute(reverse('astakos.im.views.logout')),
- 'name': "Sign out"})
-
- callback = request.GET.get('callback', None)
- data = json.dumps(tuple(l))
- mimetype = 'application/json'
-
- if callback:
- mimetype = 'application/javascript'
- data = '%s(%s)' % (callback, data)
-
- return HttpResponse(content=data, mimetype=mimetype)
@api_method(http_method='GET', token_required=True, perms=['im.can_access_userinfo'])
def get_user_by_email(request, user=None):
email = request.GET.get('name')
return _get_user_by_email(email)
+
@api_method(http_method='GET', token_required=True, perms=['im.can_access_userinfo'])
def get_user_by_username(request, user_id, user=None):
# Normal Response Codes: 200
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.
-import logging
-import json
+from astakos.im.api.backends.lib.django import DjangoBackend
-from astakos.im.functions import set_user_credibility
-logger = logging.getLogger(__name__)
-
-def on_creditevent(msg):
- try:
- email = msg.get('userid')
- has_credits = True if msg.get('status') == 'on' else False
- set_user_credibility(email, has_credits)
- except Exception, e:
- logger.exception(e)
\ No newline at end of file
+def get_backend():
+ return DjangoBackend()
--- /dev/null
+import astakos.im.api.backends.errors
+
+(SUCCESS, FAILURE) = range(2)
+
+class BaseBackend(object):
+ #TODO filled
+ pass
+
+class SuccessResult():
+ def __init__(self, data):
+ self.data = data
+
+ @property
+ def is_success(self):
+ return True
+
+class FailureResult():
+ def __init__(self, reason):
+ self.reason = reason
+
+ @property
+ def is_success(self):
+ return False
--- /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.
+
+class ItemNotExists(ValueError):
+ def __init__(self, type, **kwargs):
+ fields = " and ".join('%s=%s' % (k,v) for k,v in kwargs.iteritems())
+ msg = "%(type)s with %(fields)s does not exist."
+ super(ItemNotExists, self).__init__(msg % locals())
+
+class ItemExists(ValueError):
+ def __init__(self, type, **kwargs):
+ fields = " and ".join('%s=%s' % (k,v) for k,v in kwargs.iteritems())
+ msg = "%(type)s with %(fields)s already exists."
+ super(ItemExists, self).__init__(msg % locals())
+
+class MultipleItemsExist(ValueError):
+ def __init__(self, type, **kwargs):
+ fields = " and ".join('%s=%s' % (k,v) for k,v in kwargs.iteritems())
+ msg = "There are mulptiple %(type)s with %(fields)s."
+ super(MultipleItemsExist, self).__init__(msg % locals())
+
+class MissingIdentifier(IOError):
+ pass
\ No newline at end of file
--- /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.qh 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(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:
+ email = r.get('email')
+ realname = r.get('realname')
+ try:
+ user.invite(email, 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', ())
+
+ 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,))
--- /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.
+
+import logging
+import socket
+
+from smtplib import SMTPException
+
+from django.conf import settings
+from django.core.mail import send_mail
+from django.template.loader import render_to_string
+
+import astakos.im.messages as astakos_messages
+
+from astakos.im.settings import LOGGING_LEVEL
+
+logger = logging.getLogger(__name__)
+
+def _send_admin_notification(template_name,
+ dictionary=None,
+ subject='alpha2 testing notification',):
+ """
+ Send notification email to settings.ADMINS.
+
+ Raises SendNotificationError
+ """
+ if not settings.ADMINS:
+ return
+ dictionary = dictionary or {}
+ message = render_to_string(template_name, dictionary)
+ sender = settings.SERVER_EMAIL
+ try:
+ send_mail(subject,
+ message, sender, [i[1] for i in settings.ADMINS])
+ except (SMTPException, socket.error) as e:
+ logger.exception(e)
+ raise SendNotificationError()
+ else:
+ msg = 'Sent admin notification for user %s' % dictionary
+ logger.log(LOGGING_LEVEL, msg)
+
+class EmailNotification(Notification):
+ def send(self):
+ send_mail(
+ subject,
+ message,
+ sender,
+ recipients
+ )
+
+class Notification(object):
+ def __init__(self, sender, recipients, subject, message):
+ self.sender = sender
+ self.recipients = recipients
+ self.subject = subject
+ self.message = message
+
+ def send(self):
+ pass
+
+class SendMailError(Exception):
+ pass
+
+class SendNotificationError(SendMailError):
+ def __init__(self):
+ self.message = _(astakos_messages.NOTIFICATION_SEND_ERR)
+ super(SendNotificationError, self).__init__()
--- /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 astakos.im.api.spec import AstakosAPI
+from astakos.im.api.backends import get_backend
+
+from commissioning import CorruptedError
+
+from django.db import transaction
+
+class AstakosCallpoint():
+
+ api_spec = AstakosAPI()
+
+# http_exc_lookup = {
+# CorruptedError: 550,
+# InvalidDataError: 400,
+# InvalidKeyError: 401,
+# NoEntityError: 404,
+# NoQuantityError: 413,
+# NoCapacityError: 413,
+# }
+
+ def init_connection(self, connection):
+ if connection is not None:
+ raise ValueError("Cannot specify connection args with %s" %
+ type(self).__name__)
+ pass
+
+ def commit(self):
+ transaction.commit()
+
+ def rollback(self):
+ transaction.rollback()
+
+ def do_make_call(self, call_name, data):
+ call_fn = getattr(self, call_name, None)
+ if not call_fn:
+ m = "cannot find call '%s'" % (call_name,)
+ raise CorruptedError(m)
+
+ return call_fn(**data)
+
+ def create_users(self, users=()):
+ b = get_backend()
+ rejected = (b.create_user(**u) for u in users)
+ return rejected
+
+ def update_users(self, users=()):
+ b = get_backend()
+ rejected = (b.update_user(**u) for u in users)
+ return rejected
+
+ def add_user_policies(self, user_id, update=False, policies=()):
+ b = get_backend()
+ rejected = b.add_policies(user_id, update, policies)
+ return rejected
+
+ def remove_user_policies(self, user_id, policies=()):
+ b = get_backend()
+ rejected = b.remove_policies(user_id, policies)
+ return rejected
+
+ def add_user_permissions(self, user_id, permissions=()):
+ b = get_backend()
+ rejected = b.add_permissions(user_id, permissions)
+ return rejected
+
+ def remove_user_permissions(self, user_id, permissions=()):
+ b = get_backend()
+ rejected = b.remove_permissions(user_id, permissions)
+ return rejected
+
+ def invite_users(self, sender_id, recipients=()):
+ b = get_backend()
+ rejected = b.invite_users(sender_id, recipients)
+ return rejected
+
+ def list_users(self, filter=()):
+ b = get_backend()
+ return b.list_users(filter)
+
+ def get_user_status(self, user_id):
+ b = get_backend()
+ return b.get_resource_usage(user_id)
+
+ def list_resources(self, filter=()):
+ b = get_backend()
+ return b.list_resources(filter)
+
+ def add_services(self, services=()):
+ b = get_backend()
+ rejected = (b.create_service(**s) for s in services)
+ return rejected
+
+ def update_services(self, services=()):
+ b = get_backend()
+ rejected = (b.update_service(**s) for s in services)
+ return rejected
+
+ def remove_services(self, ids=()):
+ b = get_backend()
+ rejected = b.remove_services(ids)
+ return rejected
+
+ def add_resources(self, service_id, update=False, resources=()):
+ b = get_backend()
+ rejected = b.add_resources(service_id, update, resources)
+ return rejected
+
+ def remove_resources(self, service_id, ids=()):
+ b = get_backend()
+ rejected = b.remove_resources(service_id, ids)
+ return rejected
+
+ def create_groups(self, groups=()):
+ b = get_backend()
+ rejected = (b.create_group(**g) for g in groups)
+ return rejected
+
+API_Callpoint = AstakosCallpoint
--- /dev/null
+#!/usr/bin/env python
+from commissioning.clients.http import main, HTTP_API_Client
+from astakos.im.api.spec import AstakosAPI
+
+
+class AstakosHTTP(HTTP_API_Client):
+ api_spec = AstakosAPI()
+
+
+if __name__ == '__main__':
+ main(callpoint=AstakosHTTP())
# 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
# 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.
+
def camelCase(s):
return s[0].lower() + s[1:]
+
class Fault(Exception):
def __init__(self, message='', details='', name=''):
Exception.__init__(self, message, details, name)
self.details = details
self.name = name or camelCase(self.__class__.__name__)
+
class BadRequest(Fault):
code = 400
+
class Unauthorized(Fault):
code = 401
+
class InternalServerError(Fault):
code = 500
+
class Forbidden(Fault):
code = 403
+
class ItemNotFound(Fault):
- code = 404
\ No newline at end of file
+ code = 404
# or implied, of GRNET S.A.
import logging
-import urllib
from functools import wraps
-from traceback import format_exc
from time import time, mktime
-from urllib import quote
-from urlparse import urlparse
-from collections import defaultdict
-from django.conf import settings
from django.http import HttpResponse
-from django.core.urlresolvers import reverse
from django.views.decorators.csrf import csrf_exempt
-from astakos.im.api.faults import *
+from astakos.im.api.faults import Fault, Unauthorized, InternalServerError, BadRequest
+from astakos.im.api import render_fault, _get_user_by_email, _get_user_by_username
from astakos.im.models import AstakosUser, Service
-from astakos.im.settings import INVITATIONS_ENABLED, COOKIE_NAME, EMAILCHANGE_ENABLED
-from astakos.im.util import epoch
from astakos.im.forms import FeedbackForm
-from astakos.im.functions import send_feedback as send_feedback_func, SendMailError
+from astakos.im.functions import send_feedback as send_feedback_func
logger = logging.getLogger(__name__)
-def render_fault(request, fault):
- if isinstance(fault, InternalServerError) and settings.DEBUG:
- fault.details = format_exc(fault)
-
- request.serialization = 'text'
- data = fault.message + '\n'
- if fault.details:
- data += '\n' + fault.details
- response = HttpResponse(data, status=fault.code)
- response['Content-Length'] = len(response.content)
- return response
def api_method(http_method=None, token_required=False):
"""Decorator function for views that implement an API method."""
raise Unauthorized('Access denied')
try:
service = Service.objects.get(auth_token=x_auth_token)
-
+
# Check if the token has expired.
if (time() - mktime(service.auth_token_expires.timetuple())) > 0:
raise Unauthorized('Authentication expired')
return wrapper
return decorator
+
@api_method(http_method='GET', token_required=True)
def get_user_by_email(request, user=None):
# Normal Response Codes: 200
email = request.GET.get('name')
return _get_user_by_email(email)
+
@api_method(http_method='GET', token_required=True)
def get_user_by_username(request, user_id, user=None):
# Normal Response Codes: 200
# itemNotFound (404)
return _get_user_by_username(user_id)
+
@csrf_exempt
@api_method(http_method='POST', token_required=True)
def send_feedback(request, email_template_name='im/feedback_mail.txt'):
auth_token = request.POST.get('auth', '')
if not auth_token:
raise BadRequest('Missing user authentication')
-
- user = None
+
+ user = None
try:
user = AstakosUser.objects.get(auth_token=auth_token)
except:
pass
-
+
if not user:
raise BadRequest('Invalid user authentication')
-
+
form = FeedbackForm(request.POST)
if not form.is_valid():
raise BadRequest('Invalid data')
-
+
msg = form.cleaned_data['feedback_msg']
data = form.cleaned_data['feedback_data']
send_feedback_func(msg, data, user, email_template_name)
response = HttpResponse(status=200)
response['Content-Length'] = len(response.content)
- return response
\ No newline at end of file
+ return response
--- /dev/null
+from commissioning.specificator import (
+ Specificator, Integer, Text, ListOf
+)
+
+
+class Name(Text):
+ def init(self):
+ self.opts.update({'regex': "[\w.:]+", 'maxlen': 512})
+Name = Name()
+
+
+class Email(Text):
+ def init(self):
+ pattern = "[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?"
+ self.opts.update({'regex': pattern, 'maxlen': 512})
+Email = Email()
+
+
+class Url(Text):
+ def init(self):
+ pattern = "(((f|ht){1}tp://)[-a-zA-Z0-9@:%_\+.~#?&//=]+)"
+ self.opts.update({'regex': pattern, 'maxlen': 512})
+Url = Url()
+
+
+class Filepath(Text):
+ def init(self):
+ self.opts.update({'regex': "", 'maxlen': 512})
+Filepath = Filepath()
+
+
+class Nonnegative(Integer):
+ def init(self):
+ self.opts.update({'minimum': 0})
+Nonnegative = Nonnegative()
+
+
+class Boolean(Integer):
+ def init(self):
+ self.opts.update({'minimum': 0, 'maximum': 1})
+Boolean = Boolean()
+
+
+# class GroupKind(Integer):
+# def init(self):
+# self.opts.update({'minimum': 1, 'maximum': 5})
+# GroupKind = GroupKind()
+
+Timepoint = Text(classname='Timepoint', maxlen=24)
+
+
+class AstakosAPI(Specificator):
+ def create_users(
+ self,
+ users=ListOf(
+ email=Email,
+ first_name=Name,
+ last_name=Name,
+ is_active=Boolean,
+ is_superuser=Boolean,
+ affiliation=Name,
+ password=Name,
+ provider=Name,
+ level=Nonnegative,
+ invitations=Nonnegative,
+ is_verified=Boolean,
+ third_party_identifier=Name,
+ email_verified=Boolean),
+ policies=ListOf(resource=Name, supimit=Nonnegative),
+ groups=ListOf(Name),
+ permissions=ListOf(Name)
+ ):
+ rejected = ListOf(user=Email, reason=Text())
+ return rejected
+
+ def update_users(
+ self,
+ users=ListOf(
+ pk=Nonnegative,
+ renew_token=Boolean,
+ data=ListOf(
+ first_name=Name,
+ last_name=Name,
+ is_active=Boolean,
+ is_superuser=Boolean,
+ affiliation=Name,
+ password=Name,
+ provider=Name,
+ level=Nonnegative,
+ invitations=Nonnegative,
+ is_verified=Boolean,
+ third_party_identifier=Name,
+ email_verified=Boolean
+ )
+ )
+ ):
+ rejected = ListOf(user_id=Nonnegative, reason=Text())
+ return rejected
+
+ def add_user_policies(
+ self,
+ pk=Nonnegative,
+ update=Boolean,
+ policies=ListOf(service=Name, resource=Name, upimit=Nonnegative)
+ ):
+ rejected = ListOf(resource=Name, reason=Text())
+ return rejected
+
+ def remove_user_policies(
+ self,
+ pk=Nonnegative,
+ policies=ListOf(service=Name, resource=Name)
+ ):
+ rejected = ListOf(service=Name, resource=Name)
+ return rejected
+
+ def add_user_permissions(
+ self,
+ pk=Nonnegative,
+ permissions=ListOf(permission=Name)
+ ):
+ rejected = ListOf(permission=Name)
+ return rejected
+
+ def remove_user_permissions(
+ self,
+ pk=Nonnegative,
+ permissions=ListOf(permission=Name)
+ ):
+ rejected = ListOf(permission=Name)
+ return rejected
+
+ def invite_users(
+ self,
+ sender=Email,
+ data=ListOf(email=Email, realname=Name)
+ ):
+ rejected = ListOf(receiver=Email)
+ return rejected
+
+ def list_users(
+ self,
+ filter=ListOf(id=Nonnegative)
+ ):
+ return ListOf(
+ activation_sent=Timepoint,
+ affiliation=Name,
+ auth_token=Name,
+ auth_token_created=Timepoint,
+ auth_token_expires=Timepoint,
+ date_joined=Timepoint,
+ date_signed_terms=Timepoint,
+ email=Email,
+ email_verified=Boolean,
+ first_name=Name,
+ has_credits=Boolean,
+ has_signed_terms=Boolean,
+ id=Nonnegative,
+ invitations=Nonnegative,
+ invitations_sent=ListOf(
+ code=Name,
+ consumed=Boolean,
+ created=Timepoint,
+ id=Nonnegative,
+ realname=Name,
+ username=Email
+ ),
+ is_active=Boolean,
+ is_staff=Boolean,
+ is_superuser=Boolean,
+ is_verified=Boolean,
+ last_login=Timepoint,
+ last_name=Name,
+ level=Nonnegative,
+ password=Name,
+ provider=Name,
+ third_party_identifier=Name,
+ updated=Timepoint,
+ user_permissions=ListOf(
+ codename=Name,
+ id=Nonnegative,
+ name=Name
+ ),
+ username=Name,
+ astakos_groups=ListOf(
+ approval_date=Timepoint,
+ creation_date=Timepoint,
+ desc=Text(),
+ max_participants=Nonnegative,
+ expiration_date=Timepoint,
+ group_ptr=Url,
+ homepage=Url,
+ id=Nonnegative,
+ issue_date=Timepoint,
+ kind=Name,
+ moderation_enabled=Boolean,
+ name=Name,
+ #permissions=ListOf(),
+ policy=ListOf(id=Nonnegative, name=Name)
+ )
+ )
+
+ def get_user_status(
+ self,
+ user_id=Nonnegative
+ ):
+ return ListOf(
+ name=Name,
+ description=Text(),
+ unit=Name,
+ maxValue=Integer(),
+ currValue=Integer()
+ )
+
+ def list_resources(self, filter=ListOf(id=Nonnegative)):
+ return ListOf(
+ desc=Text(),
+ group=Name,
+ id=Nonnegative,
+ meta=ListOf(key=Name, value=Name),
+ name=Name,
+ service=Name,
+ unit=Name
+ )
+
+ def add_services(
+ self,
+ services=ListOf(
+ name=Name,
+ url=Url,
+ icon=Filepath,
+ resources=ListOf(
+ name=Name,
+ desc=Text(),
+ unit=Name,
+ group=Name
+ )
+ )
+ ):
+ rejected = ListOf(service=Name)
+ return rejected
+
+ def update_services(
+ self,
+ services=ListOf(id=Nonnegative, url=Url, icon=Filepath)
+ ):
+ rejected = ListOf(service=Name)
+ return rejected
+
+ def remove_services(self, ids=ListOf(Nonnegative)):
+ rejected = ListOf(service=Name)
+ return rejected
+
+ def add_resources(
+ self,
+ service_id=Nonnegative,
+ update=Boolean,
+ resources=ListOf(
+ name=Name,
+ resources=ListOf(
+ name=Name,
+ desc=Text(),
+ unit=Name,
+ group=Name)
+ )
+ ):
+ rejected = ListOf(service=Name)
+ return rejected
+
+ def remove_resources(
+ self,
+ service_id=Nonnegative,
+ ids=ListOf(Nonnegative)
+ ):
+ rejected = ListOf(Name)
+ return rejected
+
+ def create_groups(
+ self,
+ groups=ListOf(
+ name=Name,
+ kind=Name,
+ homepage=Url,
+ desc=Text(),
+ policies=ListOf(resource=Name, upimit=Nonnegative),
+ issue_date=Timepoint,
+ expiration_date=Timepoint,
+ moderation_enabled=Boolean,
+ participants=Nonnegative,
+ permissions=ListOf(permission=Name),
+ members=ListOf(user=Email, is_approved=Boolean),
+ owners=ListOf(user=Email)
+ )
+ ):
+ rejected = ListOf(group=Name)
+ return rejected
+
+ def enable_groups(self, data=ListOf(group=Name)):
+ rejected = ListOf(group=Name)
+ return rejected
+
+ def search_groups(self, key=Name):
+ return ListOf(
+ group=Name,
+ kind=Nonnegative,
+ homepage=Url,
+ desc=Text(),
+ creation_date=Timepoint,
+ issue_date=Timepoint,
+ expiration_date=Timepoint,
+ moderation_enabled=Boolean,
+ participants=Nonnegative,
+ owner=ListOf(user=Email),
+ policies=ListOf(resource=Name, upimit=Nonnegative),
+ members=ListOf(user=Email, is_approved=Boolean)
+ )
+
+ def list_groups(self):
+ return ListOf(
+ group=Name,
+ kind=Nonnegative,
+ homepage=Url,
+ desc=Text(),
+ creation_date=Timepoint,
+ issue_date=Timepoint,
+ expiration_date=Timepoint,
+ moderation_enabled=Boolean,
+ participants=Nonnegative,
+ owners=ListOf(user=Email),
+ policies=ListOf(resource=Name, upimit=Nonnegative),
+ members=ListOf(user=Email, is_approved=Boolean)
+ )
+
+ def add_owners(
+ self,
+ data=ListOf(group=Name, owners=ListOf(user=Email))
+ ):
+ rejected = ListOf(user=Email)
+ return rejected
+
+ def remove_owners(
+ self,
+ data=ListOf(group=Name, owners=ListOf(user=Email))
+ ):
+ rejected = ListOf(user=Email)
+ return rejected
+
+ def add_members(
+ self,
+ data=ListOf(group=Name, members=ListOf(user=Email))
+ ):
+ rejected = ListOf(user=Email)
+ return rejected
+
+ def remove_members(
+ self,
+ data=ListOf(group=Name, members=ListOf(user=Email))
+ ):
+ rejected = ListOf(user=Email)
+ return rejected
+
+ def add_policies(
+ self,
+ data=ListOf(group=Name, resource=Name, upimit=Nonnegative)
+ ):
+ rejected = ListOf(group=Name, resource=Name)
+ return rejected
+
+ def remove_group_policies(
+ self,
+ data=ListOf(group=Name, resource=Name, upimit=Nonnegative)
+ ):
+ rejected = ListOf(group=Name, resource=Name)
+ return rejected
+
+ def update_group_policies(
+ self, data=ListOf(group=Name, resource=Name, upimit=Nonnegative)
+ ):
+ rejected = ListOf(group=Name, resource=Name)
+ return rejected
+
+ def approve_members(
+ self,
+ data=ListOf(group=Name, members=ListOf(user=Email))
+ ):
+ rejected = ListOf(user=Email)
+ return rejected
+
+ def disapprove_members(
+ self,
+ data=ListOf(group=Name, members=ListOf(user=Email))
+ ):
+ rejected = ListOf(user=Email)
+ return rejected
+
+ def add_group_permissions(
+ self,
+ data=ListOf(group=Name, permission=Name)
+ ):
+ rejected = ListOf(group=Name, permission=Name)
+ return rejected
+
+ def delete_group_permissions(
+ self,
+ data=ListOf(group=Name, permission=Name)
+ ):
+ rejected = ListOf(group=Name, permission=Name)
+ return rejected
+
+ def list_resource_units(self):
+ return ListOf(Name)
+
+ def get_approval_terms(self, term=Nonnegative):
+ return Text()
+
+ def add_approval_terms(self, location=Filepath):
+ return Nonnegative
+
+# def change_emails():
+# pass
logger = logging.getLogger(__name__)
+
class TokenBackend(ModelBackend):
"""
AuthenticationBackend used to authenticate using token instead
except AstakosUser.DoesNotExist:
return None
+
class EmailBackend(ModelBackend):
"""
If the ``username`` parameter is actually an email uses email to authenticate
the user else tries the username.
-
+
Used from ``astakos.im.forms.LoginForm`` to authenticate.
"""
def authenticate(self, username=None, password=None):
else:
msg = 'Invalid password during authentication for %s' % username
logger._log(LOGGING_LEVEL, msg, [])
-
-
+
+
def get_user(self, user_id):
try:
return AstakosUser.objects.get(pk=user_id)
except AstakosUser.DoesNotExist:
- return None
\ No newline at end of file
+ return None
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.
-from astakos.im.settings import IM_MODULES, INVITATIONS_ENABLED, IM_STATIC_URL, \
- COOKIE_NAME, LOGIN_MESSAGES, SIGNUP_MESSAGES, PROFILE_MESSAGES, \
- GLOBAL_MESSAGES, PROFILE_EXTRA_LINKS
-from astakos.im.api.admin import get_menu
+from astakos.im.settings import (
+ IM_MODULES, INVITATIONS_ENABLED, IM_STATIC_URL,
+ LOGIN_MESSAGES, SIGNUP_MESSAGES, PROFILE_MESSAGES,
+ GLOBAL_MESSAGES, PROFILE_EXTRA_LINKS
+)
+from astakos.im.api import get_menu
from astakos.im.util import get_query
+from astakos.im.models import GroupKind
-from django.conf import settings
-from django.core.urlresolvers import reverse
from django.utils import simplejson as json
+
def im_modules(request):
return {'im_modules': IM_MODULES}
+
def next(request):
- return {'next' : get_query(request).get('next', '')}
+ return {'next': get_query(request).get('next', '')}
+
def code(request):
- return {'code' : request.GET.get('code', '')}
+ return {'code': request.GET.get('code', '')}
+
def invitations(request):
- return {'invitations_enabled' :INVITATIONS_ENABLED}
+ return {'invitations_enabled': INVITATIONS_ENABLED}
+
def media(request):
- return {'IM_STATIC_URL' : IM_STATIC_URL}
+ return {'IM_STATIC_URL': IM_STATIC_URL}
+
def custom_messages(request):
global GLOBAL_MESSAGES, SIGNUP_MESSAGES, LOGIN_MESSAGES, PROFILE_MESSAGES
if type(PROFILE_MESSAGES) == dict:
PROFILE_MESSAGES = PROFILE_MESSAGES.items()
- EXTRA_MESSAGES_SET = bool(GLOBAL_MESSAGES or SIGNUP_MESSAGES or \
- LOGIN_MESSAGES or PROFILE_MESSAGES)
+ EXTRA_MESSAGES_SET = bool(GLOBAL_MESSAGES or SIGNUP_MESSAGES or
+ LOGIN_MESSAGES or PROFILE_MESSAGES)
return {
- 'GLOBAL_MESSAGES' : GLOBAL_MESSAGES,
- 'SIGNUP_MESSAGES' : SIGNUP_MESSAGES,
- 'LOGIN_MESSAGES' : LOGIN_MESSAGES,
- 'PROFILE_MESSAGES' : PROFILE_MESSAGES,
- 'PROFILE_EXTRA_LINKS' : PROFILE_EXTRA_LINKS,
- 'EXTRA_MESSAGES_SET' : EXTRA_MESSAGES_SET
- }
+ 'GLOBAL_MESSAGES': GLOBAL_MESSAGES,
+ 'SIGNUP_MESSAGES': SIGNUP_MESSAGES,
+ 'LOGIN_MESSAGES': LOGIN_MESSAGES,
+ 'PROFILE_MESSAGES': PROFILE_MESSAGES,
+ 'PROFILE_EXTRA_LINKS': PROFILE_EXTRA_LINKS,
+ 'EXTRA_MESSAGES_SET': EXTRA_MESSAGES_SET
+ }
+
def menu(request):
- absolute = lambda (url): request.build_absolute_uri(url)
- resp = get_menu(request, True, False)
try:
+ resp = get_menu(request, True, False)
menu_items = json.loads(resp.content)[1:]
except Exception, e:
return {}
else:
- for item in menu_items:
- item['is_active'] = absolute(request.path) == item['url']
- return {'menu':menu_items}
+ return {'menu': menu_items}
+
+
+def group_kinds(request):
+ return {'group_kinds': GroupKind.objects.exclude(
+ name='default').values_list('name', flat=True)}
COOKIE_NAME, COOKIE_DOMAIN, COOKIE_SECURE, LOGGING_LEVEL
)
+import astakos.im.messages as astakos_messages
+
logger = logging.getLogger(__name__)
class Cookie():
@property
def is_set(self):
- no_token = not self.auth_token
+ no_token = not self.auth_token
return not no_token
@property
def __set(self):
if not self.response:
- raise ValueError(_('There is no response.'))
+ raise ValueError(_(astakos_messages.NO_RESPONSE))
user = self.user
expire_fmt = user.auth_token_expires.strftime('%a, %d-%b-%Y %H:%M:%S %Z')
cookie_value = quote(user.email + '|' + user.auth_token)
def __delete(self):
if not self.response:
- raise ValueError(_('There is no response.'))
+ raise ValueError(_(astakos_messages.NO_RESPONSE))
self.response.delete_cookie(COOKIE_NAME, path='/', domain=COOKIE_DOMAIN)
msg = 'Cookie deleted for %(email)s' % self.__dict__
logger._log(LOGGING_LEVEL, msg, [])
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.
-import json
+import requests
-from time import time
-from hashlib import sha1
-from random import random
+from django.utils import simplejson as json
-class UserEvent(object):
- def __init__(self, client, user, eventType, details={}):
- self.eventVersion = '1'
- self.occurredMillis = int(time() * 1000)
- self.receivedMillis = self.occurredMillis
- self.clientID = client
- self.userID = user.email
- self.isActive = user.is_active
- self.role = 'default'
- self.eventType = eventType
- self.details = details
- hash = sha1()
- hash.update(json.dumps([client, self.userID, self.isActive, self.role,
- self.eventType, self.details, self.occurredMillis]))
- self.id = hash.hexdigest()
-
- def format(self):
- return self.__dict__
\ No newline at end of file
+from astakos.im.settings import AQUARIUM_URL
+
+
+class AquariumClient():
+ def get_billing(self, user, start, end):
+ if not AQUARIUM_URL:
+ return
+
+ url = AQUARIUM_URL.rstrip('/')
+ url = '%s/%s/bill/%d/%d' % (url, user, start, end)
+ r = requests.get(url)
+ try:
+ return r.status_code, json.loads(r.text)
+ except ValueError:
+ pass
+ return r.status_code, None
--- /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.
+
+import logging
+
+logging.basicConfig(format='%(asctime)s [%(levelname)s] %(name)s %(message)s',
+ datefmt='%Y-%m-%d %H:%M:%S'
+ )
+logger = logging.getLogger('endpoint.aquarium')
+
+from astakos.im.models import AstakosUser
+
+
+def on_creditevent(msg):
+ """
+ Queue handler for updating AstakosUser has_credits
+ """
+ try:
+ email = msg.get('userid')
+ has_credits = msg.get('status') == 'on' or False
+ user = AstakosUser.objects.get(email=email, is_active=True)
+ user.has_credits = has_credits
+ user.save()
+ except BaseException, e:
+ logger.exception(e)
--- /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.
+
+import logging
+
+from functools import wraps
+from urlparse import urlparse
+
+from astakos.im.settings import QUEUE_CONNECTION
+
+if QUEUE_CONNECTION:
+ from synnefo.lib.queue import (exchange_connect, exchange_send,
+ exchange_close, UserEvent, Receipt
+ )
+
+QUEUE_CLIENT_ID = '3' # Astakos.
+INSTANCE_ID = '1'
+RESOURCE = 'addcredits'
+DEFAULT_CREDITS = 1000
+
+logging.basicConfig(format='%(asctime)s [%(levelname)s] %(name)s %(message)s',
+ datefmt='%Y-%m-%d %H:%M:%S'
+ )
+logger = logging.getLogger('endpoint.aquarium')
+
+
+def call(func):
+ @wraps(func)
+ def wrapper(*args, **kwargs):
+ if not QUEUE_CONNECTION:
+ return
+
+ try:
+ body, key = func(*args, **kwargs)
+ conn = exchange_connect(QUEUE_CONNECTION)
+ parts = urlparse(QUEUE_CONNECTION)
+ exchange = parts.path[1:]
+ routing_key = key % exchange
+ exchange_send(conn, routing_key, body)
+ exchange_close(conn)
+ except BaseException, e:
+ logger.exception(e)
+ return wrapper
+
+
+@call
+def report_user_event(user, create=False):
+ eventType = 'create' if not create else 'modify'
+ body = UserEvent(QUEUE_CLIENT_ID, user.email, user.is_active, eventType, {}
+ ).format()
+ routing_key = '%s.user'
+ return body, routing_key
+
+
+@call
+def report_user_credits_event(user):
+ body = Receipt(QUEUE_CLIENT_ID, user.email, INSTANCE_ID, RESOURCE,
+ DEFAULT_CREDITS, details={}
+ ).format()
+ routing_key = '%s.resource'
+ return body, routing_key
+
+
+def report_credits_event():
+ # better approach?
+ from astakos.im.models import AstakosUser
+ map(report_user_credits_event, AstakosUser.objects.all())
--- /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.
+
+import logging
+import itertools
+
+from functools import wraps
+
+from django.utils.translation import ugettext as _
+
+from astakos.im.settings import QUOTA_HOLDER_URL, LOGGING_LEVEL
+
+if QUOTA_HOLDER_URL:
+ from kamaki.clients.quotaholder import QuotaholderClient
+
+ENTITY_KEY = '1'
+
+inf = float('inf')
+
+logger = logging.getLogger(__name__)
+
+inf = float('inf')
+
+def call(func_name):
+ """Decorator function for Quotaholder client calls."""
+ def decorator(payload_func):
+ @wraps(payload_func)
+ def wrapper(entities=(), client=None, **kwargs):
+ if not entities:
+ return client, ()
+
+ if not QUOTA_HOLDER_URL:
+ return client, ()
+
+ c = client or QuotaholderClient(QUOTA_HOLDER_URL)
+ func = c.__dict__.get(func_name)
+ if not func:
+ return c, ()
+
+ data = payload_func(entities, client, **kwargs)
+ if not data:
+ return c, data
+
+ funcname = func.__name__
+ kwargs = {'context': {}, funcname: data}
+ rejected = func(**kwargs)
+ msg = _('%s: %s - Rejected: %s' % (funcname, data, rejected,))
+ logger.log(LOGGING_LEVEL, msg)
+ return c, rejected
+ return wrapper
+ return decorator
+
+
+@call('set_quota')
+def send_quota(users, client=None):
+ data = []
+ append = data.append
+ for user in users:
+ for resource, uplimit in user.quota.iteritems():
+ key = ENTITY_KEY
+ quantity = None
+ capacity = uplimit if uplimit != inf else None
+ import_limit = None
+ export_limit = None
+ flags = 0
+ args = (
+ user.email, resource, key, quantity, capacity, import_limit,
+ export_limit, flags)
+ append(args)
+ return data
+
+
+@call('set_quota')
+def send_resource_quantities(resources, client=None):
+ data = []
+ append = data.append
+ for resource in resources:
+ key = ENTITY_KEY
+ quantity = resource.meta.filter(key='quantity') or None
+ capacity = None
+ import_limit = None
+ export_limit = None
+ flags = 0
+ args = (resource.service, resource, key, quantity, capacity,
+ import_limit, export_limit, flags)
+ append(args)
+ return data
+
+
+@call('get_quota')
+def get_quota(users, client=None):
+ data = []
+ append = data.append
+ for user in users:
+ try:
+ entity = user.email
+ except AttributeError:
+ continue
+ else:
+ for r in user.quota.keys():
+ args = entity, r, ENTITY_KEY
+ append(args)
+ return data
+
+
+@call('create_entity')
+def create_entities(entities, client=None, field=''):
+ data = []
+ append = data.append
+ for entity in entities:
+ try:
+ entity = entity.__getattribute__(field)
+ except AttributeError:
+ continue
+ owner = 'system'
+ key = ENTITY_KEY
+ ownerkey = ''
+ args = entity, owner, key, ownerkey
+ append(args)
+ return data
+
+
+def register_users(users, client=None):
+ users, copy = itertools.tee(users)
+ client, rejected = create_entities(entities=users,
+ client=client,
+ field='email')
+ created = (e for e in copy if unicode(e) not in rejected)
+ return send_quota(created, client)
+
+
+def register_resources(resources, client=None):
+ resources, copy = itertools.tee(resources)
+ client, rejected = create_entities(entities=resources,
+ client=client,
+ field='service')
+ created = (e for e in copy if unicode(e) not in rejected)
+ return send_resource_quantities(created, client)
+
+
+from datetime import datetime
+
+strptime = datetime.strptime
+timefmt = '%Y-%m-%dT%H:%M:%S.%f'
+
+SECOND_RESOLUTION = 1
+
+
+def total_seconds(timedelta_object):
+ return timedelta_object.seconds + timedelta_object.days * 86400
+
+
+def iter_timeline(timeline, before):
+ if not timeline:
+ return
+
+ for t in timeline:
+ yield t
+
+ t = dict(t)
+ t['issue_time'] = before
+ yield t
+
+
+def _usage_units(timeline, after, before, details=0):
+
+ t_total = 0
+ uu_total = 0
+ t_after = strptime(after, timefmt)
+ t_before = strptime(before, timefmt)
+ t0 = t_after
+ u0 = 0
+
+ for point in iter_timeline(timeline, before):
+ issue_time = point['issue_time']
+
+ if issue_time <= after:
+ u0 = point['target_allocated_through']
+ continue
+
+ t = strptime(issue_time, timefmt) if issue_time <= before else t_before
+ t_diff = int(total_seconds(t - t0) * SECOND_RESOLUTION)
+ t_total += t_diff
+ uu_cost = u0 * t_diff
+ uu_total += uu_cost
+ t0 = t
+ u0 = point['target_allocated_through']
+
+ target = point['target']
+ if details:
+ yield (target,
+ point['resource'],
+ point['name'],
+ issue_time,
+ uu_cost,
+ uu_total)
+
+ if not t_total:
+ return
+
+ yield (target,
+ 'total',
+ point['resource'],
+ issue_time,
+ uu_total / t_total,
+ uu_total)
+
+
+def usage_units(timeline, after, before, details=0):
+ return list(_usage_units(timeline, after, before, details=details))
+
+
+def traffic_units(timeline, after, before, details=0):
+ tu_total = 0
+ target = None
+ issue_time = None
+
+ for point in timeline:
+ issue_time = point['issue_time']
+ if issue_time <= after:
+ continue
+ if issue_time > before:
+ break
+
+ target = point['target']
+ tu = point['target_allocated_through']
+ tu_total += tu
+
+ if details:
+ yield (target,
+ point['resource'],
+ point['name'],
+ issue_time,
+ tu,
+ tu_total)
+
+ if not tu_total:
+ return
+
+ yield (target,
+ 'total',
+ point['resource'],
+ issue_time,
+ tu_total // len(timeline),
+ tu_total)
+
+
+def timeline_charge(entity, resource, after, before, details, charge_type):
+ key = '1'
+ if charge_type == 'charge_usage':
+ charge_units = usage_units
+ elif charge_type == 'charge_traffic':
+ charge_units = traffic_units
+ else:
+ m = 'charge type %s not supported' % charge_type
+ raise ValueError(m)
+
+ quotaholder = QuotaholderClient(QUOTA_HOLDER_URL)
+ timeline = quotaholder.get_timeline(
+ context={},
+ after=after,
+ before=before,
+ get_timeline=[[entity, resource, key]])
+ cu = charge_units(timeline, after, before, details=details)
+ return cu
+++ /dev/null
-[
- {
- "model": "auth.group",
- "pk": 1,
- "fields": {
- "name": "default"
- }
- },
- {
- "model": "auth.group",
- "pk": 2,
- "fields": {
- "name": "academic"
- }
- },
- {
- "model": "auth.group",
- "pk": 3,
- "fields": {
- "name": "shibboleth"
- }
- },
- {
- "model": "auth.group",
- "pk": 4,
- "fields": {
- "name": "helpdesk"
- }
- }
-]
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.
from urlparse import urljoin
-from datetime import datetime
from django import forms
from django.utils.translation import ugettext as _
-from django.contrib.auth.forms import UserCreationForm, AuthenticationForm, \
- PasswordResetForm, PasswordChangeForm, SetPasswordForm
+from django.contrib.auth.forms import (UserCreationForm, AuthenticationForm,
+ PasswordResetForm, PasswordChangeForm,
+ SetPasswordForm)
from django.core.mail import send_mail
from django.contrib.auth.tokens import default_token_generator
from django.template import Context, loader
from django.utils.http import int_to_base36
from django.core.urlresolvers import reverse
-from django.utils.functional import lazy
from django.utils.safestring import mark_safe
-from django.contrib import messages
from django.utils.encoding import smart_str
-from django.forms.models import fields_for_model
+from django.conf import settings
from astakos.im.models import (
- AstakosUser, Invitation, get_latest_terms,
- EmailChange, PendingThirdPartyUser
+ AstakosUser, EmailChange, AstakosGroup, Invitation, GroupKind,
+ Resource, PendingThirdPartyUser, get_latest_terms, RESOURCE_SEPARATOR
)
-from astakos.im.settings import (INVITATIONS_PER_LEVEL, DEFAULT_FROM_EMAIL,
- BASEURL, SITENAME, RECAPTCHA_PRIVATE_KEY, DEFAULT_CONTACT_EMAIL,
- RECAPTCHA_ENABLED, LOGGING_LEVEL, PASSWORD_RESET_EMAIL_SUBJECT,
- NEWPASSWD_INVALIDATE_TOKEN
+from astakos.im.settings import (
+ INVITATIONS_PER_LEVEL, BASEURL, SITENAME, RECAPTCHA_PRIVATE_KEY,
+ RECAPTCHA_ENABLED, DEFAULT_CONTACT_EMAIL, LOGGING_LEVEL,
+ PASSWORD_RESET_EMAIL_SUBJECT, NEWPASSWD_INVALIDATE_TOKEN
)
from astakos.im.widgets import DummyWidget, RecaptchaWidget
from astakos.im.functions import send_change_email
-# since Django 1.4 use django.core.urlresolvers.reverse_lazy instead
-from astakos.im.util import reverse_lazy, reserved_email, get_query
+from astakos.im.util import reserved_email, get_query
+
+import astakos.im.messages as astakos_messages
import logging
import hashlib
logger = logging.getLogger(__name__)
+
class LocalUserCreationForm(UserCreationForm):
"""
Extends the built in UserCreationForm in several ways:
* User created is not active.
"""
recaptcha_challenge_field = forms.CharField(widget=DummyWidget)
- recaptcha_response_field = forms.CharField(widget=RecaptchaWidget, label='')
+ recaptcha_response_field = forms.CharField(
+ widget=RecaptchaWidget, label='')
class Meta:
model = AstakosUser
- fields = ("email", "first_name", "last_name", "has_signed_terms", "has_signed_terms")
+ fields = ("email", "first_name", "last_name",
+ "has_signed_terms", "has_signed_terms")
def __init__(self, *args, **kwargs):
"""
if RECAPTCHA_ENABLED:
self.fields.keyOrder.extend(['recaptcha_challenge_field',
- 'recaptcha_response_field',])
+ 'recaptcha_response_field', ])
if get_latest_terms():
self.fields.keyOrder.append('has_signed_terms')
# Overriding field label since we need to apply a link
# to the terms within the label
terms_link_html = '<a href="%s" target="_blank">%s</a>' \
- % (reverse('latest_terms'), _("the terms"))
+ % (reverse('latest_terms'), _("the terms"))
self.fields['has_signed_terms'].label = \
- mark_safe("I agree with %s" % terms_link_html)
+ mark_safe("I agree with %s" % terms_link_html)
def clean_email(self):
email = self.cleaned_data['email']
if not email:
- raise forms.ValidationError(_("This field is required"))
+ 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):
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):
"""
user = super(LocalUserCreationForm, self).save(commit=False)
if commit:
user.save()
- logger._log(LOGGING_LEVEL, 'Created user %s' % user.email, [])
+ logger.log(LOGGING_LEVEL, 'Created user %s' % user.email)
return user
+
class InvitedLocalUserCreationForm(LocalUserCreationForm):
"""
Extends the LocalUserCreationForm: email is readonly.
for f in ro:
self.fields[f].widget.attrs['readonly'] = True
-
def save(self, commit=True):
user = super(InvitedLocalUserCreationForm, self).save(commit=False)
level = user.invitation.inviter.level + 1
user.save()
return user
+
class ThirdPartyUserCreationForm(forms.ModelForm):
id = forms.CharField(
widget=forms.HiddenInput(),
# Overriding field label since we need to apply a link
# to the terms within the label
terms_link_html = '<a href="%s" target="_blank">%s</a>' \
- % (reverse('latest_terms'), _("the terms"))
+ % (reverse('latest_terms'), _("the terms"))
self.fields['has_signed_terms'].label = \
mark_safe("I agree with %s" % terms_link_html)
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):
user.provider = get_query(self.request).get('provider')
if commit:
user.save()
- logger._log(LOGGING_LEVEL, 'Created user %s' % user.email, [])
+ logger.log(LOGGING_LEVEL, 'Created user %s' % user.email)
return user
+
class InvitedThirdPartyUserCreationForm(ThirdPartyUserCreationForm):
"""
Extends the ThirdPartyUserCreationForm: email is readonly.
"""
Changes the order of fields, and removes the username field.
"""
- super(InvitedThirdPartyUserCreationForm, self).__init__(*args, **kwargs)
+ super(
+ InvitedThirdPartyUserCreationForm, self).__init__(*args, **kwargs)
#set readonly form fields
ro = ('email',)
self.fields[f].widget.attrs['readonly'] = True
def save(self, commit=True):
- user = super(InvitedThirdPartyUserCreationForm, self).save(commit=False)
+ user = super(
+ InvitedThirdPartyUserCreationForm, self).save(commit=False)
level = user.invitation.inviter.level + 1
user.level = level
user.invitations = INVITATIONS_PER_LEVEL.get(level, 0)
user.save()
return user
+
class ShibbolethUserCreationForm(ThirdPartyUserCreationForm):
- additional_email = forms.CharField(widget=forms.HiddenInput(), label='', required = False)
+ additional_email = forms.CharField(
+ widget=forms.HiddenInput(), label='', required=False)
def __init__(self, *args, **kwargs):
super(ShibbolethUserCreationForm, self).__init__(*args, **kwargs)
p.delete()
return user
-class InvitedShibbolethUserCreationForm(ShibbolethUserCreationForm, InvitedThirdPartyUserCreationForm):
+
+class InvitedShibbolethUserCreationForm(ShibbolethUserCreationForm,
+ InvitedThirdPartyUserCreationForm):
pass
+
class LoginForm(AuthenticationForm):
username = forms.EmailField(label=_("Email"))
recaptcha_challenge_field = forms.CharField(widget=DummyWidget)
- recaptcha_response_field = forms.CharField(widget=RecaptchaWidget, label='')
+ recaptcha_response_field = forms.CharField(
+ widget=RecaptchaWidget, label='')
def __init__(self, *args, **kwargs):
was_limited = kwargs.get('was_limited', False)
self.fields.keyOrder = ['username', 'password']
if was_limited and RECAPTCHA_ENABLED:
self.fields.keyOrder.extend(['recaptcha_challenge_field',
- 'recaptcha_response_field',])
+ 'recaptcha_response_field', ])
+
+ def clean_username(self):
+ if 'username' in self.cleaned_data:
+ return self.cleaned_data['username'].lower()
def clean_recaptcha_response_field(self):
if 'recaptcha_challenge_field' in self.cleaned_data:
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):
"""
raise
return self.cleaned_data
+
class ProfileForm(forms.ModelForm):
"""
Subclass of ``ModelForm`` for permiting user to edit his/her profile.
- Most of the fields are readonly since the user is not allowed to change them.
+ Most of the fields are readonly since the user is not allowed to change
+ them.
- The class defines a save method which sets ``is_verified`` to True so as the user
- during the next login will not to be redirected to profile page.
+ The class defines a save method which sets ``is_verified`` to True so as the
+ user during the next login will not to be redirected to profile page.
"""
renew = forms.BooleanField(label='Renew token', required=False)
class Meta:
model = AstakosUser
- fields = ('email', 'first_name', 'last_name', 'auth_token', 'auth_token_expires')
+ fields = ('email', 'first_name', 'last_name', 'auth_token',
+ 'auth_token_expires')
def __init__(self, *args, **kwargs):
self.session_key = kwargs.pop('session_key', None)
user.save()
return user
+
class FeedbackForm(forms.Form):
"""
Form for writing feedback.
feedback_data = forms.CharField(widget=forms.HiddenInput(), label='',
required=False)
+
class SendInvitationForm(forms.Form):
"""
Form for sending an invitations
"""
- email = forms.EmailField(required = True, label = 'Email address')
- first_name = forms.EmailField(label = 'First name')
- last_name = forms.EmailField(label = 'Last name')
+ email = forms.EmailField(required=True, label='Email address')
+ first_name = forms.EmailField(label='First name')
+ last_name = forms.EmailField(label='Last name')
+
class ExtendedPasswordResetForm(PasswordResetForm):
"""
try:
user = AstakosUser.objects.get(email=email, is_active=True)
if not user.has_usable_password():
- raise forms.ValidationError(_("This account has not a usable password."))
- except AstakosUser.DoesNotExist, e:
- raise forms.ValidationError(_('That e-mail address doesn\'t have an associated user account. Are you sure you\'ve registered?'))
+ raise forms.ValidationError(_(astakos_messages.UNUSABLE_PASSWORD))
+ except AstakosUser.DoesNotExist:
+ raise forms.ValidationError(_(astakos_messages.EMAIL_UNKNOWN))
return email
- def save(self, domain_override=None, email_template_name='registration/password_reset_email.html',
- use_https=False, token_generator=default_token_generator, request=None):
+ def save(
+ self, domain_override=None, email_template_name='registration/password_reset_email.html',
+ use_https=False, token_generator=default_token_generator, request=None):
"""
Generates a one-use only link for resetting password and sends to the user.
"""
for user in self.users_cache:
url = reverse('django.contrib.auth.views.password_reset_confirm',
- kwargs={'uidb36':int_to_base36(user.id),
- 'token':token_generator.make_token(user)})
+ kwargs={'uidb36': int_to_base36(user.id),
+ 'token': token_generator.make_token(user)
+ }
+ )
url = urljoin(BASEURL, url)
t = loader.get_template(email_template_name)
c = {
'baseurl': BASEURL,
'support': DEFAULT_CONTACT_EMAIL
}
- from_email = DEFAULT_FROM_EMAIL
+ from_email = settings.SERVER_EMAIL
send_mail(_(PASSWORD_RESET_EMAIL_SUBJECT),
- t.render(Context(c)), from_email, [user.email])
+ t.render(Context(c)), from_email, [user.email])
+
class EmailChangeForm(forms.ModelForm):
class Meta:
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):
ec = super(EmailChangeForm, self).save(commit=False)
ec.user = request.user
- activation_key = hashlib.sha1(str(random()) + smart_str(ec.new_email_address))
- ec.activation_key=activation_key.hexdigest()
+ activation_key = hashlib.sha1(
+ str(random()) + smart_str(ec.new_email_address))
+ ec.activation_key = activation_key.hexdigest()
if commit:
ec.save()
send_change_email(ec, request, email_template_name=email_template_name)
+
class SignApprovalTermsForm(forms.ModelForm):
class Meta:
model = AstakosUser
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
+
class InvitationForm(forms.ModelForm):
username = forms.EmailField(label=_("Email"))
def clean_username(self):
username = self.cleaned_data['username']
try:
- Invitation.objects.get(username = username)
- raise forms.ValidationError(_('There is already invitation for this email.'))
+ Invitation.objects.get(username=username)
+ raise forms.ValidationError(_(astakos_messages.INVITATION_EMAIL_EXISTS))
except Invitation.DoesNotExist:
pass
return username
+
class ExtendedPasswordChangeForm(PasswordChangeForm):
"""
Extends PasswordChangeForm by enabling user
pass
return super(ExtendedPasswordChangeForm, self).save(commit=commit)
+
+class AstakosGroupCreationForm(forms.ModelForm):
+ kind = forms.ModelChoiceField(
+ queryset=GroupKind.objects.all(),
+ label="",
+ widget=forms.HiddenInput()
+ )
+ name = forms.URLField(widget=forms.TextInput(attrs={'placeholder': 'eg. foo.ece.ntua.gr'}), help_text="Name should be in the form of dns",)
+ moderation_enabled = forms.BooleanField(
+ help_text="Check if you want to approve members participation manually",
+ required=False,
+ initial=True
+ )
+ max_participants = forms.IntegerField(
+ required=True, min_value=1
+ )
+
+ class Meta:
+ model = AstakosGroup
+
+ def __init__(self, *args, **kwargs):
+ #update QueryDict
+ args = list(args)
+ qd = args.pop(0).copy()
+ members_unlimited = qd.pop('members_unlimited', False)
+ members_uplimit = qd.pop('members_uplimit', None)
+# max_participants = None if members_unlimited else members_uplimit
+# qd['max_participants']= max_participants.pop(0) if max_participants else None
+
+ #substitue QueryDict
+ args.insert(0, qd)
+
+ super(AstakosGroupCreationForm, self).__init__(*args, **kwargs)
+ self.fields.keyOrder = ['kind', 'name', 'homepage', 'desc',
+ 'issue_date', 'expiration_date',
+ 'moderation_enabled', 'max_participants']
+ def add_fields((k, v)):
+ k = k.partition('_proxy')[0]
+ self.fields[k] = forms.IntegerField(
+ required=False,
+ widget=forms.HiddenInput(),
+ min_value=1
+ )
+ map(add_fields,
+ ((k, v) for k,v in qd.iteritems() if k.endswith('_uplimit'))
+ )
+
+ def add_fields((k, v)):
+ self.fields[k] = forms.BooleanField(
+ required=False,
+ #widget=forms.HiddenInput()
+ )
+ map(add_fields,
+ ((k, v) for k,v in qd.iteritems() if k.startswith('is_selected_'))
+ )
+
+ def policies(self):
+ self.clean()
+ policies = []
+ append = policies.append
+ for name, uplimit in self.cleaned_data.iteritems():
+
+ subs = name.split('_uplimit')
+ if len(subs) == 2:
+ prefix, suffix = subs
+ s, sep, r = prefix.partition(RESOURCE_SEPARATOR)
+ resource = Resource.objects.get(service__name=s, name=r)
+
+ # keep only resource limits for selected resource groups
+ if self.cleaned_data.get(
+ 'is_selected_%s' % resource.group, False
+ ):
+ append(dict(service=s, resource=r, uplimit=uplimit))
+ return policies
+
+class AstakosGroupCreationSummaryForm(forms.ModelForm):
+ kind = forms.ModelChoiceField(
+ queryset=GroupKind.objects.all(),
+ label="",
+ widget=forms.HiddenInput()
+ )
+ name = forms.URLField()
+ moderation_enabled = forms.BooleanField(
+ help_text="Check if you want to approve members participation manually",
+ required=False,
+ initial=True
+ )
+ max_participants = forms.IntegerField(
+ required=False, min_value=1
+ )
+
+ class Meta:
+ model = AstakosGroup
+
+ def __init__(self, *args, **kwargs):
+ #update QueryDict
+ args = list(args)
+ qd = args.pop(0).copy()
+ members_unlimited = qd.pop('members_unlimited', False)
+ members_uplimit = qd.pop('members_uplimit', None)
+# max_participants = None if members_unlimited else members_uplimit
+# qd['max_participants']= max_participants.pop(0) if max_participants else None
+
+ #substitue QueryDict
+ args.insert(0, qd)
+
+ super(AstakosGroupCreationSummaryForm, self).__init__(*args, **kwargs)
+ self.fields.keyOrder = ['kind', 'name', 'homepage', 'desc',
+ 'issue_date', 'expiration_date',
+ 'moderation_enabled', 'max_participants']
+ def add_fields((k, v)):
+ self.fields[k] = forms.IntegerField(
+ required=False,
+ widget=forms.TextInput(),
+ min_value=1
+ )
+ map(add_fields,
+ ((k, v) for k,v in qd.iteritems() if k.endswith('_uplimit'))
+ )
+
+ def add_fields((k, v)):
+ self.fields[k] = forms.BooleanField(
+ required=False,
+ widget=forms.HiddenInput()
+ )
+ map(add_fields,
+ ((k, v) for k,v in qd.iteritems() if k.startswith('is_selected_'))
+ )
+ for f in self.fields.values():
+ f.widget = forms.HiddenInput()
+
+ def clean(self):
+ super(AstakosGroupCreationSummaryForm, self).clean()
+ self.cleaned_data['policies'] = []
+ append = self.cleaned_data['policies'].append
+ #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():
+ subs = name.split('_uplimit')
+ if len(subs) == 2:
+ tbd.append(name)
+ prefix, suffix = subs
+ s, sep, r = prefix.partition(RESOURCE_SEPARATOR)
+ resource = Resource.objects.get(service__name=s, name=r)
+
+ # keep only resource limits for selected resource groups
+ if self.cleaned_data.get(
+ 'is_selected_%s' % resource.group, False
+ ):
+ append(dict(service=s, resource=r, uplimit=uplimit))
+ for name in tbd:
+ self.cleaned_data.pop(name, None)
+ return self.cleaned_data
+
+class AstakosGroupUpdateForm(forms.ModelForm):
+ class Meta:
+ model = AstakosGroup
+ fields = ('homepage', 'desc')
+
+
+class AddGroupMembersForm(forms.Form):
+ q = forms.CharField(
+ max_length=800, widget=forms.Textarea, label=_('Add members'),
+ help_text=_(astakos_messages.ADD_GROUP_MEMBERS_Q_HELP),
+ required=True)
+
+ def clean(self):
+ q = self.cleaned_data.get('q') or ''
+ users = q.split(',')
+ users = list(u.strip() for u in users if u)
+ db_entries = AstakosUser.objects.filter(email__in=users)
+ unknown = list(set(users) - set(u.email for u in db_entries))
+ if unknown:
+ raise forms.ValidationError(_(astakos_messages.UNKNOWN_USERS) % ','.join(unknown))
+ self.valid_users = db_entries
+ return self.cleaned_data
+
+ def get_valid_users(self):
+ """Should be called after form cleaning"""
+ try:
+ return self.valid_users
+ except:
+ return ()
+
+
+class AstakosGroupSearchForm(forms.Form):
+ q = forms.CharField(max_length=200, label='Search group')
+
+
+class TimelineForm(forms.Form):
+ entity = forms.ModelChoiceField(
+ queryset=AstakosUser.objects.filter(is_active=True)
+ )
+ resource = forms.ModelChoiceField(
+ queryset=Resource.objects.all()
+ )
+ start_date = forms.DateTimeField()
+ end_date = forms.DateTimeField()
+ details = forms.BooleanField(required=False, label="Detailed Listing")
+ operation = forms.ChoiceField(
+ label='Charge Method',
+ choices=(('', '-------------'),
+ ('charge_usage', 'Charge Usage'),
+ ('charge_traffic', 'Charge Traffic'), )
+ )
+
+ def clean(self):
+ super(TimelineForm, self).clean()
+ d = self.cleaned_data
+ if 'resource' in d:
+ d['resource'] = str(d['resource'])
+ if 'start_date' in d:
+ d['start_date'] = d['start_date'].strftime(
+ "%Y-%m-%dT%H:%M:%S.%f")[:24]
+ if 'end_date' in d:
+ d['end_date'] = d['end_date'].strftime("%Y-%m-%dT%H:%M:%S.%f")[:24]
+ if 'entity' in d:
+ d['entity'] = d['entity'].email
+ return d
+
+
+class AstakosGroupSortForm(forms.Form):
+ sort_by = forms.ChoiceField(label='Sort by',
+ choices=(('groupname', 'Name'),
+ ('kindname', 'Type'),
+ ('issue_date', 'Issue Date'),
+ ('expiration_date',
+ 'Expiration Date'),
+ ('approved_members_num',
+ 'Participants'),
+ ('is_enabled', 'Status'),
+ ('moderation_enabled', 'Moderation'),
+ ('membership_status',
+ 'Enrollment Status')
+ ),
+ required=False)
+
+
+class MembersSortForm(forms.Form):
+ sort_by = forms.ChoiceField(label='Sort by',
+ choices=(('person__email', 'User Id'),
+ ('person__first_name', 'Name'),
+ ('date_joined', 'Status')
+ ),
+ required=False)
+
+
+class PickResourceForm(forms.Form):
+ resource = forms.ModelChoiceField(
+ queryset=Resource.objects.select_related().all()
+ )
+ resource.widget.attrs["onchange"] = "this.form.submit()"
+
+
class ExtendedSetPasswordForm(SetPasswordForm):
"""
Extends SetPasswordForm by enabling user
def __init__(self, user, *args, **kwargs):
super(ExtendedSetPasswordForm, self).__init__(user, *args, **kwargs)
-
+
def save(self, commit=True):
try:
self.user = AstakosUser.objects.get(id=self.user.id)
self.user.flush_sessions()
except BaseException, e:
logger.exception(e)
- pass
return super(ExtendedSetPasswordForm, self).save(commit=commit)
# Copyright 2011 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
# 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
from django.template.loader import render_to_string
from django.core.mail import send_mail
from django.core.urlresolvers import reverse
-from django.core.exceptions import ValidationError
from django.template import Context, loader
from django.contrib.auth import (
login as auth_login,
- logout as auth_logout,
- SESSION_KEY
+ logout as auth_logout
)
-from django.http import HttpRequest
+from django.conf import settings
+from django.contrib.auth.models import AnonymousUser
from urllib import quote
from urlparse import urljoin
from functools import wraps
from astakos.im.settings import (
- DEFAULT_CONTACT_EMAIL, DEFAULT_FROM_EMAIL,
- SITENAME, BASEURL, DEFAULT_ADMIN_EMAIL, LOGGING_LEVEL,
- VERIFICATION_EMAIL_SUBJECT, ADMIN_NOTIFICATION_EMAIL_SUBJECT,
- HELPDESK_NOTIFICATION_EMAIL_SUBJECT, INVITATION_EMAIL_SUBJECT,
- GREETING_EMAIL_SUBJECT, FEEDBACK_EMAIL_SUBJECT, EMAIL_CHANGE_EMAIL_SUBJECT
+ DEFAULT_CONTACT_EMAIL, SITENAME, BASEURL, LOGGING_LEVEL,
+ VERIFICATION_EMAIL_SUBJECT, ACCOUNT_CREATION_SUBJECT,
+ GROUP_CREATION_SUBJECT, HELPDESK_NOTIFICATION_EMAIL_SUBJECT,
+ INVITATION_EMAIL_SUBJECT, GREETING_EMAIL_SUBJECT, FEEDBACK_EMAIL_SUBJECT,
+ EMAIL_CHANGE_EMAIL_SUBJECT
)
-from astakos.im.models import Invitation, AstakosUser, SessionCatalog
+import astakos.im.messages as astakos_messages
+import astakos.im.models
logger = logging.getLogger(__name__)
+
def logged(func, msg):
@wraps(func)
def with_logging(*args, **kwargs):
email = ''
user = None
- if len(args) == 2 and isinstance(args[1], AstakosUser):
- user = args[1]
- elif len(args) == 1 and isinstance(args[0], HttpRequest):
+ try:
request = args[0]
- user = request.user
- email = user.email if user and user.is_authenticated() else ''
+ email = request.user.email
+ except (KeyError, AttributeError), e:
+ email = ''
r = func(*args, **kwargs)
if LOGGING_LEVEL:
- logger._log(LOGGING_LEVEL, msg % email, [])
+ logger.log(LOGGING_LEVEL, msg % email)
return r
return with_logging
def login(request, user):
auth_login(request, user)
- SessionCatalog(session_key=request.session.session_key, user=user).save()
+ astakos.im.models.SessionCatalog(
+ session_key=request.session.session_key,
+ user=user).save()
login = logged(login, '%s logged in.')
logout = logged(auth_logout, '%s logged out.')
+
def send_verification(user, template_name='im/activation_email.txt'):
"""
Send email to user to verify his/her email and activate his/her account.
-
+
Raises SendVerificationError
"""
- url = '%s?auth=%s&next=%s' % (urljoin(BASEURL, reverse('astakos.im.views.activate')),
- quote(user.auth_token),
- quote(urljoin(BASEURL, reverse('astakos.im.views.index'))))
+ url = '%s?auth=%s&next=%s' % (urljoin(BASEURL, reverse('activate')),
+ quote(user.auth_token),
+ quote(urljoin(BASEURL, reverse('index'))))
message = render_to_string(template_name, {
- 'user': user,
- 'url': url,
- 'baseurl': BASEURL,
- 'site_name': SITENAME,
- 'support': DEFAULT_CONTACT_EMAIL})
- sender = DEFAULT_FROM_EMAIL
+ 'user': user,
+ 'url': url,
+ 'baseurl': BASEURL,
+ 'site_name': SITENAME,
+ 'support': DEFAULT_CONTACT_EMAIL})
+ sender = settings.SERVER_EMAIL
try:
send_mail(_(VERIFICATION_EMAIL_SUBJECT), message, sender, [user.email])
except (SMTPException, socket.error) as e:
raise SendVerificationError()
else:
msg = 'Sent activation %s' % user.email
- logger._log(LOGGING_LEVEL, msg, [])
+ logger.log(LOGGING_LEVEL, msg)
+
def send_activation(user, template_name='im/activation_email.txt'):
send_verification(user, template_name)
user.activation_sent = datetime.now()
user.save()
-def send_admin_notification(user, template_name='im/admin_notification.txt'):
+
+def _send_admin_notification(template_name,
+ dictionary=None,
+ subject='alpha2 testing notification',):
"""
- Send email to DEFAULT_ADMIN_EMAIL to notify for a new user registration.
-
+ Send notification email to settings.ADMINS.
+
Raises SendNotificationError
"""
- if not DEFAULT_ADMIN_EMAIL:
+ if not settings.ADMINS:
return
- message = render_to_string(template_name, {
- 'user': user,
- 'baseurl': BASEURL,
- 'site_name': SITENAME,
- 'support': DEFAULT_CONTACT_EMAIL})
- sender = DEFAULT_FROM_EMAIL
+ dictionary = dictionary or {}
+ message = render_to_string(template_name, dictionary)
+ sender = settings.SERVER_EMAIL
try:
- send_mail(_(ADMIN_NOTIFICATION_EMAIL_SUBJECT) % {'user': user.email}, message, sender, [DEFAULT_ADMIN_EMAIL])
+ send_mail(subject,
+ message, sender, [i[1] for i in settings.ADMINS])
except (SMTPException, socket.error) as e:
logger.exception(e)
raise SendNotificationError()
else:
- msg = 'Sent admin notification for user %s' % user.email
- logger._log(LOGGING_LEVEL, msg, [])
+ msg = 'Sent admin notification for user %s' % dictionary
+ logger.log(LOGGING_LEVEL, msg)
+
+
+def send_account_creation_notification(template_name, dictionary=None):
+ user = dictionary.get('user', AnonymousUser())
+ subject = _(ACCOUNT_CREATION_SUBJECT) % {'user':user.get('email', '')}
+ return _send_admin_notification(template_name, dictionary, subject=subject)
+
-def send_helpdesk_notification(user, template_name='im/helpdesk_notification.txt'):
+def send_group_creation_notification(template_name, dictionary=None):
+ group = dictionary.get('group', astakos.im.models.AstakosGroup())
+ subject = _(GROUP_CREATION_SUBJECT) % {'group':group.get('name', '')}
+ return _send_admin_notification(template_name, dictionary, subject=subject)
+
+
+def send_helpdesk_notification(user, template_name='im/account_notification.txt'):
"""
Send email to DEFAULT_CONTACT_EMAIL to notify for a new user activation.
-
+
Raises SendNotificationError
"""
if not DEFAULT_CONTACT_EMAIL:
return
- message = render_to_string(template_name, {
- 'user': user,
- 'baseurl': BASEURL,
- 'site_name': SITENAME,
- 'support': DEFAULT_ADMIN_EMAIL})
- sender = DEFAULT_FROM_EMAIL
+ message = render_to_string(
+ template_name,
+ {'user': user}
+ )
+ sender = settings.SERVER_EMAIL
try:
- send_mail(_(HELPDESK_NOTIFICATION_EMAIL_SUBJECT) % {'user': user.email}, message, sender, [DEFAULT_CONTACT_EMAIL])
+ send_mail(
+ _(HELPDESK_NOTIFICATION_EMAIL_SUBJECT) % {'user': user.email},
+ message, sender, [DEFAULT_CONTACT_EMAIL])
except (SMTPException, socket.error) as e:
logger.exception(e)
raise SendNotificationError()
else:
- msg = 'Sent helpdesk admin notification for user %s' % user.email
- logger._log(LOGGING_LEVEL, msg, [])
+ msg = 'Sent helpdesk admin notification for %s' % user.email
+ logger.log(LOGGING_LEVEL, msg)
+
def send_invitation(invitation, template_name='im/invitation.txt'):
"""
Send invitation email.
-
+
Raises SendInvitationError
"""
subject = _(INVITATION_EMAIL_SUBJECT)
- url = '%s?code=%d' % (urljoin(BASEURL, reverse('astakos.im.views.index')), invitation.code)
- message = render_to_string('im/invitation.txt', {
- 'invitation': invitation,
- 'url': url,
- 'baseurl': BASEURL,
- 'site_name': SITENAME,
- 'support': DEFAULT_CONTACT_EMAIL})
- sender = DEFAULT_FROM_EMAIL
+ url = '%s?code=%d' % (urljoin(BASEURL, reverse('index')), invitation.code)
+ message = render_to_string(template_name, {
+ 'invitation': invitation,
+ 'url': url,
+ 'baseurl': BASEURL,
+ 'site_name': SITENAME,
+ 'support': DEFAULT_CONTACT_EMAIL})
+ sender = settings.SERVER_EMAIL
try:
send_mail(subject, message, sender, [invitation.username])
except (SMTPException, socket.error) as e:
raise SendInvitationError()
else:
msg = 'Sent invitation %s' % invitation
- logger._log(LOGGING_LEVEL, msg, [])
+ logger.log(LOGGING_LEVEL, msg)
+ invitation.inviter.invitations = max(0, invitation.inviter.invitations - 1)
+ invitation.inviter.save()
+
def send_greeting(user, email_template_name='im/welcome_email.txt'):
"""
Send welcome email.
-
+
Raises SMTPException, socket.error
"""
subject = _(GREETING_EMAIL_SUBJECT)
message = render_to_string(email_template_name, {
- 'user': user,
- 'url': urljoin(BASEURL, reverse('astakos.im.views.index')),
- 'baseurl': BASEURL,
- 'site_name': SITENAME,
- 'support': DEFAULT_CONTACT_EMAIL})
- sender = DEFAULT_FROM_EMAIL
+ 'user': user,
+ 'url': urljoin(BASEURL, reverse('index')),
+ 'baseurl': BASEURL,
+ 'site_name': SITENAME,
+ 'support': DEFAULT_CONTACT_EMAIL})
+ sender = settings.SERVER_EMAIL
try:
send_mail(subject, message, sender, [user.email])
except (SMTPException, socket.error) as e:
raise SendGreetingError()
else:
msg = 'Sent greeting %s' % user.email
- logger._log(LOGGING_LEVEL, msg, [])
+ logger.log(LOGGING_LEVEL, msg)
+
def send_feedback(msg, data, user, email_template_name='im/feedback_mail.txt'):
subject = _(FEEDBACK_EMAIL_SUBJECT)
raise SendFeedbackError()
else:
msg = 'Sent feedback from %s' % user.email
- logger._log(LOGGING_LEVEL, msg, [])
+ logger.log(LOGGING_LEVEL, msg)
+
def send_change_email(ec, request, email_template_name='registration/email_change_email.txt'):
try:
url = reverse('email_change_confirm',
- kwargs={'activation_key':ec.activation_key})
+ kwargs={'activation_key': ec.activation_key})
url = request.build_absolute_uri(url)
t = loader.get_template(email_template_name)
c = {'url': url, 'site_name': SITENAME}
- from_email = DEFAULT_FROM_EMAIL
+ from_email = settings.SERVER_EMAIL
send_mail(_(EMAIL_CHANGE_EMAIL_SUBJECT),
- t.render(Context(c)), from_email, [ec.new_email_address])
+ t.render(Context(c)), from_email, [ec.new_email_address])
except (SMTPException, socket.error) as e:
logger.exception(e)
raise ChangeEmailError()
else:
msg = 'Sent change email for %s' % ec.user.email
- logger._log(LOGGING_LEVEL, msg, [])
+ logger.log(LOGGING_LEVEL, msg)
+
def activate(user, email_template_name='im/welcome_email.txt',
- helpdesk_email_template_name='im/helpdesk_notification.txt', verify_email=False):
+ helpdesk_email_template_name='im/helpdesk_notification.txt', verify_email=False):
"""
Activates the specific user and sends email.
-
+
Raises SendGreetingError, ValidationError
"""
user.is_active = True
send_helpdesk_notification(user, helpdesk_email_template_name)
send_greeting(user, email_template_name)
-def invite(invitation, inviter, email_template_name='im/welcome_email.txt'):
- """
- Send an invitation email and upon success reduces inviter's invitation by one.
-
- Raises SendInvitationError
- """
- invitation.inviter = inviter
- invitation.save()
- send_invitation(invitation, email_template_name)
- inviter.invitations = max(0, inviter.invitations - 1)
- inviter.save()
-def set_user_credibility(email, has_credits):
+def switch_account_to_shibboleth(user, local_user,
+ greeting_template_name='im/welcome_email.txt'):
try:
- user = AstakosUser.objects.get(email=email, is_active=True)
- user.has_credits = has_credits
- user.save()
- except AstakosUser.DoesNotExist, e:
- logger.exception(e)
- except ValidationError, e:
- logger.exception(e)
+ provider = user.provider
+ except AttributeError:
+ return
+ else:
+ if not provider == 'shibboleth':
+ return
+ user.delete()
+ local_user.provider = 'shibboleth'
+ local_user.third_party_identifier = user.third_party_identifier
+ local_user.save()
+ send_greeting(local_user, greeting_template_name)
+ return local_user
+
class SendMailError(Exception):
pass
+
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__()
from astakos.im.models import AstakosUser
-content_type = None
+DEFAULT_CONTENT_TYPE = None
+
def get_user(email_or_id, **kwargs):
try:
except AstakosUser.DoesNotExist, AstakosUser.MultipleObjectsReturned:
return None
+
def format_bool(b):
return 'YES' if b else 'NO'
else:
return 'in ' + timeuntil(d)
+
def get_astakosuser_content_type():
- if content_type:
- return content_type
-
try:
return ContentType.objects.get(app_label='im',
model='astakosuser')
except:
- return content_type
-
+ return DEFAULT_CONTENT_TYPE
+
+
def add_user_permission(user, pname):
content_type = get_astakosuser_content_type()
if user.has_perm(pname):
user.user_permissions.add(p)
return 1, created
+
def add_group_permission(group, pname):
content_type = get_astakosuser_content_type()
if pname in [p.codename for p in group.permissions.all()]:
group.permissions.add(p)
return 1, created
+
def remove_user_permission(user, pname):
content_type = get_astakosuser_content_type()
if user.has_perm(pname):
return 0
try:
p = Permission.objects.get(codename=pname,
- content_type=content_type)
+ content_type=content_type)
user.user_permissions.remove(p)
return 1
- except Permission.DoesNotExist, e:
+ except Permission.DoesNotExist:
return -1
+
def remove_group_permission(group, pname):
content_type = get_astakosuser_content_type()
if pname not in [p.codename for p in group.permissions.all()]:
return 0
try:
p = Permission.objects.get(codename=pname,
- content_type=content_type)
+ content_type=content_type)
group.permissions.remove(p)
return 1
- except Permission.DoesNotExist, e:
- return -1
\ No newline at end of file
+ except Permission.DoesNotExist:
+ return -1
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.
-from optparse import make_option
-from random import choice
-from string import digits, lowercase, uppercase
-from uuid import uuid4
-from time import time
-from os.path import abspath
-
from django.core.management.base import BaseCommand, CommandError
-from django.contrib.auth.models import Group
+
+from astakos.im.models import AstakosGroup
from ._common import add_group_permission
+
class Command(BaseCommand):
args = "<groupname> [<permission> ...]"
help = "Insert group"
-
+
def handle(self, *args, **options):
if len(args) < 1:
raise CommandError("Invalid number of arguments")
-
+
name = args[0].decode('utf8')
-
+
try:
- Group.objects.get(name=name)
+ AstakosGroup.objects.get(name=name)
raise CommandError("A group with this name already exists")
- except Group.DoesNotExist, e:
- group = Group(name=name)
+ except AstakosGroup.DoesNotExist, e:
+ group = AstakosGroup(name=name)
group.save()
msg = "Created group id %d" % (group.id,)
self.stdout.write(msg + '\n')
for pname in args[1:]:
r, created = add_group_permission(group, pname)
if created:
- self.stdout.write('Permission: %s created successfully\n' % pname)
+ self.stdout.write(
+ 'Permission: %s created successfully\n' % pname)
if r == 0:
- self.stdout.write('Group has already permission: %s\n' % pname)
+ self.stdout.write(
+ 'Group has already permission: %s\n' % pname)
else:
- self.stdout.write('Permission: %s added successfully\n' % pname)
+ self.stdout.write(
+ 'Permission: %s added successfully\n' % pname)
except Exception, e:
- raise CommandError(e)
\ No newline at end of file
+ raise CommandError(e)
from optparse import make_option
-from django.core.management.base import BaseCommand, CommandError
-from django.contrib.auth.models import Group
+from django.core.management.base import NoArgsCommand
-from astakos.im.models import AstakosUser
+from astakos.im.models import AstakosGroup
from ._common import format_bool
-class Command(BaseCommand):
+class Command(NoArgsCommand):
help = "List groups"
- option_list = BaseCommand.option_list + (
+ option_list = NoArgsCommand.option_list + (
make_option('-c',
- action='store_true',
- dest='csv',
- default=False,
- help="Use pipes to separate values"),
+ action='store_true',
+ dest='csv',
+ default=False,
+ help="Use pipes to separate values"),
+ make_option('-p',
+ action='store_true',
+ dest='pending',
+ default=False,
+ help="List only groups pending enable"),
)
-
- def handle(self, *args, **options):
- if args:
- raise CommandError("Command doesn't accept any arguments")
-
- groups = Group.objects.all().order_by('id')
-
- labels = ('id', 'name', 'permissions')
- columns = (3, 12, 50)
-
- if not options['csv']:
+
+ def handle_noargs(self, **options):
+ groups = AstakosGroup.objects.all()
+
+ if options.get('pending'):
+ groups = filter(lambda g: g.is_disabled, groups)
+
+ labels = ('id', 'name', 'enabled', 'moderation', 'permissions')
+ columns = (3, 25, 12, 12, 50)
+
+ if not options.get('csv'):
line = ' '.join(l.rjust(w) for l, w in zip(labels, columns))
self.stdout.write(line + '\n')
sep = '-' * len(line)
self.stdout.write(sep + '\n')
-
+
for group in groups:
- fields = (str(group.id), group.name,
+ fields = (str(group.id),
+ group.name,
+ format_bool(group.is_enabled),
+ format_bool(group.moderation_enabled),
','.join(p.codename for p in group.permissions.all()))
-
- if options['csv']:
+
+ if options.get('csv'):
line = '|'.join(fields)
else:
line = ' '.join(f.rjust(w) for f, w in zip(fields, columns))
-
+
self.stdout.write(line.encode('utf8') + '\n')
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.
-from optparse import make_option
-
from django.core.management.base import BaseCommand, CommandError
-from django.contrib.auth.models import Group, Permission
+from django.contrib.auth.models import Group
from django.contrib.contenttypes.models import ContentType
-from django.core.exceptions import ValidationError
-from astakos.im.models import AstakosUser
from ._common import add_group_permission
+
class Command(BaseCommand):
args = "<groupname> <permission> [<permissions> ...]"
help = "Add group permissions"
-
+
def handle(self, *args, **options):
if len(args) < 2:
- raise CommandError("Please provide a group name and at least one permission")
-
+ raise CommandError(
+ "Please provide a group name and at least one permission")
+
group = None
try:
if args[0].isdigit():
group = Group.objects.get(name=args[0])
except Group.DoesNotExist, e:
raise CommandError("Invalid group")
-
+
try:
content_type = ContentType.objects.get(app_label='im',
- model='astakosuser')
+ model='astakosuser')
for pname in args[1:]:
r, created = add_group_permission(group, pname)
if created:
- self.stdout.write('Permission: %s created successfully\n' % pname)
+ self.stdout.write(
+ 'Permission: %s created successfully\n' % pname)
if r == 0:
- self.stdout.write('Group has already permission: %s\n' % pname)
+ self.stdout.write(
+ 'Group has already permission: %s\n' % pname)
else:
- self.stdout.write('Permission: %s added successfully\n' % pname)
+ self.stdout.write(
+ 'Permission: %s added successfully\n' % pname)
except Exception, e:
- raise CommandError(e)
\ No newline at end of file
+ raise CommandError(e)
from astakos.im.models import AstakosUser
from ._common import remove_group_permission
+
class Command(BaseCommand):
args = "<groupname> <permission> [<permissions> ...]"
help = "Remove group permissions"
-
+
def handle(self, *args, **options):
if len(args) < 2:
- raise CommandError("Please provide a group name and at least one permission")
-
+ raise CommandError(
+ "Please provide a group name and at least one permission")
+
group = None
try:
if args[0].isdigit():
group = Group.objects.get(name=args[0])
except Group.DoesNotExist, e:
raise CommandError("Invalid group")
-
+
try:
for pname in args[1:]:
r = remove_group_permission(group, pname)
if r < 0:
- self.stdout.write('Invalid permission codename: %s\n' % pname)
+ self.stdout.write(
+ 'Invalid permission codename: %s\n' % pname)
elif r == 0:
self.stdout.write('Group has not permission: %s\n' % pname)
elif r > 0:
- self.stdout.write('Permission: %s removed successfully\n' % pname)
+ self.stdout.write(
+ 'Permission: %s removed successfully\n' % pname)
except Exception, e:
- raise CommandError(e)
\ No newline at end of file
+ raise CommandError(e)
--- /dev/null
+# Copyright 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 optparse import make_option
+
+from django.core.management.base import BaseCommand, CommandError
+from django.contrib.auth.models import Group
+from django.core.exceptions import ValidationError
+
+from astakos.im.models import AstakosUser
+from ._common import remove_group_permission
+
+
+class Command(BaseCommand):
+ args = "<groupname> <permission> [<permissions> ...]"
+ help = "Remove group permissions"
+
+ def handle(self, *args, **options):
+ if len(args) < 2:
+ raise CommandError(
+ "Please provide a group name and at least one permission")
+
+ group = None
+ try:
+ if args[0].isdigit():
+ group = Group.objects.get(id=args[0])
+ else:
+ group = Group.objects.get(name=args[0])
+ except Group.DoesNotExist, e:
+ raise CommandError("Invalid group")
+
+ try:
+ for pname in args[1:]:
+ r = remove_group_permission(group, pname)
+ if r < 0:
+ self.stdout.write(
+ 'Invalid permission codename: %s\n' % pname)
+ elif r == 0:
+ self.stdout.write('Group has not permission: %s\n' % pname)
+ elif r > 0:
+ self.stdout.write(
+ 'Permission: %s removed successfully\n' % pname)
+ except Exception, e:
+ raise CommandError(e)
--- /dev/null
+# Copyright 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 optparse import make_option
+
+from django.core.management.base import BaseCommand, CommandError
+
+from astakos.im.models import AstakosGroup
+from ._common import add_group_permission, remove_group_permission
+
+
+class Command(BaseCommand):
+ args = "<groupname>"
+ help = "Update group"
+
+ option_list = BaseCommand.option_list + (
+ make_option('--add-permission',
+ dest='add-permission',
+ help="Add user permission"),
+ make_option('--delete-permission',
+ dest='delete-permission',
+ help="Delete user permission"),
+ make_option('--enable',
+ action='store_true',
+ dest='enable',
+ default=False,
+ help="Enable group"),
+ make_option('--disable',
+ action='store_true',
+ dest='disable',
+ default=False,
+ help="Disable group"),
+ )
+
+ def handle(self, *args, **options):
+ if len(args) < 1:
+ raise CommandError("Please provide a group identifier")
+
+ group = None
+ try:
+ if args[0].isdigit():
+ group = AstakosGroup.objects.get(id=args[0])
+ else:
+ group = AstakosGroup.objects.get(name=args[0])
+ except AstakosGroup.DoesNotExist, e:
+ raise CommandError("Invalid group")
+
+ try:
+ pname = options.get('add-permission')
+ if pname:
+ r, created = add_group_permission(group, pname)
+ if created:
+ self.stdout.write(
+ 'Permission: %s created successfully\n' % pname)
+ if r == 0:
+ self.stdout.write(
+ 'Group has already permission: %s\n' % pname)
+ else:
+ self.stdout.write(
+ 'Permission: %s added successfully\n' % pname)
+
+ pname = options.get('delete-permission')
+ if pname:
+ r = remove_group_permission(group, pname)
+ if r < 0:
+ self.stdout.write(
+ 'Invalid permission codename: %s\n' % pname)
+ elif r == 0:
+ self.stdout.write('Group has not permission: %s\n' % pname)
+ elif r > 0:
+ self.stdout.write(
+ 'Permission: %s removed successfully\n' % pname)
+
+ if options.get('enable'):
+ group.enable()
+ elif options.get('disable'):
+ group.disable()
+
+ except Exception, e:
+ raise CommandError(e)
class Command(BaseCommand):
args = "<invitation ID>"
help = "Show invitation info"
-
+
def handle(self, *args, **options):
if len(args) != 1:
raise CommandError("Please provide an invitation id")
-
+
try:
invitation = Invitation.objects.get(id=int(args[0]))
except Invitation.DoesNotExist:
raise CommandError("Unknown invitation id '%s'" % (args[0],))
-
+
kv = {
'id': invitation.id,
'real name': invitation.realname,
'inviter real name': invitation.inviter.realname,
'invitater email': invitation.inviter.email,
}
-
+
for key, val in sorted(kv.items()):
line = '%s: %s\n' % (key.rjust(18), val)
self.stdout.write(line.encode('utf8'))
class Command(BaseCommand):
help = "List invitations"
-
+
option_list = BaseCommand.option_list + (
make_option('-c',
- action='store_true',
- dest='csv',
- default=False,
- help="Use pipes to separate values"),
- )
-
+ action='store_true',
+ dest='csv',
+ default=False,
+ help="Use pipes to separate values"),
+ )
+
def handle(self, *args, **options):
if args:
raise CommandError("Command doesn't accept any arguments")
labels = ('id', 'inviter', 'email', 'real name', 'code', 'consumed')
columns = (3, 24, 24, 24, 20, 4, 8)
-
+
if not options['csv']:
line = ' '.join(l.rjust(w) for l, w in zip(labels, columns))
self.stdout.write(line + '\n')
sep = '-' * len(line)
self.stdout.write(sep + '\n')
-
+
for invitation in invitations:
id = str(invitation.id)
code = str(invitation.code)
consumed = format_bool(invitation.is_consumed)
- fields = (id, invitation.inviter.email, invitation.username, invitation.realname,
- code, consumed)
-
+ fields = (
+ id, invitation.inviter.email, invitation.username, invitation.realname,
+ code, consumed)
+
if options['csv']:
line = '|'.join(fields)
else:
line = ' '.join(f.rjust(w) for f, w in zip(fields, columns))
-
+
self.stdout.write(line.encode('utf8') + '\n')
--- /dev/null
+# Copyright 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.core.management.base import BaseCommand, CommandError
+
+from astakos.im.models import AstakosUser, Resource
+from astakos.im.endpoints.qh import register_users, register_resources
+
+import logging
+logger = logging.getLogger(__name__)
+
+
+class Command(BaseCommand):
+ help = "Send user information and resource quota in the Quotaholder"
+
+ def handle(self, *args, **options):
+ try:
+ register_resources(Resource.objects.all())
+ register_users(AstakosUser.objects.all())
+ except BaseException, e:
+ logger.exception(e)
+ raise CommandError("Syncing failed.")
--- /dev/null
+# Copyright 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.core.management.base import BaseCommand, CommandError
+from django.db.utils import IntegrityError
+
+from astakos.im.models import Resource, Service
+
+
+class Command(BaseCommand):
+ args = "<service> <resource> <desc> <unit>"
+ help = "Add a resource"
+
+ def handle(self, *args, **options):
+ if len(args) < 2:
+ raise CommandError("Invalid number of arguments")
+
+ service_name = args[0]
+ resource_name = args[1]
+
+ try:
+ service = Service.objects.get(name=service_name)
+ except Service.DoesNotExist:
+ raise CommandError("Invalid service name")
+ else:
+ try:
+ resource = Resource(name=resource_name, service=service)
+ resource.save()
+ except IntegrityError, e:
+ raise CommandError(e)
+ # else:
+# resource.meta.add(args[2:])
--- /dev/null
+# Copyright 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 optparse import make_option
+
+from django.core.management.base import BaseCommand
+
+from astakos.im.models import Resource
+
+
+class Command(BaseCommand):
+ help = "List resources"
+
+ option_list = BaseCommand.option_list + (
+ make_option('-c',
+ action='store_true',
+ dest='csv',
+ default=False,
+ help="Use pipes to separate values"),
+ )
+
+ def handle(self, *args, **options):
+ resources = Resource.objects.select_related().all()
+
+ labels = ('id', 'service', 'name')
+ columns = (3, 40, 40)
+
+ if not options['csv']:
+ line = ' '.join(l.rjust(w) for l, w in zip(labels, columns))
+ self.stdout.write(line + '\n')
+ sep = '-' * len(line)
+ self.stdout.write(sep + '\n')
+
+ for r in resources:
+ fields = (str(r.id), r.service.name, r.name)
+
+ if options['csv']:
+ line = '|'.join(fields)
+ else:
+ line = ' '.join(f.rjust(w) for f, w in zip(fields, columns))
+
+ self.stdout.write(line.encode('utf8') + '\n')
--- /dev/null
+# Copyright 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.core.management.base import BaseCommand, CommandError
+
+from astakos.im.models import Resource
+
+
+class Command(BaseCommand):
+ args = "<resource>"
+ help = "Add a resource"
+
+ def handle(self, *args, **options):
+ if len(args) < 1:
+ raise CommandError("Invalid number of arguments")
+
+ kwargs = {}
+ if args[0].isdigit():
+ kwargs['id'] = args[0]
+ else:
+ kwargs['name'] = args[0]
+
+ try:
+ r = Resource.objects.get(**kwargs)
+ except Resource.DoesNotExist, e:
+ raise CommandError("Invalid resource")
+ r.delete()
from django.core.management.base import BaseCommand, CommandError
-from astakos.im.models import Service
+from astakos.im.api.callpoint import AstakosCallpoint
class Command(BaseCommand):
args = "<name> <url> [<icon>]"
help = "Register a service"
-
+
def handle(self, *args, **options):
if len(args) < 2:
raise CommandError("Invalid number of arguments")
-
- service = Service(name=args[0], url=args[1])
+
+ s = {'name':args[0], 'url':args[1]}
if len(args) == 3:
- service.icon = args[2]
+ s['icon'] = args[2]
try:
- service.save()
- self.stdout.write('Service created with token: %s\n' % service.auth_token)
+ c = AstakosCallpoint()
+ c.add_services((s,))
except Exception, e:
- raise CommandError(e)
\ No newline at end of file
+ raise CommandError(e)
+ else:
+ self.stdout.write(
+ 'Service created successfully\n')
from optparse import make_option
-from django.core.management.base import BaseCommand, CommandError
+from django.core.management.base import NoArgsCommand
from astakos.im.models import Service
-class Command(BaseCommand):
+
+class Command(NoArgsCommand):
help = "List services"
- option_list = BaseCommand.option_list + (
+ option_list = NoArgsCommand.option_list + (
make_option('-c',
- action='store_true',
- dest='csv',
- default=False,
- help="Use pipes to separate values"),
+ action='store_true',
+ dest='csv',
+ default=False,
+ help="Use pipes to separate values"),
)
- def handle(self, *args, **options):
- if args:
- raise CommandError("Command doesn't accept any arguments")
-
+ def handle_noargs(self, **options):
services = Service.objects.all().order_by('id')
labels = ('id', 'name', 'url', 'auth_token', 'icon')
self.stdout.write(sep + '\n')
for service in services:
- fields = (str(service.id), service.name, service.url,
- service.auth_token,
- service.icon)
+ fields = (str(service.id), service.name,
+ service.url,
+ service.auth_token or '',
+ service.icon)
if options['csv']:
line = '|'.join(fields)
from astakos.im.models import Service
+
class Command(BaseCommand):
args = "<name>"
help = "Unregister a service"
-
+
def handle(self, *args, **options):
if len(args) < 1:
raise CommandError("Invalid number of arguments")
-
+
try:
service = Service.objects.get(name=args[0])
service.delete()
except Service.DoesNotExist, e:
- raise CommandError(e)
\ No newline at end of file
+ raise CommandError(e)
from astakos.im.models import Service
+
class Command(BaseCommand):
args = "<name>"
help = "Renew service token"
-
+
def handle(self, *args, **options):
if len(args) != 1:
raise CommandError("Invalid number of arguments")
-
+
try:
service = Service.objects.get(name=args[0])
service.renew_token()
except Service.DoesNotExist:
raise CommandError("Invalid service name")
except Exception, e:
- raise CommandError(e)
\ No newline at end of file
+ raise CommandError(e)
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.
-from optparse import make_option
-from random import choice
-from string import digits, lowercase, uppercase
-from uuid import uuid4
-from time import time
from os.path import abspath
from django.core.management.base import BaseCommand, CommandError
from astakos.im.models import ApprovalTerms
+
class Command(BaseCommand):
args = "<location>"
help = "Insert approval terms"
-
+
def handle(self, *args, **options):
if len(args) != 1:
raise CommandError("Invalid number of arguments")
-
+
location = abspath(args[0].decode('utf8'))
try:
- f = open(location, 'r')
+ open(location, 'r')
except IOError:
raise CommandError("Invalid location")
-
+
terms = ApprovalTerms(location=location)
terms.save()
-
+
msg = "Created term id %d" % (terms.id,)
self.stdout.write(msg + '\n')
from ._common import get_user
+
class Command(BaseCommand):
args = "<user ID or email> [user ID or email] ..."
help = "Sends an activation email to one or more users"
-
+
def handle(self, *args, **options):
if not args:
raise CommandError("No user was given")
-
+
for email_or_id in args:
user = get_user(email_or_id, is_active=False)
if not user:
self.stderr.write("Unknown user '%s'\n" % (email_or_id,))
continue
if user.is_active:
- self.stderr.write("Already active user '%s'\n" % (email_or_id,))
+ self.stderr.write(
+ "Already active user '%s'\n" % (email_or_id,))
continue
-
+
try:
send_activation(user)
except SendMailError, e:
raise CommandError(e.message)
-
- self.stdout.write("Activation sent to '%s'\n" % (user.email,))
\ No newline at end of file
+
+ self.stdout.write("Activation sent to '%s'\n" % (user.email,))
import socket
from optparse import make_option
-from random import choice
-from string import digits, lowercase, uppercase
-from uuid import uuid4
from django.core.management.base import BaseCommand, CommandError
from django.core.validators import validate_email
from django.core.exceptions import ValidationError
-from django.contrib.auth.models import Group
from astakos.im.models import AstakosUser
-from astakos.im.util import reserved_email
+from astakos.im.api.callpoint import AstakosCallpoint
+
+def filter_custom_options(options):
+ base_dests = list(
+ getattr(o, 'dest', None) for o in BaseCommand.option_list)
+ return dict((k, v) for k, v in options.iteritems() if k not in base_dests)
-from ._common import add_user_permission
class Command(BaseCommand):
- args = "<email> <first name> <last name> <affiliation>"
+ args = "<email>"
help = "Create a user"
-
+
option_list = BaseCommand.option_list + (
+ make_option('--first-name',
+ dest='first_name',
+ metavar='NAME',
+ help="Set user's first name"),
+ make_option('--last-name',
+ dest='last_name',
+ metavar='NAME',
+ help="Set user's last name"),
+ make_option('--affiliation',
+ dest='affiliation',
+ metavar='AFFILIATION',
+ help="Set user's affiliation"),
+ make_option('--password',
+ dest='password',
+ metavar='PASSWORD',
+ help="Set user's password"),
make_option('--active',
- action='store_true',
- dest='active',
- default=False,
- help="Activate user"),
+ action='store_true',
+ dest='is_active',
+ default=False,
+ help="Activate user"),
make_option('--admin',
- action='store_true',
- dest='admin',
- default=False,
- help="Give user admin rights"),
- make_option('--password',
- dest='password',
- metavar='PASSWORD',
- help="Set user's password"),
- make_option('--add-group',
- dest='add-group',
- help="Add user group"),
- make_option('--add-permission',
- dest='add-permission',
- help="Add user permission")
- )
-
+ action='store_true',
+ dest='is_superuser',
+ default=False,
+ help="Give user admin rights"),
+ make_option('-g',
+ action='append',
+ dest='groups',
+ help="Add user group (may be used multiple times)"),
+ make_option('-p',
+ action='append',
+ dest='permissions',
+ help="Add user permission (may be used multiple times)")
+ )
+
def handle(self, *args, **options):
- if len(args) != 4:
+ if len(args) != 1:
raise CommandError("Invalid number of arguments")
-
- args = [a.decode('utf8') for a in args]
- email, first, last, affiliation = args
-
+
+ email = args[0].decode('utf8')
+
try:
- validate_email( email )
+ validate_email(email)
except ValidationError:
raise CommandError("Invalid email")
-
- username = uuid4().hex[:30]
- password = options.get('password')
- if password is None:
- password = AstakosUser.objects.make_random_password()
-
- if reserved_email(email):
- raise CommandError("A user with this email already exists")
-
- user = AstakosUser(username=username, first_name=first, last_name=last,
- email=email, affiliation=affiliation,
- provider='local')
- user.set_password(password)
-
- if options['active']:
- user.is_active = True
- if options['admin']:
- user.is_admin = True
-
+
+ u = {'email': email}
+ u.update(filter_custom_options(options))
+ if not u.get('password'):
+ u['password'] = AstakosUser.objects.make_random_password()
+
try:
- user.save()
+ c = AstakosCallpoint()
+ r = c.create_users((u,))
except socket.error, e:
raise CommandError(e)
except ValidationError, e:
raise CommandError(e)
else:
- msg = "Created user id %d" % (user.id,)
- if options['password'] is None:
- msg += " with password '%s'" % (password,)
- self.stdout.write(msg + '\n')
-
- groupname = options.get('add-group')
- if groupname is not None:
- try:
- group = Group.objects.get(name=groupname)
- user.groups.add(group)
- self.stdout.write('Group: %s added successfully\n' % groupname)
- except Group.DoesNotExist, e:
- self.stdout.write('Group named %s does not exist\n' % groupname)
-
- pname = options.get('add-permission')
- if pname is not None:
- try:
- r, created = add_user_permission(user, pname)
- if created:
- self.stdout.write('Permission: %s created successfully\n' % pname)
- if r > 0:
- self.stdout.write('Permission: %s added successfully\n' % pname)
- elif r==0:
- self.stdout.write('User has already permission: %s\n' % pname)
- except Exception, e:
- raise CommandError(e)
\ No newline at end of file
+ failed = (res for res in r if not res.is_success)
+ for r in failed:
+ if not r.is_success:
+ raise CommandError(r.reason)
+ if not failed:
+ self.stdout.write('User created successfully')
+ if not u.get('password'):
+ self.stdout.write('with password: %s' % u['password'])
class Command(BaseCommand):
args = "<user ID or email>"
help = "Show user info"
-
+
def handle(self, *args, **options):
if len(args) != 1:
raise CommandError("Please provide a user ID or email")
-
+
email_or_id = args[0]
if email_or_id.isdigit():
users = AstakosUser.objects.filter(id=int(email_or_id))
field = 'id' if email_or_id.isdigit() else 'email'
msg = "Unknown user with %s '%s'" % (field, email_or_id)
raise CommandError(msg)
-
+
for user in users:
kv = {
'id': user.id,
'provider': user.provider,
'verified': format_bool(user.is_verified),
'has_credits': format_bool(user.has_credits),
- 'groups': [elem.name for elem in user.groups.all()],
+ 'groups': [elem.name for elem in user.astakos_groups.all()],
'permissions': [elem.codename for elem in user.user_permissions.all()],
'group_permissions': user.get_group_permissions(),
'third_party_identifier': user.third_party_identifier,
'email_verified': format_bool(user.email_verified),
'username': user.username,
- 'activation_sent_date': format_date(user.activation_sent)
+ 'activation_sent_date': format_date(user.activation_sent),
+ 'resources': dict(user.quota)
}
if get_latest_terms():
- has_signed_terms = user.signed_terms()
+ has_signed_terms = user.signed_terms
kv['has_signed_terms'] = format_bool(has_signed_terms)
if has_signed_terms:
- kv['date_signed_terms'] = format_date(user.date_signed_terms)
-
+ kv['date_signed_terms'] = format_date(
+ user.date_signed_terms)
+
for key, val in sorted(kv.items()):
line = '%s: %s\n' % (key.rjust(22), val)
self.stdout.write(line.encode('utf8'))
- self.stdout.write('\n')
\ No newline at end of file
+ self.stdout.write('\n')
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.
-import socket
-
from django.core.management.base import BaseCommand, CommandError
from django.db.utils import IntegrityError
from django.db import transaction
-from astakos.im.functions import invite, SendMailError
-from astakos.im.models import Invitation
+from astakos.im.functions import SendMailError
from ._common import get_user
+
@transaction.commit_manually
class Command(BaseCommand):
args = "<inviter id or email> <email> <real name>"
help = "Invite a user"
-
+
def handle(self, *args, **options):
if len(args) != 3:
raise CommandError("Invalid number of arguments")
-
+
inviter = get_user(args[0], is_active=True)
if not inviter:
raise CommandError("Unknown inviter")
- if not inviter.is_active:
+ if not inviter.is_active:
raise CommandError("Inactive inviter")
-
+
if inviter.invitations > 0:
email = args[1]
realname = args[2]
-
+
try:
- invitation = Invitation(username = email, realname=realname, inviter=inviter)
- invite(invitation, inviter)
+ inviter.invite(email, realname)
self.stdout.write("Invitation sent to '%s'\n" % (email,))
except SendMailError, e:
transaction.rollback()
raise CommandError(e.message)
except IntegrityError, e:
transaction.rollback()
- raise CommandError("There is already an invitation for %s" % (email,))
+ raise CommandError(
+ "There is already an invitation for %s" % (email,))
else:
transaction.commit()
else:
from optparse import make_option
-from django.core.management.base import BaseCommand, CommandError
+from django.core.management.base import NoArgsCommand
from astakos.im.models import AstakosUser
from ._common import format_bool
-class Command(BaseCommand):
+class Command(NoArgsCommand):
help = "List users"
-
- option_list = BaseCommand.option_list + (
+
+ option_list = NoArgsCommand.option_list + (
make_option('-c',
- action='store_true',
- dest='csv',
- default=False,
- help="Use pipes to separate values"),
+ action='store_true',
+ dest='csv',
+ default=False,
+ help="Use pipes to separate values"),
make_option('-p',
- action='store_true',
- dest='pending',
- default=False,
- help="List only users pending activation"),
+ action='store_true',
+ dest='pending',
+ default=False,
+ help="List only users pending activation"),
make_option('-n',
- action='store_true',
- dest='pending_send_mail',
- default=False,
- help="List only users who have not received activation"),
- )
-
- def handle(self, *args, **options):
- if args:
- raise CommandError("Command doesn't accept any arguments")
-
+ action='store_true',
+ dest='pending_send_mail',
+ default=False,
+ help="List only users who have not received activation"),
+ )
+
+ def handle_noargs(self, **options):
users = AstakosUser.objects.all().order_by('id')
if options['pending']:
users = users.filter(is_active=False)
elif options['pending_send_mail']:
users = users.filter(is_active=False, activation_sent=None)
-
- labels = ('id', 'email', 'real name', 'active', 'admin', 'provider', 'groups')
+
+ labels = ('id', 'email', 'real name', 'active', 'admin', 'provider')
columns = (3, 24, 24, 6, 5, 12, 24)
-
+
if not options['csv']:
line = ' '.join(l.rjust(w) for l, w in zip(labels, columns))
self.stdout.write(line + '\n')
sep = '-' * len(line)
self.stdout.write(sep + '\n')
-
+
for user in users:
id = str(user.id)
active = format_bool(user.is_active)
admin = format_bool(user.is_superuser)
- fields = (id, user.email, user.realname, active, admin, user.provider,
- ','.join([g.name for g in user.groups.all()]))
-
+ fields = (
+ id, user.email, user.realname, active, admin, user.provider
+ )
+
if options['csv']:
line = '|'.join(fields)
else:
line = ' '.join(f.rjust(w) for f, w in zip(fields, columns))
-
+
self.stdout.write(line.encode('utf8') + '\n')
from optparse import make_option
from django.core.management.base import BaseCommand, CommandError
-from django.contrib.auth.models import Group, Permission
-from django.contrib.contenttypes.models import ContentType
+from django.contrib.auth.models import Group
from django.core.exceptions import ValidationError
from astakos.im.models import AstakosUser
from ._common import remove_user_permission, add_user_permission
+
class Command(BaseCommand):
args = "<user ID>"
help = "Modify a user's attributes"
-
+
option_list = BaseCommand.option_list + (
make_option('--invitations',
- dest='invitations',
- metavar='NUM',
- help="Update user's invitations"),
+ dest='invitations',
+ metavar='NUM',
+ help="Update user's invitations"),
make_option('--level',
- dest='level',
- metavar='NUM',
- help="Update user's level"),
+ dest='level',
+ metavar='NUM',
+ help="Update user's level"),
make_option('--password',
- dest='password',
- metavar='PASSWORD',
- help="Set user's password"),
+ dest='password',
+ metavar='PASSWORD',
+ help="Set user's password"),
make_option('--provider',
- dest='provider',
- metavar='PROVIDER',
- help="Set user's provider"),
+ dest='provider',
+ metavar='PROVIDER',
+ help="Set user's provider"),
make_option('--renew-token',
- action='store_true',
- dest='renew_token',
- default=False,
- help="Renew the user's token"),
+ action='store_true',
+ dest='renew_token',
+ default=False,
+ help="Renew the user's token"),
make_option('--renew-password',
- action='store_true',
- dest='renew_password',
- default=False,
- help="Renew the user's password"),
+ action='store_true',
+ dest='renew_password',
+ default=False,
+ help="Renew the user's password"),
make_option('--set-admin',
- action='store_true',
- dest='admin',
- default=False,
- help="Give user admin rights"),
+ action='store_true',
+ dest='admin',
+ default=False,
+ help="Give user admin rights"),
make_option('--set-noadmin',
- action='store_true',
- dest='noadmin',
- default=False,
- help="Revoke user's admin rights"),
+ action='store_true',
+ dest='noadmin',
+ default=False,
+ help="Revoke user's admin rights"),
make_option('--set-active',
- action='store_true',
- dest='active',
- default=False,
- help="Change user's state to inactive"),
+ action='store_true',
+ dest='active',
+ default=False,
+ help="Change user's state to inactive"),
make_option('--set-inactive',
- action='store_true',
- dest='inactive',
- default=False,
- help="Change user's state to inactive"),
+ action='store_true',
+ dest='inactive',
+ default=False,
+ help="Change user's state to inactive"),
make_option('--add-group',
- dest='add-group',
- help="Add user group"),
+ dest='add-group',
+ help="Add user group"),
make_option('--delete-group',
- dest='delete-group',
- help="Delete user group"),
+ dest='delete-group',
+ help="Delete user group"),
make_option('--add-permission',
- dest='add-permission',
- help="Add user permission"),
+ dest='add-permission',
+ help="Add user permission"),
make_option('--delete-permission',
- dest='delete-permission',
- help="Delete user permission"),
- )
-
+ dest='delete-permission',
+ help="Delete user permission"),
+ )
+
def handle(self, *args, **options):
if len(args) != 1:
raise CommandError("Please provide a user ID")
-
+
if args[0].isdigit():
- user = AstakosUser.objects.get(id=int( args[0]))
+ user = AstakosUser.objects.get(id=int(args[0]))
else:
raise CommandError("Invalid ID")
-
+
if not user:
raise CommandError("Unknown user")
-
+
if options.get('admin'):
user.is_superuser = True
elif options.get('noadmin'):
user.is_superuser = False
-
+
if options.get('active'):
user.is_active = True
elif options.get('inactive'):
user.is_active = False
-
+
invitations = options.get('invitations')
if invitations is not None:
user.invitations = int(invitations)
-
+
groupname = options.get('add-group')
if groupname is not None:
try:
group = Group.objects.get(name=groupname)
user.groups.add(group)
except Group.DoesNotExist, e:
- self.stdout.write("Group named %s does not exist\n" % groupname)
-
+ self.stdout.write(
+ "Group named %s does not exist\n" % groupname)
+
groupname = options.get('delete-group')
if groupname is not None:
try:
group = Group.objects.get(name=groupname)
user.groups.remove(group)
except Group.DoesNotExist, e:
- self.stdout.write("Group named %s does not exist\n" % groupname)
-
+ self.stdout.write(
+ "Group named %s does not exist\n" % groupname)
+
pname = options.get('add-permission')
if pname is not None:
try:
r, created = add_user_permission(user, pname)
if created:
- self.stdout.write('Permission: %s created successfully\n' % pname)
+ self.stdout.write(
+ 'Permission: %s created successfully\n' % pname)
if r > 0:
- self.stdout.write('Permission: %s added successfully\n' % pname)
- elif r==0:
- self.stdout.write('User has already permission: %s\n' % pname)
+ self.stdout.write(
+ 'Permission: %s added successfully\n' % pname)
+ elif r == 0:
+ self.stdout.write(
+ 'User has already permission: %s\n' % pname)
except Exception, e:
raise CommandError(e)
-
- pname = options.get('delete-permission')
+
+ pname = options.get('delete-permission')
if pname is not None and not user.has_perm(pname):
try:
r = remove_user_permission(user, pname)
if r < 0:
- self.stdout.write('Invalid permission codename: %s\n' % pname)
+ self.stdout.write(
+ 'Invalid permission codename: %s\n' % pname)
elif r == 0:
self.stdout.write('User has not permission: %s\n' % pname)
elif r > 0:
- self.stdout.write('Permission: %s removed successfully\n' % pname)
+ self.stdout.write(
+ 'Permission: %s removed successfully\n' % pname)
except Exception, e:
raise CommandError(e)
-
+
level = options.get('level')
if level is not None:
user.level = int(level)
-
+
password = options.get('password')
if password is not None:
user.set_password(password)
-
+
provider = options.get('provider')
if provider is not None:
user.provider = provider
-
-
+
password = None
if options['renew_password']:
password = AstakosUser.objects.make_random_password()
user.set_password(password)
-
+
if options['renew_token']:
user.renew_token()
-
+
try:
user.save()
except ValidationError, e:
raise CommandError(e)
-
+
if password:
self.stdout.write('User\'s new password: %s\n' % password)
--- /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.
+
+ACCOUNT_AUTHENTICATION_FAILED = 'Cannot authenticate account.'
+ACCOUNT_ALREADY_ACTIVE = 'Account is already active.'
+ACCOUNT_PENDING_ACTIVATION = 'Your request is pending activation.'
+ACCOUNT_RESEND_ACTIVATION = 'You have not followed the activation link. <a href="%(send_activation_url)s">Resend activation email?</a>'
+INACTIVE_ACCOUNT_CHANGE_EMAIL = ''.join([ACCOUNT_RESEND_ACTIVATION, ' or <a href="%(signup_url)s">Provide new email?</a>'])
+
+ACCOUNT_UNKNOWN = 'There is no such account.'
+TOKEN_UNKNOWN = 'There is no user matching this token.'
+
+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_name)s was created successfully.'
+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.'
+
+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_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.'
+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.'
+SHIBBOLETH_MISSING_EPPN = 'Missing unique token in request.'
+SHIBBOLETH_MISSING_NAME = 'Missing user name in request.'
+
+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'
+NO_LOCAL_AUTH = 'Local login is not the current authentication method for this account.'
+SWITCH_ACCOUNT_FAILURE = 'Account failed to switch. Invalid parameters.'
+SWITCH_ACCOUNT_SUCCESS_WITH_PROVIDER = 'Account failed to switch to %(provider)s.'
+SWITCH_ACCOUNT_SUCCESS = 'Account successfully switched to %(provider)s.'
+
+# 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'
+
+INVITATION_SENT = 'Invitation sent to %(email)s.'
+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 %(email)s. \
+ Otherwise just ignore it.'
+NOTIFICATION_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.'
+ACTIVATION_SENT = 'Activation sent.'
+
+REGISTRATION_COMPLETED = 'Registration completed. You can now login.'
+
+NO_RESPONSE = 'There is no response.'
+NOT_ALLOWED_NEXT_PARAM = 'Not allowed next parameter.'
+MISSING_KEY_PARAMETER = 'Missing key parameter.'
+INVALID_KEY_PARAMETER = 'Invalid key.'
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.
-from urllib import unquote
from urlparse import urlunsplit, urlsplit
from django.http import HttpResponse
from django.utils.http import urlencode
from astakos.im.cookie import Cookie
-from astakos.im.settings import COOKIE_NAME
from astakos.im.util import get_query
+
class CookieAuthenticationMiddleware(object):
def process_request(self, request):
cookie = Cookie(request)
# encoding: utf-8
-import datetime
from south.db import db
from south.v2 import SchemaMigration
-from django.db import models
+
class Migration(SchemaMigration):
def forwards(self, orm):
-
+
# Adding model 'AstakosUser'
db.create_table('im_astakosuser', (
('user_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['auth.User'], unique=True, primary_key=True)),
- ('affiliation', self.gf('django.db.models.fields.CharField')(default='', max_length=255)),
- ('provider', self.gf('django.db.models.fields.CharField')(default='', max_length=255)),
- ('level', self.gf('django.db.models.fields.IntegerField')(default=4)),
- ('invitations', self.gf('django.db.models.fields.IntegerField')(default=0)),
- ('auth_token', self.gf('django.db.models.fields.CharField')(max_length=32, null=True, blank=True)),
- ('auth_token_created', self.gf('django.db.models.fields.DateTimeField')(null=True)),
- ('auth_token_expires', self.gf('django.db.models.fields.DateTimeField')(null=True)),
+ ('affiliation', self.gf('django.db.models.fields.CharField')
+ (default='', max_length=255)),
+ ('provider', self.gf('django.db.models.fields.CharField')
+ (default='', max_length=255)),
+ ('level', self.gf(
+ 'django.db.models.fields.IntegerField')(default=4)),
+ ('invitations', self.gf(
+ 'django.db.models.fields.IntegerField')(default=0)),
+ ('auth_token', self.gf('django.db.models.fields.CharField')
+ (max_length=32, null=True, blank=True)),
+ ('auth_token_created', self.gf(
+ 'django.db.models.fields.DateTimeField')(null=True)),
+ ('auth_token_expires', self.gf(
+ 'django.db.models.fields.DateTimeField')(null=True)),
('updated', self.gf('django.db.models.fields.DateTimeField')()),
- ('is_verified', self.gf('django.db.models.fields.BooleanField')(default=False)),
+ ('is_verified', self.gf(
+ 'django.db.models.fields.BooleanField')(default=False)),
))
db.send_create_signal('im', ['AstakosUser'])
# Adding model 'Invitation'
db.create_table('im_invitation', (
- ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('id', self.gf(
+ 'django.db.models.fields.AutoField')(primary_key=True)),
('inviter', self.gf('django.db.models.fields.related.ForeignKey')(related_name='invitations_sent', null=True, to=orm['im.AstakosUser'])),
- ('realname', self.gf('django.db.models.fields.CharField')(max_length=255)),
- ('username', self.gf('django.db.models.fields.CharField')(max_length=255)),
- ('code', self.gf('django.db.models.fields.BigIntegerField')(db_index=True)),
- ('is_accepted', self.gf('django.db.models.fields.BooleanField')(default=False)),
- ('is_consumed', self.gf('django.db.models.fields.BooleanField')(default=False)),
- ('created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
- ('accepted', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)),
- ('consumed', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)),
+ ('realname', self.gf(
+ 'django.db.models.fields.CharField')(max_length=255)),
+ ('username', self.gf(
+ 'django.db.models.fields.CharField')(max_length=255)),
+ ('code', self.gf(
+ 'django.db.models.fields.BigIntegerField')(db_index=True)),
+ ('is_accepted', self.gf(
+ 'django.db.models.fields.BooleanField')(default=False)),
+ ('is_consumed', self.gf(
+ 'django.db.models.fields.BooleanField')(default=False)),
+ ('created', self.gf('django.db.models.fields.DateTimeField')
+ (auto_now_add=True, blank=True)),
+ ('accepted', self.gf('django.db.models.fields.DateTimeField')
+ (null=True, blank=True)),
+ ('consumed', self.gf('django.db.models.fields.DateTimeField')
+ (null=True, blank=True)),
))
db.send_create_signal('im', ['Invitation'])
-
def backwards(self, orm):
-
+
# Deleting model 'AstakosUser'
db.delete_table('im_astakosuser')
# Deleting model 'Invitation'
db.delete_table('im_invitation')
-
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
# encoding: utf-8
-import datetime
from south.db import db
from south.v2 import SchemaMigration
-from django.db import models
+
class Migration(SchemaMigration):
def forwards(self, orm):
-
+
# Adding field 'AstakosUser.third_party_identifier'
db.add_column('im_astakosuser', 'third_party_identifier', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True), keep_default=False)
-
def backwards(self, orm):
-
+
# Deleting field 'AstakosUser.third_party_identifier'
db.delete_column('im_astakosuser', 'third_party_identifier')
-
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
# encoding: utf-8
-import datetime
from south.db import db
from south.v2 import SchemaMigration
-from django.db import models
+
class Migration(SchemaMigration):
def forwards(self, orm):
-
+
# Adding unique constraint on 'Invitation', fields ['username']
db.create_unique('im_invitation', ['username'])
-
def backwards(self, orm):
-
+
# Removing unique constraint on 'Invitation', fields ['username']
db.delete_unique('im_invitation', ['username'])
-
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
# encoding: utf-8
-import datetime
from south.db import db
from south.v2 import SchemaMigration
-from django.db import models
+
class Migration(SchemaMigration):
def forwards(self, orm):
-
+
# Adding field 'AstakosUser.email_verified'
db.add_column('im_astakosuser', 'email_verified', self.gf('django.db.models.fields.BooleanField')(default=False), keep_default=False)
-
def backwards(self, orm):
-
+
# Deleting field 'AstakosUser.email_verified'
db.delete_column('im_astakosuser', 'email_verified')
-
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
# encoding: utf-8
-import datetime
from south.db import db
from south.v2 import SchemaMigration
-from django.db import models
+
class Migration(SchemaMigration):
def forwards(self, orm):
-
+
# Adding field 'AstakosUser.has_credits'
db.add_column('im_astakosuser', 'has_credits', self.gf('django.db.models.fields.BooleanField')(default=False), keep_default=False)
-
def backwards(self, orm):
-
+
# Deleting field 'AstakosUser.has_credits'
db.delete_column('im_astakosuser', 'has_credits')
-
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
import datetime
from south.db import db
from south.v2 import SchemaMigration
-from django.db import models
+
class Migration(SchemaMigration):
def forwards(self, orm):
-
+
# Adding model 'ApprovalTerms'
db.create_table('im_approvalterms', (
- ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('id', self.gf(
+ 'django.db.models.fields.AutoField')(primary_key=True)),
('date', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime(2012, 3, 20, 14, 24, 30, 616341), db_index=True)),
- ('location', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('location', self.gf(
+ 'django.db.models.fields.CharField')(max_length=255)),
))
db.send_create_signal('im', ['ApprovalTerms'])
# Adding field 'AstakosUser.date_signed_terms'
db.add_column('im_astakosuser', 'date_signed_terms', self.gf('django.db.models.fields.DateTimeField')(null=True), keep_default=False)
-
def backwards(self, orm):
-
+
# Deleting model 'ApprovalTerms'
db.delete_table('im_approvalterms')
# Deleting field 'AstakosUser.date_signed_terms'
db.delete_column('im_astakosuser', 'date_signed_terms')
-
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
# encoding: utf-8
-import datetime
from south.db import db
from south.v2 import SchemaMigration
-from django.db import models
+
class Migration(SchemaMigration):
-
+
def forwards(self, orm):
-
+
# Changing field 'AstakosUser.email_verified'
- db.alter_column('im_astakosuser', 'email_verified', self.gf('django.db.models.fields.BooleanField')(blank=True))
+ db.alter_column('im_astakosuser', 'email_verified', self.gf(
+ 'django.db.models.fields.BooleanField')(blank=True))
# Changing field 'AstakosUser.has_credits'
- db.alter_column('im_astakosuser', 'has_credits', self.gf('django.db.models.fields.BooleanField')(blank=True))
+ db.alter_column('im_astakosuser', 'has_credits', self.gf(
+ 'django.db.models.fields.BooleanField')(blank=True))
# Changing field 'AstakosUser.date_signed_terms'
- db.alter_column('im_astakosuser', 'date_signed_terms', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True))
+ db.alter_column('im_astakosuser', 'date_signed_terms', self.gf(
+ 'django.db.models.fields.DateTimeField')(null=True, blank=True))
# Changing field 'AstakosUser.is_verified'
- db.alter_column('im_astakosuser', 'is_verified', self.gf('django.db.models.fields.BooleanField')(blank=True))
+ db.alter_column('im_astakosuser', 'is_verified', self.gf(
+ 'django.db.models.fields.BooleanField')(blank=True))
# Changing field 'AstakosUser.has_signed_terms'
- db.alter_column('im_astakosuser', 'has_signed_terms', self.gf('django.db.models.fields.BooleanField')(blank=True))
+ db.alter_column('im_astakosuser', 'has_signed_terms', self.gf(
+ 'django.db.models.fields.BooleanField')(blank=True))
# Changing field 'Invitation.is_accepted'
- db.alter_column('im_invitation', 'is_accepted', self.gf('django.db.models.fields.BooleanField')(blank=True))
+ db.alter_column('im_invitation', 'is_accepted', self.gf(
+ 'django.db.models.fields.BooleanField')(blank=True))
# Changing field 'Invitation.is_consumed'
- db.alter_column('im_invitation', 'is_consumed', self.gf('django.db.models.fields.BooleanField')(blank=True))
-
-
+ db.alter_column('im_invitation', 'is_consumed', self.gf(
+ 'django.db.models.fields.BooleanField')(blank=True))
+
def backwards(self, orm):
-
+
# Changing field 'AstakosUser.email_verified'
- db.alter_column('im_astakosuser', 'email_verified', self.gf('django.db.models.fields.BooleanField')())
+ db.alter_column('im_astakosuser', 'email_verified',
+ self.gf('django.db.models.fields.BooleanField')())
# Changing field 'AstakosUser.has_credits'
- db.alter_column('im_astakosuser', 'has_credits', self.gf('django.db.models.fields.BooleanField')())
+ db.alter_column('im_astakosuser', 'has_credits', self.gf(
+ 'django.db.models.fields.BooleanField')())
# Changing field 'AstakosUser.date_signed_terms'
- db.alter_column('im_astakosuser', 'date_signed_terms', self.gf('django.db.models.fields.DateTimeField')(null=True))
+ db.alter_column('im_astakosuser', 'date_signed_terms', self.gf(
+ 'django.db.models.fields.DateTimeField')(null=True))
# Changing field 'AstakosUser.is_verified'
- db.alter_column('im_astakosuser', 'is_verified', self.gf('django.db.models.fields.BooleanField')())
+ db.alter_column('im_astakosuser', 'is_verified', self.gf(
+ 'django.db.models.fields.BooleanField')())
# Changing field 'AstakosUser.has_signed_terms'
- db.alter_column('im_astakosuser', 'has_signed_terms', self.gf('django.db.models.fields.BooleanField')())
+ db.alter_column('im_astakosuser', 'has_signed_terms',
+ self.gf('django.db.models.fields.BooleanField')())
# Changing field 'Invitation.is_accepted'
- db.alter_column('im_invitation', 'is_accepted', self.gf('django.db.models.fields.BooleanField')())
+ db.alter_column('im_invitation', 'is_accepted', self.gf(
+ 'django.db.models.fields.BooleanField')())
# Changing field 'Invitation.is_consumed'
- db.alter_column('im_invitation', 'is_consumed', self.gf('django.db.models.fields.BooleanField')())
-
-
+ db.alter_column('im_invitation', 'is_consumed', self.gf(
+ 'django.db.models.fields.BooleanField')())
+
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'})
}
}
-
+
complete_apps = ['im']
from south.v2 import SchemaMigration
from django.db import models
+
class Migration(SchemaMigration):
def forwards(self, orm):
-
+
# Deleting field 'Invitation.accepted'
db.delete_column('im_invitation', 'accepted')
# Deleting field 'Invitation.is_accepted'
db.delete_column('im_invitation', 'is_accepted')
-
def backwards(self, orm):
-
+
# Adding field 'Invitation.accepted'
db.add_column('im_invitation', 'accepted', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True), keep_default=False)
# Adding field 'Invitation.is_accepted'
db.add_column('im_invitation', 'is_accepted', self.gf('django.db.models.fields.BooleanField')(default=False), keep_default=False)
-
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
import datetime
from south.db import db
from south.v2 import SchemaMigration
-from django.db import models
+
class Migration(SchemaMigration):
def forwards(self, orm):
-
+
# Adding model 'EmailChange'
db.create_table('im_emailchange', (
- ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
- ('new_email_address', self.gf('django.db.models.fields.EmailField')(max_length=75)),
+ ('id', self.gf(
+ 'django.db.models.fields.AutoField')(primary_key=True)),
+ ('new_email_address', self.gf(
+ 'django.db.models.fields.EmailField')(max_length=75)),
('user', self.gf('django.db.models.fields.related.ForeignKey')(related_name='emailchange_user', unique=True, to=orm['im.AstakosUser'])),
- ('requested_at', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime(2012, 5, 3, 12, 23, 46, 711119))),
- ('activation_key', self.gf('django.db.models.fields.CharField')(unique=True, max_length=40, db_index=True)),
+ ('requested_at', self.gf('django.db.models.fields.DateTimeField')
+ (default=datetime.datetime(2012, 5, 3, 12, 23, 46, 711119))),
+ ('activation_key', self.gf('django.db.models.fields.CharField')
+ (unique=True, max_length=40, db_index=True)),
))
db.send_create_signal('im', ['EmailChange'])
-
def backwards(self, orm):
-
+
# Deleting model 'EmailChange'
db.delete_table('im_emailchange')
-
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
# encoding: utf-8
-import datetime
from south.db import db
from south.v2 import SchemaMigration
-from django.db import models
+
class Migration(SchemaMigration):
def forwards(self, orm):
-
+
# Adding model 'Service'
db.create_table('im_service', (
- ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
- ('name', self.gf('django.db.models.fields.CharField')(unique=True, max_length=255)),
- ('url', self.gf('django.db.models.fields.URLField')(max_length=200)),
- ('icon', self.gf('django.db.models.fields.FilePathField')(max_length=100)),
- ('auth_token', self.gf('django.db.models.fields.CharField')(max_length=32, null=True, blank=True)),
- ('auth_token_created', self.gf('django.db.models.fields.DateTimeField')(null=True)),
- ('auth_token_expires', self.gf('django.db.models.fields.DateTimeField')(null=True)),
+ ('id', self.gf(
+ 'django.db.models.fields.AutoField')(primary_key=True)),
+ ('name', self.gf('django.db.models.fields.CharField')
+ (unique=True, max_length=255)),
+ ('url', self.gf('django.db.models.fields.URLField')
+ (max_length=200)),
+ ('icon', self.gf(
+ 'django.db.models.fields.FilePathField')(max_length=100)),
+ ('auth_token', self.gf('django.db.models.fields.CharField')
+ (max_length=32, null=True, blank=True)),
+ ('auth_token_created', self.gf(
+ 'django.db.models.fields.DateTimeField')(null=True)),
+ ('auth_token_expires', self.gf(
+ 'django.db.models.fields.DateTimeField')(null=True)),
))
db.send_create_signal('im', ['Service'])
-
def backwards(self, orm):
-
+
# Deleting model 'Service'
db.delete_table('im_service')
-
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
# encoding: utf-8
-import datetime
from south.db import db
from south.v2 import SchemaMigration
-from django.db import models
+
class Migration(SchemaMigration):
def forwards(self, orm):
-
+
# Adding field 'AstakosUser.activation_sent'
db.add_column('im_astakosuser', 'activation_sent', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True), keep_default=False)
# Changing field 'Service.url'
- db.alter_column('im_service', 'url', self.gf('django.db.models.fields.FilePathField')(max_length=100))
-
+ db.alter_column('im_service', 'url', self.gf(
+ 'django.db.models.fields.FilePathField')(max_length=100))
def backwards(self, orm):
-
+
# Deleting field 'AstakosUser.activation_sent'
db.delete_column('im_astakosuser', 'activation_sent')
# Changing field 'Service.url'
- db.alter_column('im_service', 'url', self.gf('django.db.models.fields.URLField')(max_length=200))
-
+ db.alter_column('im_service', 'url', self.gf(
+ 'django.db.models.fields.URLField')(max_length=200))
models = {
'auth.group': {
# encoding: utf-8
import datetime
-from south.db import db
from south.v2 import DataMigration
-from django.db import models
+
class Migration(DataMigration):
# encoding: utf-8
-import datetime
from south.db import db
from south.v2 import SchemaMigration
-from django.db import models
+
class Migration(SchemaMigration):
def forwards(self, orm):
-
+
# Adding model 'AdditionalMail'
db.create_table('im_additionalmail', (
- ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
- ('owner', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['im.AstakosUser'])),
- ('email', self.gf('django.db.models.fields.EmailField')(unique=True, max_length=75)),
+ ('id', self.gf(
+ 'django.db.models.fields.AutoField')(primary_key=True)),
+ ('owner', self.gf('django.db.models.fields.related.ForeignKey')
+ (to=orm['im.AstakosUser'])),
+ ('email', self.gf('django.db.models.fields.EmailField')
+ (unique=True, max_length=75)),
))
db.send_create_signal('im', ['AdditionalMail'])
-
def backwards(self, orm):
-
+
# Deleting model 'AdditionalMail'
db.delete_table('im_additionalmail')
-
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
# encoding: utf-8
-import datetime
from south.db import db
from south.v2 import SchemaMigration
-from django.db import models
+
class Migration(SchemaMigration):
def forwards(self, orm):
-
+
# Removing unique constraint on 'AdditionalMail', fields ['email']
db.delete_unique('im_additionalmail', ['email'])
-
def backwards(self, orm):
-
+
# Adding unique constraint on 'AdditionalMail', fields ['email']
db.create_unique('im_additionalmail', ['email'])
-
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
# encoding: utf-8
-import datetime
from south.db import db
from south.v2 import SchemaMigration
-from django.db import models
+
class Migration(SchemaMigration):
def forwards(self, orm):
-
- # Adding unique constraint on 'AstakosUser', fields ['third_party_identifier', 'provider']
- db.create_unique('im_astakosuser', ['third_party_identifier', 'provider'])
+ # Adding unique constraint on 'AstakosUser', fields ['third_party_identifier', 'provider']
+ db.create_unique(
+ 'im_astakosuser', ['third_party_identifier', 'provider'])
def backwards(self, orm):
-
- # Removing unique constraint on 'AstakosUser', fields ['third_party_identifier', 'provider']
- db.delete_unique('im_astakosuser', ['third_party_identifier', 'provider'])
+ # Removing unique constraint on 'AstakosUser', fields ['third_party_identifier', 'provider']
+ db.delete_unique(
+ 'im_astakosuser', ['third_party_identifier', 'provider'])
models = {
'auth.group': {
--- /dev/null
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Adding model 'GroupKind'
+ db.create_table('im_groupkind', (
+ ('id', self.gf(
+ 'django.db.models.fields.AutoField')(primary_key=True)),
+ ('name', self.gf('django.db.models.fields.CharField')
+ (unique=True, max_length=255, db_index=True)),
+ ))
+ db.send_create_signal('im', ['GroupKind'])
+
+ # Adding model 'AstakosGroup'
+ db.create_table('im_astakosgroup', (
+ ('group_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['auth.Group'], unique=True, primary_key=True)),
+ ('kind', self.gf('django.db.models.fields.related.ForeignKey')
+ (to=orm['im.GroupKind'])),
+ ('desc', self.gf('django.db.models.fields.TextField')(null=True)),
+ ('creation_date', self.gf('django.db.models.fields.DateTimeField')
+ (default=datetime.datetime(2012, 8, 3, 11, 26, 47, 642626))),
+ ('issue_date', self.gf(
+ 'django.db.models.fields.DateTimeField')(null=True)),
+ ('expiration_date', self.gf(
+ 'django.db.models.fields.DateTimeField')(null=True)),
+ ('moderation_enabled', self.gf(
+ 'django.db.models.fields.BooleanField')(default=False)),
+ ('approval_date', self.gf('django.db.models.fields.DateTimeField')
+ (null=True, blank=True)),
+ ('estimated_participants', self.gf(
+ 'django.db.models.fields.PositiveIntegerField')(null=True)),
+ ))
+ db.send_create_signal('im', ['AstakosGroup'])
+
+ # Adding model 'ResourceMetadata'
+ db.create_table('im_resourcemetadata', (
+ ('id', self.gf(
+ 'django.db.models.fields.AutoField')(primary_key=True)),
+ ('key', self.gf('django.db.models.fields.CharField')
+ (unique=True, max_length=255, db_index=True)),
+ ('value', self.gf(
+ 'django.db.models.fields.CharField')(max_length=255)),
+ ))
+ db.send_create_signal('im', ['ResourceMetadata'])
+
+ # Adding model 'AstakosGroupQuota'
+ db.create_table('im_astakosgroupquota', (
+ ('id', self.gf(
+ 'django.db.models.fields.AutoField')(primary_key=True)),
+ ('limit', self.gf(
+ 'django.db.models.fields.PositiveIntegerField')()),
+ ('resource', self.gf('django.db.models.fields.related.ForeignKey')
+ (to=orm['im.Resource'])),
+ ('group', self.gf('django.db.models.fields.related.ForeignKey')
+ (to=orm['im.AstakosGroup'], blank=True)),
+ ))
+ db.send_create_signal('im', ['AstakosGroupQuota'])
+
+ # Adding unique constraint on 'AstakosGroupQuota', fields ['resource', 'group']
+ db.create_unique('im_astakosgroupquota', ['resource_id', 'group_id'])
+
+ # Adding model 'Resource'
+ db.create_table('im_resource', (
+ ('id', self.gf(
+ 'django.db.models.fields.AutoField')(primary_key=True)),
+ ('name', self.gf('django.db.models.fields.CharField')
+ (unique=True, max_length=255, db_index=True)),
+ ('service', self.gf('django.db.models.fields.related.ForeignKey')
+ (to=orm['im.Service'])),
+ ))
+ db.send_create_signal('im', ['Resource'])
+
+ # Adding M2M table for field meta on 'Resource'
+ db.create_table('im_resource_meta', (
+ ('id', models.AutoField(
+ verbose_name='ID', primary_key=True, auto_created=True)),
+ ('resource', models.ForeignKey(orm['im.resource'], null=False)),
+ ('resourcemetadata', models.ForeignKey(orm[
+ 'im.resourcemetadata'], null=False))
+ ))
+ db.create_unique(
+ 'im_resource_meta', ['resource_id', 'resourcemetadata_id'])
+
+ # Adding model 'Membership'
+ db.create_table('im_membership', (
+ ('id', self.gf(
+ 'django.db.models.fields.AutoField')(primary_key=True)),
+ ('person', self.gf('django.db.models.fields.related.ForeignKey')
+ (to=orm['im.AstakosUser'])),
+ ('group', self.gf('django.db.models.fields.related.ForeignKey')
+ (to=orm['im.AstakosGroup'])),
+ ('date_requested', self.gf('django.db.models.fields.DateField')
+ (default=datetime.datetime(2012, 8, 3, 11, 26, 47, 646518))),
+ ('date_joined', self.gf('django.db.models.fields.DateField')
+ (null=True, db_index=True)),
+ ))
+ db.send_create_signal('im', ['Membership'])
+
+ # Adding unique constraint on 'Membership', fields ['person', 'group']
+ db.create_unique('im_membership', ['person_id', 'group_id'])
+
+ # Adding model 'AstakosUserQuota'
+ db.create_table('im_astakosuserquota', (
+ ('id', self.gf(
+ 'django.db.models.fields.AutoField')(primary_key=True)),
+ ('limit', self.gf(
+ 'django.db.models.fields.PositiveIntegerField')()),
+ ('resource', self.gf('django.db.models.fields.related.ForeignKey')
+ (to=orm['im.Resource'])),
+ ('user', self.gf('django.db.models.fields.related.ForeignKey')
+ (to=orm['im.AstakosUser'])),
+ ))
+ db.send_create_signal('im', ['AstakosUserQuota'])
+
+ # Adding unique constraint on 'AstakosUserQuota', fields ['resource', 'user']
+ db.create_unique('im_astakosuserquota', ['resource_id', 'user_id'])
+
+ # Adding index on 'Service', fields ['name']
+ db.create_index('im_service', ['name'])
+
+ # Adding M2M table for field owner on 'AstakosUser'
+ db.create_table('im_astakosuser_owner', (
+ ('id', models.AutoField(
+ verbose_name='ID', primary_key=True, auto_created=True)),
+ ('astakosuser', models.ForeignKey(orm[
+ 'im.astakosuser'], null=False)),
+ ('astakosgroup', models.ForeignKey(orm[
+ 'im.astakosgroup'], null=False))
+ ))
+ db.create_unique(
+ 'im_astakosuser_owner', ['astakosuser_id', 'astakosgroup_id'])
+
+ def backwards(self, orm):
+
+ # Removing index on 'Service', fields ['name']
+ db.delete_index('im_service', ['name'])
+
+ # Removing unique constraint on 'AstakosUserQuota', fields ['resource', 'user']
+ db.delete_unique('im_astakosuserquota', ['resource_id', 'user_id'])
+
+ # Removing unique constraint on 'Membership', fields ['person', 'group']
+ db.delete_unique('im_membership', ['person_id', 'group_id'])
+
+ # Removing unique constraint on 'AstakosGroupQuota', fields ['resource', 'group']
+ db.delete_unique('im_astakosgroupquota', ['resource_id', 'group_id'])
+
+ # Deleting model 'GroupKind'
+ db.delete_table('im_groupkind')
+
+ # Deleting model 'AstakosGroup'
+ db.delete_table('im_astakosgroup')
+
+ # Deleting model 'ResourceMetadata'
+ db.delete_table('im_resourcemetadata')
+
+ # Deleting model 'AstakosGroupQuota'
+ db.delete_table('im_astakosgroupquota')
+
+ # Deleting model 'Resource'
+ db.delete_table('im_resource')
+
+ # Removing M2M table for field meta on 'Resource'
+ db.delete_table('im_resource_meta')
+
+ # Deleting model 'Membership'
+ db.delete_table('im_membership')
+
+ # Deleting model 'AstakosUserQuota'
+ db.delete_table('im_astakosuserquota')
+
+ # Removing M2M table for field owner on 'AstakosUser'
+ db.delete_table('im_astakosuser_owner')
+
+ models = {
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'im.additionalmail': {
+ 'Meta': {'object_name': 'AdditionalMail'},
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"})
+ },
+ 'im.approvalterms': {
+ 'Meta': {'object_name': 'ApprovalTerms'},
+ 'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 8, 3, 11, 26, 47, 648667)', 'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'location': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'im.astakosgroup': {
+ 'Meta': {'object_name': 'AstakosGroup', '_ormbases': ['auth.Group']},
+ 'approval_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 8, 3, 11, 26, 47, 642626)'}),
+ 'desc': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'estimated_participants': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'expiration_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'group_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.Group']", 'unique': 'True', 'primary_key': 'True'}),
+ 'issue_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'kind': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.GroupKind']"}),
+ 'moderation_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'policy': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['im.Resource']", 'null': 'True', 'through': "orm['im.AstakosGroupQuota']", 'blank': 'True'})
+ },
+ 'im.astakosgroupquota': {
+ 'Meta': {'unique_together': "(('resource', 'group'),)", 'object_name': 'AstakosGroupQuota'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosGroup']", 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'limit': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'resource': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Resource']"})
+ },
+ 'im.astakosuser': {
+ 'Meta': {'unique_together': "(('provider', 'third_party_identifier'),)", 'object_name': 'AstakosUser', '_ormbases': ['auth.User']},
+ 'activation_sent': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'astakos_groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['im.AstakosGroup']", 'symmetrical': 'False', 'through': "orm['im.Membership']", 'blank': 'True'}),
+ 'auth_token': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
+ 'auth_token_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'auth_token_expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'date_signed_terms': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'email_verified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'has_credits': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'has_signed_terms': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'invitations': ('django.db.models.fields.IntegerField', [], {'default': '100'}),
+ 'is_verified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'level': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'owner': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'owner'", 'null': 'True', 'to': "orm['im.AstakosGroup']"}),
+ 'policy': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['im.Resource']", 'null': 'True', 'through': "orm['im.AstakosUserQuota']", 'symmetrical': 'False'}),
+ 'provider': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'third_party_identifier': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
+ 'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'})
+ },
+ 'im.astakosuserquota': {
+ 'Meta': {'unique_together': "(('resource', 'user'),)", 'object_name': 'AstakosUserQuota'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'limit': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'resource': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Resource']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"})
+ },
+ 'im.emailchange': {
+ 'Meta': {'object_name': 'EmailChange'},
+ 'activation_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40', 'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'new_email_address': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+ 'requested_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 8, 3, 11, 26, 47, 650373)'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'emailchange_user'", 'unique': 'True', 'to': "orm['im.AstakosUser']"})
+ },
+ 'im.groupkind': {
+ 'Meta': {'object_name': 'GroupKind'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'})
+ },
+ 'im.invitation': {
+ 'Meta': {'object_name': 'Invitation'},
+ 'code': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'consumed': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'inviter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'invitations_sent'", 'null': 'True', 'to': "orm['im.AstakosUser']"}),
+ 'is_consumed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'realname': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'})
+ },
+ 'im.membership': {
+ 'Meta': {'unique_together': "(('person', 'group'),)", 'object_name': 'Membership'},
+ 'date_joined': ('django.db.models.fields.DateField', [], {'null': 'True', 'db_index': 'True'}),
+ 'date_requested': ('django.db.models.fields.DateField', [], {'default': 'datetime.datetime(2012, 8, 3, 11, 26, 47, 646518)'}),
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosGroup']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"})
+ },
+ 'im.resource': {
+ 'Meta': {'object_name': 'Resource'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'meta': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['im.ResourceMetadata']", 'symmetrical': 'False'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
+ 'service': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Service']"})
+ },
+ 'im.resourcemetadata': {
+ 'Meta': {'object_name': 'ResourceMetadata'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
+ 'value': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'im.service': {
+ 'Meta': {'object_name': 'Service'},
+ 'auth_token': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
+ 'auth_token_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'auth_token_expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'icon': ('django.db.models.fields.FilePathField', [], {'max_length': '100', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
+ 'url': ('django.db.models.fields.FilePathField', [], {'max_length': '100'})
+ }
+ }
+
+ complete_apps = ['im']
--- /dev/null
+# encoding: utf-8
+
+from south.v2 import DataMigration
+
+
+class Migration(DataMigration):
+
+ def forwards(self, orm):
+ "Obsolete migration."
+ return
+
+ def backwards(self, orm):
+ "Obsolete migration."
+ return
+
+ models = {
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'im.additionalmail': {
+ 'Meta': {'object_name': 'AdditionalMail'},
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"})
+ },
+ 'im.approvalterms': {
+ 'Meta': {'object_name': 'ApprovalTerms'},
+ 'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 8, 9, 11, 14, 9, 289091)', 'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'location': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'im.astakosgroup': {
+ 'Meta': {'object_name': 'AstakosGroup', '_ormbases': ['auth.Group']},
+ 'approval_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 8, 9, 11, 14, 9, 283154)'}),
+ 'desc': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'estimated_participants': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'expiration_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'group_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.Group']", 'unique': 'True', 'primary_key': 'True'}),
+ 'issue_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'kind': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.GroupKind']"}),
+ 'moderation_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'policy': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['im.Resource']", 'null': 'True', 'through': "orm['im.AstakosGroupQuota']", 'blank': 'True'})
+ },
+ 'im.astakosgroupquota': {
+ 'Meta': {'unique_together': "(('resource', 'group'),)", 'object_name': 'AstakosGroupQuota'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosGroup']", 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'limit': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'resource': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Resource']"})
+ },
+ 'im.astakosuser': {
+ 'Meta': {'unique_together': "(('provider', 'third_party_identifier'),)", 'object_name': 'AstakosUser', '_ormbases': ['auth.User']},
+ 'activation_sent': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'astakos_groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['im.AstakosGroup']", 'symmetrical': 'False', 'through': "orm['im.Membership']", 'blank': 'True'}),
+ 'auth_token': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
+ 'auth_token_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'auth_token_expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'date_signed_terms': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'email_verified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'has_credits': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'has_signed_terms': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'invitations': ('django.db.models.fields.IntegerField', [], {'default': '100'}),
+ 'is_verified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'level': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'owner': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'owner'", 'null': 'True', 'to': "orm['im.AstakosGroup']"}),
+ 'policy': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['im.Resource']", 'null': 'True', 'through': "orm['im.AstakosUserQuota']", 'symmetrical': 'False'}),
+ 'provider': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'third_party_identifier': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
+ 'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'})
+ },
+ 'im.astakosuserquota': {
+ 'Meta': {'unique_together': "(('resource', 'user'),)", 'object_name': 'AstakosUserQuota'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'limit': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'resource': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Resource']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"})
+ },
+ 'im.emailchange': {
+ 'Meta': {'object_name': 'EmailChange'},
+ 'activation_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40', 'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'new_email_address': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+ 'requested_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 8, 9, 11, 14, 9, 290713)'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'emailchange_user'", 'unique': 'True', 'to': "orm['im.AstakosUser']"})
+ },
+ 'im.groupkind': {
+ 'Meta': {'object_name': 'GroupKind'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'})
+ },
+ 'im.invitation': {
+ 'Meta': {'object_name': 'Invitation'},
+ 'code': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'consumed': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'inviter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'invitations_sent'", 'null': 'True', 'to': "orm['im.AstakosUser']"}),
+ 'is_consumed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'realname': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'})
+ },
+ 'im.membership': {
+ 'Meta': {'unique_together': "(('person', 'group'),)", 'object_name': 'Membership'},
+ 'date_joined': ('django.db.models.fields.DateField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+ 'date_requested': ('django.db.models.fields.DateField', [], {'default': 'datetime.datetime(2012, 8, 9, 11, 14, 9, 286925)', 'blank': 'True'}),
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosGroup']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"})
+ },
+ 'im.resource': {
+ 'Meta': {'object_name': 'Resource'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'meta': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['im.ResourceMetadata']", 'symmetrical': 'False'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
+ 'service': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Service']"})
+ },
+ 'im.resourcemetadata': {
+ 'Meta': {'object_name': 'ResourceMetadata'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
+ 'value': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'im.service': {
+ 'Meta': {'object_name': 'Service'},
+ 'auth_token': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
+ 'auth_token_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'auth_token_expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'icon': ('django.db.models.fields.FilePathField', [], {'max_length': '100', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
+ 'url': ('django.db.models.fields.FilePathField', [], {'max_length': '100'})
+ }
+ }
+
+ complete_apps = ['im']
--- /dev/null
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Adding field 'AstakosGroup.homepage'
+ db.add_column('im_astakosgroup', 'homepage', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True), keep_default=False)
+
+ def backwards(self, orm):
+
+ # Deleting field 'AstakosGroup.homepage'
+ db.delete_column('im_astakosgroup', 'homepage')
+
+ models = {
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'im.additionalmail': {
+ 'Meta': {'object_name': 'AdditionalMail'},
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"})
+ },
+ 'im.approvalterms': {
+ 'Meta': {'object_name': 'ApprovalTerms'},
+ 'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 9, 6, 16, 11, 52, 429294)', 'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'location': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'im.astakosgroup': {
+ 'Meta': {'object_name': 'AstakosGroup', '_ormbases': ['auth.Group']},
+ 'approval_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 9, 6, 16, 11, 52, 423356)'}),
+ 'desc': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'estimated_participants': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'expiration_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'group_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.Group']", 'unique': 'True', 'primary_key': 'True'}),
+ 'homepage': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'issue_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'kind': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.GroupKind']"}),
+ 'moderation_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'policy': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['im.Resource']", 'null': 'True', 'through': "orm['im.AstakosGroupQuota']", 'blank': 'True'})
+ },
+ 'im.astakosgroupquota': {
+ 'Meta': {'unique_together': "(('resource', 'group'),)", 'object_name': 'AstakosGroupQuota'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosGroup']", 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'limit': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'resource': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Resource']"})
+ },
+ 'im.astakosuser': {
+ 'Meta': {'unique_together': "(('provider', 'third_party_identifier'),)", 'object_name': 'AstakosUser', '_ormbases': ['auth.User']},
+ 'activation_sent': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'astakos_groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['im.AstakosGroup']", 'symmetrical': 'False', 'through': "orm['im.Membership']", 'blank': 'True'}),
+ 'auth_token': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
+ 'auth_token_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'auth_token_expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'date_signed_terms': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'email_verified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'has_credits': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'has_signed_terms': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'invitations': ('django.db.models.fields.IntegerField', [], {'default': '100'}),
+ 'is_verified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'level': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'owner': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'owner'", 'null': 'True', 'to': "orm['im.AstakosGroup']"}),
+ 'policy': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['im.Resource']", 'null': 'True', 'through': "orm['im.AstakosUserQuota']", 'symmetrical': 'False'}),
+ 'provider': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'third_party_identifier': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
+ 'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'})
+ },
+ 'im.astakosuserquota': {
+ 'Meta': {'unique_together': "(('resource', 'user'),)", 'object_name': 'AstakosUserQuota'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'limit': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'resource': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Resource']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"})
+ },
+ 'im.emailchange': {
+ 'Meta': {'object_name': 'EmailChange'},
+ 'activation_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40', 'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'new_email_address': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+ 'requested_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 9, 6, 16, 11, 52, 430810)'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'emailchange_user'", 'unique': 'True', 'to': "orm['im.AstakosUser']"})
+ },
+ 'im.groupkind': {
+ 'Meta': {'object_name': 'GroupKind'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'})
+ },
+ 'im.invitation': {
+ 'Meta': {'object_name': 'Invitation'},
+ 'code': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'consumed': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'inviter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'invitations_sent'", 'null': 'True', 'to': "orm['im.AstakosUser']"}),
+ 'is_consumed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'realname': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'})
+ },
+ 'im.membership': {
+ 'Meta': {'unique_together': "(('person', 'group'),)", 'object_name': 'Membership'},
+ 'date_joined': ('django.db.models.fields.DateField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+ 'date_requested': ('django.db.models.fields.DateField', [], {'default': 'datetime.datetime(2012, 9, 6, 16, 11, 52, 427324)', 'blank': 'True'}),
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosGroup']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"})
+ },
+ 'im.resource': {
+ 'Meta': {'object_name': 'Resource'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'meta': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['im.ResourceMetadata']", 'symmetrical': 'False'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
+ 'service': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Service']"})
+ },
+ 'im.resourcemetadata': {
+ 'Meta': {'object_name': 'ResourceMetadata'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
+ 'value': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'im.service': {
+ 'Meta': {'object_name': 'Service'},
+ 'auth_token': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
+ 'auth_token_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'auth_token_expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'icon': ('django.db.models.fields.FilePathField', [], {'max_length': '100', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
+ 'url': ('django.db.models.fields.FilePathField', [], {'max_length': '100'})
+ }
+ }
+
+ complete_apps = ['im']
--- /dev/null
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Changing field 'AstakosGroup.homepage'
+ db.alter_column('im_astakosgroup', 'homepage', self.gf(
+ 'django.db.models.fields.URLField')(max_length=255, null=True))
+
+ def backwards(self, orm):
+
+ # Changing field 'AstakosGroup.homepage'
+ db.alter_column('im_astakosgroup', 'homepage', self.gf(
+ 'django.db.models.fields.CharField')(max_length=255, null=True))
+
+ models = {
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'im.additionalmail': {
+ 'Meta': {'object_name': 'AdditionalMail'},
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"})
+ },
+ 'im.approvalterms': {
+ 'Meta': {'object_name': 'ApprovalTerms'},
+ 'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 9, 18, 14, 54, 13, 70454)', 'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'location': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'im.astakosgroup': {
+ 'Meta': {'object_name': 'AstakosGroup', '_ormbases': ['auth.Group']},
+ 'approval_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 9, 18, 14, 54, 13, 64622)'}),
+ 'desc': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'estimated_participants': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'expiration_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'group_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.Group']", 'unique': 'True', 'primary_key': 'True'}),
+ 'homepage': ('django.db.models.fields.URLField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'issue_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'kind': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.GroupKind']"}),
+ 'moderation_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'policy': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['im.Resource']", 'null': 'True', 'through': "orm['im.AstakosGroupQuota']", 'blank': 'True'})
+ },
+ 'im.astakosgroupquota': {
+ 'Meta': {'unique_together': "(('resource', 'group'),)", 'object_name': 'AstakosGroupQuota'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosGroup']", 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'limit': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'resource': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Resource']"})
+ },
+ 'im.astakosuser': {
+ 'Meta': {'unique_together': "(('provider', 'third_party_identifier'),)", 'object_name': 'AstakosUser', '_ormbases': ['auth.User']},
+ 'activation_sent': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'astakos_groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['im.AstakosGroup']", 'symmetrical': 'False', 'through': "orm['im.Membership']", 'blank': 'True'}),
+ 'auth_token': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
+ 'auth_token_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'auth_token_expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'date_signed_terms': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'email_verified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'has_credits': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'has_signed_terms': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'invitations': ('django.db.models.fields.IntegerField', [], {'default': '100'}),
+ 'is_verified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'level': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'owner': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'owner'", 'null': 'True', 'to': "orm['im.AstakosGroup']"}),
+ 'policy': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['im.Resource']", 'null': 'True', 'through': "orm['im.AstakosUserQuota']", 'symmetrical': 'False'}),
+ 'provider': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'third_party_identifier': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
+ 'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'})
+ },
+ 'im.astakosuserquota': {
+ 'Meta': {'unique_together': "(('resource', 'user'),)", 'object_name': 'AstakosUserQuota'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'limit': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'resource': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Resource']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"})
+ },
+ 'im.emailchange': {
+ 'Meta': {'object_name': 'EmailChange'},
+ 'activation_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40', 'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'new_email_address': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+ 'requested_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 9, 18, 14, 54, 13, 72050)'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'emailchange_user'", 'unique': 'True', 'to': "orm['im.AstakosUser']"})
+ },
+ 'im.groupkind': {
+ 'Meta': {'object_name': 'GroupKind'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'})
+ },
+ 'im.invitation': {
+ 'Meta': {'object_name': 'Invitation'},
+ 'code': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'consumed': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'inviter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'invitations_sent'", 'null': 'True', 'to': "orm['im.AstakosUser']"}),
+ 'is_consumed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'realname': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'})
+ },
+ 'im.membership': {
+ 'Meta': {'unique_together': "(('person', 'group'),)", 'object_name': 'Membership'},
+ 'date_joined': ('django.db.models.fields.DateField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+ 'date_requested': ('django.db.models.fields.DateField', [], {'default': 'datetime.datetime(2012, 9, 18, 14, 54, 13, 68464)', 'blank': 'True'}),
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosGroup']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"})
+ },
+ 'im.resource': {
+ 'Meta': {'object_name': 'Resource'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'meta': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['im.ResourceMetadata']", 'symmetrical': 'False'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
+ 'service': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Service']"})
+ },
+ 'im.resourcemetadata': {
+ 'Meta': {'object_name': 'ResourceMetadata'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
+ 'value': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'im.service': {
+ 'Meta': {'object_name': 'Service'},
+ 'auth_token': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
+ 'auth_token_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'auth_token_expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'icon': ('django.db.models.fields.FilePathField', [], {'max_length': '100', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
+ 'url': ('django.db.models.fields.FilePathField', [], {'max_length': '100'})
+ }
+ }
+
+ complete_apps = ['im']
--- /dev/null
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Adding field 'AstakosGroupQuota.uplimit'
+ db.add_column('im_astakosgroupquota', 'uplimit', self.gf('django.db.models.fields.BigIntegerField')(null=True), keep_default=False)
+
+ # Adding field 'AstakosUserQuota.uplimit'
+ db.add_column('im_astakosuserquota', 'uplimit', self.gf('django.db.models.fields.BigIntegerField')(null=True), keep_default=False)
+
+ def backwards(self, orm):
+
+ # Deleting field 'AstakosGroupQuota.uplimit'
+ db.delete_column('im_astakosgroupquota', 'uplimit')
+
+ # Deleting field 'AstakosUserQuota.uplimit'
+ db.delete_column('im_astakosuserquota', 'uplimit')
+
+ models = {
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'im.additionalmail': {
+ 'Meta': {'object_name': 'AdditionalMail'},
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"})
+ },
+ 'im.approvalterms': {
+ 'Meta': {'object_name': 'ApprovalTerms'},
+ 'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 9, 18, 14, 54, 24, 940454)', 'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'location': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'im.astakosgroup': {
+ 'Meta': {'object_name': 'AstakosGroup', '_ormbases': ['auth.Group']},
+ 'approval_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 9, 18, 14, 54, 24, 934376)'}),
+ 'desc': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'estimated_participants': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'expiration_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'group_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.Group']", 'unique': 'True', 'primary_key': 'True'}),
+ 'homepage': ('django.db.models.fields.URLField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'issue_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'kind': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.GroupKind']"}),
+ 'moderation_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'policy': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['im.Resource']", 'null': 'True', 'through': "orm['im.AstakosGroupQuota']", 'blank': 'True'})
+ },
+ 'im.astakosgroupquota': {
+ 'Meta': {'unique_together': "(('resource', 'group'),)", 'object_name': 'AstakosGroupQuota'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosGroup']", 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'limit': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'resource': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Resource']"}),
+ 'uplimit': ('django.db.models.fields.BigIntegerField', [], {'null': 'True'})
+ },
+ 'im.astakosuser': {
+ 'Meta': {'unique_together': "(('provider', 'third_party_identifier'),)", 'object_name': 'AstakosUser', '_ormbases': ['auth.User']},
+ 'activation_sent': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'astakos_groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['im.AstakosGroup']", 'symmetrical': 'False', 'through': "orm['im.Membership']", 'blank': 'True'}),
+ 'auth_token': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
+ 'auth_token_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'auth_token_expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'date_signed_terms': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'email_verified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'has_credits': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'has_signed_terms': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'invitations': ('django.db.models.fields.IntegerField', [], {'default': '100'}),
+ 'is_verified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'level': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'owner': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'owner'", 'null': 'True', 'to': "orm['im.AstakosGroup']"}),
+ 'policy': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['im.Resource']", 'null': 'True', 'through': "orm['im.AstakosUserQuota']", 'symmetrical': 'False'}),
+ 'provider': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'third_party_identifier': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
+ 'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'})
+ },
+ 'im.astakosuserquota': {
+ 'Meta': {'unique_together': "(('resource', 'user'),)", 'object_name': 'AstakosUserQuota'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'limit': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'resource': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Resource']"}),
+ 'uplimit': ('django.db.models.fields.BigIntegerField', [], {'null': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"})
+ },
+ 'im.emailchange': {
+ 'Meta': {'object_name': 'EmailChange'},
+ 'activation_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40', 'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'new_email_address': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+ 'requested_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 9, 18, 14, 54, 24, 942004)'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'emailchange_user'", 'unique': 'True', 'to': "orm['im.AstakosUser']"})
+ },
+ 'im.groupkind': {
+ 'Meta': {'object_name': 'GroupKind'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'})
+ },
+ 'im.invitation': {
+ 'Meta': {'object_name': 'Invitation'},
+ 'code': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'consumed': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'inviter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'invitations_sent'", 'null': 'True', 'to': "orm['im.AstakosUser']"}),
+ 'is_consumed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'realname': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'})
+ },
+ 'im.membership': {
+ 'Meta': {'unique_together': "(('person', 'group'),)", 'object_name': 'Membership'},
+ 'date_joined': ('django.db.models.fields.DateField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+ 'date_requested': ('django.db.models.fields.DateField', [], {'default': 'datetime.datetime(2012, 9, 18, 14, 54, 24, 938314)', 'blank': 'True'}),
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosGroup']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"})
+ },
+ 'im.resource': {
+ 'Meta': {'object_name': 'Resource'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'meta': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['im.ResourceMetadata']", 'symmetrical': 'False'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
+ 'service': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Service']"})
+ },
+ 'im.resourcemetadata': {
+ 'Meta': {'object_name': 'ResourceMetadata'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
+ 'value': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'im.service': {
+ 'Meta': {'object_name': 'Service'},
+ 'auth_token': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
+ 'auth_token_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'auth_token_expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'icon': ('django.db.models.fields.FilePathField', [], {'max_length': '100', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
+ 'url': ('django.db.models.fields.FilePathField', [], {'max_length': '100'})
+ }
+ }
+
+ complete_apps = ['im']
--- /dev/null
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import DataMigration
+from django.db import models
+
+
+class Migration(DataMigration):
+
+ def forwards(self, orm):
+ for q in orm.AstakosGroupQuota.objects.all():
+ q.uplimit = q.limit
+ q.save()
+
+ def backwards(self, orm):
+ return
+
+ models = {
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'im.additionalmail': {
+ 'Meta': {'object_name': 'AdditionalMail'},
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"})
+ },
+ 'im.approvalterms': {
+ 'Meta': {'object_name': 'ApprovalTerms'},
+ 'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 9, 18, 14, 56, 37, 729945)', 'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'location': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'im.astakosgroup': {
+ 'Meta': {'object_name': 'AstakosGroup', '_ormbases': ['auth.Group']},
+ 'approval_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 9, 18, 14, 56, 37, 723535)'}),
+ 'desc': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'estimated_participants': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'expiration_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'group_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.Group']", 'unique': 'True', 'primary_key': 'True'}),
+ 'homepage': ('django.db.models.fields.URLField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'issue_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'kind': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.GroupKind']"}),
+ 'moderation_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'policy': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['im.Resource']", 'null': 'True', 'through': "orm['im.AstakosGroupQuota']", 'blank': 'True'})
+ },
+ 'im.astakosgroupquota': {
+ 'Meta': {'unique_together': "(('resource', 'group'),)", 'object_name': 'AstakosGroupQuota'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosGroup']", 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'limit': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'resource': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Resource']"}),
+ 'uplimit': ('django.db.models.fields.BigIntegerField', [], {'null': 'True'})
+ },
+ 'im.astakosuser': {
+ 'Meta': {'unique_together': "(('provider', 'third_party_identifier'),)", 'object_name': 'AstakosUser', '_ormbases': ['auth.User']},
+ 'activation_sent': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'astakos_groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['im.AstakosGroup']", 'symmetrical': 'False', 'through': "orm['im.Membership']", 'blank': 'True'}),
+ 'auth_token': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
+ 'auth_token_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'auth_token_expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'date_signed_terms': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'email_verified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'has_credits': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'has_signed_terms': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'invitations': ('django.db.models.fields.IntegerField', [], {'default': '100'}),
+ 'is_verified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'level': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'owner': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'owner'", 'null': 'True', 'to': "orm['im.AstakosGroup']"}),
+ 'policy': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['im.Resource']", 'null': 'True', 'through': "orm['im.AstakosUserQuota']", 'symmetrical': 'False'}),
+ 'provider': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'third_party_identifier': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
+ 'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'})
+ },
+ 'im.astakosuserquota': {
+ 'Meta': {'unique_together': "(('resource', 'user'),)", 'object_name': 'AstakosUserQuota'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'limit': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'resource': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Resource']"}),
+ 'uplimit': ('django.db.models.fields.BigIntegerField', [], {'null': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"})
+ },
+ 'im.emailchange': {
+ 'Meta': {'object_name': 'EmailChange'},
+ 'activation_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40', 'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'new_email_address': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+ 'requested_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 9, 18, 14, 56, 37, 731491)'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'emailchange_user'", 'unique': 'True', 'to': "orm['im.AstakosUser']"})
+ },
+ 'im.groupkind': {
+ 'Meta': {'object_name': 'GroupKind'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'})
+ },
+ 'im.invitation': {
+ 'Meta': {'object_name': 'Invitation'},
+ 'code': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'consumed': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'inviter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'invitations_sent'", 'null': 'True', 'to': "orm['im.AstakosUser']"}),
+ 'is_consumed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'realname': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'})
+ },
+ 'im.membership': {
+ 'Meta': {'unique_together': "(('person', 'group'),)", 'object_name': 'Membership'},
+ 'date_joined': ('django.db.models.fields.DateField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+ 'date_requested': ('django.db.models.fields.DateField', [], {'default': 'datetime.datetime(2012, 9, 18, 14, 56, 37, 727517)', 'blank': 'True'}),
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosGroup']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"})
+ },
+ 'im.resource': {
+ 'Meta': {'object_name': 'Resource'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'meta': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['im.ResourceMetadata']", 'symmetrical': 'False'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
+ 'service': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Service']"})
+ },
+ 'im.resourcemetadata': {
+ 'Meta': {'object_name': 'ResourceMetadata'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
+ 'value': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'im.service': {
+ 'Meta': {'object_name': 'Service'},
+ 'auth_token': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
+ 'auth_token_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'auth_token_expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'icon': ('django.db.models.fields.FilePathField', [], {'max_length': '100', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
+ 'url': ('django.db.models.fields.FilePathField', [], {'max_length': '100'})
+ }
+ }
+
+ complete_apps = ['im']
--- /dev/null
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Changing field 'AstakosGroupQuota.limit'
+ db.alter_column('im_astakosgroupquota', 'limit', self.gf(
+ 'django.db.models.fields.PositiveIntegerField')(null=True))
+
+ # Changing field 'AstakosUserQuota.limit'
+ db.alter_column('im_astakosuserquota', 'limit', self.gf(
+ 'django.db.models.fields.PositiveIntegerField')(null=True))
+
+ def backwards(self, orm):
+ pass
+# # Changing field 'AstakosGroupQuota.limit'
+# db.alter_column('im_astakosgroupquota', 'limit', self.gf(
+# 'django.db.models.fields.PositiveIntegerField')(default=None))
+#
+# # Changing field 'AstakosUserQuota.limit'
+# db.alter_column('im_astakosuserquota', 'limit', self.gf(
+# 'django.db.models.fields.PositiveIntegerField')(default=None))
+
+ models = {
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'im.additionalmail': {
+ 'Meta': {'object_name': 'AdditionalMail'},
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"})
+ },
+ 'im.approvalterms': {
+ 'Meta': {'object_name': 'ApprovalTerms'},
+ 'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 10, 2, 10, 33, 53, 42109)', 'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'location': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'im.astakosgroup': {
+ 'Meta': {'object_name': 'AstakosGroup', '_ormbases': ['auth.Group']},
+ 'approval_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 10, 2, 10, 33, 53, 36319)'}),
+ 'desc': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'estimated_participants': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'expiration_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'group_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.Group']", 'unique': 'True', 'primary_key': 'True'}),
+ 'homepage': ('django.db.models.fields.URLField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'issue_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'kind': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.GroupKind']"}),
+ 'moderation_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'policy': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['im.Resource']", 'null': 'True', 'through': "orm['im.AstakosGroupQuota']", 'blank': 'True'})
+ },
+ 'im.astakosgroupquota': {
+ 'Meta': {'unique_together': "(('resource', 'group'),)", 'object_name': 'AstakosGroupQuota'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosGroup']", 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'limit': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'resource': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Resource']"}),
+ 'uplimit': ('django.db.models.fields.BigIntegerField', [], {'null': 'True'})
+ },
+ 'im.astakosuser': {
+ 'Meta': {'unique_together': "(('provider', 'third_party_identifier'),)", 'object_name': 'AstakosUser', '_ormbases': ['auth.User']},
+ 'activation_sent': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'astakos_groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['im.AstakosGroup']", 'symmetrical': 'False', 'through': "orm['im.Membership']", 'blank': 'True'}),
+ 'auth_token': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
+ 'auth_token_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'auth_token_expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'date_signed_terms': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'email_verified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'has_credits': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'has_signed_terms': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'invitations': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'is_verified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'level': ('django.db.models.fields.IntegerField', [], {'default': '4'}),
+ 'owner': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'owner'", 'null': 'True', 'to': "orm['im.AstakosGroup']"}),
+ 'policy': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['im.Resource']", 'null': 'True', 'through': "orm['im.AstakosUserQuota']", 'symmetrical': 'False'}),
+ 'provider': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'third_party_identifier': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
+ 'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'})
+ },
+ 'im.astakosuserquota': {
+ 'Meta': {'unique_together': "(('resource', 'user'),)", 'object_name': 'AstakosUserQuota'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'limit': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'resource': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Resource']"}),
+ 'uplimit': ('django.db.models.fields.BigIntegerField', [], {'null': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"})
+ },
+ 'im.emailchange': {
+ 'Meta': {'object_name': 'EmailChange'},
+ 'activation_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40', 'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'new_email_address': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+ 'requested_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 10, 2, 10, 33, 53, 43584)'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'emailchange_user'", 'unique': 'True', 'to': "orm['im.AstakosUser']"})
+ },
+ 'im.groupkind': {
+ 'Meta': {'object_name': 'GroupKind'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'})
+ },
+ 'im.invitation': {
+ 'Meta': {'object_name': 'Invitation'},
+ 'code': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'consumed': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'inviter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'invitations_sent'", 'null': 'True', 'to': "orm['im.AstakosUser']"}),
+ 'is_consumed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'realname': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'})
+ },
+ 'im.membership': {
+ 'Meta': {'unique_together': "(('person', 'group'),)", 'object_name': 'Membership'},
+ 'date_joined': ('django.db.models.fields.DateField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+ 'date_requested': ('django.db.models.fields.DateField', [], {'default': 'datetime.datetime(2012, 10, 2, 10, 33, 53, 40054)', 'blank': 'True'}),
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosGroup']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"})
+ },
+ 'im.resource': {
+ 'Meta': {'object_name': 'Resource'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'meta': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['im.ResourceMetadata']", 'symmetrical': 'False'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
+ 'service': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Service']"})
+ },
+ 'im.resourcemetadata': {
+ 'Meta': {'object_name': 'ResourceMetadata'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
+ 'value': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'im.service': {
+ 'Meta': {'object_name': 'Service'},
+ 'auth_token': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
+ 'auth_token_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'auth_token_expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'icon': ('django.db.models.fields.FilePathField', [], {'max_length': '100', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
+ 'url': ('django.db.models.fields.FilePathField', [], {'max_length': '100'})
+ }
+ }
+
+ complete_apps = ['im']
--- /dev/null
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import DataMigration
+from django.db import models
+
+
+class Migration(DataMigration):
+
+ def forwards(self, orm):
+ "Write your forwards methods here."
+ for u in orm.AstakosUser.objects.all():
+ u.email = u.email.lower()
+ u.save()
+
+ def backwards(self, orm):
+ "Write your backwards methods here."
+
+ models = {
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'im.additionalmail': {
+ 'Meta': {'object_name': 'AdditionalMail'},
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"})
+ },
+ 'im.approvalterms': {
+ 'Meta': {'object_name': 'ApprovalTerms'},
+ 'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 10, 4, 9, 47, 13, 40029)', 'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'location': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'im.astakosgroup': {
+ 'Meta': {'object_name': 'AstakosGroup', '_ormbases': ['auth.Group']},
+ 'approval_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 10, 4, 9, 47, 13, 34050)'}),
+ 'desc': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'estimated_participants': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'expiration_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'group_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.Group']", 'unique': 'True', 'primary_key': 'True'}),
+ 'homepage': ('django.db.models.fields.URLField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'issue_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'kind': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.GroupKind']"}),
+ 'moderation_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'policy': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['im.Resource']", 'null': 'True', 'through': "orm['im.AstakosGroupQuota']", 'blank': 'True'})
+ },
+ 'im.astakosgroupquota': {
+ 'Meta': {'unique_together': "(('resource', 'group'),)", 'object_name': 'AstakosGroupQuota'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosGroup']", 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'limit': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'resource': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Resource']"}),
+ 'uplimit': ('django.db.models.fields.BigIntegerField', [], {'null': 'True'})
+ },
+ 'im.astakosuser': {
+ 'Meta': {'unique_together': "(('provider', 'third_party_identifier'),)", 'object_name': 'AstakosUser', '_ormbases': ['auth.User']},
+ 'activation_sent': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'astakos_groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['im.AstakosGroup']", 'symmetrical': 'False', 'through': "orm['im.Membership']", 'blank': 'True'}),
+ 'auth_token': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
+ 'auth_token_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'auth_token_expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'date_signed_terms': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'email_verified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'has_credits': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'has_signed_terms': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'invitations': ('django.db.models.fields.IntegerField', [], {'default': '100'}),
+ 'is_verified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'level': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'owner': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'owner'", 'null': 'True', 'to': "orm['im.AstakosGroup']"}),
+ 'policy': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['im.Resource']", 'null': 'True', 'through': "orm['im.AstakosUserQuota']", 'symmetrical': 'False'}),
+ 'provider': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'third_party_identifier': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
+ 'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'})
+ },
+ 'im.astakosuserquota': {
+ 'Meta': {'unique_together': "(('resource', 'user'),)", 'object_name': 'AstakosUserQuota'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'limit': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'resource': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Resource']"}),
+ 'uplimit': ('django.db.models.fields.BigIntegerField', [], {'null': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"})
+ },
+ 'im.emailchange': {
+ 'Meta': {'object_name': 'EmailChange'},
+ 'activation_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40', 'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'new_email_address': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+ 'requested_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 10, 4, 9, 47, 13, 41566)'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'emailchange_user'", 'unique': 'True', 'to': "orm['im.AstakosUser']"})
+ },
+ 'im.groupkind': {
+ 'Meta': {'object_name': 'GroupKind'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'})
+ },
+ 'im.invitation': {
+ 'Meta': {'object_name': 'Invitation'},
+ 'code': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'consumed': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'inviter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'invitations_sent'", 'null': 'True', 'to': "orm['im.AstakosUser']"}),
+ 'is_consumed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'realname': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'})
+ },
+ 'im.membership': {
+ 'Meta': {'unique_together': "(('person', 'group'),)", 'object_name': 'Membership'},
+ 'date_joined': ('django.db.models.fields.DateField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+ 'date_requested': ('django.db.models.fields.DateField', [], {'default': 'datetime.datetime(2012, 10, 4, 9, 47, 13, 37772)', 'blank': 'True'}),
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosGroup']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"})
+ },
+ 'im.resource': {
+ 'Meta': {'object_name': 'Resource'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'meta': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['im.ResourceMetadata']", 'symmetrical': 'False'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
+ 'service': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Service']"})
+ },
+ 'im.resourcemetadata': {
+ 'Meta': {'object_name': 'ResourceMetadata'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
+ 'value': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'im.service': {
+ 'Meta': {'object_name': 'Service'},
+ 'auth_token': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
+ 'auth_token_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'auth_token_expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'icon': ('django.db.models.fields.FilePathField', [], {'max_length': '100', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
+ 'url': ('django.db.models.fields.FilePathField', [], {'max_length': '100'})
+ }
+ }
+
+ complete_apps = ['im']
--- /dev/null
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Adding field 'Resource.desc'
+ db.add_column('im_resource', 'desc', self.gf('django.db.models.fields.TextField')(null=True), keep_default=False)
+
+ # Adding field 'Resource.unit'
+ db.add_column('im_resource', 'unit', self.gf('django.db.models.fields.CharField')(max_length=255, null=True), keep_default=False)
+
+ def backwards(self, orm):
+
+ # Deleting field 'Resource.desc'
+ db.delete_column('im_resource', 'desc')
+
+ # Deleting field 'Resource.unit'
+ db.delete_column('im_resource', 'unit')
+
+ models = {
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'im.additionalmail': {
+ 'Meta': {'object_name': 'AdditionalMail'},
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"})
+ },
+ 'im.approvalterms': {
+ 'Meta': {'object_name': 'ApprovalTerms'},
+ 'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 10, 30, 16, 37, 5, 608037)', 'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'location': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'im.astakosgroup': {
+ 'Meta': {'object_name': 'AstakosGroup', '_ormbases': ['auth.Group']},
+ 'approval_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 10, 30, 16, 37, 5, 601308)'}),
+ 'desc': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'estimated_participants': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'expiration_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'group_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.Group']", 'unique': 'True', 'primary_key': 'True'}),
+ 'homepage': ('django.db.models.fields.URLField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'issue_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'kind': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.GroupKind']"}),
+ 'moderation_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'policy': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['im.Resource']", 'null': 'True', 'through': "orm['im.AstakosGroupQuota']", 'blank': 'True'})
+ },
+ 'im.astakosgroupquota': {
+ 'Meta': {'unique_together': "(('resource', 'group'),)", 'object_name': 'AstakosGroupQuota'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosGroup']", 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'limit': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'resource': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Resource']"}),
+ 'uplimit': ('django.db.models.fields.BigIntegerField', [], {'null': 'True'})
+ },
+ 'im.astakosuser': {
+ 'Meta': {'unique_together': "(('provider', 'third_party_identifier'),)", 'object_name': 'AstakosUser', '_ormbases': ['auth.User']},
+ 'activation_sent': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'astakos_groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['im.AstakosGroup']", 'symmetrical': 'False', 'through': "orm['im.Membership']", 'blank': 'True'}),
+ 'auth_token': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
+ 'auth_token_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'auth_token_expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'date_signed_terms': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'email_verified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'has_credits': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'has_signed_terms': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'invitations': ('django.db.models.fields.IntegerField', [], {'default': '100'}),
+ 'is_verified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'level': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'owner': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'owner'", 'null': 'True', 'to': "orm['im.AstakosGroup']"}),
+ 'policy': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['im.Resource']", 'null': 'True', 'through': "orm['im.AstakosUserQuota']", 'symmetrical': 'False'}),
+ 'provider': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'third_party_identifier': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
+ 'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'})
+ },
+ 'im.astakosuserquota': {
+ 'Meta': {'unique_together': "(('resource', 'user'),)", 'object_name': 'AstakosUserQuota'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'limit': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'resource': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Resource']"}),
+ 'uplimit': ('django.db.models.fields.BigIntegerField', [], {'null': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"})
+ },
+ 'im.emailchange': {
+ 'Meta': {'object_name': 'EmailChange'},
+ 'activation_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40', 'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'new_email_address': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+ 'requested_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 10, 30, 16, 37, 5, 609676)'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'emailchange_user'", 'unique': 'True', 'to': "orm['im.AstakosUser']"})
+ },
+ 'im.groupkind': {
+ 'Meta': {'object_name': 'GroupKind'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'})
+ },
+ 'im.invitation': {
+ 'Meta': {'object_name': 'Invitation'},
+ 'code': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'consumed': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'inviter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'invitations_sent'", 'null': 'True', 'to': "orm['im.AstakosUser']"}),
+ 'is_consumed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'realname': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'})
+ },
+ 'im.membership': {
+ 'Meta': {'unique_together': "(('person', 'group'),)", 'object_name': 'Membership'},
+ 'date_joined': ('django.db.models.fields.DateField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+ 'date_requested': ('django.db.models.fields.DateField', [], {'default': 'datetime.datetime(2012, 10, 30, 16, 37, 5, 605488)', 'blank': 'True'}),
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosGroup']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"})
+ },
+ 'im.resource': {
+ 'Meta': {'object_name': 'Resource'},
+ 'desc': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'meta': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['im.ResourceMetadata']", 'symmetrical': 'False'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
+ 'service': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Service']"}),
+ 'unit': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'})
+ },
+ 'im.resourcemetadata': {
+ 'Meta': {'object_name': 'ResourceMetadata'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
+ 'value': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'im.service': {
+ 'Meta': {'object_name': 'Service'},
+ 'auth_token': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
+ 'auth_token_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'auth_token_expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'icon': ('django.db.models.fields.FilePathField', [], {'max_length': '100', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
+ 'url': ('django.db.models.fields.FilePathField', [], {'max_length': '100'})
+ }
+ }
+
+ complete_apps = ['im']
--- /dev/null
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Adding field 'Resource.group'
+ db.add_column('im_resource', 'group', self.gf('django.db.models.fields.CharField')(max_length=255, null=True), keep_default=False)
+
+
+ def backwards(self, orm):
+
+ # Deleting field 'Resource.group'
+ db.delete_column('im_resource', 'group')
+
+
+ models = {
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'im.additionalmail': {
+ 'Meta': {'object_name': 'AdditionalMail'},
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"})
+ },
+ 'im.approvalterms': {
+ 'Meta': {'object_name': 'ApprovalTerms'},
+ 'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 11, 5, 11, 38, 19, 866217)', 'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'location': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'im.astakosgroup': {
+ 'Meta': {'object_name': 'AstakosGroup', '_ormbases': ['auth.Group']},
+ 'approval_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 11, 5, 11, 38, 19, 860127)'}),
+ 'desc': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'estimated_participants': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'expiration_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'group_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.Group']", 'unique': 'True', 'primary_key': 'True'}),
+ 'homepage': ('django.db.models.fields.URLField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'issue_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'kind': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.GroupKind']"}),
+ 'moderation_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'policy': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['im.Resource']", 'null': 'True', 'through': "orm['im.AstakosGroupQuota']", 'blank': 'True'})
+ },
+ 'im.astakosgroupquota': {
+ 'Meta': {'unique_together': "(('resource', 'group'),)", 'object_name': 'AstakosGroupQuota'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosGroup']", 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'limit': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'resource': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Resource']"}),
+ 'uplimit': ('django.db.models.fields.BigIntegerField', [], {'null': 'True'})
+ },
+ 'im.astakosuser': {
+ 'Meta': {'unique_together': "(('provider', 'third_party_identifier'),)", 'object_name': 'AstakosUser', '_ormbases': ['auth.User']},
+ 'activation_sent': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'astakos_groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['im.AstakosGroup']", 'symmetrical': 'False', 'through': "orm['im.Membership']", 'blank': 'True'}),
+ 'auth_token': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
+ 'auth_token_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'auth_token_expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'date_signed_terms': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'email_verified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'has_credits': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'has_signed_terms': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'invitations': ('django.db.models.fields.IntegerField', [], {'default': '100'}),
+ 'is_verified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'level': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'owner': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'owner'", 'null': 'True', 'to': "orm['im.AstakosGroup']"}),
+ 'policy': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['im.Resource']", 'null': 'True', 'through': "orm['im.AstakosUserQuota']", 'symmetrical': 'False'}),
+ 'provider': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'third_party_identifier': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
+ 'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'})
+ },
+ 'im.astakosuserquota': {
+ 'Meta': {'unique_together': "(('resource', 'user'),)", 'object_name': 'AstakosUserQuota'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'limit': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'resource': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Resource']"}),
+ 'uplimit': ('django.db.models.fields.BigIntegerField', [], {'null': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"})
+ },
+ 'im.emailchange': {
+ 'Meta': {'object_name': 'EmailChange'},
+ 'activation_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40', 'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'new_email_address': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+ 'requested_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 11, 5, 11, 38, 19, 867741)'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'emailchange_user'", 'unique': 'True', 'to': "orm['im.AstakosUser']"})
+ },
+ 'im.groupkind': {
+ 'Meta': {'object_name': 'GroupKind'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'})
+ },
+ 'im.invitation': {
+ 'Meta': {'object_name': 'Invitation'},
+ 'code': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'consumed': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'inviter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'invitations_sent'", 'null': 'True', 'to': "orm['im.AstakosUser']"}),
+ 'is_consumed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'realname': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'})
+ },
+ 'im.membership': {
+ 'Meta': {'unique_together': "(('person', 'group'),)", 'object_name': 'Membership'},
+ 'date_joined': ('django.db.models.fields.DateField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+ 'date_requested': ('django.db.models.fields.DateField', [], {'default': 'datetime.datetime(2012, 11, 5, 11, 38, 19, 863952)', 'blank': 'True'}),
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosGroup']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"})
+ },
+ 'im.resource': {
+ 'Meta': {'object_name': 'Resource'},
+ 'desc': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'group': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'meta': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['im.ResourceMetadata']", 'symmetrical': 'False'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
+ 'service': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Service']"}),
+ 'unit': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'})
+ },
+ 'im.resourcemetadata': {
+ 'Meta': {'object_name': 'ResourceMetadata'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
+ 'value': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'im.service': {
+ 'Meta': {'object_name': 'Service'},
+ 'auth_token': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
+ 'auth_token_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'auth_token_expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'icon': ('django.db.models.fields.FilePathField', [], {'max_length': '100', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
+ 'url': ('django.db.models.fields.FilePathField', [], {'max_length': '100'})
+ }
+ }
+
+ complete_apps = ['im']
--- /dev/null
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Adding field 'AstakosUser.disturbed_quota'
+ db.add_column('im_astakosuser', 'disturbed_quota', self.gf('django.db.models.fields.BooleanField')(default=False, db_index=True), keep_default=False)
+
+
+ def backwards(self, orm):
+
+ # Deleting field 'AstakosUser.disturbed_quota'
+ db.delete_column('im_astakosuser', 'disturbed_quota')
+
+
+ models = {
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'im.additionalmail': {
+ 'Meta': {'object_name': 'AdditionalMail'},
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"})
+ },
+ 'im.approvalterms': {
+ 'Meta': {'object_name': 'ApprovalTerms'},
+ 'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 11, 5, 11, 38, 41, 105536)', 'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'location': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'im.astakosgroup': {
+ 'Meta': {'object_name': 'AstakosGroup', '_ormbases': ['auth.Group']},
+ 'approval_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 11, 5, 11, 38, 41, 99282)'}),
+ 'desc': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'estimated_participants': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'expiration_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'group_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.Group']", 'unique': 'True', 'primary_key': 'True'}),
+ 'homepage': ('django.db.models.fields.URLField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'issue_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'kind': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.GroupKind']"}),
+ 'moderation_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'policy': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['im.Resource']", 'null': 'True', 'through': "orm['im.AstakosGroupQuota']", 'blank': 'True'})
+ },
+ 'im.astakosgroupquota': {
+ 'Meta': {'unique_together': "(('resource', 'group'),)", 'object_name': 'AstakosGroupQuota'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosGroup']", 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'limit': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'resource': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Resource']"}),
+ 'uplimit': ('django.db.models.fields.BigIntegerField', [], {'null': 'True'})
+ },
+ 'im.astakosuser': {
+ 'Meta': {'unique_together': "(('provider', 'third_party_identifier'),)", 'object_name': 'AstakosUser', '_ormbases': ['auth.User']},
+ 'activation_sent': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'astakos_groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['im.AstakosGroup']", 'symmetrical': 'False', 'through': "orm['im.Membership']", 'blank': 'True'}),
+ 'auth_token': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
+ 'auth_token_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'auth_token_expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'date_signed_terms': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'disturbed_quota': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+ 'email_verified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'has_credits': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'has_signed_terms': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'invitations': ('django.db.models.fields.IntegerField', [], {'default': '100'}),
+ 'is_verified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'level': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'owner': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'owner'", 'null': 'True', 'to': "orm['im.AstakosGroup']"}),
+ 'policy': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['im.Resource']", 'null': 'True', 'through': "orm['im.AstakosUserQuota']", 'symmetrical': 'False'}),
+ 'provider': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'third_party_identifier': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
+ 'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'})
+ },
+ 'im.astakosuserquota': {
+ 'Meta': {'unique_together': "(('resource', 'user'),)", 'object_name': 'AstakosUserQuota'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'limit': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'resource': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Resource']"}),
+ 'uplimit': ('django.db.models.fields.BigIntegerField', [], {'null': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"})
+ },
+ 'im.emailchange': {
+ 'Meta': {'object_name': 'EmailChange'},
+ 'activation_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40', 'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'new_email_address': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+ 'requested_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 11, 5, 11, 38, 41, 107071)'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'emailchange_user'", 'unique': 'True', 'to': "orm['im.AstakosUser']"})
+ },
+ 'im.groupkind': {
+ 'Meta': {'object_name': 'GroupKind'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'})
+ },
+ 'im.invitation': {
+ 'Meta': {'object_name': 'Invitation'},
+ 'code': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'consumed': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'inviter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'invitations_sent'", 'null': 'True', 'to': "orm['im.AstakosUser']"}),
+ 'is_consumed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'realname': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'})
+ },
+ 'im.membership': {
+ 'Meta': {'unique_together': "(('person', 'group'),)", 'object_name': 'Membership'},
+ 'date_joined': ('django.db.models.fields.DateField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+ 'date_requested': ('django.db.models.fields.DateField', [], {'default': 'datetime.datetime(2012, 11, 5, 11, 38, 41, 103264)', 'blank': 'True'}),
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosGroup']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"})
+ },
+ 'im.resource': {
+ 'Meta': {'object_name': 'Resource'},
+ 'desc': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'group': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'meta': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['im.ResourceMetadata']", 'symmetrical': 'False'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
+ 'service': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Service']"}),
+ 'unit': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'})
+ },
+ 'im.resourcemetadata': {
+ 'Meta': {'object_name': 'ResourceMetadata'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
+ 'value': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'im.service': {
+ 'Meta': {'object_name': 'Service'},
+ 'auth_token': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
+ 'auth_token_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'auth_token_expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'icon': ('django.db.models.fields.FilePathField', [], {'max_length': '100', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
+ 'url': ('django.db.models.fields.FilePathField', [], {'max_length': '100'})
+ }
+ }
+
+ complete_apps = ['im']
--- /dev/null
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Adding field 'AstakosGroup.max_participants'
+ db.add_column('im_astakosgroup', 'max_participants', self.gf('django.db.models.fields.PositiveIntegerField')(null=True, blank=True), keep_default=False)
+
+
+ def backwards(self, orm):
+
+ # Deleting field 'AstakosGroup.max_participants'
+ db.delete_column('im_astakosgroup', 'max_participants')
+
+
+ models = {
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'im.additionalmail': {
+ 'Meta': {'object_name': 'AdditionalMail'},
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"})
+ },
+ 'im.approvalterms': {
+ 'Meta': {'object_name': 'ApprovalTerms'},
+ 'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 11, 5, 11, 38, 52, 727224)', 'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'location': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'im.astakosgroup': {
+ 'Meta': {'object_name': 'AstakosGroup', '_ormbases': ['auth.Group']},
+ 'approval_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 11, 5, 11, 38, 52, 720807)'}),
+ 'desc': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'estimated_participants': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'expiration_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'group_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.Group']", 'unique': 'True', 'primary_key': 'True'}),
+ 'homepage': ('django.db.models.fields.URLField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'issue_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'kind': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.GroupKind']"}),
+ 'max_participants': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'moderation_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'policy': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['im.Resource']", 'null': 'True', 'through': "orm['im.AstakosGroupQuota']", 'blank': 'True'})
+ },
+ 'im.astakosgroupquota': {
+ 'Meta': {'unique_together': "(('resource', 'group'),)", 'object_name': 'AstakosGroupQuota'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosGroup']", 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'limit': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'resource': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Resource']"}),
+ 'uplimit': ('django.db.models.fields.BigIntegerField', [], {'null': 'True'})
+ },
+ 'im.astakosuser': {
+ 'Meta': {'unique_together': "(('provider', 'third_party_identifier'),)", 'object_name': 'AstakosUser', '_ormbases': ['auth.User']},
+ 'activation_sent': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'astakos_groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['im.AstakosGroup']", 'symmetrical': 'False', 'through': "orm['im.Membership']", 'blank': 'True'}),
+ 'auth_token': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
+ 'auth_token_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'auth_token_expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'date_signed_terms': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'disturbed_quota': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+ 'email_verified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'has_credits': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'has_signed_terms': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'invitations': ('django.db.models.fields.IntegerField', [], {'default': '100'}),
+ 'is_verified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'level': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'owner': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'owner'", 'null': 'True', 'to': "orm['im.AstakosGroup']"}),
+ 'policy': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['im.Resource']", 'null': 'True', 'through': "orm['im.AstakosUserQuota']", 'symmetrical': 'False'}),
+ 'provider': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'third_party_identifier': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
+ 'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'})
+ },
+ 'im.astakosuserquota': {
+ 'Meta': {'unique_together': "(('resource', 'user'),)", 'object_name': 'AstakosUserQuota'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'limit': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'resource': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Resource']"}),
+ 'uplimit': ('django.db.models.fields.BigIntegerField', [], {'null': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"})
+ },
+ 'im.emailchange': {
+ 'Meta': {'object_name': 'EmailChange'},
+ 'activation_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40', 'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'new_email_address': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+ 'requested_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 11, 5, 11, 38, 52, 728753)'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'emailchange_user'", 'unique': 'True', 'to': "orm['im.AstakosUser']"})
+ },
+ 'im.groupkind': {
+ 'Meta': {'object_name': 'GroupKind'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'})
+ },
+ 'im.invitation': {
+ 'Meta': {'object_name': 'Invitation'},
+ 'code': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'consumed': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'inviter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'invitations_sent'", 'null': 'True', 'to': "orm['im.AstakosUser']"}),
+ 'is_consumed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'realname': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'})
+ },
+ 'im.membership': {
+ 'Meta': {'unique_together': "(('person', 'group'),)", 'object_name': 'Membership'},
+ 'date_joined': ('django.db.models.fields.DateField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+ 'date_requested': ('django.db.models.fields.DateField', [], {'default': 'datetime.datetime(2012, 11, 5, 11, 38, 52, 724952)', 'blank': 'True'}),
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosGroup']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"})
+ },
+ 'im.resource': {
+ 'Meta': {'object_name': 'Resource'},
+ 'desc': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'group': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'meta': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['im.ResourceMetadata']", 'symmetrical': 'False'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
+ 'service': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Service']"}),
+ 'unit': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'})
+ },
+ 'im.resourcemetadata': {
+ 'Meta': {'object_name': 'ResourceMetadata'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
+ 'value': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'im.service': {
+ 'Meta': {'object_name': 'Service'},
+ 'auth_token': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
+ 'auth_token_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'auth_token_expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'icon': ('django.db.models.fields.FilePathField', [], {'max_length': '100', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
+ 'url': ('django.db.models.fields.FilePathField', [], {'max_length': '100'})
+ }
+ }
+
+ complete_apps = ['im']
--- /dev/null
+# encoding: utf-8
+
+from south.v2 import DataMigration
+
+from astakos.im.settings import SERVICES
+
+import logging
+
+logger = logging.getLogger(__name__)
+
+class Migration(DataMigration):
+
+ def forwards(self, orm):
+ "Write your forwards methods here."
+
+ try:
+ default = orm.AstakosGroup.objects.get(name='default')
+ except orm.AstakosGroup.DoesNotExist:
+ return
+
+ def create_policies(args):
+ sn, dict = args
+ url = dict.get('url')
+ resources = dict.get('resources') or ()
+ s, created = orm.Service.objects.get_or_create(
+ name=sn,
+ defaults={'url': url}
+ )
+
+ for r in resources:
+ try:
+ rn = r.pop('name', '')
+ uplimit = r.pop('uplimit', None)
+ r, created = orm.Resource.objects.get_or_create(
+ service=s,
+ name=rn,
+ defaults=r)
+ except Exception, e:
+ print "Cannot create resource ", rn
+ continue
+ else:
+ q, created = orm.AstakosGroupQuota.objects.get_or_create(
+ group=default,
+ resource=r,
+ defaults={
+ 'uplimit':uplimit,
+ }
+ )
+ map(create_policies, SERVICES.iteritems())
+
+ def backwards(self, orm):
+ try:
+ default = orm.AstakosGroup.objects.get(name='default')
+ except orm.AstakosGroup.DoesNotExist:
+ return
+
+ def destroy_policies(args):
+ sn, dict = args
+ url = dict.get('url')
+ resources = dict.get('resources') or ()
+ for r in resources:
+ rn = r.get('name', '')
+ try:
+ q = orm.AstakosGroupQuota.objects.get(
+ group=default,
+ resource__name=rn)
+ q.delete()
+ q = orm.Resource.objects.get(service__name=sn, name=rn)
+ q.delete()
+ except Exception, e:
+ print "Cannot create resource ", rn
+ continue
+
+ map(destroy_policies, SERVICES.iteritems())
+
+ models = {
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'im.additionalmail': {
+ 'Meta': {'object_name': 'AdditionalMail'},
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"})
+ },
+ 'im.approvalterms': {
+ 'Meta': {'object_name': 'ApprovalTerms'},
+ 'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 11, 5, 12, 47, 39, 465319)', 'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'location': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'im.astakosgroup': {
+ 'Meta': {'object_name': 'AstakosGroup', '_ormbases': ['auth.Group']},
+ 'approval_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 11, 5, 12, 47, 39, 456067)'}),
+ 'desc': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'estimated_participants': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'expiration_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'group_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.Group']", 'unique': 'True', 'primary_key': 'True'}),
+ 'homepage': ('django.db.models.fields.URLField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'issue_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'kind': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.GroupKind']"}),
+ 'max_participants': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'moderation_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'policy': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['im.Resource']", 'null': 'True', 'through': "orm['im.AstakosGroupQuota']", 'blank': 'True'})
+ },
+ 'im.astakosgroupquota': {
+ 'Meta': {'unique_together': "(('resource', 'group'),)", 'object_name': 'AstakosGroupQuota'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosGroup']", 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'limit': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'resource': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Resource']"}),
+ 'uplimit': ('django.db.models.fields.BigIntegerField', [], {'null': 'True'})
+ },
+ 'im.astakosuser': {
+ 'Meta': {'unique_together': "(('provider', 'third_party_identifier'),)", 'object_name': 'AstakosUser', '_ormbases': ['auth.User']},
+ 'activation_sent': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'astakos_groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['im.AstakosGroup']", 'symmetrical': 'False', 'through': "orm['im.Membership']", 'blank': 'True'}),
+ 'auth_token': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
+ 'auth_token_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'auth_token_expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'date_signed_terms': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'disturbed_quota': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+ 'email_verified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'has_credits': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'has_signed_terms': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'invitations': ('django.db.models.fields.IntegerField', [], {'default': '100'}),
+ 'is_verified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'level': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'owner': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'owner'", 'null': 'True', 'to': "orm['im.AstakosGroup']"}),
+ 'policy': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['im.Resource']", 'null': 'True', 'through': "orm['im.AstakosUserQuota']", 'symmetrical': 'False'}),
+ 'provider': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'third_party_identifier': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
+ 'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'})
+ },
+ 'im.astakosuserquota': {
+ 'Meta': {'unique_together': "(('resource', 'user'),)", 'object_name': 'AstakosUserQuota'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'limit': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'resource': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Resource']"}),
+ 'uplimit': ('django.db.models.fields.BigIntegerField', [], {'null': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"})
+ },
+ 'im.emailchange': {
+ 'Meta': {'object_name': 'EmailChange'},
+ 'activation_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40', 'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'new_email_address': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+ 'requested_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 11, 5, 12, 47, 39, 466946)'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'emailchange_user'", 'unique': 'True', 'to': "orm['im.AstakosUser']"})
+ },
+ 'im.groupkind': {
+ 'Meta': {'object_name': 'GroupKind'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'})
+ },
+ 'im.invitation': {
+ 'Meta': {'object_name': 'Invitation'},
+ 'code': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'consumed': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'inviter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'invitations_sent'", 'null': 'True', 'to': "orm['im.AstakosUser']"}),
+ 'is_consumed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'realname': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'})
+ },
+ 'im.membership': {
+ 'Meta': {'unique_together': "(('person', 'group'),)", 'object_name': 'Membership'},
+ 'date_joined': ('django.db.models.fields.DateField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+ 'date_requested': ('django.db.models.fields.DateField', [], {'default': 'datetime.datetime(2012, 11, 5, 12, 47, 39, 462857)', 'blank': 'True'}),
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosGroup']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"})
+ },
+ 'im.resource': {
+ 'Meta': {'object_name': 'Resource'},
+ 'desc': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'group': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'meta': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['im.ResourceMetadata']", 'symmetrical': 'False'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
+ 'service': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Service']"}),
+ 'unit': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'})
+ },
+ 'im.resourcemetadata': {
+ 'Meta': {'object_name': 'ResourceMetadata'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
+ 'value': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'im.service': {
+ 'Meta': {'object_name': 'Service'},
+ 'auth_token': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
+ 'auth_token_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'auth_token_expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'icon': ('django.db.models.fields.FilePathField', [], {'max_length': '100', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
+ 'url': ('django.db.models.fields.FilePathField', [], {'max_length': '100'})
+ }
+ }
+
+ complete_apps = ['im']
--- /dev/null
+# encoding: utf-8
+import datetime
+from south.v2 import DataMigration
+from django.db.models import Count
+
+
+class Migration(DataMigration):
+ def forwards(self, orm):
+
+ def _create_groupkind(name):
+ try:
+ orm.GroupKind(name=name).save()
+ except:
+ pass
+
+ t = ('default', 'course', 'project', 'laboratory', 'organization')
+ map(_create_groupkind, t)
+
+ default = orm.GroupKind.objects.get(name='default')
+
+ groups = {}
+
+ def _create_astakogroup(name):
+ try:
+ groups[name] = orm['im.AstakosGroup'].objects.get(name=name)
+ except orm.AstakosGroup.DoesNotExist:
+ try:
+ g = orm['auth.Group'].objects.get(name=name)
+ groups[
+ name] = extended = orm.AstakosGroup(group_ptr_id=g.pk)
+ extended.__dict__.update(g.__dict__)
+ extended.kind = default
+ extended.approval_date = datetime.datetime.now()
+ extended.issue_date = datetime.datetime.now()
+ extended.moderation_enabled = False
+ extended.save()
+ map(lambda u: orm.Membership(group=extended,
+ person=orm.AstakosUser.objects.get(id=u.id),
+ date_joined=datetime.datetime.now()
+ ).save(),
+ g.user_set.all())
+ except orm['auth.Group'].DoesNotExist:
+ groups[name] = orm.AstakosGroup(name=name,
+ kind=default,
+ approval_date=datetime.datetime.now(),
+ issue_date=datetime.datetime.now(),
+ moderation_enabled=False
+ )
+ groups[name].save()
+
+ # catch integrate
+ t = ('default', 'shibboleth', 'helpdesk', 'faculty',
+ 'ugrad', 'grad', 'researcher', 'associate')
+ map(_create_astakogroup, t)
+
+ orphans = orm.AstakosUser.objects.annotate(
+ num_groups=Count('astakos_groups')).filter(num_groups=0)
+ map(lambda u: orm.Membership(group=groups['default'],
+ person=u, date_joined=datetime.datetime.now()).save(), orphans)
+
+ def backwards(self, orm):
+ def _delete_groupkind(name):
+ try:
+ orm.GroupKind.objects.get(name=name).delete()
+ except orm.GroupKind.DoesNotExist:
+ pass
+
+ def _delete_astakosgroup(name):
+ try:
+ orm.AstakosGroup.objects.get(name=name).delete()
+ except orm.AstakosGroup.DoesNotExist:
+ pass
+
+ t = ('default', 'shibboleth', 'helpdesk', 'faculty',
+ 'ugrad', 'grad', 'researcher', 'associate')
+ map(_delete_astakosgroup, t)
+
+ t = ('default', 'course', 'project', 'laboratory', 'organization')
+ map(_delete_groupkind, t)
+
+ models = {
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'im.additionalmail': {
+ 'Meta': {'object_name': 'AdditionalMail'},
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"})
+ },
+ 'im.approvalterms': {
+ 'Meta': {'object_name': 'ApprovalTerms'},
+ 'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 8, 8, 12, 40, 8, 181485)', 'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'location': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'im.astakosgroup': {
+ 'Meta': {'object_name': 'AstakosGroup', '_ormbases': ['auth.Group']},
+ 'approval_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 8, 8, 12, 40, 8, 175548)'}),
+ 'desc': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'estimated_participants': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'expiration_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'group_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.Group']", 'unique': 'True', 'primary_key': 'True'}),
+ 'issue_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'kind': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.GroupKind']"}),
+ 'moderation_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'policy': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['im.Resource']", 'null': 'True', 'through': "orm['im.AstakosGroupQuota']", 'blank': 'True'})
+ },
+ 'im.astakosgroupquota': {
+ 'Meta': {'unique_together': "(('resource', 'group'),)", 'object_name': 'AstakosGroupQuota'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosGroup']", 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'limit': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'resource': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Resource']"})
+ },
+ 'im.astakosuser': {
+ 'Meta': {'unique_together': "(('provider', 'third_party_identifier'),)", 'object_name': 'AstakosUser', '_ormbases': ['auth.User']},
+ 'activation_sent': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'astakos_groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['im.AstakosGroup']", 'symmetrical': 'False', 'through': "orm['im.Membership']", 'blank': 'True'}),
+ 'auth_token': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
+ 'auth_token_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'auth_token_expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'date_signed_terms': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'email_verified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'has_credits': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'has_signed_terms': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'invitations': ('django.db.models.fields.IntegerField', [], {'default': '100'}),
+ 'is_verified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'level': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'owner': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'owner'", 'null': 'True', 'to': "orm['im.AstakosGroup']"}),
+ 'policy': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['im.Resource']", 'null': 'True', 'through': "orm['im.AstakosUserQuota']", 'symmetrical': 'False'}),
+ 'provider': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'third_party_identifier': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
+ 'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'})
+ },
+ 'im.astakosuserquota': {
+ 'Meta': {'unique_together': "(('resource', 'user'),)", 'object_name': 'AstakosUserQuota'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'limit': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'resource': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Resource']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"})
+ },
+ 'im.emailchange': {
+ 'Meta': {'object_name': 'EmailChange'},
+ 'activation_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40', 'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'new_email_address': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+ 'requested_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 8, 8, 12, 40, 8, 183025)'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'emailchange_user'", 'unique': 'True', 'to': "orm['im.AstakosUser']"})
+ },
+ 'im.groupkind': {
+ 'Meta': {'object_name': 'GroupKind'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'})
+ },
+ 'im.invitation': {
+ 'Meta': {'object_name': 'Invitation'},
+ 'code': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'consumed': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'inviter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'invitations_sent'", 'null': 'True', 'to': "orm['im.AstakosUser']"}),
+ 'is_consumed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'realname': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'})
+ },
+ 'im.membership': {
+ 'Meta': {'unique_together': "(('person', 'group'),)", 'object_name': 'Membership'},
+ 'date_joined': ('django.db.models.fields.DateField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+ 'date_requested': ('django.db.models.fields.DateField', [], {'default': 'datetime.datetime(2012, 8, 8, 12, 40, 8, 179349)', 'blank': 'True'}),
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosGroup']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"})
+ },
+ 'im.resource': {
+ 'Meta': {'object_name': 'Resource'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'meta': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['im.ResourceMetadata']", 'symmetrical': 'False'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
+ 'service': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Service']"})
+ },
+ 'im.resourcemetadata': {
+ 'Meta': {'object_name': 'ResourceMetadata'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
+ 'value': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'im.service': {
+ 'Meta': {'object_name': 'Service'},
+ 'auth_token': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
+ 'auth_token_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'auth_token_expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'icon': ('django.db.models.fields.FilePathField', [], {'max_length': '100', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
+ 'url': ('django.db.models.fields.FilePathField', [], {'max_length': '100'})
+ }
+ }
+
+ complete_apps = ['im']
import hashlib
import uuid
import logging
-import json
from time import asctime
from datetime import datetime, timedelta
from base64 import b64encode
-from urlparse import urlparse
from random import randint
+from collections import defaultdict
from django.db import models, IntegrityError
-from django.contrib.auth.models import User, UserManager, Group
+from django.contrib.auth.models import User, UserManager, Group, Permission
from django.utils.translation import ugettext as _
-from django.core.exceptions import ValidationError
-from django.template.loader import render_to_string
-from django.core.mail import send_mail
from django.db import transaction
-from django.db.models.signals import post_save, pre_save, post_syncdb
+from django.core.exceptions import ValidationError
+from django.db.models.signals import (
+ pre_save, post_save, post_syncdb, post_delete
+)
+from django.contrib.contenttypes.models import ContentType
+
+from django.dispatch import Signal
from django.db.models import Q
from django.conf import settings
from django.utils.importlib import import_module
-from astakos.im.settings import (
- DEFAULT_USER_LEVEL, INVITATIONS_PER_LEVEL,
- AUTH_TOKEN_DURATION, BILLING_FIELDS, QUEUE_CONNECTION, SITENAME,
- EMAILCHANGE_ACTIVATION_DAYS, LOGGING_LEVEL
+from astakos.im.settings import (DEFAULT_USER_LEVEL, INVITATIONS_PER_LEVEL,
+ AUTH_TOKEN_DURATION, BILLING_FIELDS,
+ EMAILCHANGE_ACTIVATION_DAYS, LOGGING_LEVEL)
+from astakos.im.endpoints.qh import (
+ register_users, send_quota, register_resources
)
+from astakos.im.endpoints.aquarium.producer import report_user_event
+from astakos.im.functions import send_invitation
+from astakos.im.tasks import propagate_groupmembers_quota
-QUEUE_CLIENT_ID = 3 # Astakos.
+import astakos.im.messages as astakos_messages
logger = logging.getLogger(__name__)
+DEFAULT_CONTENT_TYPE = None
+try:
+ content_type = ContentType.objects.get(app_label='im', model='astakosuser')
+except:
+ content_type = DEFAULT_CONTENT_TYPE
+
+RESOURCE_SEPARATOR = '.'
+
+inf = float('inf')
+
+class Service(models.Model):
+ name = models.CharField('Name', max_length=255, unique=True, db_index=True)
+ url = models.FilePathField()
+ icon = models.FilePathField(blank=True)
+ auth_token = models.CharField('Authentication Token', max_length=32,
+ null=True, blank=True)
+ auth_token_created = models.DateTimeField('Token creation date', null=True)
+ auth_token_expires = models.DateTimeField(
+ 'Token expiration date', null=True)
+
+ def renew_token(self):
+ md5 = hashlib.md5()
+ md5.update(self.name.encode('ascii', 'ignore'))
+ md5.update(self.url.encode('ascii', 'ignore'))
+ md5.update(asctime())
+
+ self.auth_token = b64encode(md5.digest())
+ self.auth_token_created = datetime.now()
+ self.auth_token_expires = self.auth_token_created + \
+ timedelta(hours=AUTH_TOKEN_DURATION)
+
+ def __str__(self):
+ return self.name
+
+ @property
+ def resources(self):
+ return self.resource_set.all()
+
+ @resources.setter
+ def resources(self, resources):
+ for s in resources:
+ self.resource_set.create(**s)
+
+ def add_resource(self, service, resource, uplimit, update=True):
+ """Raises ObjectDoesNotExist, IntegrityError"""
+ resource = Resource.objects.get(service__name=service, name=resource)
+ if update:
+ AstakosUserQuota.objects.update_or_create(user=self,
+ resource=resource,
+ defaults={'uplimit': uplimit})
+ else:
+ q = self.astakosuserquota_set
+ q.create(resource=resource, uplimit=uplimit)
+
+
+class ResourceMetadata(models.Model):
+ key = models.CharField('Name', max_length=255, unique=True, db_index=True)
+ value = models.CharField('Value', max_length=255)
+
+
+class Resource(models.Model):
+ name = models.CharField('Name', max_length=255, unique=True, db_index=True)
+ meta = models.ManyToManyField(ResourceMetadata)
+ service = models.ForeignKey(Service)
+ desc = models.TextField('Description', null=True)
+ unit = models.CharField('Name', null=True, max_length=255)
+ group = models.CharField('Group', null=True, max_length=255)
+
+ def __str__(self):
+ return '%s%s%s' % (self.service, RESOURCE_SEPARATOR, self.name)
+
+
+class GroupKind(models.Model):
+ name = models.CharField('Name', max_length=255, unique=True, db_index=True)
+
+ def __str__(self):
+ return self.name
+
+
+class AstakosGroup(Group):
+ kind = models.ForeignKey(GroupKind)
+ homepage = models.URLField(
+ 'Homepage Url', max_length=255, null=True, blank=True)
+ desc = models.TextField('Description', null=True)
+ policy = models.ManyToManyField(
+ Resource,
+ null=True,
+ blank=True,
+ through='AstakosGroupQuota'
+ )
+ creation_date = models.DateTimeField(
+ 'Creation date',
+ default=datetime.now()
+ )
+ issue_date = models.DateTimeField('Issue date', null=True)
+ expiration_date = models.DateTimeField(
+ 'Expiration date',
+ null=True
+ )
+ moderation_enabled = models.BooleanField(
+ 'Moderated membership?',
+ default=True
+ )
+ approval_date = models.DateTimeField(
+ 'Activation date',
+ null=True,
+ blank=True
+ )
+ estimated_participants = models.PositiveIntegerField(
+ 'Estimated #members',
+ null=True,
+ blank=True,
+ )
+ max_participants = models.PositiveIntegerField(
+ 'Maximum numder of participants',
+ null=True,
+ blank=True
+ )
+
+ @property
+ def is_disabled(self):
+ if not self.approval_date:
+ return True
+ return False
+
+ @property
+ def is_enabled(self):
+ if self.is_disabled:
+ return False
+ if not self.issue_date:
+ return False
+ if not self.expiration_date:
+ return True
+ now = datetime.now()
+ if self.issue_date > now:
+ return False
+ if now >= self.expiration_date:
+ return False
+ return True
+
+ def enable(self):
+ if self.is_enabled:
+ return
+ self.approval_date = datetime.now()
+ self.save()
+ quota_disturbed.send(sender=self, users=self.approved_members)
+ propagate_groupmembers_quota.apply_async(
+ args=[self], eta=self.issue_date)
+ propagate_groupmembers_quota.apply_async(
+ args=[self], eta=self.expiration_date)
+
+ def disable(self):
+ if self.is_disabled:
+ return
+ self.approval_date = None
+ 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)
+ try:
+ m.approve()
+ except:
+ transaction.rollback()
+ raise
+ else:
+ transaction.commit()
+
+# def disapprove_member(self, person):
+# self.membership_set.remove(person=person)
+
+ @property
+ def members(self):
+ q = self.membership_set.select_related().all()
+ return [m.person for m in q]
+
+ @property
+ def approved_members(self):
+ q = self.membership_set.select_related().all()
+ return [m.person for m in q if m.is_approved]
+
+ @property
+ def quota(self):
+ d = defaultdict(int)
+ for q in self.astakosgroupquota_set.select_related().all():
+ d[q.resource] += q.uplimit or inf
+ return d
+
+ def add_policy(self, service, resource, uplimit, update=True):
+ """Raises ObjectDoesNotExist, IntegrityError"""
+ resource = Resource.objects.get(service__name=service, name=resource)
+ if update:
+ AstakosGroupQuota.objects.update_or_create(
+ group=self,
+ resource=resource,
+ defaults={'uplimit': uplimit}
+ )
+ else:
+ q = self.astakosgroupquota_set
+ q.create(resource=resource, uplimit=uplimit)
+
+ @property
+ def policies(self):
+ return self.astakosgroupquota_set.select_related().all()
+
+ @policies.setter
+ def policies(self, policies):
+ for p in policies:
+ service = p.get('service', None)
+ resource = p.get('resource', None)
+ uplimit = p.get('uplimit', 0)
+ update = p.get('update', True)
+ self.add_policy(service, resource, uplimit, update)
+
+ @property
+ def owners(self):
+ return self.owner.all()
+
+ @property
+ def owner_details(self):
+ return self.owner.select_related().all()
+
+ @owners.setter
+ def owners(self, l):
+ self.owner = l
+ map(self.approve_member, l)
+
+
class AstakosUser(User):
"""
Extends ``django.contrib.auth.models.User`` by defining additional fields.
#for invitations
user_level = DEFAULT_USER_LEVEL
level = models.IntegerField('Inviter level', default=user_level)
- invitations = models.IntegerField('Invitations left', default=INVITATIONS_PER_LEVEL.get(user_level, 0))
+ invitations = models.IntegerField(
+ 'Invitations left', default=INVITATIONS_PER_LEVEL.get(user_level, 0))
auth_token = models.CharField('Authentication Token', max_length=32,
null=True, blank=True)
auth_token_created = models.DateTimeField('Token creation date', null=True)
- auth_token_expires = models.DateTimeField('Token expiration date', null=True)
+ auth_token_expires = models.DateTimeField(
+ 'Token expiration date', null=True)
updated = models.DateTimeField('Update date')
is_verified = models.BooleanField('Is verified?', default=False)
# ex. screen_name for twitter, eppn for shibboleth
- third_party_identifier = models.CharField('Third-party identifier', max_length=255, null=True, blank=True)
+ third_party_identifier = models.CharField(
+ 'Third-party identifier', max_length=255, null=True, blank=True)
email_verified = models.BooleanField('Email verified?', default=False)
has_credits = models.BooleanField('Has credits?', default=False)
- has_signed_terms = models.BooleanField('Agree with the terms?', default=False)
- date_signed_terms = models.DateTimeField('Signed terms date', null=True, blank=True)
-
- activation_sent = models.DateTimeField('Activation sent data', null=True, blank=True)
-
+ has_signed_terms = models.BooleanField(
+ 'I agree with the terms', default=False)
+ date_signed_terms = models.DateTimeField(
+ 'Signed terms date', null=True, blank=True)
+
+ activation_sent = models.DateTimeField(
+ 'Activation sent data', null=True, blank=True)
+
+ policy = models.ManyToManyField(
+ Resource, null=True, through='AstakosUserQuota')
+
+ astakos_groups = models.ManyToManyField(
+ AstakosGroup, verbose_name=_('agroups'), blank=True,
+ help_text=_(astakos_messages.ASTAKOSUSER_GROUPS_HELP),
+ through='Membership')
+
__has_signed_terms = False
- __groupnames = []
-
+ disturbed_quota = models.BooleanField('Needs quotaholder syncing',
+ default=False, db_index=True)
+
+ owner = models.ManyToManyField(
+ AstakosGroup, related_name='owner', null=True)
+
class Meta:
unique_together = ("provider", "third_party_identifier")
-
+
def __init__(self, *args, **kwargs):
super(AstakosUser, self).__init__(*args, **kwargs)
self.__has_signed_terms = self.has_signed_terms
- if self.id:
- self.__groupnames = [g.name for g in self.groups.all()]
- else:
+ if not self.id:
self.is_active = False
-
+
@property
def realname(self):
- return '%s %s' %(self.first_name, self.last_name)
+ return '%s %s' % (self.first_name, self.last_name)
@realname.setter
def realname(self, value):
else:
self.last_name = parts[0]
+ def add_permission(self, pname):
+ if self.has_perm(pname):
+ return
+ p, created = Permission.objects.get_or_create(codename=pname,
+ name=pname.capitalize(),
+ content_type=content_type)
+ self.user_permissions.add(p)
+
+ def remove_permission(self, pname):
+ if self.has_perm(pname):
+ return
+ p = Permission.objects.get(codename=pname,
+ content_type=content_type)
+ self.user_permissions.remove(p)
+
@property
def invitation(self):
try:
except Invitation.DoesNotExist:
return None
+ def invite(self, email, realname):
+ inv = Invitation(inviter=self, username=email, realname=realname)
+ inv.save()
+ send_invitation(inv)
+ self.invitations = max(0, self.invitations - 1)
+ self.save()
+
+ @property
+ def quota(self):
+ """Returns a dict with the sum of quota limits per resource"""
+ d = defaultdict(int)
+ for q in self.policies:
+ d[q.resource] += q.uplimit or inf
+ for m in self.extended_groups:
+ if not m.is_approved:
+ continue
+ g = m.group
+ if not g.is_enabled:
+ continue
+ for r, uplimit in g.quota.iteritems():
+ d[r] += uplimit or inf
+ # TODO set default for remaining
+ return d
+
+ @property
+ def policies(self):
+ return self.astakosuserquota_set.select_related().all()
+
+ @policies.setter
+ def policies(self, policies):
+ for p in policies:
+ service = policies.get('service', None)
+ resource = policies.get('resource', None)
+ uplimit = policies.get('uplimit', 0)
+ update = policies.get('update', True)
+ self.add_policy(service, resource, uplimit, update)
+
+ def add_policy(self, service, resource, uplimit, update=True):
+ """Raises ObjectDoesNotExist, IntegrityError"""
+ resource = Resource.objects.get(service__name=service, name=resource)
+ if update:
+ AstakosUserQuota.objects.update_or_create(user=self,
+ resource=resource,
+ defaults={'uplimit': uplimit})
+ else:
+ q = self.astakosuserquota_set
+ q.create(resource=resource, uplimit=uplimit)
+
+ def remove_policy(self, service, resource):
+ """Raises ObjectDoesNotExist, IntegrityError"""
+ resource = Resource.objects.get(service__name=service, name=resource)
+ q = self.policies.get(resource=resource).delete()
+
+ @property
+ def extended_groups(self):
+ return self.membership_set.select_related().all()
+
+ @extended_groups.setter
+ def extended_groups(self, groups):
+ #TODO exceptions
+ for name in (groups or ()):
+ group = AstakosGroup.objects.get(name=name)
+ self.membership_set.create(group=group)
+
def save(self, update_timestamps=True, **kwargs):
if update_timestamps:
if not self.id:
self.date_joined = datetime.now()
self.updated = datetime.now()
-
+
# update date_signed_terms if necessary
if self.__has_signed_terms != self.has_signed_terms:
self.date_signed_terms = datetime.now()
-
+
if not self.id:
# set username
while not self.username:
- username = uuid.uuid4().hex[:30]
+ username = uuid.uuid4().hex[:30]
try:
- AstakosUser.objects.get(username = username)
- except AstakosUser.DoesNotExist, e:
+ AstakosUser.objects.get(username=username)
+ except AstakosUser.DoesNotExist:
self.username = username
if not self.provider:
self.provider = 'local'
- report_user_event(self)
+ self.email = self.email.lower()
self.validate_unique_email_isactive()
if self.is_active and self.activation_sent:
# reset the activation sent
self.activation_sent = None
+
super(AstakosUser, self).save(**kwargs)
-
- # set default group if does not exist
- groupname = 'default'
- if groupname not in self.__groupnames:
- try:
- group = Group.objects.get(name = groupname)
- self.groups.add(group)
- except Group.DoesNotExist, e:
- logger.exception(e)
def renew_token(self, flush_sessions=False, current_key=None):
md5 = hashlib.md5()
if flush_sessions:
self.flush_sessions(current_key)
msg = 'Token renewed for %s' % self.email
- logger._log(LOGGING_LEVEL, msg, [])
+ logger.log(LOGGING_LEVEL, msg)
def flush_sessions(self, current_key=None):
q = self.sessions
keys = q.values_list('session_key', flat=True)
if keys:
msg = 'Flushing sessions: %s' % ','.join(keys)
- logger._log(LOGGING_LEVEL, msg, [])
+ logger.log(LOGGING_LEVEL, msg, [])
engine = import_module(settings.SESSION_ENGINE)
for k in keys:
s = engine.SessionStore(k)
s.flush()
def __unicode__(self):
- return self.username
-
+ return '%s (%s)' % (self.realname, self.email)
+
def conflicting_email(self):
- q = AstakosUser.objects.exclude(username = self.username)
- q = q.filter(email = self.email)
+ q = AstakosUser.objects.exclude(username=self.username)
+ q = q.filter(email=self.email)
if q.count() != 0:
return True
return False
-
+
def validate_unique_email_isactive(self):
"""
Implements a unique_together constraint for email and is_active fields.
if self.id:
q = q.filter(~Q(id = self.id))
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):
term = get_latest_terms()
if not term:
return False
return True
+ def store_disturbed_quota(self, set=True):
+ self.disturbed_quota = set
+ self.save()
+
+
+class Membership(models.Model):
+ person = models.ForeignKey(AstakosUser)
+ group = models.ForeignKey(AstakosGroup)
+ date_requested = models.DateField(default=datetime.now(), blank=True)
+ date_joined = models.DateField(null=True, db_index=True, blank=True)
+
+ class Meta:
+ unique_together = ("person", "group")
+
+ def save(self, *args, **kwargs):
+ if not self.id:
+ if not self.group.moderation_enabled:
+ self.date_joined = datetime.now()
+ super(Membership, self).save(*args, **kwargs)
+
+ @property
+ def is_approved(self):
+ if self.date_joined:
+ return True
+ return False
+
+ def approve(self):
+ if self.is_approved:
+ return
+ if self.group.max_participants:
+ assert len(self.group.approved_members) + 1 <= self.group.max_participants, \
+ 'Maximum participant number has been reached.'
+ self.date_joined = datetime.now()
+ self.save()
+ quota_disturbed.send(sender=self, users=(self.person,))
+
+ def disapprove(self):
+ self.delete()
+ quota_disturbed.send(sender=self, users=(self.person,))
+
+class AstakosQuotaManager(models.Manager):
+ def _update_or_create(self, **kwargs):
+ assert kwargs, \
+ 'update_or_create() must be passed at least one keyword argument'
+ obj, created = self.get_or_create(**kwargs)
+ defaults = kwargs.pop('defaults', {})
+ if created:
+ return obj, True, False
+ else:
+ try:
+ params = dict(
+ [(k, v) for k, v in kwargs.items() if '__' not in k])
+ params.update(defaults)
+ for attr, val in params.items():
+ if hasattr(obj, attr):
+ setattr(obj, attr, val)
+ sid = transaction.savepoint()
+ obj.save(force_update=True)
+ transaction.savepoint_commit(sid)
+ return obj, False, True
+ except IntegrityError, e:
+ transaction.savepoint_rollback(sid)
+ try:
+ return self.get(**kwargs), False, False
+ except self.model.DoesNotExist:
+ raise e
+
+ update_or_create = _update_or_create
+
+class AstakosGroupQuota(models.Model):
+ objects = AstakosQuotaManager()
+ limit = models.PositiveIntegerField('Limit', null=True) # obsolete field
+ uplimit = models.BigIntegerField('Up limit', null=True)
+ resource = models.ForeignKey(Resource)
+ group = models.ForeignKey(AstakosGroup, blank=True)
+
+ class Meta:
+ unique_together = ("resource", "group")
+
+class AstakosUserQuota(models.Model):
+ objects = AstakosQuotaManager()
+ limit = models.PositiveIntegerField('Limit', null=True) # obsolete field
+ uplimit = models.BigIntegerField('Up limit', null=True)
+ resource = models.ForeignKey(Resource)
+ user = models.ForeignKey(AstakosUser)
+
+ class Meta:
+ unique_together = ("resource", "user")
+
+
class ApprovalTerms(models.Model):
"""
Model for approval terms
"""
- date = models.DateTimeField('Issue date', db_index=True, default=datetime.now())
+ date = models.DateTimeField(
+ 'Issue date', db_index=True, default=datetime.now())
location = models.CharField('Terms location', max_length=255)
+
class Invitation(models.Model):
"""
Model for registring invitations
is_consumed = models.BooleanField('Consumed?', default=False)
created = models.DateTimeField('Creation date', auto_now_add=True)
consumed = models.DateTimeField('Consumption date', null=True, blank=True)
-
+
def __init__(self, *args, **kwargs):
super(Invitation, self).__init__(*args, **kwargs)
if not self.id:
self.code = _generate_invitation_code()
-
+
def consume(self):
self.is_consumed = True
self.consumed = datetime.now()
def __unicode__(self):
return '%s -> %s [%d]' % (self.inviter, self.username, self.code)
-def report_user_event(user):
- def should_send(user):
- # report event incase of new user instance
- # or if specific fields are modified
- if not user.id:
- return True
- db_instance = AstakosUser.objects.get(id = user.id)
- for f in BILLING_FIELDS:
- if (db_instance.__getattribute__(f) != user.__getattribute__(f)):
- return True
- return False
-
- if QUEUE_CONNECTION and should_send(user):
-
- from astakos.im.queue.userevent import UserEvent
- from synnefo.lib.queue import exchange_connect, exchange_send, \
- exchange_close
-
- eventType = 'create' if not user.id else 'modify'
- body = UserEvent(QUEUE_CLIENT_ID, user, eventType, {}).format()
- conn = exchange_connect(QUEUE_CONNECTION)
- parts = urlparse(QUEUE_CONNECTION)
- exchange = parts.path[1:]
- routing_key = '%s.user' % exchange
- exchange_send(conn, routing_key, body)
- exchange_close(conn)
-
-def _generate_invitation_code():
- while True:
- code = randint(1, 2L**63 - 1)
- try:
- Invitation.objects.get(code=code)
- # An invitation with this code already exists, try again
- except Invitation.DoesNotExist:
- return code
-
-def get_latest_terms():
- try:
- term = ApprovalTerms.objects.order_by('-id')[0]
- return term
- except IndexError:
- pass
- return None
class EmailChangeManager(models.Manager):
@transaction.commit_on_success
Throws ValueError if there is already
"""
try:
- email_change = self.model.objects.get(activation_key=activation_key)
+ email_change = self.model.objects.get(
+ activation_key=activation_key)
if email_change.activation_key_expired():
email_change.delete()
raise EmailChange.DoesNotExist
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
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.'))
- user = models.ForeignKey(AstakosUser, unique=True, related_name='emailchange_user')
+ new_email_address = models.EmailField(_(u'new e-mail address'),
+ 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())
- activation_key = models.CharField(max_length=40, unique=True, db_index=True)
+ activation_key = models.CharField(
+ max_length=40, unique=True, db_index=True)
objects = EmailChangeManager()
expiration_date = timedelta(days=EMAILCHANGE_ACTIVATION_DAYS)
return self.requested_at + expiration_date < datetime.now()
-class Service(models.Model):
- name = models.CharField('Name', max_length=255, unique=True)
- url = models.FilePathField()
- icon = models.FilePathField(blank=True)
- auth_token = models.CharField('Authentication Token', max_length=32,
- null=True, blank=True)
- auth_token_created = models.DateTimeField('Token creation date', null=True)
- auth_token_expires = models.DateTimeField('Token expiration date', null=True)
-
- def renew_token(self):
- md5 = hashlib.md5()
- md5.update(settings.SECRET_KEY)
- md5.update(self.name.encode('ascii', 'ignore'))
- md5.update(self.url.encode('ascii', 'ignore'))
- md5.update(asctime())
-
- self.auth_token = b64encode(md5.digest())
- self.auth_token_created = datetime.now()
- self.auth_token_expires = self.auth_token_created + \
- timedelta(hours=AUTH_TOKEN_DURATION)
- msg = 'Token renewed for %s' % self.name
- logger._log(LOGGING_LEVEL, msg, [])
class AdditionalMail(models.Model):
"""
owner = models.ForeignKey(AstakosUser)
email = models.EmailField()
+
+def _generate_invitation_code():
+ while True:
+ code = randint(1, 2L ** 63 - 1)
+ try:
+ Invitation.objects.get(code=code)
+ # An invitation with this code already exists, try again
+ except Invitation.DoesNotExist:
+ return code
+
+
+def get_latest_terms():
+ try:
+ term = ApprovalTerms.objects.order_by('-id')[0]
+ return term
+ except IndexError:
+ pass
+ return None
+
class PendingThirdPartyUser(models.Model):
"""
Model for registring successful third party user authentications
session_key = models.CharField(_('session key'), max_length=40)
user = models.ForeignKey(AstakosUser, related_name='sessions', null=True)
+
def create_astakos_user(u):
try:
AstakosUser.objects.get(user_ptr=u.pk)
extended_user = AstakosUser(user_ptr_id=u.pk)
extended_user.__dict__.update(u.__dict__)
extended_user.save()
- except:
- pass
+ except BaseException, e:
+ logger.exception(e)
+
-def superuser_post_syncdb(sender, **kwargs):
- # if there was created a superuser
- # associate it with an AstakosUser
+def fix_superusers(sender, **kwargs):
+ # Associate superusers with AstakosUser
admins = User.objects.filter(is_superuser=True)
for u in admins:
create_astakos_user(u)
-post_syncdb.connect(superuser_post_syncdb)
-def superuser_post_save(sender, instance, **kwargs):
- if instance.is_superuser:
- create_astakos_user(instance)
+def user_post_save(sender, instance, created, **kwargs):
+ if not created:
+ return
+ create_astakos_user(instance)
-def astakosuser_post_save(sender, instance, **kwargs):
- pass
-post_save.connect(superuser_post_save, sender=User)
+def set_default_group(user):
+ try:
+ default = AstakosGroup.objects.get(name='default')
+ Membership(
+ group=default, person=user, date_joined=datetime.now()).save()
+ except AstakosGroup.DoesNotExist, e:
+ logger.exception(e)
+
+
+def astakosuser_pre_save(sender, instance, **kwargs):
+ instance.aquarium_report = False
+ instance.new = False
+ try:
+ db_instance = AstakosUser.objects.get(id=instance.id)
+ except AstakosUser.DoesNotExist:
+ # create event
+ instance.aquarium_report = True
+ instance.new = True
+ else:
+ get = AstakosUser.__getattribute__
+ l = filter(lambda f: get(db_instance, f) != get(instance, f),
+ BILLING_FIELDS)
+ instance.aquarium_report = True if l else False
+
+
+def astakosuser_post_save(sender, instance, created, **kwargs):
+ if instance.aquarium_report:
+ report_user_event(instance, create=instance.new)
+ if not created:
+ return
+ set_default_group(instance)
+ # TODO handle socket.error & IOError
+ register_users((instance,))
+ instance.renew_token()
+
+
+def resource_post_save(sender, instance, created, **kwargs):
+ if not created:
+ return
+ register_resources((instance,))
+
+
+def send_quota_disturbed(sender, instance, **kwargs):
+ users = []
+ extend = users.extend
+ if sender == Membership:
+ if not instance.group.is_enabled:
+ return
+ extend([instance.person])
+ elif sender == AstakosUserQuota:
+ extend([instance.user])
+ elif sender == AstakosGroupQuota:
+ if not instance.group.is_enabled:
+ return
+ extend(instance.group.astakosuser_set.all())
+ elif sender == AstakosGroup:
+ if not instance.is_enabled:
+ return
+ quota_disturbed.send(sender=sender, users=users)
+
+
+def on_quota_disturbed(sender, users, **kwargs):
+# print '>>>', locals()
+ if not users:
+ return
+ send_quota(users)
def renew_token(sender, instance, **kwargs):
if not instance.id:
instance.renew_token()
+post_syncdb.connect(fix_superusers)
+post_save.connect(user_post_save, sender=User)
+pre_save.connect(astakosuser_pre_save, sender=AstakosUser)
+post_save.connect(astakosuser_post_save, sender=AstakosUser)
+post_save.connect(resource_post_save, sender=Resource)
+
+quota_disturbed = Signal(providing_args=["users"])
+quota_disturbed.connect(on_quota_disturbed)
+
+post_delete.connect(send_quota_disturbed, sender=AstakosGroup)
+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)
+
pre_save.connect(renew_token, sender=AstakosUser)
pre_save.connect(renew_token, sender=Service)
\ No newline at end of file
DEFAULT_USER_LEVEL = getattr(settings, 'ASTAKOS_DEFAULT_USER_LEVEL', 4)
INVITATIONS_PER_LEVEL = getattr(settings, 'ASTAKOS_INVITATIONS_PER_LEVEL', {
- 0 : 100,
- 1 : 2,
- 2 : 0,
- 3 : 0,
- 4 : 0
+ 0: 100,
+ 1: 2,
+ 2: 0,
+ 3: 0,
+ 4: 0
})
# Address to use for outgoing emails
-DEFAULT_FROM_EMAIL = getattr(settings, 'ASTAKOS_DEFAULT_FROM_EMAIL', 'GRNET Cloud <no-reply@grnet.gr>')
-DEFAULT_CONTACT_EMAIL = getattr(settings, 'ASTAKOS_DEFAULT_CONTACT_EMAIL', 'support@cloud.grnet.gr')
-DEFAULT_ADMIN_EMAIL = getattr(settings, 'ASTAKOS_DEFAULT_ADMIN_EMAIL', 'support@cloud.grnet.gr')
+DEFAULT_CONTACT_EMAIL = getattr(
+ settings, 'ASTAKOS_DEFAULT_CONTACT_EMAIL', 'support@cloud.grnet.gr')
# Identity Management enabled modules
IM_MODULES = getattr(settings, 'ASTAKOS_IM_MODULES', ['local', 'shibboleth'])
RECAPTCHA_PUBLIC_KEY = getattr(settings, 'ASTAKOS_RECAPTCHA_PUBLIC_KEY', '')
RECAPTCHA_PRIVATE_KEY = getattr(settings, 'ASTAKOS_RECAPTCHA_PRIVATE_KEY', '')
RECAPTCHA_OPTIONS = getattr(settings, 'ASTAKOS_RECAPTCHA_OPTIONS',
- {'theme' : 'custom', 'custom_theme_widget': 'okeanos_recaptcha'})
+ {'theme': 'custom', 'custom_theme_widget': 'okeanos_recaptcha'})
RECAPTCHA_USE_SSL = getattr(settings, 'ASTAKOS_RECAPTCHA_USE_SSL', True)
RECAPTCHA_ENABLED = getattr(settings, 'ASTAKOS_RECAPTCHA_ENABLED', True)
BILLING_FIELDS = getattr(settings, 'ASTAKOS_BILLING_FIELDS', ['is_active'])
# Queue for billing.
-QUEUE_CONNECTION = getattr(settings, 'ASTAKOS_QUEUE_CONNECTION', None) # Example: 'rabbitmq://guest:guest@localhost:5672/astakos'
+QUEUE_CONNECTION = getattr(settings, 'ASTAKOS_QUEUE_CONNECTION', None) # Example: 'rabbitmq://guest:guest@localhost:5672/astakos'
# Set where the user should be redirected after logout
LOGOUT_NEXT = getattr(settings, 'ASTAKOS_LOGOUT_NEXT', '')
# Set user email patterns that are automatically activated
-RE_USER_EMAIL_PATTERNS = getattr(settings, 'ASTAKOS_RE_USER_EMAIL_PATTERNS', [])
+RE_USER_EMAIL_PATTERNS = getattr(
+ settings, 'ASTAKOS_RE_USER_EMAIL_PATTERNS', [])
# Messages to display on login page header
# e.g. {'warning': 'This warning message will be displayed on the top of login page'}
# e.g. {'https://cms.okeanos.grnet.gr/': 'Back to ~okeanos'}
PROFILE_EXTRA_LINKS = getattr(settings, 'ASTAKOS_PROFILE_EXTRA_LINKS', {})
-# The number of unsuccessful login requests per minute allowed for a specific email
-RATELIMIT_RETRIES_ALLOWED = getattr(settings, 'ASTAKOS_RATELIMIT_RETRIES_ALLOWED', 3)
+# The number of unsuccessful login requests per minute allowed for a specific user
+RATELIMIT_RETRIES_ALLOWED = getattr(
+ settings, 'ASTAKOS_RATELIMIT_RETRIES_ALLOWED', 3)
# If False the email change mechanism is disabled
EMAILCHANGE_ENABLED = getattr(settings, 'ASTAKOS_EMAILCHANGE_ENABLED', False)
# Set the expiration time (in days) of email change requests
-EMAILCHANGE_ACTIVATION_DAYS = getattr(settings, 'ASTAKOS_EMAILCHANGE_ACTIVATION_DAYS', 10)
+EMAILCHANGE_ACTIVATION_DAYS = getattr(
+ settings, 'ASTAKOS_EMAILCHANGE_ACTIVATION_DAYS', 10)
# Set the astakos main functions logging severity (None to disable)
from logging import INFO
LOGGING_LEVEL = getattr(settings, 'ASTAKOS_LOGGING_LEVEL', INFO)
# Configurable email subjects
-INVITATION_EMAIL_SUBJECT = getattr(settings, 'ASTAKOS_INVITATION_EMAIL_SUBJECT',
- 'Invitation to %s alpha2 testing' % SITENAME)
+INVITATION_EMAIL_SUBJECT = getattr(
+ settings, 'ASTAKOS_INVITATION_EMAIL_SUBJECT',
+ 'Invitation to %s alpha2 testing' % SITENAME)
GREETING_EMAIL_SUBJECT = getattr(settings, 'ASTAKOS_GREETING_EMAIL_SUBJECT',
- 'Welcome to %s alpha2 testing' % SITENAME)
+ 'Welcome to %s alpha2 testing' % SITENAME)
FEEDBACK_EMAIL_SUBJECT = getattr(settings, 'ASTAKOS_FEEDBACK_EMAIL_SUBJECT',
- 'Feedback from %s alpha2 testing' % SITENAME)
-VERIFICATION_EMAIL_SUBJECT = getattr(settings, 'ASTAKOS_VERIFICATION_EMAIL_SUBJECT',
- '%s alpha2 testing account activation is needed' % SITENAME)
-ADMIN_NOTIFICATION_EMAIL_SUBJECT = getattr(settings, 'ASTAKOS_ADMIN_NOTIFICATION_EMAIL_SUBJECT',
- '%s alpha2 testing account created (%%(user)s)' % SITENAME)
-HELPDESK_NOTIFICATION_EMAIL_SUBJECT = getattr(settings, 'ASTAKOS_HELPDESK_NOTIFICATION_EMAIL_SUBJECT',
- '%s alpha2 testing account activated (%%(user)s)' % SITENAME)
-EMAIL_CHANGE_EMAIL_SUBJECT = getattr(settings, 'ASTAKOS_EMAIL_CHANGE_EMAIL_SUBJECT',
- 'Email change on %s alpha2 testing' % SITENAME)
-PASSWORD_RESET_EMAIL_SUBJECT = getattr(settings, 'ASTAKOS_PASSWORD_RESET_EMAIL_SUBJECT',
- 'Password reset on %s alpha2 testing' % SITENAME)
+ 'Feedback from %s alpha2 testing' % SITENAME)
+VERIFICATION_EMAIL_SUBJECT = getattr(
+ settings, 'ASTAKOS_VERIFICATION_EMAIL_SUBJECT',
+ '%s alpha2 testing account activation is needed' % SITENAME)
+ACCOUNT_CREATION_SUBJECT = getattr(
+ settings, 'ASTAKOS_ACCOUNT_CREATION_SUBJECT',
+ '%s alpha2 testing account created (%%(user)s)' % SITENAME)
+GROUP_CREATION_SUBJECT = getattr(settings, 'ASTAKOS_GROUP_CREATION_SUBJECT',
+ '%s alpha2 testing group created (%%(group)s)' % SITENAME)
+HELPDESK_NOTIFICATION_EMAIL_SUBJECT = getattr(
+ settings, 'ASTAKOS_HELPDESK_NOTIFICATION_EMAIL_SUBJECT',
+ '%s alpha2 testing account activated (%%(user)s)' % SITENAME)
+EMAIL_CHANGE_EMAIL_SUBJECT = getattr(
+ settings, 'ASTAKOS_EMAIL_CHANGE_EMAIL_SUBJECT',
+ 'Email change on %s alpha2 testing' % SITENAME)
+PASSWORD_RESET_EMAIL_SUBJECT = getattr(
+ settings, 'ASTAKOS_PASSWORD_RESET_EMAIL_SUBJECT',
+ 'Password reset on %s alpha2 testing' % SITENAME)
+
+# Set the quota holder component URI
+QUOTA_HOLDER_URL = getattr(settings, 'ASTAKOS_QUOTA_HOLDER_URL', '')
+
+# Set the cloud service properties
+SERVICES = getattr(settings, 'ASTAKOS_SERVICES', {
+ 'cyclades': {
+ 'url': 'https://node1.example.com/ui/',
+ 'resources': [{
+ 'name':'vm',
+ 'group':'compute',
+ 'uplimit':2,
+ 'desc': 'Number of virtual machines'
+ },{
+ 'name':'disk',
+ 'group':'compute',
+ 'uplimit':30*1024*1024*1024,
+ 'unit':'bytes',
+ 'desc': 'Virtual machine disk size'
+ },{
+ 'name':'cpu',
+ 'group':'compute',
+ 'uplimit':6,
+ 'desc': 'Number of virtual machine processors'
+ },{
+ 'name':'ram',
+ 'group':'compute',
+ 'uplimit':6*1024*1024*1024,
+ 'unit':'bytes',
+ 'desc': 'Virtual machines'
+ },{
+ 'name':'network.private',
+ 'group':'network',
+ 'uplimit':1,
+ 'desc': 'Private networks'
+ }
+ ]
+ },
+ 'pithos+': {
+ 'url': 'https://node2.example.com/ui/',
+ 'resources':[{
+ 'name':'diskspace',
+ 'group':'storage',
+ 'uplimit':5 * 1024 * 1024 * 1024,
+ 'unit':'bytes',
+ 'desc': 'Pithos account diskspace'
+ }]
+ }
+})
+
+# Set the billing URI
+AQUARIUM_URL = getattr(settings, 'ASTAKOS_AQUARIUM_URL', '')
+
+# Set how many objects should be displayed per page
+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)
+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'
+ }
+
+ }
+
+ })
# Permit local account migration
ENABLE_LOCAL_ACCOUNT_MIGRATION = getattr(settings, 'ASTAKOS_ENABLE_LOCAL_ACCOUNT_MIGRATION', True)
\ No newline at end of file
div.cloudbar a,
div.cloudbar .profile span,
-div.cloudbar li {font-family:'Didact Gothic',Verdana,sans-serif !important; letter-spacing: 1px !important}
+div.cloudbar li {font-family:'Open Sans',sans-serif !important; letter-spacing: 1px !important}
.cloudbar .wrapper { width:auto; padding:0;}
.cloudbar a { color:#fff; text-decoration:none;}
$("head").append(css);
// load fonts
- var font_url = 'https://fonts.googleapis.com/css?family=Didact+Gothic&subset=latin,greek,greek-ext';
+ var font_url = 'http://fonts.googleapis.com/css?family=Open+Sans:400,600,700&subset=latin,greek-ext,greek';
var css_font = $("<link />");
css_font.attr({rel:'stylesheet', type:'text/css', href:font_url});
$("head").append(css_font);
/***** Begin Theme, feel free to edit in here! ******/
+
+/* 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_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;}
+.form-row .dk_focus .dk_toggle { background-color:transparent; border:0 none; color:#000; box-shadow: none;}
+.1form-row .dk_options { display:block; }
+.form-row .dk_options { box-shadow:none; border-radius:0; z-index:3; margin:6px -1px 0; width:auto; left:0;}
+.form-row .dk_options a { font-weight:normal;color:#808080; padding:5px 0; text-indent:1.5em; border-bottom-color: #ccc }
+.form-row .dk_options a:hover { border-bottom-color: #ccc }
+.form-row .dk_options_inner { padding:0; margin:0; box-shadow:none; text-shadow:none; border-radius:0; border:1px solid #ccc ; margin-top:4px;}
+.form-row .dk-options_inner li { list-style:none outside; }
+.form-row .dk_options a:hover,
+.form-row .dk_option_current a { text-shadow:none; background-color: #fff; text-decoration:none; color:#F89A1C}
+
+/* end custom theme */
+
+
+
+
+
/* One container to bind them... */
.dk_container {
- font-family: 'Antic', sans-serif;
-
- font-weight: normal;
- line-height: 42px;
- letter-spacing: 1px;
- border: 1px solid #808080;
- height:42px;
- display: inline-block;
- margin-bottom: -1px;
- padding-left:21px;
-
- z-index: 2;
- width:308px;
+ background: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#f5f5f5));
+ background: -moz-linear-gradient(top, #fff, #f5f5f5);
+ background: -o-linear-gradient(top, #fff, #f5f5f5);
+ background-color: #f5f5f5;
+ font-family: 'Helvetica', Arial, sans-serif;
+ font-size: 12px;
+ font-weight: bold;
+ line-height: 14px;
+ margin-bottom: 18px;
+ border-radius: 5px;
+ -moz-border-radius: 5px;
+ -webkit-border-radius: 5px;
}
.dk_container:focus {
outline: 0;
.dk_container a {
cursor: pointer;
text-decoration: none;
- color:#808080;
-
}
/* Opens the dropdown and holds the menu label */
* Help: Arrow image not appearing
* Try updating this property to your correct dk_arrows.png path
*/
- background:url(../images/arrow_02.jpg) no-repeat 276px center;
-
+ background-image: url('images/dk_arrows.png');
+ background-repeat: no-repeat;
+ background-position: 90% center;
+ border: 1px solid #ccc;
+ color: #333;
+ padding: 7px 45px 7px 10px;
+ text-shadow: #fff 1px 1px 0;
+ border-radius: 5px;
+ -moz-border-radius: 5px;
+ -webkit-border-radius: 5px;
+ -webkit-transition: border-color .5s;
+ -moz-transition: border-color .5s;
+ -o-transition: border-color .5s;
+ transition: border-color .5s;
}
.dk_toggle:hover {
-
+ border-color: #8c8c8c;
}
/* Applied when the dropdown is focused */
.dk_focus .dk_toggle {
-
+ border-color: #40b5e2;
}
.dk_focus .dk_toggle {
-
+ box-shadow: 0 0 5px #40b5e2;
+ -moz-box-shadow: 0 0 5px #40b5e2;
+ -webkit-box-shadow: 0 0 5px #40b5e2;
}
/* Applied whenever the dropdown is open */
.dk_open {
-
+ box-shadow: 0 0 5px #40b5e2;
+ -moz-box-shadow: 0 0 5px #40b5e2;
+ -webkit-box-shadow: 0 0 5px #40b5e2;
/**
* Help: Dropdown menu is covered by something
* Try setting this value higher
z-index: 10;
}
.dk_open .dk_toggle {
-
-
+ background-color: #ececec;
+ border-color: #8c8c8c;
+ color: #ccc;
+ box-shadow: inset 0 -2px 5px #ccc;
+ border-radius: 5px 5px 0 0;
+ -moz-border-radius: 5px 5px 0 0;
+ -webkit-border-radius: 5px 5px 0 0;
}
/* The outer container of the options */
.dk_options {
-
+ box-shadow: rgba(0, 0, 0, .2) 0 2px 8px;
+ -moz-box-shadow: rgba(0, 0, 0, .2) 0 2px 8px;
+ -webkit-box-shadow: rgba(0, 0, 0, .2) 0 2px 8px;
+ border-radius: 0 0 5px 5px;
+ -moz-border-radius: 0 0 5px 5px;
+ -webkit-border-radius: 0 0 5px 5px;
}
.dk_options a {
background-color: #fff;
border-bottom: 1px solid #999;
- padding: 8px 20px;
+ font-weight: bold;
+ padding: 8px 10px;
}
.dk_options li:last-child a {
border-bottom: none;
}
.dk_options a:hover,
.dk_option_current a {
-
+ background-color: #0084c7;
+ border-bottom-color: #004c72;
+ color: #fff;
+ text-decoration: none;
+ text-shadow: rgba(0, 0, 0, .5) 0 1px 0;
}
/* Inner container for options, this is what makes the scrollbar possible. */
-.dk_options_inner {
- padding:0;
- margin:0;
- border: 1px solid #808080;
- border-top:0 none;
+.dk_options_inner {
+ border: 1px solid #8c8c8e;
+ border-bottom-width: 2px;
+ border-bottom-color: #999;
+ color: #333;
+ max-height: 250px;
+ text-shadow: #fff 0 1px 0;
+ border-radius: 0 0 5px 5px;
+ -moz-border-radius: 0 0 5px 5px;
+ -webkit-border-radius: 0 0 5px 5px;
}
-.dk_options_inner li { list-style:none outside;}
/* Set a max-height on the options inner */
.dk_options_inner,
.dk_container {
display: none;
+ float: left;
position: relative;
}
.dk_container a {
a span { cursor:pointer; }\r
abbr[title], dfn[title] { border-bottom:1px dotted; cursor:help; }\r
address { font-style:italic; margin:0 0 1.5em 0; }\r
-body { background:white; color:#222; font-family: 'Didact Gothic', Verdana, sans-serif; font-style:normal; font-size:81.3%; line-height:1.5; text-align:left; letter-spacing:1px;}\r
+body { background:white; color:#222; font-family: 'Open Sans', sans-serif; font-style:normal; font-size:81.3%; line-height:1.5; text-align:left; letter-spacing:1px;}\r
code, kbd, pre, samp { font-family:monospace, sans-serif; }\r
del { text-decoration:line-through; }\r
dl { margin:1em 0; }\r
#forms input:focus { position: relative; border: 1px solid #000; z-index: 100; }\r
#forms .input:focus label,\r
#forms input:focus label { z-index: 300; }\r
-form.withlabels label { width: 224px; display: block; float: left; padding-top: 1em; }\r
+form.withlabels label { width: 224px; display: block; float: left; padding-top: 0.8em; }\r
form.withlabels input[type=text], \r
-form.withlabels input[type=password] { width: 240px; }\r
+form.withlabels input[type=password] { width: 270px; }\r
form.withlabels input[type=text].long, \r
form.withlabels input[type=password].long, \r
form.withlabels textarea.long { width: 224px; }\r
+form.withlabels textarea { width:270px; }\r
.login-section a.button { margin-bottom: 12px; }\r
.login-section a.button:last-child { margin-bottom: none;}\r
.login-section a.button.withicon { background-repeat: no-repeat; background-position: 15px 50%; padding-left: 40px; }\r
form input.text,\r
form input[type="text"],\r
form input[type="password"] { color:#808080; font-family: 'Didact Gothic', Verdana, sans-serif; font-weight: normal; line-height: 22px; letter-spacing: 1px;border: 1px solid #808080; height: 21px; display: inline-block; margin-bottom: -1px; padding: 0.8em; padding-left: 1.5em; z-index: 2; width:300px; }\r
-form select { font-family: 'Didact Gothic', Verdana, sans-serif; font-weight: normal; line-height: 22px; letter-spacing: 1px; border: 1px solid #808080; display:block; margin-bottom: -1px; padding: 0.8em; padding-left: 1.5em; z-index: 2; width:331px; }\r
+form select { font-family: 'Didact Gothic', Verdana, sans-serif; font-weight: normal; line-height: 22px; letter-spacing: 1px; border: 1px solid #808080; display:block; margin-bottom: -1px; padding: 0.8em; padding-left: 1.5em; z-index: 2; width:301px; }\r
form textarea:focus,\r
form input.text:focus,\r
form input[type="text"]:focus,\r
form input[type="password"]:focus label { z-index: 300;}\r
form input.submit, \r
form input[type="submit"],\r
-a.submit { font-family: 'Didact Gothic', Verdana, sans-serif; font-size: 14px; font-weight: normal; line-height: 22px; letter-spacing:1px; background-color: #f89a1c;color: #ffffff; border: none; padding: 10px 22px;font-size: 1em; margin:15px 0 0 223px; height:43px; }\r
+a.submit { font-family: 'Open Sans', sans-serif; font-size: 14px; font-weight: normal; line-height: 22px; letter-spacing:1px; background-color: #f89a1c;color: #ffffff; border: none; padding: 10px 22px;font-size: 1em; margin:15px 0 0 223px; height:43px; }\r
form.innerlabels input.submit, \r
form.innerlabels input[type="submit"] {margin-left:0;}\r
form input.submit:hover, \r
form input[type="submit"]:hover,\r
-a.submit:hover { background-color:#3582AC;border-color:#3582ac; text-decoration:none;}\r
+.content a.submit:hover { background-color:#3582AC;border-color:#3582ac; text-decoration:none;}\r
form input.submit.back, \r
form input[type="submit"].back { text-decoration: none; bottom: 0; float: right; z-index: 500; }\r
form input.submit.back.right, \r
div.form-stacked { margin-bottom: 4em; }\r
.checkbox-widget.checked { background-color: #FF0000; background-image: url("../images/checkbox.png"); background-position: 50% 50%; }\r
.content a.checkbox-widget { border: 1px solid #808080; cursor: pointer; display: block; float: left; height: 25px; margin:5px 20px 0 0 ; width: 25px; }\r
-form.withlabels .checkbox-widget { margin-top:20px; }\r
+form.withlabels .checkbox-widget { margin-top:12px; }\r
form.innerlabels .checkbox-widget + label { position:static; line-height:36px; color:#808080; }\r
form.innerlabels .checkbox-widget + label + a { border-bottom:1px solid #F89A1C; font-size: 1.1em; }\r
-form span.info { position:absolute;z-index:10; bottom:32px; }\r
+form span.info { position:absolute;z-index:101; top:10px; }\r
form.innerlabels span.info { left: 290px; }\r
-form.withlabels span.info { left: 440px; }\r
+form.withlabels span.info { left:485px; }\r
form span.info em { display:block; overflow:hidden; position:absolute; left:0; text-indent:-100px; top:0; height:21px; width:21px; background:url(../images/symbols.png) no-repeat -4px -31px;cursor:pointer; }\r
form span.info:hover em { background-position:-4px -3px; }\r
-form span.info span { position:absolute; left:29px; top:-2px; width:120px; padding-left:30px; background:url(../images/black-line.jpg ) no-repeat left 12px; min-height:50px; display:none; font-size:0.846em;}\r
+form span.info span { position:absolute; left:29px; top:-2px; width:120px; padding-left:30px; background:url(../images/black-line.jpg ) no-repeat left 8px; min-height:50px; display:none; font-size:0.846em;}\r
form span.info:hover span { display:block; }\r
form .with-errors span.info { display:none;}\r
form p { margin-bottom:0;position:relative;}\r
form.withlabels .with-checkbox .checkbox-widget { position:absolute; left:224px; top:0; }\r
\r
form .extra-img { display:block; width:21px; height:21px; overflow:hidden; position:absolute; }\r
-form.withlabels .extra-img { left:460px; bottom:12px; }\r
+form.withlabels .extra-img { left:485px; bottom:12px; }\r
form.innerlabels .extra-img { bottom:10px; left:290px }\r
-form .with-checkbox .extra-img { top:20px; } \r
+form .with-checkbox .extra-img { } \r
form .with-errors .extra-img { background:url(../images/symbols.png) no-repeat -58px -3px; z-index:101;}\r
form .with-errors textarea+.extra-img,\r
form .with-errors noscript+.extra-img { background:transparent;}\r
form .with-errors input[type="password"] { border:1px solid red;}\r
form.innerlabels .with-errors .extra-img { left:290px;}\r
form input[readonly="True"]+ span.extra-img { background:url(../images/symbols.png) no-repeat -111px -3px; z-index:101;}\r
+form.withlabels .with-checkbox .extra-img { display:none; } \r
.errorlist { margin:0; padding:0;}\r
.errorlist li { list-style:none outside;}\r
\r
#okeanos_recaptcha .actions-wrap { border-top:1px solid #808080 }\r
\r
input.submit:focus,\r
-input[type="submit"]:focus { box-shadow: 0 0 0 1px #FFFFFF inset; border:1px solid #F89A1C; outline:0 none; } \r
+input[type="submit"]:focus,\r
+.content a.submit:focus { box-shadow: 0 0 0 1px #FFFFFF inset; border:1px solid #F89A1C; outline:0 none; } \r
+.content a.submit:hover { border-color:#3582AC; }\r
\r
-@media screen and (max-width : 591px) { \r
- form.withlabels .extra-img { left:222px; }\r
- form.withlabels span.info { left:222px; }\r
- form .extra-img { left:222px; }\r
+p+ form { margin:2em 0; }\r
+.full-dotted legend { padding-top:40px; }\r
+form .form-row .radios label { width:auto; float:none; display:inline-block; }\r
+form .form-row .radios input[type="radio"] { margin-right:165px; vertical-align:middle; }\r
+form.withfieldset textarea { width: 224px; }\r
+form.withfieldset select { width: 256px; background:#fff;}\r
+form+p:first-child, form legend + p { margin-bottom:2em; }\r
+\r
+\r
+form.link-like { display:inline-block; margin:0 5px; float:right;}\r
+form.link-like input[type="submit"] { margin:0; padding:0 5px; background:transparent; color:#F89A1C; cursor:pointer; height:auto; line-height:120%; }\r
+form.link-like input[type="submit"]:hover { text-decoration:underline; }\r
+form.link-like.alone { float:none; margin:0;}\r
+form.link-like.alone .form-row { margin:0; }\r
+form.link-like.alone input[type="submit"] { padding:0; } \r
+.projects form.withlabels .checkbox-widget { margin-top:5px; }\r
+.projects form .with-checkbox { margin:20px 0; }\r
+.projects form .with-checkbox label { padding-top:7px; }\r
+.projects form .with-checkbox span.info { bottom:24px; }\r
+.projects .minimal { float:right; position:relative; margin-bottom:-28px;}\r
+.projects .minimal select { padding:3px; width:250px; }\r
+.projects .minimal label { position:absolute; right:290px; top:5px; white-space:nowrap}\r
+\r
+@media screen and (max-width : 630px) { \r
+ form.withlabels .extra-img { left:260px; }\r
+ form.withlabels span.info { left:260px; }\r
+ form .extra-img { left:260px; }\r
.form-error { margin-left:0!important; } \r
\r
}\r
\r
\r
@media screen and (max-width : 410px) { \r
+ form.withlabels .extra-img { left:90% } \r
form.login { width:auto; }\r
form textarea, \r
form input.text, \r
form input[type="text"], \r
form input[type="password"] { width:90%; }\r
- form.withlabels select { width:90%; }\r
+ form.withlabels input[type="text"], \r
+ form.withlabels input[type="password"],\r
+ form.withlabels textarea,\r
+ form textarea, \r
+ form input.text, \r
+ form input[type="text"], \r
+ form input[type="password"] { width:90%; }\r
+ form.withlabels select { width:90%; }\r
form.innerlabels .extra-img,\r
form.innerlabels .with-errors .extra-img { left:90%; }\r
form.innerlabels { width:auto; }\r
}\r
\r
\r
-@media screen and ( width : 320px) { \r
+@media screen and ( max-width : 320px) { \r
#okeanos_recaptcha { width:259px }\r
- \r
-}\r
-\r
-p+ form { margin:2em 0; }\r
-.full-dotted legend { padding-top:40px; }\r
-form .form-row .radios label { width:auto; float:none; display:inline-block; }\r
-form .form-row .radios input[type="radio"] { margin-right:165px; vertical-align:middle; }\r
-form.withfieldset textarea { width: 224px; }\r
-form.withfieldset select { width: 256px; background:#fff;}\r
-form+p:first-child, form legend + p { margin-bottom:2em; }\r
-\r
-/* dropkick select extra styles */\r
-\r
-.form-row .dk_container { border-radius:0; margin-bottom:0; border: 1px solid #808080; height: 21px; letter-spacing: 1px; line-height: 22px; margin-bottom: -1px; width:254px; padding:0.8em 0; font-weight:normal; font-family: 'Didact Gothic', Verdana, sans-serif; font-size:1em; background:transparent; color:#808080;}\r
-.form-row .dk_toggle { border-radius:0; padding:7px 0; border:0 none; text-indent:1.5em; text-decoration:none;background-image:url(../images/arrow_02.jpg); background-position:90% 5px;}\r
-.form-row .dk_toggle:hover { text-decoration:none; }\r
-.form-row .dk_open { background:transparent; box-shadow: none; }\r
-.form-row .dk_open .dk_toggle { background-color:transparent; border:0 none; color:#000; box-shadow: none;}\r
-.form-row .dk_focus .dk_toggle { background-color:transparent; border:0 none; color:#000; box-shadow: none;}\r
-.1form-row .dk_options { display:block; }
-.form-row .dk_options { box-shadow:none; border-radius:0; z-index:3; margin:6px -1px 0; width:auto; left:0;}\r
-.form-row .dk_options a { font-weight:normal;color:#808080 }\r
-.form-row .dk_options_inner { padding:0; margin:0; box-shadow:none; text-shadow:none; border-radius:0; border:1px solid #8C8C8E ;}\r
-.form-row .dk-options_inner li { list-style:none outside; }\r
-.form-row .dk_options a:hover, \r
-.form-row .dk_option_current a { text-shadow:none; background-color: #E7E7E3;}
\ No newline at end of file
+ form.withlabels input[type="text"], \r
+ form.withlabels input[type="password"],\r
+ form.withlabels textarea,\r
+ form textarea, \r
+ form input.text, \r
+ form input[type="text"], \r
+ form input[type="password"] { width:90%; }\r
+ .form-row .dk_container { width:100%; }\r
+}
\ No newline at end of file
--- /dev/null
+/*!
+ * jQuery UI CSS Framework 1.8.21
+ *
+ * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Theming/API
+ */
+
+/* Layout helpers
+----------------------------------*/
+.ui-helper-hidden { display: none; }
+.ui-helper-hidden-accessible { position: absolute !important; clip: rect(1px 1px 1px 1px); clip: rect(1px,1px,1px,1px); }
+.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; }
+.ui-helper-clearfix:before, .ui-helper-clearfix:after { content: ""; display: table; }
+.ui-helper-clearfix:after { clear: both; }
+.ui-helper-clearfix { zoom: 1; }
+.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); }
+
+
+/* Interaction Cues
+----------------------------------*/
+.ui-state-disabled { cursor: default !important; }
+
+
+/* Icons
+----------------------------------*/
+
+/* states and images */
+.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; }
+
+
+/* Misc visuals
+----------------------------------*/
+
+/* Overlays */
+.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
+
+
+/*!
+ * jQuery UI CSS Framework 1.8.21
+ *
+ * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Theming/API
+ *
+ * To view and modify this theme, visit http://jqueryui.com/themeroller/?ctl=themeroller
+ */
+
+
+/* Component containers
+----------------------------------*/
+.ui-widget { font-family: Verdana,Arial,sans-serif; font-size: 1.1em; }
+.ui-widget .ui-widget { font-size: 1em; }
+.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Verdana,Arial,sans-serif; font-size: 1em; }
+.ui-widget-content { border: 1px solid #aaaaaa; background: #ffffff url(images/ui-bg_glass_75_ffffff_1x400.png) 50% 50% repeat-x; color: #222222; }
+.ui-widget-content a { color: #222222; }
+.ui-widget-header { border: 1px solid #aaaaaa; background: #cccccc url(images/ui-bg_highlight-soft_75_cccccc_1x100.png) 50% 50% repeat-x; color: #222222; font-weight: bold; }
+.ui-widget-header a { color: #222222; }
+
+/* Interaction states
+----------------------------------*/
+.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #d3d3d3; background: #e6e6e6 url(images/ui-bg_glass_75_e6e6e6_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #555555; }
+.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #555555; text-decoration: none; }
+.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #999999; background: #dadada url(images/ui-bg_glass_75_dadada_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #212121; }
+.ui-state-hover a, .ui-state-hover a:hover { color: #212121; text-decoration: none; }
+.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #aaaaaa; background: #ffffff url(images/ui-bg_glass_65_ffffff_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #212121; }
+.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #212121; text-decoration: none; }
+.ui-widget :active { outline: none; }
+
+/* Interaction Cues
+----------------------------------*/
+.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight {border: 1px solid #fcefa1; background: #fbf9ee url(images/ui-bg_glass_55_fbf9ee_1x400.png) 50% 50% repeat-x; color: #363636; }
+.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #363636; }
+.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #cd0a0a; background: #fef1ec url(images/ui-bg_inset-soft_95_fef1ec_1x100.png) 50% bottom repeat-x; color: #cd0a0a; }
+.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #cd0a0a; }
+.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #cd0a0a; }
+.ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; }
+.ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; }
+.ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; }
+
+/* Icons
+----------------------------------*/
+
+/* states and images */
+.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_222222_256x240.png); }
+.ui-widget-content .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); }
+.ui-widget-header .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); }
+.ui-state-default .ui-icon { background-image: url(images/ui-icons_888888_256x240.png); }
+.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_454545_256x240.png); }
+.ui-state-active .ui-icon {background-image: url(images/ui-icons_454545_256x240.png); }
+.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_2e83ff_256x240.png); }
+.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_cd0a0a_256x240.png); }
+
+/* positioning */
+.ui-icon-carat-1-n { background-position: 0 0; }
+.ui-icon-carat-1-ne { background-position: -16px 0; }
+.ui-icon-carat-1-e { background-position: -32px 0; }
+.ui-icon-carat-1-se { background-position: -48px 0; }
+.ui-icon-carat-1-s { background-position: -64px 0; }
+.ui-icon-carat-1-sw { background-position: -80px 0; }
+.ui-icon-carat-1-w { background-position: -96px 0; }
+.ui-icon-carat-1-nw { background-position: -112px 0; }
+.ui-icon-carat-2-n-s { background-position: -128px 0; }
+.ui-icon-carat-2-e-w { background-position: -144px 0; }
+.ui-icon-triangle-1-n { background-position: 0 -16px; }
+.ui-icon-triangle-1-ne { background-position: -16px -16px; }
+.ui-icon-triangle-1-e { background-position: -32px -16px; }
+.ui-icon-triangle-1-se { background-position: -48px -16px; }
+.ui-icon-triangle-1-s { background-position: -64px -16px; }
+.ui-icon-triangle-1-sw { background-position: -80px -16px; }
+.ui-icon-triangle-1-w { background-position: -96px -16px; }
+.ui-icon-triangle-1-nw { background-position: -112px -16px; }
+.ui-icon-triangle-2-n-s { background-position: -128px -16px; }
+.ui-icon-triangle-2-e-w { background-position: -144px -16px; }
+.ui-icon-arrow-1-n { background-position: 0 -32px; }
+.ui-icon-arrow-1-ne { background-position: -16px -32px; }
+.ui-icon-arrow-1-e { background-position: -32px -32px; }
+.ui-icon-arrow-1-se { background-position: -48px -32px; }
+.ui-icon-arrow-1-s { background-position: -64px -32px; }
+.ui-icon-arrow-1-sw { background-position: -80px -32px; }
+.ui-icon-arrow-1-w { background-position: -96px -32px; }
+.ui-icon-arrow-1-nw { background-position: -112px -32px; }
+.ui-icon-arrow-2-n-s { background-position: -128px -32px; }
+.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
+.ui-icon-arrow-2-e-w { background-position: -160px -32px; }
+.ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
+.ui-icon-arrowstop-1-n { background-position: -192px -32px; }
+.ui-icon-arrowstop-1-e { background-position: -208px -32px; }
+.ui-icon-arrowstop-1-s { background-position: -224px -32px; }
+.ui-icon-arrowstop-1-w { background-position: -240px -32px; }
+.ui-icon-arrowthick-1-n { background-position: 0 -48px; }
+.ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
+.ui-icon-arrowthick-1-e { background-position: -32px -48px; }
+.ui-icon-arrowthick-1-se { background-position: -48px -48px; }
+.ui-icon-arrowthick-1-s { background-position: -64px -48px; }
+.ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
+.ui-icon-arrowthick-1-w { background-position: -96px -48px; }
+.ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
+.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
+.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
+.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
+.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
+.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
+.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
+.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
+.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
+.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
+.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
+.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
+.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
+.ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
+.ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
+.ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
+.ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
+.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
+.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
+.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
+.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
+.ui-icon-arrow-4 { background-position: 0 -80px; }
+.ui-icon-arrow-4-diag { background-position: -16px -80px; }
+.ui-icon-extlink { background-position: -32px -80px; }
+.ui-icon-newwin { background-position: -48px -80px; }
+.ui-icon-refresh { background-position: -64px -80px; }
+.ui-icon-shuffle { background-position: -80px -80px; }
+.ui-icon-transfer-e-w { background-position: -96px -80px; }
+.ui-icon-transferthick-e-w { background-position: -112px -80px; }
+.ui-icon-folder-collapsed { background-position: 0 -96px; }
+.ui-icon-folder-open { background-position: -16px -96px; }
+.ui-icon-document { background-position: -32px -96px; }
+.ui-icon-document-b { background-position: -48px -96px; }
+.ui-icon-note { background-position: -64px -96px; }
+.ui-icon-mail-closed { background-position: -80px -96px; }
+.ui-icon-mail-open { background-position: -96px -96px; }
+.ui-icon-suitcase { background-position: -112px -96px; }
+.ui-icon-comment { background-position: -128px -96px; }
+.ui-icon-person { background-position: -144px -96px; }
+.ui-icon-print { background-position: -160px -96px; }
+.ui-icon-trash { background-position: -176px -96px; }
+.ui-icon-locked { background-position: -192px -96px; }
+.ui-icon-unlocked { background-position: -208px -96px; }
+.ui-icon-bookmark { background-position: -224px -96px; }
+.ui-icon-tag { background-position: -240px -96px; }
+.ui-icon-home { background-position: 0 -112px; }
+.ui-icon-flag { background-position: -16px -112px; }
+.ui-icon-calendar { background-position: -32px -112px; }
+.ui-icon-cart { background-position: -48px -112px; }
+.ui-icon-pencil { background-position: -64px -112px; }
+.ui-icon-clock { background-position: -80px -112px; }
+.ui-icon-disk { background-position: -96px -112px; }
+.ui-icon-calculator { background-position: -112px -112px; }
+.ui-icon-zoomin { background-position: -128px -112px; }
+.ui-icon-zoomout { background-position: -144px -112px; }
+.ui-icon-search { background-position: -160px -112px; }
+.ui-icon-wrench { background-position: -176px -112px; }
+.ui-icon-gear { background-position: -192px -112px; }
+.ui-icon-heart { background-position: -208px -112px; }
+.ui-icon-star { background-position: -224px -112px; }
+.ui-icon-link { background-position: -240px -112px; }
+.ui-icon-cancel { background-position: 0 -128px; }
+.ui-icon-plus { background-position: -16px -128px; }
+.ui-icon-plusthick { background-position: -32px -128px; }
+.ui-icon-minus { background-position: -48px -128px; }
+.ui-icon-minusthick { background-position: -64px -128px; }
+.ui-icon-close { background-position: -80px -128px; }
+.ui-icon-closethick { background-position: -96px -128px; }
+.ui-icon-key { background-position: -112px -128px; }
+.ui-icon-lightbulb { background-position: -128px -128px; }
+.ui-icon-scissors { background-position: -144px -128px; }
+.ui-icon-clipboard { background-position: -160px -128px; }
+.ui-icon-copy { background-position: -176px -128px; }
+.ui-icon-contact { background-position: -192px -128px; }
+.ui-icon-image { background-position: -208px -128px; }
+.ui-icon-video { background-position: -224px -128px; }
+.ui-icon-script { background-position: -240px -128px; }
+.ui-icon-alert { background-position: 0 -144px; }
+.ui-icon-info { background-position: -16px -144px; }
+.ui-icon-notice { background-position: -32px -144px; }
+.ui-icon-help { background-position: -48px -144px; }
+.ui-icon-check { background-position: -64px -144px; }
+.ui-icon-bullet { background-position: -80px -144px; }
+.ui-icon-radio-off { background-position: -96px -144px; }
+.ui-icon-radio-on { background-position: -112px -144px; }
+.ui-icon-pin-w { background-position: -128px -144px; }
+.ui-icon-pin-s { background-position: -144px -144px; }
+.ui-icon-play { background-position: 0 -160px; }
+.ui-icon-pause { background-position: -16px -160px; }
+.ui-icon-seek-next { background-position: -32px -160px; }
+.ui-icon-seek-prev { background-position: -48px -160px; }
+.ui-icon-seek-end { background-position: -64px -160px; }
+.ui-icon-seek-start { background-position: -80px -160px; }
+/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */
+.ui-icon-seek-first { background-position: -80px -160px; }
+.ui-icon-stop { background-position: -96px -160px; }
+.ui-icon-eject { background-position: -112px -160px; }
+.ui-icon-volume-off { background-position: -128px -160px; }
+.ui-icon-volume-on { background-position: -144px -160px; }
+.ui-icon-power { background-position: 0 -176px; }
+.ui-icon-signal-diag { background-position: -16px -176px; }
+.ui-icon-signal { background-position: -32px -176px; }
+.ui-icon-battery-0 { background-position: -48px -176px; }
+.ui-icon-battery-1 { background-position: -64px -176px; }
+.ui-icon-battery-2 { background-position: -80px -176px; }
+.ui-icon-battery-3 { background-position: -96px -176px; }
+.ui-icon-circle-plus { background-position: 0 -192px; }
+.ui-icon-circle-minus { background-position: -16px -192px; }
+.ui-icon-circle-close { background-position: -32px -192px; }
+.ui-icon-circle-triangle-e { background-position: -48px -192px; }
+.ui-icon-circle-triangle-s { background-position: -64px -192px; }
+.ui-icon-circle-triangle-w { background-position: -80px -192px; }
+.ui-icon-circle-triangle-n { background-position: -96px -192px; }
+.ui-icon-circle-arrow-e { background-position: -112px -192px; }
+.ui-icon-circle-arrow-s { background-position: -128px -192px; }
+.ui-icon-circle-arrow-w { background-position: -144px -192px; }
+.ui-icon-circle-arrow-n { background-position: -160px -192px; }
+.ui-icon-circle-zoomin { background-position: -176px -192px; }
+.ui-icon-circle-zoomout { background-position: -192px -192px; }
+.ui-icon-circle-check { background-position: -208px -192px; }
+.ui-icon-circlesmall-plus { background-position: 0 -208px; }
+.ui-icon-circlesmall-minus { background-position: -16px -208px; }
+.ui-icon-circlesmall-close { background-position: -32px -208px; }
+.ui-icon-squaresmall-plus { background-position: -48px -208px; }
+.ui-icon-squaresmall-minus { background-position: -64px -208px; }
+.ui-icon-squaresmall-close { background-position: -80px -208px; }
+.ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
+.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
+.ui-icon-grip-solid-vertical { background-position: -32px -224px; }
+.ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
+.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
+.ui-icon-grip-diagonal-se { background-position: -80px -224px; }
+
+
+/* Misc visuals
+----------------------------------*/
+
+/* Corner radius */
+.ui-corner-all, .ui-corner-top, .ui-corner-left, .ui-corner-tl { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; -khtml-border-top-left-radius: 4px; border-top-left-radius: 4px; }
+.ui-corner-all, .ui-corner-top, .ui-corner-right, .ui-corner-tr { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; -khtml-border-top-right-radius: 4px; border-top-right-radius: 4px; }
+.ui-corner-all, .ui-corner-bottom, .ui-corner-left, .ui-corner-bl { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; -khtml-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; }
+.ui-corner-all, .ui-corner-bottom, .ui-corner-right, .ui-corner-br { -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; -khtml-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; }
+
+/* Overlays */
+.ui-widget-overlay { background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); }
+.ui-widget-shadow { margin: -8px 0 0 -8px; padding: 8px; background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); -moz-border-radius: 8px; -khtml-border-radius: 8px; -webkit-border-radius: 8px; border-radius: 8px; }/*!
+ * jQuery UI Resizable 1.8.21
+ *
+ * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Resizable#theming
+ */
+.ui-resizable { position: relative;}
+.ui-resizable-handle { position: absolute;font-size: 0.1px; display: block; }
+.ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; }
+.ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0; }
+.ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0; }
+.ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0; height: 100%; }
+.ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0; height: 100%; }
+.ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; }
+.ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; }
+.ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; }
+.ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}/*!
+ * jQuery UI Selectable 1.8.21
+ *
+ * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Selectable#theming
+ */
+.ui-selectable-helper { position: absolute; z-index: 100; border:1px dotted black; }
+/*!
+ * jQuery UI Accordion 1.8.21
+ *
+ * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Accordion#theming
+ */
+/* IE/Win - Fix animation bug - #4615 */
+.ui-accordion { width: 100%; }
+.ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; }
+.ui-accordion .ui-accordion-li-fix { display: inline; }
+.ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; }
+.ui-accordion .ui-accordion-header a { display: block; font-size: 1em; padding: .5em .5em .5em .7em; }
+.ui-accordion-icons .ui-accordion-header a { padding-left: 2.2em; }
+.ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; }
+.ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; zoom: 1; }
+.ui-accordion .ui-accordion-content-active { display: block; }
+/*!
+ * jQuery UI Autocomplete 1.8.21
+ *
+ * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Autocomplete#theming
+ */
+.ui-autocomplete { position: absolute; cursor: default; }
+
+/* workarounds */
+* html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */
+
+/*
+ * jQuery UI Menu 1.8.21
+ *
+ * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Menu#theming
+ */
+.ui-menu {
+ list-style:none;
+ padding: 2px;
+ margin: 0;
+ display:block;
+ float: left;
+}
+.ui-menu .ui-menu {
+ margin-top: -3px;
+}
+.ui-menu .ui-menu-item {
+ margin:0;
+ padding: 0;
+ zoom: 1;
+ float: left;
+ clear: left;
+ width: 100%;
+}
+.ui-menu .ui-menu-item a {
+ text-decoration:none;
+ display:block;
+ padding:.2em .4em;
+ line-height:1.5;
+ zoom:1;
+}
+.ui-menu .ui-menu-item a.ui-state-hover,
+.ui-menu .ui-menu-item a.ui-state-active {
+ font-weight: normal;
+ margin: -1px;
+}
+/*!
+ * jQuery UI Button 1.8.21
+ *
+ * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Button#theming
+ */
+.ui-button { display: inline-block; position: relative; padding: 0; margin-right: .1em; text-decoration: none !important; cursor: pointer; text-align: center; zoom: 1; overflow: visible; } /* the overflow property removes extra width in IE */
+.ui-button-icon-only { width: 2.2em; } /* to make room for the icon, a width needs to be set here */
+button.ui-button-icon-only { width: 2.4em; } /* button elements seem to need a little more width */
+.ui-button-icons-only { width: 3.4em; }
+button.ui-button-icons-only { width: 3.7em; }
+
+/*button text element */
+.ui-button .ui-button-text { display: block; line-height: 1.4; }
+.ui-button-text-only .ui-button-text { padding: .4em 1em; }
+.ui-button-icon-only .ui-button-text, .ui-button-icons-only .ui-button-text { padding: .4em; text-indent: -9999999px; }
+.ui-button-text-icon-primary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 1em .4em 2.1em; }
+.ui-button-text-icon-secondary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 2.1em .4em 1em; }
+.ui-button-text-icons .ui-button-text { padding-left: 2.1em; padding-right: 2.1em; }
+/* no icon support for input elements, provide padding by default */
+input.ui-button { padding: .4em 1em; }
+
+/*button icon element(s) */
+.ui-button-icon-only .ui-icon, .ui-button-text-icon-primary .ui-icon, .ui-button-text-icon-secondary .ui-icon, .ui-button-text-icons .ui-icon, .ui-button-icons-only .ui-icon { position: absolute; top: 50%; margin-top: -8px; }
+.ui-button-icon-only .ui-icon { left: 50%; margin-left: -8px; }
+.ui-button-text-icon-primary .ui-button-icon-primary, .ui-button-text-icons .ui-button-icon-primary, .ui-button-icons-only .ui-button-icon-primary { left: .5em; }
+.ui-button-text-icon-secondary .ui-button-icon-secondary, .ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; }
+.ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; }
+
+/*button sets*/
+.ui-buttonset { margin-right: 7px; }
+.ui-buttonset .ui-button { margin-left: 0; margin-right: -.3em; }
+
+/* workarounds */
+button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra padding in Firefox */
+/*!
+ * jQuery UI Dialog 1.8.21
+ *
+ * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Dialog#theming
+ */
+.ui-dialog { position: absolute; padding: .2em; width: 300px; overflow: hidden; }
+.ui-dialog .ui-dialog-titlebar { padding: .4em 1em; position: relative; }
+.ui-dialog .ui-dialog-title { float: left; margin: .1em 16px .1em 0; }
+.ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; }
+.ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; }
+.ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; }
+.ui-dialog .ui-dialog-content { position: relative; border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; }
+.ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; }
+.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { float: right; }
+.ui-dialog .ui-dialog-buttonpane button { margin: .5em .4em .5em 0; cursor: pointer; }
+.ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; }
+.ui-draggable .ui-dialog-titlebar { cursor: move; }
+/*!
+ * jQuery UI Slider 1.8.21
+ *
+ * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Slider#theming
+ */
+.ui-slider { position: relative; text-align: left; }
+.ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; }
+.ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; background-position: 0 0; }
+
+.ui-slider-horizontal { height: .8em; }
+.ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; }
+.ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; }
+.ui-slider-horizontal .ui-slider-range-min { left: 0; }
+.ui-slider-horizontal .ui-slider-range-max { right: 0; }
+
+.ui-slider-vertical { width: .8em; height: 100px; }
+.ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; }
+.ui-slider-vertical .ui-slider-range { left: 0; width: 100%; }
+.ui-slider-vertical .ui-slider-range-min { bottom: 0; }
+.ui-slider-vertical .ui-slider-range-max { top: 0; }/*!
+ * jQuery UI Tabs 1.8.21
+ *
+ * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Tabs#theming
+ */
+.ui-tabs { position: relative; padding: .2em; zoom: 1; } /* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */
+.ui-tabs .ui-tabs-nav { margin: 0; padding: .2em .2em 0; }
+.ui-tabs .ui-tabs-nav li { list-style: none; float: left; position: relative; top: 1px; margin: 0 .2em 1px 0; border-bottom: 0 !important; padding: 0; white-space: nowrap; }
+.ui-tabs .ui-tabs-nav li a { float: left; padding: .5em 1em; text-decoration: none; }
+.ui-tabs .ui-tabs-nav li.ui-tabs-selected { margin-bottom: 0; padding-bottom: 1px; }
+.ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; }
+.ui-tabs .ui-tabs-nav li a, .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */
+.ui-tabs .ui-tabs-panel { display: block; border-width: 0; padding: 1em 1.4em; background: none; }
+.ui-tabs .ui-tabs-hide { display: none !important; }
+/*!
+ * jQuery UI Datepicker 1.8.21
+ *
+ * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Datepicker#theming
+ */
+.ui-datepicker { width: 17em; padding: .2em .2em 0; display: none; }
+.ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; }
+.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; }
+.ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; }
+.ui-datepicker .ui-datepicker-prev { left:2px; }
+.ui-datepicker .ui-datepicker-next { right:2px; }
+.ui-datepicker .ui-datepicker-prev-hover { left:1px; }
+.ui-datepicker .ui-datepicker-next-hover { right:1px; }
+.ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; }
+.ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; }
+.ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; }
+.ui-datepicker select.ui-datepicker-month-year {width: 100%;}
+.ui-datepicker select.ui-datepicker-month,
+.ui-datepicker select.ui-datepicker-year { width: 49%;}
+.ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; }
+.ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; }
+.ui-datepicker td { border: 0; padding: 1px; }
+.ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; }
+.ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; }
+.ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; }
+.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; }
+
+/* with multiple calendars */
+.ui-datepicker.ui-datepicker-multi { width:auto; }
+.ui-datepicker-multi .ui-datepicker-group { float:left; }
+.ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; }
+.ui-datepicker-multi-2 .ui-datepicker-group { width:50%; }
+.ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; }
+.ui-datepicker-multi-4 .ui-datepicker-group { width:25%; }
+.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; }
+.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; }
+.ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; }
+.ui-datepicker-row-break { clear:both; width:100%; font-size:0em; }
+
+/* RTL support */
+.ui-datepicker-rtl { direction: rtl; }
+.ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; }
+.ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; }
+.ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; }
+.ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; }
+.ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; }
+.ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; }
+.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; }
+.ui-datepicker-rtl .ui-datepicker-group { float:right; }
+.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
+.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
+
+/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */
+.ui-datepicker-cover {
+ display: none; /*sorry for IE5*/
+ display/**/: block; /*sorry for IE5*/
+ position: absolute; /*must have*/
+ z-index: -1; /*must have*/
+ filter: mask(); /*must have*/
+ top: -4px; /*must have*/
+ left: -4px; /*must have*/
+ width: 200px; /*must have*/
+ height: 200px; /*must have*/
+}/*!
+ * jQuery UI Progressbar 1.8.21
+ *
+ * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Progressbar#theming
+ */
+.ui-progressbar { height:2em; text-align: left; overflow: hidden; }
+.ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; }
\ No newline at end of file
\r
\r
/*top message*/\r
-.top-msg { margin:-36px -70px 36px; background:blue; padding:100px; color:#fff; display:none; position:relative; font-size:1.538em; text-align:center;}\r
+.top-msg { margin:-36px -70px 36px; background:blue; padding:50px; color:#fff; display:none; position:relative; font-size:1.538em; text-align:center;}\r
.top-msg p { text-align:center; }\r
.top-msg p.title { font-size:1.3em; }\r
.top-msg p.title span { border-bottom:2px dotted #fff; padding:0 0 10px 0;}\r
.top-msg.active { display:block;}\r
.top-msg +.mainlogo { margin-top:-73px;}\r
-.top-msg .close { position:absolute; bottom:20px; right:20px; font-size:1.3em; font-weight:bold; border:0 none; color:#fff; text-decoration:none;}\r
+.top-msg .close { position:absolute; bottom:20px; right:20px; font-size:1.3em; border:0 none; color:#fff; text-decoration:none;}\r
.top-msg .close:hover { color:#000;}\r
.top-msg.success { background-color:#77C596; color: #fff}\r
.top-msg.error { background-color:#EF4F54; color: #fff}\r
.two-cols .lt { float:left; width:400px;}\r
.two-cols-blog .rt { float:right; width:220px; margin-left:80px; padding-right:65px;}\r
.two-cols-blog .lt { overflow:hidden;}\r
-.container h2, .container h3 { font-weight:normal; margin-bottom:1em; }\r
+.container h2 { font-weight:normal; font-size:1.308em; margin-bottom:1em; }\r
+.container h3 { font-weight:normal; margin-bottom:1em; }\r
.container h2 em { color: #3582AC; font-style:normal; }\r
/*.content a { border-bottom: 1px solid #F89A1C; text-decoration:none; color:#000; }\r
.content a:hover { border-bottom: 1px solid #F89A1C; text-decoration:none; color:#F89A1C; }*/\r
.content .backlink { margin:1em 0; }\r
.faq h3 { color:#4085a6; margin:0;}\r
.faq ul { padding:0; margin:0; }\r
-.faq ul li { list-style:none outside; padding:0; margin:0; }\r
-.faq ul li a { color:#222222; border:0 none;} \r
+.faq ul li { padding:0; margin:0 0 8px 0; list-style:none outside none;line-height:140%; }\r
+.faq ul li a { color:#222222; border:0 none; } \r
.faq .faq-category { margin: 0 0 1.5em;}\r
.follow h3 a { color:#4085A6;}\r
.follow a { border:0 none;}\r
\r
/* Style for im/projects */\r
table.alt-style { color:#000; width:100%; }\r
-table.alt-style caption { font-weight:normal; font-size:1.154em; }\r
+table.alt-style caption { font-weight:normal; font-size:1.154em; margin-bottom:15px;}\r
table.alt-style tr th { font-weight:normal; color:#3582AC }\r
table.alt-style tr td { color:#222; }\r
table.alt-style tr td:first-child,\r
table.alt-style tr th:first-child { padding-left:5px; }\r
-.content a.submit { margin:0; display:inline-block; margin:10px 0 ; height:auto;}\r
+table.alt-style tr td a { margin:0 0 0 20px; }\r
+table.alt-style tr td:first-child a { margin:0; }\r
+.content a.submit { margin:0; display:inline-block; margin:10px 0 ; height:auto; min-width:100px; text-align:center;}\r
table.alt-style tr:nth-child(2n) td { background:#F2F2F2 }\r
dl.alt-style { width:500px; }\r
dl.alt-style dt { width:50%; float:left; color:#3582AC; font-weight:normal;}\r
\r
.question .content a:hover { color:#F6921E }\r
.details .extra-menu { background: url(../images/dots.jpg) repeat-x center top ;padding-top:20px; margin-top:20px; }\r
+.details .extra-menu h3 { margin-bottom:1em; font-weight:bold;}\r
.details .faq-category h2 { font-size:1em; }\r
.question .next-prev { margin:10px 0; }\r
.question .next { float:right; }\r
.details img { max-width:100%; }\r
.question .section { margin-top:1em; }\r
.question pre { border:1px dashed #000; padding:5px; margin:10px 0; line-height:auto; }\r
-.widjets { margin: 0; padding:0; }\r
+/*\r
+.widjets { position:relative; }\r
+.widjets ul { margin: 0; padding:0; }\r
.widjets li { width:50%; float:left; list-style:none outside; margin:30px 0; }\r
.widjets li div { border:1px dashed #000; padding:20px 20px 70px; width:60%; margin:0 auto; position:relative; }\r
.widjets li div img { max-width:100%; }\r
.widjets li .btn { text-align:center; position:absolute; bottom:0; left:0; right:0; }\r
+.widjets .widjet-x { position:absolute; right:0;top:0; font-weight:bold; font-size:1.5em; }\r
+.widjets .widjet-x:hover { text-decoration:none; color:#000; }\r
+\r
+ */\r
+.widjets { position:relative; }\r
+.widjets ul { margin: 0; padding:0; }\r
+.widjets li { width:50%; float:left; list-style:none outside; margin:30px 0; }\r
+.widjets li div { background:url(../images/dots.jpg) repeat-x top ; margin:0 0 0 20px; position:relative; padding:2em 0; }\r
+.widjets li div .wrap { background:url(../images/dots.jpg) repeat-x bottom ; }\r
+.widjets li:first-child div { margin-right:20px; margin-left:0; }\r
+.widjets li div img { max-width:100%; margin:2em 0; }\r
+.widjets li div .txt { height:100px; }\r
+.widjets .widjet-x { position:absolute; right:0;top:0; font-weight:bold; font-size:1.5em; }\r
+.widjets .widjet-x:hover { text-decoration:none; color:#000; }\r
+.widjets li.create h2, \r
+.widjets li.create a { color: #55B577}\r
+.widjets li.join h2, \r
+.widjets li.join a { color: #F24E53}\r
+.widjets li a { font-size:1.154em; }\r
+\r
+\r
+/* billing styles */\r
+.alt-style .table-div { border:1px dashed #000; }\r
+.billing table.complex tr:nth-child(2n) td { background:transparent; }\r
+.billing table.alt-style tr.zebra td { background:#F2F2F2; }\r
+.billing .highlight { text-align:center; padding:10px; border:1px dashed #000; font-size:1.231em; margin:0 0 2em; position:relative;}\r
+.billing .highlight em { color:#3582AC; font-style:normal; font-weight:bold; }\r
+.billing table.marginless { margin-bottom:0; }\r
+.billing .sum { background:#5A97B8; padding:5px 5px 5px 10px; color:#fff; }\r
+.billing.details .last { width:15%;}\r
+.billing.details .prelast { width:10%;}\r
+.billing.list .last { width:20%; }\r
+.billing.list .prelast { width:20%; }\r
+.billing form { margin-top:50px; }\r
+.billing .categories { margin-bottom:2em; }\r
+.billing .categories .clear { color: #000000; position:relative; top:-1px; line-height:100%; display:inline-block;}\r
+.billing a, .billing a:hover { border:0 none;}\r
+.billing .categories ul { margin:0;padding:0;}\r
+.billing .categories ul li { float: left; list-style:none outside;}\r
+.billing .categories .title { margin-bottom: 0.5em; }\r
+.billing .categories ul li a { color: #000000; margin-right: 22px; text-decoration: none; }\r
+.billing .categories ul li a:hover, \r
+.billing .categories ul li a.selected { color: #000000;}\r
+.billing .categories ul li.active a { text-decoration: underline }\r
+.billing .categories ul li.inactive { opacity: 0.3; }\r
+.billing .categories ul li.active { opacity: 1; }\r
+.billing .resource-cat-1.filter-item a,\r
+.billing .resource-cat-1.filter-item a:hover { color:#ff6f00 }\r
+.billing .resource-cat-2.filter-item a,\r
+.billing .resource-cat-2.filter-item a:hover { color:#4085A5 }\r
+.billing span.info { position:absolute;z-index:10; bottom:32px; right:40px; }\r
+.billing span.info em { display:block; overflow:hidden; position:absolute; left:0; text-indent:-110px; top:0; height:21px; width:21px; background:url(../images/symbols.png) no-repeat -4px -31px;cursor:pointer; }\r
+.billing span.info:hover em { background-position:-4px -3px; }\r
+.billing span.info span { position:absolute; left:29px; top:-2px; width:120px; padding-left:30px; background:url(../images/black-line.jpg ) no-repeat left 12px ; min-height:50px; display:none; font-size:12px;}\r
+.billing span.info:hover span { display:block; }\r
+.billing span.info.foo span { padding:0; background:transparent; left:-48px; top:22px; font-size:12px;}\r
+.billing .highlight a { float:right; }\r
+.billing .highlight .popup { position:absolute;right:20px; top:40px; width: 100px; border:1px solid #000; }\r
+.billing table.alt-style tr td { border-top:10px solid #fff; }\r
+.billing table.alt-style .last { text-align:right; }\r
+\r
+.table_sorting tr th { cursor:pointer; }\r
+\r
+.table_sorting tr th:hover { text-decoration:underline }\r
+\r
+table.alt-style tr.tr1 td,\r
+table.alt-style tr.tmore1 td { background:#F2F2F2 }\r
+table.alt-style tr.tr2 td,\r
+table.alt-style tr.tmore2 td { background:#fff }\r
+table.alt-style tr td.info-td { padding:5px; }\r
+table.alt-style tr td.info-td div { padding:15px; border:1px dashed #000 }\r
+ \r
+\r
+.projects .details a.edit { float:right; margin-left:20px; }\r
+.projects .details .data { overflow:hidden; }\r
+.projects .editable form textarea { width:70%; height:50px; max-width:70%; width:270px; height:120px;}\r
+\r
+\r
+/* quotas-form */\r
+\r
+.quotas-form fieldset { background:url(../images/dots.jpg) repeat-x scroll center bottom transparent; margin-bottom:3em; padding-bottom:3em; position:relative; }\r
+.quotas-form legend { color:#55B577; font-size:1.308em; margin-bottom:3em; position:relative; }\r
+.quotas-form legend span { color:#222; font-size:0.867em; }\r
+form.quotas-form legend span.info { position:relative; display:inline-block; top:auto; left:auto; margin-left:10px; vertical-align:middle; margin-top:-2px;}\r
+form.quotas-form legend span.info em { position:static; }\r
+form.quotas-form legend span.info span { width:395px; }\r
+.quotas-form .with-checkbox .checkbox-widget { margin-top:9px; } \r
+.quotas-form .with-checkbox span.info { top:12px; }\r
+.quotas-form .form-row.submit { text-align:center; }\r
+.quotas-form input[type="submit"] { margin:15px 0; background-color:#B3B3B3 }\r
+.quotas-form input[type="submit"]:hover { background:#55B577 }\r
+.quotas-form input[type="submit"]:focus { border-color: #B3B3B3}\r
+.quotas-form input[type="submit"]:focus:hover { border-color: #55B577}\r
+.quotas-form fieldset ul { padding:0; margin:0 0 1em; position:relative; }\r
+.quotas-form fieldset ul li { list-style:none outside none; float:left; padding:0 0 0 60px; margin:0; }\r
+.quotas-form fieldset ul li:first-child { padding-left:0; }\r
+.quotas-form fieldset ul li a { display:block; width:82px; height:82px; overflow:hidden; }\r
+.quotas-form fieldset ul li a:hover img { margin-top:-84px; }\r
+.quotas-form fieldset ul li a.selected img { margin-top:-168px; }\r
+.quotas-form fieldset ul li a.selected:hover { cursor:default }\r
+.quotas-form fieldset ul li a.selected:focus { outline:0 none; }\r
+.quotas-form fieldset ul li p { position:absolute; top:95px; left:0; display: none;}\r
+.quotas-form fieldset ul li:hover p { display:block; }\r
+.quotas-form p.msg { color:#B3B3B3; }\r
+.quotas-form a.delete { position:absolute; right:0; top:0; color:#B3B3B3; z-index:2 }\r
+.quotas-form .group { display:none; position:relative; background:url(../images/dots.jpg) repeat-x scroll center bottom; margin-bottom:2em; padding-bottom:2em;}\r
+.quotas-form .group fieldset { background:transparent; margin-bottom:1em; padding-bottom:1em; }\r
+.quotas-form .group fieldset legend { margin-bottom:1em; padding-bottom:1em; }\r
+.quotas-form fieldset ul li.rel+li.rel { background:url(../images/quota-related-bg.png) no-repeat left center; }\r
+.quotas-form .double-checks label { font-size:1.077em; }\r
+.quotas-form .double-checks .form-row { float:left; margin-right:10px;}\r
+.quotas-form .double-checks .with-checkbox .checkbox-widget { left:0; }\r
+.quotas-form .double-checks .with-checkbox input[type="text"] { width:60px; float:left; margin:9px 15px -9px; display:none; padding:6px; }\r
+.quotas-form .double-checks .with-checkbox label{ width:auto; float:left; margin-left:35px; }\r
+.quotas-form .double-checks .with-checkbox input[type="text"].hideshow { display:block; }\r
+.quotas-form .with-checkbox+.with-checkbox { width:196px; }\r
+.summary dl.alt-style dt { color:#55B577; }\r
+.quotas-form .with-info .double-checks p { clear:both; }\r
+.quotas-form .with-info .with-checkbox+.with-checkbox { width:auto; }\r
+.quotas-form .with-info .double-checks { position:relative; margin-bottom:70px; }\r
+.quotas-form .with-info .double-checks .form-row+.form-row { position:absolute; left:224px; top:40px;}\r
+.quotas-form .with-info .double-checks span.info { left:262px; }\r
+.quotas-form .with-info .with-checkbox { margin-bottom:12px; }
+.quotas-form .quota input[type="text"] { width:150px;}\r
+.quotas-form .quota .error-msg { display:none; color:red; font-size:0.8em; margin:0; margin-left:224px; margin-bottom:5px; padding:5px; }\r
+.quotas-form .quota .with-errors .error-msg { display:block;} \r
+::-webkit-input-placeholder { color: #D4D4D4; font-style:italic; }\r
+:-moz-placeholder { color: #D4D4D4;font-style:italic; }\r
+\r
+/* stats */\r
+.stats ul { margin:0; padding:0; list-style:none outside none; }\r
+.stats ul li { margin:0 0 1em 0; padding:0 0 1em 0; list-style:none outside none; background:url(../images/stats-line.jpg) repeat-x left bottom}\r
+.stats .bar { padding: 0; text-align:center; float:left; width:200px;}\r
+.stats .bar div { width:340px; height:30px; border:1px solid #000; margin-top:20px; overflow:hidden;}\r
+.stats .bar span { text-align:right; display:block; height:100%; color:#222; line-height:30px; font-size:1.231em; text-indent:10px;}\r
+.stats .red .bar span { background:#ef4f54; }\r
+.stats .yellow .bar span { background:#f6921e; }\r
+.stats .green .bar span { background:#55b577; }\r
+.stats .img-wrap { float:left; width:100px; background:url(../images/statistics_icons.png) no-repeat center center; padding:30px 0; }\r
+.stats .info { margin:0 25px ; width:320px; float:left; }\r
+.stats .info p { color:#999; margin:0; }\r
+.stats .info h3 { font-size:1.231em; color:#222222 }\r
+.stats .vm .img-wrap { background-image:url(../images/vm-stats.png) }\r
+.stats .ram .img-wrap { background-image:url(../images/ram-stats.png) }\r
+.stats .cpu .img-wrap { background-image:url(../images/cpu-stats.png) }\r
+.stats .network .img-wrap { background-image:url(../images/network-stats.png) }\r
+.stats .disksize .img-wrap { background-image:url(../images/disk-stats.png) }\r
+.stats .disk .img-wrap { background-image:url(../images/disk-stats.png) }\r
+.stats .diskspace .img-wrap { background-image:url(../images/storage-stats.png) }\r
+.stats .bandwidth .img-wrap { background-image:url(../images/bandwidth-stats.png) }\r
+\r
+.stats .red .img-wrap { background-position: 15px 7px; }\r
+.stats .yellow .img-wrap { background-position: -124px 7px; }\r
+.stats .green .img-wrap { background-position: -263px 7px; }\r
+.projects .editable form textarea { width:70%; height:50px; max-width:70%; width:270px; height:120px;}\r
+\r
+/* temp style to hide extra menu for groups */\r
+.navigation ul+ul { display:none; }\r
+\r
+table .msg-wrap { position:relative; }\r
+table .msg-wrap .dialog { position:absolute; border:1px dashed #ccc; padding:15px; width:200px; bottom:30px; left:0; background:#fff; display:none; }\r
+table .msg-wrap .dialog .submit { min-width:30px; padding:5px 22px; }\r
+table .msg-wrap .dialog .no.submit { float:right; }\r
+table.alt-style .centered { text-align:center; }\r
+table.alt-style form.link-like { float:none }\r
+form.quotas-form span.error-msg span { display:block; color:red; }\r
+form.quotas-form span.error-msg em,\r
+form.quotas-form span.error-msg:hover em { background-position:-58px -3px; }\r
+.two-cols-links { margin:5em 0; }\r
+.two-cols-links p { width:auto; overflow:hidden; }\r
+.two-cols-links a { color:grey; display:block; margin-bottom:10px;}\r
+.two-cols-links a:hover { color:#F89A1C }\r
+.two-cols-links p:first-child { width:224px; float:left; overflow:auto }\r
+.two-cols-links p:first-child a { color:#222; }\r
+.two-cols-links p:first-child a:hover { color:#F89A1C }\r
--- /dev/null
+/*
+
+Uniform Theme: Uniform Default
+Version: 1.6
+By: Josh Pyles
+License: MIT License
+---
+For use with the Uniform plugin:
+http://pixelmatrixdesign.com/uniform/
+---
+Generated by Uniform Theme Generator:
+http://pixelmatrixdesign.com/uniform/themer.html
+
+*/
+
+/* Global Declaration */
+
+div.selector,
+div.selector span,
+div.checker span,
+div.radio span,
+div.uploader,
+div.uploader span.action,
+div.button,
+div.button span {
+ background-image: url(../images/sprite.png);
+ background-repeat: no-repeat;
+ -webkit-font-smoothing: antialiased;
+}
+
+.selector,
+.radio,
+.checker,
+.uploader,
+.button,
+.selector *,
+.radio *,
+.checker *,
+.uploader *,
+.button *{
+ margin: 0;
+ padding: 0;
+}
+
+/* INPUT & TEXTAREA */
+
+input.text,
+input.email,
+input.password,
+textarea.uniform {
+ font-size: 12px;
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-weight: normal;
+ padding: 3px;
+ color: #777;
+ background: url('../images/bg-input-focus.png') repeat-x 0px 0px;
+ background: url('../images/bg-input.png') repeat-x 0px 0px;
+ border-top: solid 1px #aaa;
+ border-left: solid 1px #aaa;
+ border-bottom: solid 1px #ccc;
+ border-right: solid 1px #ccc;
+ -webkit-border-radius: 3px;
+ -moz-border-radius: 3px;
+ border-radius: 3px;
+ outline: 0;
+}
+
+input.text:focus,
+input.email:focus,
+input.password:focus,
+textarea.uniform:focus {
+ -webkit-box-shadow: 0px 0px 4px rgba(0,0,0,0.3);
+ -moz-box-shadow: 0px 0px 4px rgba(0,0,0,0.3);
+ box-shadow: 0px 0px 4px rgba(0,0,0,0.3);
+ border-color: #999;
+ background: url('../images/bg-input-focus.png') repeat-x 0px 0px;
+}
+
+/* SPRITES */
+
+/* Select */
+
+div.selector {
+ background-position: -483px -130px;
+ line-height: 26px;
+ height: 26px;
+}
+
+div.selector span {
+ background-position: right 0px;
+ height: 26px;
+ line-height: 26px;
+}
+
+div.selector select {
+ /* change these to adjust positioning of select element */
+ top: 0px;
+ left: 0px;
+}
+
+div.selector:active,
+div.selector.active {
+ background-position: -483px -156px;
+}
+
+div.selector:active span,
+div.selector.active span {
+ background-position: right -26px;
+}
+
+div.selector.focus, div.selector.hover, div.selector:hover {
+ background-position: -483px -182px;
+}
+
+div.selector.focus span, div.selector.hover span, div.selector:hover span {
+ background-position: right -52px;
+}
+
+div.selector.focus:active,
+div.selector.focus.active,
+div.selector:hover:active,
+div.selector.active:hover {
+ background-position: -483px -208px;
+}
+
+div.selector.focus:active span,
+div.selector:hover:active span,
+div.selector.active:hover span,
+div.selector.focus.active span {
+ background-position: right -78px;
+}
+
+div.selector.disabled {
+ background-position: -483px -234px;
+}
+
+div.selector.disabled span {
+ background-position: right -104px;
+}
+
+/* Checkbox */
+
+div.checker {
+ width: 19px;
+ height: 19px;
+}
+
+div.checker input {
+ width: 19px;
+ height: 19px;
+}
+
+div.checker span {
+ background-position: 0px -260px;
+ height: 19px;
+ width: 19px;
+}
+
+div.checker:active span,
+div.checker.active span {
+ background-position: -19px -260px;
+}
+
+div.checker.focus span,
+div.checker:hover span {
+ background-position: -38px -260px;
+}
+
+div.checker.focus:active span,
+div.checker:active:hover span,
+div.checker.active:hover span,
+div.checker.focus.active span {
+ background-position: -57px -260px;
+}
+
+div.checker span.checked {
+ background-position: -76px -260px;
+}
+
+div.checker:active span.checked,
+div.checker.active span.checked {
+ background-position: -95px -260px;
+}
+
+div.checker.focus span.checked,
+div.checker:hover span.checked {
+ background-position: -114px -260px;
+}
+
+div.checker.focus:active span.checked,
+div.checker:hover:active span.checked,
+div.checker.active:hover span.checked,
+div.checker.active.focus span.checked {
+ background-position: -133px -260px;
+}
+
+div.checker.disabled span,
+div.checker.disabled:active span,
+div.checker.disabled.active span {
+ background-position: -152px -260px;
+}
+
+div.checker.disabled span.checked,
+div.checker.disabled:active span.checked,
+div.checker.disabled.active span.checked {
+ background-position: -171px -260px;
+}
+
+/* Radio */
+
+div.radio {
+ width: 25px;
+ height: 25px;
+
+}
+
+div.radio input {
+ width: 25px;
+ height: 25px;
+}
+
+div.radio span {
+ height: 25px;
+ width: 25px;
+ background:transparent;
+
+}
+
+div.radio span span {
+ border:1px solid grey;
+}
+div.radio:active span,
+div.radio.active span {
+ background-image: url(../images/checkbox.png);
+ background-position:top left;
+}
+
+div.radio.focus span,
+div.radio:hover span {
+ background:transparent;
+}
+
+div.radio.focus:active span,
+div.radio:active:hover span,
+div.radio.active:hover span,
+div.radio.active.focus span {
+ background-image: url(../images/checkbox.png);
+}
+
+div.radio span.checked {
+ background-image: url(../images/checkbox.png);
+}
+
+div.radio:active span.checked,
+div.radio.active span.checked {
+ background-image: url(../images/checkbox.png);
+}
+
+div.radio.focus span.checked, div.radio:hover span.checked {
+ background-image: url(../images/checkbox.png);
+}
+
+div.radio.focus:active span.checked,
+div.radio:hover:active span.checked,
+div.radio.focus.active span.checked,
+div.radio.active:hover span.checked {
+ background-image: url(../images/checkbox.png);
+}
+
+div.radio.disabled span,
+div.radio.disabled:active span,
+div.radio.disabled.active span {
+ background-position: -144px -279px;
+}
+
+div.radio.disabled span.checked,
+div.radio.disabled:active span.checked,
+div.radio.disabled.active span.checked {
+ background-position: -162px -279px;
+}
+
+/* Uploader */
+
+div.uploader {
+ background-position: 0px -297px;
+ height: 28px;
+}
+
+div.uploader span.action {
+ background-position: right -409px;
+ height: 24px;
+ line-height: 24px;
+}
+
+div.uploader span.filename {
+ height: 24px;
+ /* change this line to adjust positioning of filename area */
+ margin: 2px 0px 2px 2px;
+ line-height: 24px;
+}
+
+div.uploader.focus,
+div.uploader.hover,
+div.uploader:hover {
+ background-position: 0px -353px;
+}
+
+div.uploader.focus span.action,
+div.uploader.hover span.action,
+div.uploader:hover span.action {
+ background-position: right -437px;
+}
+
+div.uploader.active span.action,
+div.uploader:active span.action {
+ background-position: right -465px;
+}
+
+div.uploader.focus.active span.action,
+div.uploader:focus.active span.action,
+div.uploader.focus:active span.action,
+div.uploader:focus:active span.action {
+ background-position: right -493px;
+}
+
+div.uploader.disabled {
+ background-position: 0px -325px;
+}
+
+div.uploader.disabled span.action {
+ background-position: right -381px;
+}
+
+div.button {
+ background-position: 0px -523px;
+}
+
+div.button span {
+ background-position: right -643px;
+}
+
+div.button.focus,
+div.button:focus,
+div.button:hover,
+div.button.hover {
+ background-position: 0px -553px;
+}
+
+div.button.focus span,
+div.button:focus span,
+div.button:hover span,
+div.button.hover span {
+ background-position: right -673px;
+}
+
+div.button.active,
+div.button:active {
+ background-position: 0px -583px;
+}
+
+div.button.active span,
+div.button:active span {
+ background-position: right -703px;
+ color: #555;
+}
+
+div.button.disabled,
+div.button:disabled {
+ background-position: 0px -613px;
+}
+
+div.button.disabled span,
+div.button:disabled span {
+ background-position: right -733px;
+ color: #bbb;
+ cursor: default;
+}
+
+/* PRESENTATION */
+
+/* Button */
+
+div.button {
+ height: 30px;
+}
+
+div.button span {
+ margin-left: 13px;
+ height: 22px;
+ padding-top: 8px;
+ font-weight: bold;
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-size: 12px;
+ letter-spacing: 1px;
+ text-transform: uppercase;
+ padding-left: 2px;
+ padding-right: 15px;
+}
+
+/* Select */
+div.selector {
+ width: 190px;
+ font-size: 12px;
+}
+
+div.selector select {
+ min-width: 190px;
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-size: 12px;
+ border: solid 1px #fff;
+}
+
+div.selector span {
+ padding: 0px 25px 0px 2px;
+ cursor: pointer;
+}
+
+div.selector span {
+ color: #666;
+ width: 158px;
+ text-shadow: 0 1px 0 #fff;
+}
+
+div.selector.disabled span {
+ color: #bbb;
+}
+
+/* Checker */
+div.checker {
+ margin-right: 5px;
+}
+
+/* Radio */
+div.radio {
+ margin-right: 3px;
+}
+
+/* Uploader */
+div.uploader {
+ width: 190px;
+ cursor: pointer;
+}
+
+div.uploader span.action {
+ width: 85px;
+ text-align: center;
+ text-shadow: #fff 0px 1px 0px;
+ background-color: #fff;
+ font-size: 11px;
+ font-weight: bold;
+}
+
+div.uploader span.filename {
+ color: #777;
+ width: 82px;
+ border-right: solid 1px #bbb;
+ font-size: 11px;
+}
+
+div.uploader input {
+ width: 190px;
+}
+
+div.uploader.disabled span.action {
+ color: #aaa;
+}
+
+div.uploader.disabled span.filename {
+ border-color: #ddd;
+ color: #aaa;
+}
+/*
+
+CORE FUNCTIONALITY
+
+Not advised to edit stuff below this line
+-----------------------------------------------------
+*/
+
+.selector,
+.checker,
+.button,
+.radio,
+.uploader {
+ display: -moz-inline-box;
+ display: inline-block;
+ vertical-align: middle;
+ zoom: 1;
+ *display: inline;
+}
+
+.selector select:focus, .radio input:focus, .checker input:focus, .uploader input:focus {
+ outline: 0;
+}
+
+/* Button */
+
+div.button a,
+div.button button,
+div.button input {
+ position: absolute;
+}
+
+div.button {
+ cursor: pointer;
+ position: relative;
+}
+
+div.button span {
+ display: -moz-inline-box;
+ display: inline-block;
+ line-height: 1;
+ text-align: center;
+}
+
+/* Select */
+
+div.selector {
+ position: relative;
+ padding-left: 10px;
+ overflow: hidden;
+}
+
+div.selector span {
+ display: block;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+div.selector select {
+ position: absolute;
+ opacity: 0;
+ filter: alpha(opacity:0);
+ height: 25px;
+ border: none;
+ background: none;
+}
+
+/* Checker */
+
+div.checker {
+ position: relative;
+}
+
+div.checker span {
+ display: -moz-inline-box;
+ display: inline-block;
+ text-align: center;
+}
+
+div.checker input {
+ opacity: 0;
+ filter: alpha(opacity:0);
+ display: inline-block;
+ background: none;
+}
+
+/* Radio */
+
+div.radio {
+ position: relative;
+}
+
+div.radio span {
+ display: -moz-inline-box;
+ display: inline-block;
+ text-align: center;
+}
+
+div.radio input {
+ opacity: 0;
+ filter: alpha(opacity:0);
+ text-align: center;
+ display: inline-block;
+ background: none;
+}
+
+/* Uploader */
+
+div.uploader {
+ position: relative;
+ overflow: hidden;
+ cursor: default;
+}
+
+div.uploader span.action {
+ float: left;
+ display: inline;
+ padding: 2px 0px;
+ overflow: hidden;
+ cursor: pointer;
+}
+
+div.uploader span.filename {
+ padding: 0px 10px;
+ float: left;
+ display: block;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ cursor: default;
+}
+
+div.uploader input {
+ opacity: 0;
+ filter: alpha(opacity:0);
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ float: right;
+ height: 25px;
+ border: none;
+ cursor: default;
+}
\ No newline at end of file
//todo\r
});\r
}); \r
- //$('.dropkick-select').dropkick();\r
\r
- \r
+ \r
+ $('select.dropkicked').dropkick({\r
+ change: function (value, label) {\r
+ $(this).parents('form').submit();\r
+ \r
+ }\r
+ });\r
\r
$('.top-msg .success').parents('.top-msg').addClass('success');\r
$('.top-msg .error').parents('.top-msg').addClass('error');\r
);\r
\r
\r
- /*$('#animation a').hover(\r
- function () {\r
- \r
- $(this).animate({\r
- top: '+=-10' \r
- }, 600);\r
- $(this).siblings('p').find('img').animate({\r
- width: '60%' \r
- });\r
- }, \r
- function () {\r
-\r
- $(this).animate({top: '0'}, 600);\r
- $(this).siblings('p').find('img').animate({\r
- width: '65%' \r
- });\r
- }\r
- );*/\r
+ \r
\r
\r
- if ($('.widjets'.length > 0)) {\r
- $('.widjets li div').equalHeights();\r
- }\r
+ //if ($('.widjets'.length > 0)) {\r
+ ///$('.widjets li div').equalHeights();\r
+ ///}\r
\r
$(function() {\r
- if($("#from").length > 0 ){\r
- $( "#from" ).datepicker({\r
+ if($("#id_issue_date").length > 0 ){\r
+ $( "#id_issue_date" ).datepicker({\r
defaultDate: "+0", \r
- dateFormat: "dd-mm-yy",\r
+ dateFormat: "yy-mm-dd",\r
onSelect: function( selectedDate ) {\r
- $( "#to" ).datepicker( "option", "minDate", selectedDate );\r
+ $( "#id_expiration_date" ).datepicker( "option", "minDate", selectedDate );\r
}\r
});\r
- $( "#to" ).datepicker({\r
+ $( "#id_expiration_date" ).datepicker({\r
defaultDate: "+1w", \r
- dateFormat: "dd-mm-yy",\r
+ dateFormat: "yy-mm-dd",\r
onSelect: function( selectedDate ) {\r
- $( "#from" ).datepicker( "option", "maxDate", selectedDate );\r
+ $( "#id_issue_date" ).datepicker( "option", "maxDate", selectedDate );\r
}\r
});\r
}\r
+ \r
+ if($("#id_issue_date_demo").length > 0 ){\r
+ $( "#id_issue_date_demo" ).datepicker({\r
+ defaultDate: "+0", \r
+ dateFormat: "yy-mm-dd",\r
+ onSelect: function( selectedDate ) {\r
+ $( "#id_expiration_date_demo" ).datepicker( "option", "minDate", selectedDate );\r
+ }\r
+ });\r
+ $( "#id_expiration_date_demo" ).datepicker({\r
+ defaultDate: "+1w", \r
+ dateFormat: "yy-mm-dd",\r
+ onSelect: function( selectedDate ) {\r
+ $( "#id_issue_date_demo" ).datepicker( "option", "maxDate", selectedDate );\r
+ }\r
+ });\r
+ }\r
+ $( "#id_start_date" ).datepicker({\r
+ dateFormat: "yy-mm-dd",\r
+ onSelect: function( selectedDate ) {\r
+ $( "#id_start_date" ).datepicker( "option", "maxDate", selectedDate );\r
+ }\r
+ });\r
+ \r
+ $( "#id_end_date" ).datepicker({\r
+ dateFormat: "yy-mm-dd",\r
+ onSelect: function( selectedDate ) {\r
+ $( "#id_end_date" ).datepicker( "option", "maxDate", selectedDate );\r
+ }\r
+ });\r
});\r
-});\r
+ \r
+ \r
+ $(".table_sorting").tablesorter(); \r
+ \r
+ $('table .more-info').click(function(e){\r
+ e.preventDefault();\r
+ $(this).toggleClass('open');\r
+ if ($(this).hasClass('open')){\r
+ $(this).html('- less info ')\r
+ } else {\r
+ $(this).html('+ more info ')\r
+ }\r
+ $(this).parents('tr').next('tr').toggle();\r
+ \r
+ });\r
+ \r
+ $('.projects .details .edit').click( function(e){\r
+ e.preventDefault();\r
+ $(this).parents('.details').children('.data').hide();\r
+ $(this).parents('.details').children('.editable').show();\r
+ $(this).hide();\r
+ });\r
+ \r
+ \r
+ $('.widjet-x').click(function(e){\r
+ e.preventDefault();\r
+ $(this).siblings('ul').hide('slow');\r
+ $(this).hide();\r
+ })\r
\r
+ // todo den doulevei\r
+ $('#group_create_form').submit(function(){\r
+ if ($('.quotas-form .group .form-row.with-errors').length>0 ){\r
+ return false;\r
+ }\r
+ var flag = 0;\r
+ $('.quotas-form .group input[type="text"]').each(function() {\r
+ // get value from input\r
+ var value = $(this).val();\r
+ if (value){\r
+ flag =1;\r
+ }\r
+ });\r
+ if (flag =='0') {\r
+ $('#icons span.info').addClass('error-msg');\r
+ return false;\r
+ \r
+ }\r
+ });\r
+ \r
+ \r
+ \r
+ $("input.leave, input.join").click(function () {\r
+ $(this).parents('.msg-wrap').find('.dialog').show();\r
+ return false; \r
+ \r
+ });\r
+ \r
+ $('.msg-wrap .no').click( function(e){\r
+ e.preventDefault();\r
+ $(this).parents('.dialog').hide();\r
+ })\r
+ \r
+ $('.msg-wrap .yes').click( function(e){\r
+ e.preventDefault();\r
+ $(this).parents('.dialog').siblings('form').submit();\r
+ })\r
+ \r
+ \r
+});\r
+ \r
$(window).resize(function() {\r
\r
setContainerMinHeight('.container .wrapper');\r
- if ($('.widjets').length > 0) {\r
- $('.widjets li div').equalHeights();\r
- }\r
-\r
-});\r
-\r
-\r
-\r
-\r
- \r
-\r
+ ///if ($('.widjets').length > 0) {\r
+ //$('.widjets li div').equalHeights();\r
+ //}\r
\r
+});
\ No newline at end of file
var $this = $(this);
var el = $('<a class="checkbox-widget" href="javascript:void(0)"/>');
var form = $this.closest(".form-row");
+ var className = $this.attr('class');
+ var isRadio = $this.hasClass('radio');
// add class to identify form rows which contain a checkbox
form.addClass("with-checkbox");
+
if ($this.prev().length > 0) {
var lbl = $this.prev()[0];
$(lbl).addClass("checkbox-label");
$(lbl).click(function(e){
+ if (isRadio && $this.attr('checked')){ return; }
var src = e.srcElement.nodeName;
if (src == "LABEL" || src == "label") {
- el.toggleClass("checked");
+ el.toggleClass("checked");
+ $this.attr('checked', el.hasClass("checked"));
+ $this.trigger('changed');
};
+
})
}
}
$this.hide();
- if ($this.attr("checked")) {
+ if ($this.attr('checked')) {
el.addClass("checked");
+
}
+ el.addClass(className);
+
el.click(function() {
+ if (isRadio && $this.attr('checked')){ return; }
el.toggleClass("checked");
$this.attr('checked', el.hasClass("checked"));
+ $this.trigger('changed');
});
el.keypress(function(e){
if (e.keyCode == 0 || e.keyCode == 32){
+ if (isRadio && $this.attr('checked')){ return; }
e.preventDefault();
el.toggleClass("checked");
$this.attr('checked', el.hasClass("checked"));
+ $this.trigger('changed');
}
})
};
})( jQuery );
+
+
+
--- /dev/null
+/*! jQuery UI - v1.8.21 - 2012-06-05
+* https://github.com/jquery/jquery-ui
+* Includes: jquery.ui.core.js
+* Copyright (c) 2012 AUTHORS.txt; Licensed MIT, GPL */
+(function(a,b){function c(b,c){var e=b.nodeName.toLowerCase();if("area"===e){var f=b.parentNode,g=f.name,h;return!b.href||!g||f.nodeName.toLowerCase()!=="map"?!1:(h=a("img[usemap=#"+g+"]")[0],!!h&&d(h))}return(/input|select|textarea|button|object/.test(e)?!b.disabled:"a"==e?b.href||c:c)&&d(b)}function d(b){return!a(b).parents().andSelf().filter(function(){return a.curCSS(this,"visibility")==="hidden"||a.expr.filters.hidden(this)}).length}a.ui=a.ui||{};if(a.ui.version)return;a.extend(a.ui,{version:"1.8.21",keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38,WINDOWS:91}}),a.fn.extend({propAttr:a.fn.prop||a.fn.attr,_focus:a.fn.focus,focus:function(b,c){return typeof b=="number"?this.each(function(){var d=this;setTimeout(function(){a(d).focus(),c&&c.call(d)},b)}):this._focus.apply(this,arguments)},scrollParent:function(){var b;return a.browser.msie&&/(static|relative)/.test(this.css("position"))||/absolute/.test(this.css("position"))?b=this.parents().filter(function(){return/(relative|absolute|fixed)/.test(a.curCSS(this,"position",1))&&/(auto|scroll)/.test(a.curCSS(this,"overflow",1)+a.curCSS(this,"overflow-y",1)+a.curCSS(this,"overflow-x",1))}).eq(0):b=this.parents().filter(function(){return/(auto|scroll)/.test(a.curCSS(this,"overflow",1)+a.curCSS(this,"overflow-y",1)+a.curCSS(this,"overflow-x",1))}).eq(0),/fixed/.test(this.css("position"))||!b.length?a(document):b},zIndex:function(c){if(c!==b)return this.css("zIndex",c);if(this.length){var d=a(this[0]),e,f;while(d.length&&d[0]!==document){e=d.css("position");if(e==="absolute"||e==="relative"||e==="fixed"){f=parseInt(d.css("zIndex"),10);if(!isNaN(f)&&f!==0)return f}d=d.parent()}}return 0},disableSelection:function(){return this.bind((a.support.selectstart?"selectstart":"mousedown")+".ui-disableSelection",function(a){a.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}}),a.each(["Width","Height"],function(c,d){function h(b,c,d,f){return a.each(e,function(){c-=parseFloat(a.curCSS(b,"padding"+this,!0))||0,d&&(c-=parseFloat(a.curCSS(b,"border"+this+"Width",!0))||0),f&&(c-=parseFloat(a.curCSS(b,"margin"+this,!0))||0)}),c}var e=d==="Width"?["Left","Right"]:["Top","Bottom"],f=d.toLowerCase(),g={innerWidth:a.fn.innerWidth,innerHeight:a.fn.innerHeight,outerWidth:a.fn.outerWidth,outerHeight:a.fn.outerHeight};a.fn["inner"+d]=function(c){return c===b?g["inner"+d].call(this):this.each(function(){a(this).css(f,h(this,c)+"px")})},a.fn["outer"+d]=function(b,c){return typeof b!="number"?g["outer"+d].call(this,b):this.each(function(){a(this).css(f,h(this,b,!0,c)+"px")})}}),a.extend(a.expr[":"],{data:function(b,c,d){return!!a.data(b,d[3])},focusable:function(b){return c(b,!isNaN(a.attr(b,"tabindex")))},tabbable:function(b){var d=a.attr(b,"tabindex"),e=isNaN(d);return(e||d>=0)&&c(b,!e)}}),a(function(){var b=document.body,c=b.appendChild(c=document.createElement("div"));c.offsetHeight,a.extend(c.style,{minHeight:"100px",height:"auto",padding:0,borderWidth:0}),a.support.minHeight=c.offsetHeight===100,a.support.selectstart="onselectstart"in c,b.removeChild(c).style.display="none"}),a.extend(a.ui,{plugin:{add:function(b,c,d){var e=a.ui[b].prototype;for(var f in d)e.plugins[f]=e.plugins[f]||[],e.plugins[f].push([c,d[f]])},call:function(a,b,c){var d=a.plugins[b];if(!d||!a.element[0].parentNode)return;for(var e=0;e<d.length;e++)a.options[d[e][0]]&&d[e][1].apply(a.element,c)}},contains:function(a,b){return document.compareDocumentPosition?a.compareDocumentPosition(b)&16:a!==b&&a.contains(b)},hasScroll:function(b,c){if(a(b).css("overflow")==="hidden")return!1;var d=c&&c==="left"?"scrollLeft":"scrollTop",e=!1;return b[d]>0?!0:(b[d]=1,e=b[d]>0,b[d]=0,e)},isOverAxis:function(a,b,c){return a>b&&a<b+c},isOver:function(b,c,d,e,f,g){return a.ui.isOverAxis(b,d,f)&&a.ui.isOverAxis(c,e,g)}})})(jQuery);;/*! jQuery UI - v1.8.21 - 2012-06-05
+* https://github.com/jquery/jquery-ui
+* Includes: jquery.ui.widget.js
+* Copyright (c) 2012 AUTHORS.txt; Licensed MIT, GPL */
+(function(a,b){if(a.cleanData){var c=a.cleanData;a.cleanData=function(b){for(var d=0,e;(e=b[d])!=null;d++)try{a(e).triggerHandler("remove")}catch(f){}c(b)}}else{var d=a.fn.remove;a.fn.remove=function(b,c){return this.each(function(){return c||(!b||a.filter(b,[this]).length)&&a("*",this).add([this]).each(function(){try{a(this).triggerHandler("remove")}catch(b){}}),d.call(a(this),b,c)})}}a.widget=function(b,c,d){var e=b.split(".")[0],f;b=b.split(".")[1],f=e+"-"+b,d||(d=c,c=a.Widget),a.expr[":"][f]=function(c){return!!a.data(c,b)},a[e]=a[e]||{},a[e][b]=function(a,b){arguments.length&&this._createWidget(a,b)};var g=new c;g.options=a.extend(!0,{},g.options),a[e][b].prototype=a.extend(!0,g,{namespace:e,widgetName:b,widgetEventPrefix:a[e][b].prototype.widgetEventPrefix||b,widgetBaseClass:f},d),a.widget.bridge(b,a[e][b])},a.widget.bridge=function(c,d){a.fn[c]=function(e){var f=typeof e=="string",g=Array.prototype.slice.call(arguments,1),h=this;return e=!f&&g.length?a.extend.apply(null,[!0,e].concat(g)):e,f&&e.charAt(0)==="_"?h:(f?this.each(function(){var d=a.data(this,c),f=d&&a.isFunction(d[e])?d[e].apply(d,g):d;if(f!==d&&f!==b)return h=f,!1}):this.each(function(){var b=a.data(this,c);b?b.option(e||{})._init():a.data(this,c,new d(e,this))}),h)}},a.Widget=function(a,b){arguments.length&&this._createWidget(a,b)},a.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",options:{disabled:!1},_createWidget:function(b,c){a.data(c,this.widgetName,this),this.element=a(c),this.options=a.extend(!0,{},this.options,this._getCreateOptions(),b);var d=this;this.element.bind("remove."+this.widgetName,function(){d.destroy()}),this._create(),this._trigger("create"),this._init()},_getCreateOptions:function(){return a.metadata&&a.metadata.get(this.element[0])[this.widgetName]},_create:function(){},_init:function(){},destroy:function(){this.element.unbind("."+this.widgetName).removeData(this.widgetName),this.widget().unbind("."+this.widgetName).removeAttr("aria-disabled").removeClass(this.widgetBaseClass+"-disabled "+"ui-state-disabled")},widget:function(){return this.element},option:function(c,d){var e=c;if(arguments.length===0)return a.extend({},this.options);if(typeof c=="string"){if(d===b)return this.options[c];e={},e[c]=d}return this._setOptions(e),this},_setOptions:function(b){var c=this;return a.each(b,function(a,b){c._setOption(a,b)}),this},_setOption:function(a,b){return this.options[a]=b,a==="disabled"&&this.widget()[b?"addClass":"removeClass"](this.widgetBaseClass+"-disabled"+" "+"ui-state-disabled").attr("aria-disabled",b),this},enable:function(){return this._setOption("disabled",!1)},disable:function(){return this._setOption("disabled",!0)},_trigger:function(b,c,d){var e,f,g=this.options[b];d=d||{},c=a.Event(c),c.type=(b===this.widgetEventPrefix?b:this.widgetEventPrefix+b).toLowerCase(),c.target=this.element[0],f=c.originalEvent;if(f)for(e in f)e in c||(c[e]=f[e]);return this.element.trigger(c,d),!(a.isFunction(g)&&g.call(this.element[0],c,d)===!1||c.isDefaultPrevented())}}})(jQuery);;/*! jQuery UI - v1.8.21 - 2012-06-05
+* https://github.com/jquery/jquery-ui
+* Includes: jquery.ui.mouse.js
+* Copyright (c) 2012 AUTHORS.txt; Licensed MIT, GPL */
+(function(a,b){var c=!1;a(document).mouseup(function(a){c=!1}),a.widget("ui.mouse",{options:{cancel:":input,option",distance:1,delay:0},_mouseInit:function(){var b=this;this.element.bind("mousedown."+this.widgetName,function(a){return b._mouseDown(a)}).bind("click."+this.widgetName,function(c){if(!0===a.data(c.target,b.widgetName+".preventClickEvent"))return a.removeData(c.target,b.widgetName+".preventClickEvent"),c.stopImmediatePropagation(),!1}),this.started=!1},_mouseDestroy:function(){this.element.unbind("."+this.widgetName),a(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate)},_mouseDown:function(b){if(c)return;this._mouseStarted&&this._mouseUp(b),this._mouseDownEvent=b;var d=this,e=b.which==1,f=typeof this.options.cancel=="string"&&b.target.nodeName?a(b.target).closest(this.options.cancel).length:!1;if(!e||f||!this._mouseCapture(b))return!0;this.mouseDelayMet=!this.options.delay,this.mouseDelayMet||(this._mouseDelayTimer=setTimeout(function(){d.mouseDelayMet=!0},this.options.delay));if(this._mouseDistanceMet(b)&&this._mouseDelayMet(b)){this._mouseStarted=this._mouseStart(b)!==!1;if(!this._mouseStarted)return b.preventDefault(),!0}return!0===a.data(b.target,this.widgetName+".preventClickEvent")&&a.removeData(b.target,this.widgetName+".preventClickEvent"),this._mouseMoveDelegate=function(a){return d._mouseMove(a)},this._mouseUpDelegate=function(a){return d._mouseUp(a)},a(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate),b.preventDefault(),c=!0,!0},_mouseMove:function(b){return!a.browser.msie||document.documentMode>=9||!!b.button?this._mouseStarted?(this._mouseDrag(b),b.preventDefault()):(this._mouseDistanceMet(b)&&this._mouseDelayMet(b)&&(this._mouseStarted=this._mouseStart(this._mouseDownEvent,b)!==!1,this._mouseStarted?this._mouseDrag(b):this._mouseUp(b)),!this._mouseStarted):this._mouseUp(b)},_mouseUp:function(b){return a(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate),this._mouseStarted&&(this._mouseStarted=!1,b.target==this._mouseDownEvent.target&&a.data(b.target,this.widgetName+".preventClickEvent",!0),this._mouseStop(b)),!1},_mouseDistanceMet:function(a){return Math.max(Math.abs(this._mouseDownEvent.pageX-a.pageX),Math.abs(this._mouseDownEvent.pageY-a.pageY))>=this.options.distance},_mouseDelayMet:function(a){return this.mouseDelayMet},_mouseStart:function(a){},_mouseDrag:function(a){},_mouseStop:function(a){},_mouseCapture:function(a){return!0}})})(jQuery);;/*! jQuery UI - v1.8.21 - 2012-06-05
+* https://github.com/jquery/jquery-ui
+* Includes: jquery.ui.position.js
+* Copyright (c) 2012 AUTHORS.txt; Licensed MIT, GPL */
+(function(a,b){a.ui=a.ui||{};var c=/left|center|right/,d=/top|center|bottom/,e="center",f={},g=a.fn.position,h=a.fn.offset;a.fn.position=function(b){if(!b||!b.of)return g.apply(this,arguments);b=a.extend({},b);var h=a(b.of),i=h[0],j=(b.collision||"flip").split(" "),k=b.offset?b.offset.split(" "):[0,0],l,m,n;return i.nodeType===9?(l=h.width(),m=h.height(),n={top:0,left:0}):i.setTimeout?(l=h.width(),m=h.height(),n={top:h.scrollTop(),left:h.scrollLeft()}):i.preventDefault?(b.at="left top",l=m=0,n={top:b.of.pageY,left:b.of.pageX}):(l=h.outerWidth(),m=h.outerHeight(),n=h.offset()),a.each(["my","at"],function(){var a=(b[this]||"").split(" ");a.length===1&&(a=c.test(a[0])?a.concat([e]):d.test(a[0])?[e].concat(a):[e,e]),a[0]=c.test(a[0])?a[0]:e,a[1]=d.test(a[1])?a[1]:e,b[this]=a}),j.length===1&&(j[1]=j[0]),k[0]=parseInt(k[0],10)||0,k.length===1&&(k[1]=k[0]),k[1]=parseInt(k[1],10)||0,b.at[0]==="right"?n.left+=l:b.at[0]===e&&(n.left+=l/2),b.at[1]==="bottom"?n.top+=m:b.at[1]===e&&(n.top+=m/2),n.left+=k[0],n.top+=k[1],this.each(function(){var c=a(this),d=c.outerWidth(),g=c.outerHeight(),h=parseInt(a.curCSS(this,"marginLeft",!0))||0,i=parseInt(a.curCSS(this,"marginTop",!0))||0,o=d+h+(parseInt(a.curCSS(this,"marginRight",!0))||0),p=g+i+(parseInt(a.curCSS(this,"marginBottom",!0))||0),q=a.extend({},n),r;b.my[0]==="right"?q.left-=d:b.my[0]===e&&(q.left-=d/2),b.my[1]==="bottom"?q.top-=g:b.my[1]===e&&(q.top-=g/2),f.fractions||(q.left=Math.round(q.left),q.top=Math.round(q.top)),r={left:q.left-h,top:q.top-i},a.each(["left","top"],function(c,e){a.ui.position[j[c]]&&a.ui.position[j[c]][e](q,{targetWidth:l,targetHeight:m,elemWidth:d,elemHeight:g,collisionPosition:r,collisionWidth:o,collisionHeight:p,offset:k,my:b.my,at:b.at})}),a.fn.bgiframe&&c.bgiframe(),c.offset(a.extend(q,{using:b.using}))})},a.ui.position={fit:{left:function(b,c){var d=a(window),e=c.collisionPosition.left+c.collisionWidth-d.width()-d.scrollLeft();b.left=e>0?b.left-e:Math.max(b.left-c.collisionPosition.left,b.left)},top:function(b,c){var d=a(window),e=c.collisionPosition.top+c.collisionHeight-d.height()-d.scrollTop();b.top=e>0?b.top-e:Math.max(b.top-c.collisionPosition.top,b.top)}},flip:{left:function(b,c){if(c.at[0]===e)return;var d=a(window),f=c.collisionPosition.left+c.collisionWidth-d.width()-d.scrollLeft(),g=c.my[0]==="left"?-c.elemWidth:c.my[0]==="right"?c.elemWidth:0,h=c.at[0]==="left"?c.targetWidth:-c.targetWidth,i=-2*c.offset[0];b.left+=c.collisionPosition.left<0?g+h+i:f>0?g+h+i:0},top:function(b,c){if(c.at[1]===e)return;var d=a(window),f=c.collisionPosition.top+c.collisionHeight-d.height()-d.scrollTop(),g=c.my[1]==="top"?-c.elemHeight:c.my[1]==="bottom"?c.elemHeight:0,h=c.at[1]==="top"?c.targetHeight:-c.targetHeight,i=-2*c.offset[1];b.top+=c.collisionPosition.top<0?g+h+i:f>0?g+h+i:0}}},a.offset.setOffset||(a.offset.setOffset=function(b,c){/static/.test(a.curCSS(b,"position"))&&(b.style.position="relative");var d=a(b),e=d.offset(),f=parseInt(a.curCSS(b,"top",!0),10)||0,g=parseInt(a.curCSS(b,"left",!0),10)||0,h={top:c.top-e.top+f,left:c.left-e.left+g};"using"in c?c.using.call(b,h):d.css(h)},a.fn.offset=function(b){var c=this[0];return!c||!c.ownerDocument?null:b?a.isFunction(b)?this.each(function(c){a(this).offset(b.call(this,c,a(this).offset()))}):this.each(function(){a.offset.setOffset(this,b)}):h.call(this)}),function(){var b=document.getElementsByTagName("body")[0],c=document.createElement("div"),d,e,g,h,i;d=document.createElement(b?"div":"body"),g={visibility:"hidden",width:0,height:0,border:0,margin:0,background:"none"},b&&a.extend(g,{position:"absolute",left:"-1000px",top:"-1000px"});for(var j in g)d.style[j]=g[j];d.appendChild(c),e=b||document.documentElement,e.insertBefore(d,e.firstChild),c.style.cssText="position: absolute; left: 10.7432222px; top: 10.432325px; height: 30px; width: 201px;",h=a(c).offset(function(a,b){return b}).offset(),d.innerHTML="",e.removeChild(d),i=h.top+h.left+(b?2e3:0),f.fractions=i>21&&i<22}()})(jQuery);;/*! jQuery UI - v1.8.21 - 2012-06-05
+* https://github.com/jquery/jquery-ui
+* Includes: jquery.ui.draggable.js
+* Copyright (c) 2012 AUTHORS.txt; Licensed MIT, GPL */
+(function(a,b){a.widget("ui.draggable",a.ui.mouse,{widgetEventPrefix:"drag",options:{addClasses:!0,appendTo:"parent",axis:!1,connectToSortable:!1,containment:!1,cursor:"auto",cursorAt:!1,grid:!1,handle:!1,helper:"original",iframeFix:!1,opacity:!1,refreshPositions:!1,revert:!1,revertDuration:500,scope:"default",scroll:!0,scrollSensitivity:20,scrollSpeed:20,snap:!1,snapMode:"both",snapTolerance:20,stack:!1,zIndex:!1},_create:function(){this.options.helper=="original"&&!/^(?:r|a|f)/.test(this.element.css("position"))&&(this.element[0].style.position="relative"),this.options.addClasses&&this.element.addClass("ui-draggable"),this.options.disabled&&this.element.addClass("ui-draggable-disabled"),this._mouseInit()},destroy:function(){if(!this.element.data("draggable"))return;return this.element.removeData("draggable").unbind(".draggable").removeClass("ui-draggable ui-draggable-dragging ui-draggable-disabled"),this._mouseDestroy(),this},_mouseCapture:function(b){var c=this.options;return this.helper||c.disabled||a(b.target).is(".ui-resizable-handle")?!1:(this.handle=this._getHandle(b),this.handle?(c.iframeFix&&a(c.iframeFix===!0?"iframe":c.iframeFix).each(function(){a('<div class="ui-draggable-iframeFix" style="background: #fff;"></div>').css({width:this.offsetWidth+"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1e3}).css(a(this).offset()).appendTo("body")}),!0):!1)},_mouseStart:function(b){var c=this.options;return this.helper=this._createHelper(b),this.helper.addClass("ui-draggable-dragging"),this._cacheHelperProportions(),a.ui.ddmanager&&(a.ui.ddmanager.current=this),this._cacheMargins(),this.cssPosition=this.helper.css("position"),this.scrollParent=this.helper.scrollParent(),this.offset=this.positionAbs=this.element.offset(),this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left},a.extend(this.offset,{click:{left:b.pageX-this.offset.left,top:b.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()}),this.originalPosition=this.position=this._generatePosition(b),this.originalPageX=b.pageX,this.originalPageY=b.pageY,c.cursorAt&&this._adjustOffsetFromHelper(c.cursorAt),c.containment&&this._setContainment(),this._trigger("start",b)===!1?(this._clear(),!1):(this._cacheHelperProportions(),a.ui.ddmanager&&!c.dropBehaviour&&a.ui.ddmanager.prepareOffsets(this,b),this._mouseDrag(b,!0),a.ui.ddmanager&&a.ui.ddmanager.dragStart(this,b),!0)},_mouseDrag:function(b,c){this.position=this._generatePosition(b),this.positionAbs=this._convertPositionTo("absolute");if(!c){var d=this._uiHash();if(this._trigger("drag",b,d)===!1)return this._mouseUp({}),!1;this.position=d.position}if(!this.options.axis||this.options.axis!="y")this.helper[0].style.left=this.position.left+"px";if(!this.options.axis||this.options.axis!="x")this.helper[0].style.top=this.position.top+"px";return a.ui.ddmanager&&a.ui.ddmanager.drag(this,b),!1},_mouseStop:function(b){var c=!1;a.ui.ddmanager&&!this.options.dropBehaviour&&(c=a.ui.ddmanager.drop(this,b)),this.dropped&&(c=this.dropped,this.dropped=!1);var d=this.element[0],e=!1;while(d&&(d=d.parentNode))d==document&&(e=!0);if(!e&&this.options.helper==="original")return!1;if(this.options.revert=="invalid"&&!c||this.options.revert=="valid"&&c||this.options.revert===!0||a.isFunction(this.options.revert)&&this.options.revert.call(this.element,c)){var f=this;a(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,10),function(){f._trigger("stop",b)!==!1&&f._clear()})}else this._trigger("stop",b)!==!1&&this._clear();return!1},_mouseUp:function(b){return this.options.iframeFix===!0&&a("div.ui-draggable-iframeFix").each(function(){this.parentNode.removeChild(this)}),a.ui.ddmanager&&a.ui.ddmanager.dragStop(this,b),a.ui.mouse.prototype._mouseUp.call(this,b)},cancel:function(){return this.helper.is(".ui-draggable-dragging")?this._mouseUp({}):this._clear(),this},_getHandle:function(b){var c=!this.options.handle||!a(this.options.handle,this.element).length?!0:!1;return a(this.options.handle,this.element).find("*").andSelf().each(function(){this==b.target&&(c=!0)}),c},_createHelper:function(b){var c=this.options,d=a.isFunction(c.helper)?a(c.helper.apply(this.element[0],[b])):c.helper=="clone"?this.element.clone().removeAttr("id"):this.element;return d.parents("body").length||d.appendTo(c.appendTo=="parent"?this.element[0].parentNode:c.appendTo),d[0]!=this.element[0]&&!/(fixed|absolute)/.test(d.css("position"))&&d.css("position","absolute"),d},_adjustOffsetFromHelper:function(b){typeof b=="string"&&(b=b.split(" ")),a.isArray(b)&&(b={left:+b[0],top:+b[1]||0}),"left"in b&&(this.offset.click.left=b.left+this.margins.left),"right"in b&&(this.offset.click.left=this.helperProportions.width-b.right+this.margins.left),"top"in b&&(this.offset.click.top=b.top+this.margins.top),"bottom"in b&&(this.offset.click.top=this.helperProportions.height-b.bottom+this.margins.top)},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var b=this.offsetParent.offset();this.cssPosition=="absolute"&&this.scrollParent[0]!=document&&a.ui.contains(this.scrollParent[0],this.offsetParent[0])&&(b.left+=this.scrollParent.scrollLeft(),b.top+=this.scrollParent.scrollTop());if(this.offsetParent[0]==document.body||this.offsetParent[0].tagName&&this.offsetParent[0].tagName.toLowerCase()=="html"&&a.browser.msie)b={top:0,left:0};return{top:b.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:b.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if(this.cssPosition=="relative"){var a=this.element.position();return{top:a.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:a.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0,right:parseInt(this.element.css("marginRight"),10)||0,bottom:parseInt(this.element.css("marginBottom"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var b=this.options;b.containment=="parent"&&(b.containment=this.helper[0].parentNode);if(b.containment=="document"||b.containment=="window")this.containment=[b.containment=="document"?0:a(window).scrollLeft()-this.offset.relative.left-this.offset.parent.left,b.containment=="document"?0:a(window).scrollTop()-this.offset.relative.top-this.offset.parent.top,(b.containment=="document"?0:a(window).scrollLeft())+a(b.containment=="document"?document:window).width()-this.helperProportions.width-this.margins.left,(b.containment=="document"?0:a(window).scrollTop())+(a(b.containment=="document"?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top];if(!/^(document|window|parent)$/.test(b.containment)&&b.containment.constructor!=Array){var c=a(b.containment),d=c[0];if(!d)return;var e=c.offset(),f=a(d).css("overflow")!="hidden";this.containment=[(parseInt(a(d).css("borderLeftWidth"),10)||0)+(parseInt(a(d).css("paddingLeft"),10)||0),(parseInt(a(d).css("borderTopWidth"),10)||0)+(parseInt(a(d).css("paddingTop"),10)||0),(f?Math.max(d.scrollWidth,d.offsetWidth):d.offsetWidth)-(parseInt(a(d).css("borderLeftWidth"),10)||0)-(parseInt(a(d).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left-this.margins.right,(f?Math.max(d.scrollHeight,d.offsetHeight):d.offsetHeight)-(parseInt(a(d).css("borderTopWidth"),10)||0)-(parseInt(a(d).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top-this.margins.bottom],this.relative_container=c}else b.containment.constructor==Array&&(this.containment=b.containment)},_convertPositionTo:function(b,c){c||(c=this.position);var d=b=="absolute"?1:-1,e=this.options,f=this.cssPosition=="absolute"&&(this.scrollParent[0]==document||!a.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,g=/(html|body)/i.test(f[0].tagName);return{top:c.top+this.offset.relative.top*d+this.offset.parent.top*d-(a.browser.safari&&a.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():g?0:f.scrollTop())*d),left:c.left+this.offset.relative.left*d+this.offset.parent.left*d-(a.browser.safari&&a.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():g?0:f.scrollLeft())*d)}},_generatePosition:function(b){var c=this.options,d=this.cssPosition=="absolute"&&(this.scrollParent[0]==document||!a.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,e=/(html|body)/i.test(d[0].tagName),f=b.pageX,g=b.pageY;if(this.originalPosition){var h;if(this.containment){if(this.relative_container){var i=this.relative_container.offset();h=[this.containment[0]+i.left,this.containment[1]+i.top,this.containment[2]+i.left,this.containment[3]+i.top]}else h=this.containment;b.pageX-this.offset.click.left<h[0]&&(f=h[0]+this.offset.click.left),b.pageY-this.offset.click.top<h[1]&&(g=h[1]+this.offset.click.top),b.pageX-this.offset.click.left>h[2]&&(f=h[2]+this.offset.click.left),b.pageY-this.offset.click.top>h[3]&&(g=h[3]+this.offset.click.top)}if(c.grid){var j=c.grid[1]?this.originalPageY+Math.round((g-this.originalPageY)/c.grid[1])*c.grid[1]:this.originalPageY;g=h?j-this.offset.click.top<h[1]||j-this.offset.click.top>h[3]?j-this.offset.click.top<h[1]?j+c.grid[1]:j-c.grid[1]:j:j;var k=c.grid[0]?this.originalPageX+Math.round((f-this.originalPageX)/c.grid[0])*c.grid[0]:this.originalPageX;f=h?k-this.offset.click.left<h[0]||k-this.offset.click.left>h[2]?k-this.offset.click.left<h[0]?k+c.grid[0]:k-c.grid[0]:k:k}}return{top:g-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+(a.browser.safari&&a.browser.version<526&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollTop():e?0:d.scrollTop()),left:f-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+(a.browser.safari&&a.browser.version<526&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():e?0:d.scrollLeft())}},_clear:function(){this.helper.removeClass("ui-draggable-dragging"),this.helper[0]!=this.element[0]&&!this.cancelHelperRemoval&&this.helper.remove(),this.helper=null,this.cancelHelperRemoval=!1},_trigger:function(b,c,d){return d=d||this._uiHash(),a.ui.plugin.call(this,b,[c,d]),b=="drag"&&(this.positionAbs=this._convertPositionTo("absolute")),a.Widget.prototype._trigger.call(this,b,c,d)},plugins:{},_uiHash:function(a){return{helper:this.helper,position:this.position,originalPosition:this.originalPosition,offset:this.positionAbs}}}),a.extend(a.ui.draggable,{version:"1.8.21"}),a.ui.plugin.add("draggable","connectToSortable",{start:function(b,c){var d=a(this).data("draggable"),e=d.options,f=a.extend({},c,{item:d.element});d.sortables=[],a(e.connectToSortable).each(function(){var c=a.data(this,"sortable");c&&!c.options.disabled&&(d.sortables.push({instance:c,shouldRevert:c.options.revert}),c.refreshPositions(),c._trigger("activate",b,f))})},stop:function(b,c){var d=a(this).data("draggable"),e=a.extend({},c,{item:d.element});a.each(d.sortables,function(){this.instance.isOver?(this.instance.isOver=0,d.cancelHelperRemoval=!0,this.instance.cancelHelperRemoval=!1,this.shouldRevert&&(this.instance.options.revert=!0),this.instance._mouseStop(b),this.instance.options.helper=this.instance.options._helper,d.options.helper=="original"&&this.instance.currentItem.css({top:"auto",left:"auto"})):(this.instance.cancelHelperRemoval=!1,this.instance._trigger("deactivate",b,e))})},drag:function(b,c){var d=a(this).data("draggable"),e=this,f=function(b){var c=this.offset.click.top,d=this.offset.click.left,e=this.positionAbs.top,f=this.positionAbs.left,g=b.height,h=b.width,i=b.top,j=b.left;return a.ui.isOver(e+c,f+d,i,j,g,h)};a.each(d.sortables,function(f){this.instance.positionAbs=d.positionAbs,this.instance.helperProportions=d.helperProportions,this.instance.offset.click=d.offset.click,this.instance._intersectsWith(this.instance.containerCache)?(this.instance.isOver||(this.instance.isOver=1,this.instance.currentItem=a(e).clone().removeAttr("id").appendTo(this.instance.element).data("sortable-item",!0),this.instance.options._helper=this.instance.options.helper,this.instance.options.helper=function(){return c.helper[0]},b.target=this.instance.currentItem[0],this.instance._mouseCapture(b,!0),this.instance._mouseStart(b,!0,!0),this.instance.offset.click.top=d.offset.click.top,this.instance.offset.click.left=d.offset.click.left,this.instance.offset.parent.left-=d.offset.parent.left-this.instance.offset.parent.left,this.instance.offset.parent.top-=d.offset.parent.top-this.instance.offset.parent.top,d._trigger("toSortable",b),d.dropped=this.instance.element,d.currentItem=d.element,this.instance.fromOutside=d),this.instance.currentItem&&this.instance._mouseDrag(b)):this.instance.isOver&&(this.instance.isOver=0,this.instance.cancelHelperRemoval=!0,this.instance.options.revert=!1,this.instance._trigger("out",b,this.instance._uiHash(this.instance)),this.instance._mouseStop(b,!0),this.instance.options.helper=this.instance.options._helper,this.instance.currentItem.remove(),this.instance.placeholder&&this.instance.placeholder.remove(),d._trigger("fromSortable",b),d.dropped=!1)})}}),a.ui.plugin.add("draggable","cursor",{start:function(b,c){var d=a("body"),e=a(this).data("draggable").options;d.css("cursor")&&(e._cursor=d.css("cursor")),d.css("cursor",e.cursor)},stop:function(b,c){var d=a(this).data("draggable").options;d._cursor&&a("body").css("cursor",d._cursor)}}),a.ui.plugin.add("draggable","opacity",{start:function(b,c){var d=a(c.helper),e=a(this).data("draggable").options;d.css("opacity")&&(e._opacity=d.css("opacity")),d.css("opacity",e.opacity)},stop:function(b,c){var d=a(this).data("draggable").options;d._opacity&&a(c.helper).css("opacity",d._opacity)}}),a.ui.plugin.add("draggable","scroll",{start:function(b,c){var d=a(this).data("draggable");d.scrollParent[0]!=document&&d.scrollParent[0].tagName!="HTML"&&(d.overflowOffset=d.scrollParent.offset())},drag:function(b,c){var d=a(this).data("draggable"),e=d.options,f=!1;if(d.scrollParent[0]!=document&&d.scrollParent[0].tagName!="HTML"){if(!e.axis||e.axis!="x")d.overflowOffset.top+d.scrollParent[0].offsetHeight-b.pageY<e.scrollSensitivity?d.scrollParent[0].scrollTop=f=d.scrollParent[0].scrollTop+e.scrollSpeed:b.pageY-d.overflowOffset.top<e.scrollSensitivity&&(d.scrollParent[0].scrollTop=f=d.scrollParent[0].scrollTop-e.scrollSpeed);if(!e.axis||e.axis!="y")d.overflowOffset.left+d.scrollParent[0].offsetWidth-b.pageX<e.scrollSensitivity?d.scrollParent[0].scrollLeft=f=d.scrollParent[0].scrollLeft+e.scrollSpeed:b.pageX-d.overflowOffset.left<e.scrollSensitivity&&(d.scrollParent[0].scrollLeft=f=d.scrollParent[0].scrollLeft-e.scrollSpeed)}else{if(!e.axis||e.axis!="x")b.pageY-a(document).scrollTop()<e.scrollSensitivity?f=a(document).scrollTop(a(document).scrollTop()-e.scrollSpeed):a(window).height()-(b.pageY-a(document).scrollTop())<e.scrollSensitivity&&(f=a(document).scrollTop(a(document).scrollTop()+e.scrollSpeed));if(!e.axis||e.axis!="y")b.pageX-a(document).scrollLeft()<e.scrollSensitivity?f=a(document).scrollLeft(a(document).scrollLeft()-e.scrollSpeed):a(window).width()-(b.pageX-a(document).scrollLeft())<e.scrollSensitivity&&(f=a(document).scrollLeft(a(document).scrollLeft()+e.scrollSpeed))}f!==!1&&a.ui.ddmanager&&!e.dropBehaviour&&a.ui.ddmanager.prepareOffsets(d,b)}}),a.ui.plugin.add("draggable","snap",{start:function(b,c){var d=a(this).data("draggable"),e=d.options;d.snapElements=[],a(e.snap.constructor!=String?e.snap.items||":data(draggable)":e.snap).each(function(){var b=a(this),c=b.offset();this!=d.element[0]&&d.snapElements.push({item:this,width:b.outerWidth(),height:b.outerHeight(),top:c.top,left:c.left})})},drag:function(b,c){var d=a(this).data("draggable"),e=d.options,f=e.snapTolerance,g=c.offset.left,h=g+d.helperProportions.width,i=c.offset.top,j=i+d.helperProportions.height;for(var k=d.snapElements.length-1;k>=0;k--){var l=d.snapElements[k].left,m=l+d.snapElements[k].width,n=d.snapElements[k].top,o=n+d.snapElements[k].height;if(!(l-f<g&&g<m+f&&n-f<i&&i<o+f||l-f<g&&g<m+f&&n-f<j&&j<o+f||l-f<h&&h<m+f&&n-f<i&&i<o+f||l-f<h&&h<m+f&&n-f<j&&j<o+f)){d.snapElements[k].snapping&&d.options.snap.release&&d.options.snap.release.call(d.element,b,a.extend(d._uiHash(),{snapItem:d.snapElements[k].item})),d.snapElements[k].snapping=!1;continue}if(e.snapMode!="inner"){var p=Math.abs(n-j)<=f,q=Math.abs(o-i)<=f,r=Math.abs(l-h)<=f,s=Math.abs(m-g)<=f;p&&(c.position.top=d._convertPositionTo("relative",{top:n-d.helperProportions.height,left:0}).top-d.margins.top),q&&(c.position.top=d._convertPositionTo("relative",{top:o,left:0}).top-d.margins.top),r&&(c.position.left=d._convertPositionTo("relative",{top:0,left:l-d.helperProportions.width}).left-d.margins.left),s&&(c.position.left=d._convertPositionTo("relative",{top:0,left:m}).left-d.margins.left)}var t=p||q||r||s;if(e.snapMode!="outer"){var p=Math.abs(n-i)<=f,q=Math.abs(o-j)<=f,r=Math.abs(l-g)<=f,s=Math.abs(m-h)<=f;p&&(c.position.top=d._convertPositionTo("relative",{top:n,left:0}).top-d.margins.top),q&&(c.position.top=d._convertPositionTo("relative",{top:o-d.helperProportions.height,left:0}).top-d.margins.top),r&&(c.position.left=d._convertPositionTo("relative",{top:0,left:l}).left-d.margins.left),s&&(c.position.left=d._convertPositionTo("relative",{top:0,left:m-d.helperProportions.width}).left-d.margins.left)}!d.snapElements[k].snapping&&(p||q||r||s||t)&&d.options.snap.snap&&d.options.snap.snap.call(d.element,b,a.extend(d._uiHash(),{snapItem:d.snapElements[k].item})),d.snapElements[k].snapping=p||q||r||s||t}}}),a.ui.plugin.add("draggable","stack",{start:function(b,c){var d=a(this).data("draggable").options,e=a.makeArray(a(d.stack)).sort(function(b,c){return(parseInt(a(b).css("zIndex"),10)||0)-(parseInt(a(c).css("zIndex"),10)||0)});if(!e.length)return;var f=parseInt(e[0].style.zIndex)||0;a(e).each(function(a){this.style.zIndex=f+a}),this[0].style.zIndex=f+e.length}}),a.ui.plugin.add("draggable","zIndex",{start:function(b,c){var d=a(c.helper),e=a(this).data("draggable").options;d.css("zIndex")&&(e._zIndex=d.css("zIndex")),d.css("zIndex",e.zIndex)},stop:function(b,c){var d=a(this).data("draggable").options;d._zIndex&&a(c.helper).css("zIndex",d._zIndex)}})})(jQuery);;/*! jQuery UI - v1.8.21 - 2012-06-05
+* https://github.com/jquery/jquery-ui
+* Includes: jquery.ui.droppable.js
+* Copyright (c) 2012 AUTHORS.txt; Licensed MIT, GPL */
+(function(a,b){a.widget("ui.droppable",{widgetEventPrefix:"drop",options:{accept:"*",activeClass:!1,addClasses:!0,greedy:!1,hoverClass:!1,scope:"default",tolerance:"intersect"},_create:function(){var b=this.options,c=b.accept;this.isover=0,this.isout=1,this.accept=a.isFunction(c)?c:function(a){return a.is(c)},this.proportions={width:this.element[0].offsetWidth,height:this.element[0].offsetHeight},a.ui.ddmanager.droppables[b.scope]=a.ui.ddmanager.droppables[b.scope]||[],a.ui.ddmanager.droppables[b.scope].push(this),b.addClasses&&this.element.addClass("ui-droppable")},destroy:function(){var b=a.ui.ddmanager.droppables[this.options.scope];for(var c=0;c<b.length;c++)b[c]==this&&b.splice(c,1);return this.element.removeClass("ui-droppable ui-droppable-disabled").removeData("droppable").unbind(".droppable"),this},_setOption:function(b,c){b=="accept"&&(this.accept=a.isFunction(c)?c:function(a){return a.is(c)}),a.Widget.prototype._setOption.apply(this,arguments)},_activate:function(b){var c=a.ui.ddmanager.current;this.options.activeClass&&this.element.addClass(this.options.activeClass),c&&this._trigger("activate",b,this.ui(c))},_deactivate:function(b){var c=a.ui.ddmanager.current;this.options.activeClass&&this.element.removeClass(this.options.activeClass),c&&this._trigger("deactivate",b,this.ui(c))},_over:function(b){var c=a.ui.ddmanager.current;if(!c||(c.currentItem||c.element)[0]==this.element[0])return;this.accept.call(this.element[0],c.currentItem||c.element)&&(this.options.hoverClass&&this.element.addClass(this.options.hoverClass),this._trigger("over",b,this.ui(c)))},_out:function(b){var c=a.ui.ddmanager.current;if(!c||(c.currentItem||c.element)[0]==this.element[0])return;this.accept.call(this.element[0],c.currentItem||c.element)&&(this.options.hoverClass&&this.element.removeClass(this.options.hoverClass),this._trigger("out",b,this.ui(c)))},_drop:function(b,c){var d=c||a.ui.ddmanager.current;if(!d||(d.currentItem||d.element)[0]==this.element[0])return!1;var e=!1;return this.element.find(":data(droppable)").not(".ui-draggable-dragging").each(function(){var b=a.data(this,"droppable");if(b.options.greedy&&!b.options.disabled&&b.options.scope==d.options.scope&&b.accept.call(b.element[0],d.currentItem||d.element)&&a.ui.intersect(d,a.extend(b,{offset:b.element.offset()}),b.options.tolerance))return e=!0,!1}),e?!1:this.accept.call(this.element[0],d.currentItem||d.element)?(this.options.activeClass&&this.element.removeClass(this.options.activeClass),this.options.hoverClass&&this.element.removeClass(this.options.hoverClass),this._trigger("drop",b,this.ui(d)),this.element):!1},ui:function(a){return{draggable:a.currentItem||a.element,helper:a.helper,position:a.position,offset:a.positionAbs}}}),a.extend(a.ui.droppable,{version:"1.8.21"}),a.ui.intersect=function(b,c,d){if(!c.offset)return!1;var e=(b.positionAbs||b.position.absolute).left,f=e+b.helperProportions.width,g=(b.positionAbs||b.position.absolute).top,h=g+b.helperProportions.height,i=c.offset.left,j=i+c.proportions.width,k=c.offset.top,l=k+c.proportions.height;switch(d){case"fit":return i<=e&&f<=j&&k<=g&&h<=l;case"intersect":return i<e+b.helperProportions.width/2&&f-b.helperProportions.width/2<j&&k<g+b.helperProportions.height/2&&h-b.helperProportions.height/2<l;case"pointer":var m=(b.positionAbs||b.position.absolute).left+(b.clickOffset||b.offset.click).left,n=(b.positionAbs||b.position.absolute).top+(b.clickOffset||b.offset.click).top,o=a.ui.isOver(n,m,k,i,c.proportions.height,c.proportions.width);return o;case"touch":return(g>=k&&g<=l||h>=k&&h<=l||g<k&&h>l)&&(e>=i&&e<=j||f>=i&&f<=j||e<i&&f>j);default:return!1}},a.ui.ddmanager={current:null,droppables:{"default":[]},prepareOffsets:function(b,c){var d=a.ui.ddmanager.droppables[b.options.scope]||[],e=c?c.type:null,f=(b.currentItem||b.element).find(":data(droppable)").andSelf();g:for(var h=0;h<d.length;h++){if(d[h].options.disabled||b&&!d[h].accept.call(d[h].element[0],b.currentItem||b.element))continue;for(var i=0;i<f.length;i++)if(f[i]==d[h].element[0]){d[h].proportions.height=0;continue g}d[h].visible=d[h].element.css("display")!="none";if(!d[h].visible)continue;e=="mousedown"&&d[h]._activate.call(d[h],c),d[h].offset=d[h].element.offset(),d[h].proportions={width:d[h].element[0].offsetWidth,height:d[h].element[0].offsetHeight}}},drop:function(b,c){var d=!1;return a.each(a.ui.ddmanager.droppables[b.options.scope]||[],function(){if(!this.options)return;!this.options.disabled&&this.visible&&a.ui.intersect(b,this,this.options.tolerance)&&(d=this._drop.call(this,c)||d),!this.options.disabled&&this.visible&&this.accept.call(this.element[0],b.currentItem||b.element)&&(this.isout=1,this.isover=0,this._deactivate.call(this,c))}),d},dragStart:function(b,c){b.element.parents(":not(body,html)").bind("scroll.droppable",function(){b.options.refreshPositions||a.ui.ddmanager.prepareOffsets(b,c)})},drag:function(b,c){b.options.refreshPositions&&a.ui.ddmanager.prepareOffsets(b,c),a.each(a.ui.ddmanager.droppables[b.options.scope]||[],function(){if(this.options.disabled||this.greedyChild||!this.visible)return;var d=a.ui.intersect(b,this,this.options.tolerance),e=!d&&this.isover==1?"isout":d&&this.isover==0?"isover":null;if(!e)return;var f;if(this.options.greedy){var g=this.element.parents(":data(droppable):eq(0)");g.length&&(f=a.data(g[0],"droppable"),f.greedyChild=e=="isover"?1:0)}f&&e=="isover"&&(f.isover=0,f.isout=1,f._out.call(f,c)),this[e]=1,this[e=="isout"?"isover":"isout"]=0,this[e=="isover"?"_over":"_out"].call(this,c),f&&e=="isout"&&(f.isout=0,f.isover=1,f._over.call(f,c))})},dragStop:function(b,c){b.element.parents(":not(body,html)").unbind("scroll.droppable"),b.options.refreshPositions||a.ui.ddmanager.prepareOffsets(b,c)}}})(jQuery);;/*! jQuery UI - v1.8.21 - 2012-06-05
+* https://github.com/jquery/jquery-ui
+* Includes: jquery.ui.resizable.js
+* Copyright (c) 2012 AUTHORS.txt; Licensed MIT, GPL */
+(function(a,b){a.widget("ui.resizable",a.ui.mouse,{widgetEventPrefix:"resize",options:{alsoResize:!1,animate:!1,animateDuration:"slow",animateEasing:"swing",aspectRatio:!1,autoHide:!1,containment:!1,ghost:!1,grid:!1,handles:"e,s,se",helper:!1,maxHeight:null,maxWidth:null,minHeight:10,minWidth:10,zIndex:1e3},_create:function(){var b=this,c=this.options;this.element.addClass("ui-resizable"),a.extend(this,{_aspectRatio:!!c.aspectRatio,aspectRatio:c.aspectRatio,originalElement:this.element,_proportionallyResizeElements:[],_helper:c.helper||c.ghost||c.animate?c.helper||"ui-resizable-helper":null}),this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)&&(this.element.wrap(a('<div class="ui-wrapper" style="overflow: hidden;"></div>').css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(),top:this.element.css("top"),left:this.element.css("left")})),this.element=this.element.parent().data("resizable",this.element.data("resizable")),this.elementIsWrapper=!0,this.element.css({marginLeft:this.originalElement.css("marginLeft"),marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom")}),this.originalElement.css({marginLeft:0,marginTop:0,marginRight:0,marginBottom:0}),this.originalResizeStyle=this.originalElement.css("resize"),this.originalElement.css("resize","none"),this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"})),this.originalElement.css({margin:this.originalElement.css("margin")}),this._proportionallyResize()),this.handles=c.handles||(a(".ui-resizable-handle",this.element).length?{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne",nw:".ui-resizable-nw"}:"e,s,se");if(this.handles.constructor==String){this.handles=="all"&&(this.handles="n,e,s,w,se,sw,ne,nw");var d=this.handles.split(",");this.handles={};for(var e=0;e<d.length;e++){var f=a.trim(d[e]),g="ui-resizable-"+f,h=a('<div class="ui-resizable-handle '+g+'"></div>');h.css({zIndex:c.zIndex}),"se"==f&&h.addClass("ui-icon ui-icon-gripsmall-diagonal-se"),this.handles[f]=".ui-resizable-"+f,this.element.append(h)}}this._renderAxis=function(b){b=b||this.element;for(var c in this.handles){this.handles[c].constructor==String&&(this.handles[c]=a(this.handles[c],this.element).show());if(this.elementIsWrapper&&this.originalElement[0].nodeName.match(/textarea|input|select|button/i)){var d=a(this.handles[c],this.element),e=0;e=/sw|ne|nw|se|n|s/.test(c)?d.outerHeight():d.outerWidth();var f=["padding",/ne|nw|n/.test(c)?"Top":/se|sw|s/.test(c)?"Bottom":/^e$/.test(c)?"Right":"Left"].join("");b.css(f,e),this._proportionallyResize()}if(!a(this.handles[c]).length)continue}},this._renderAxis(this.element),this._handles=a(".ui-resizable-handle",this.element).disableSelection(),this._handles.mouseover(function(){if(!b.resizing){if(this.className)var a=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);b.axis=a&&a[1]?a[1]:"se"}}),c.autoHide&&(this._handles.hide(),a(this.element).addClass("ui-resizable-autohide").hover(function(){if(c.disabled)return;a(this).removeClass("ui-resizable-autohide"),b._handles.show()},function(){if(c.disabled)return;b.resizing||(a(this).addClass("ui-resizable-autohide"),b._handles.hide())})),this._mouseInit()},destroy:function(){this._mouseDestroy();var b=function(b){a(b).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing").removeData("resizable").unbind(".resizable").find(".ui-resizable-handle").remove()};if(this.elementIsWrapper){b(this.element);var c=this.element;c.after(this.originalElement.css({position:c.css("position"),width:c.outerWidth(),height:c.outerHeight(),top:c.css("top"),left:c.css("left")})).remove()}return this.originalElement.css("resize",this.originalResizeStyle),b(this.originalElement),this},_mouseCapture:function(b){var c=!1;for(var d in this.handles)a(this.handles[d])[0]==b.target&&(c=!0);return!this.options.disabled&&c},_mouseStart:function(b){var d=this.options,e=this.element.position(),f=this.element;this.resizing=!0,this.documentScroll={top:a(document).scrollTop(),left:a(document).scrollLeft()},(f.is(".ui-draggable")||/absolute/.test(f.css("position")))&&f.css({position:"absolute",top:e.top,left:e.left}),this._renderProxy();var g=c(this.helper.css("left")),h=c(this.helper.css("top"));d.containment&&(g+=a(d.containment).scrollLeft()||0,h+=a(d.containment).scrollTop()||0),this.offset=this.helper.offset(),this.position={left:g,top:h},this.size=this._helper?{width:f.outerWidth(),height:f.outerHeight()}:{width:f.width(),height:f.height()},this.originalSize=this._helper?{width:f.outerWidth(),height:f.outerHeight()}:{width:f.width(),height:f.height()},this.originalPosition={left:g,top:h},this.sizeDiff={width:f.outerWidth()-f.width(),height:f.outerHeight()-f.height()},this.originalMousePosition={left:b.pageX,top:b.pageY},this.aspectRatio=typeof d.aspectRatio=="number"?d.aspectRatio:this.originalSize.width/this.originalSize.height||1;var i=a(".ui-resizable-"+this.axis).css("cursor");return a("body").css("cursor",i=="auto"?this.axis+"-resize":i),f.addClass("ui-resizable-resizing"),this._propagate("start",b),!0},_mouseDrag:function(b){var c=this.helper,d=this.options,e={},f=this,g=this.originalMousePosition,h=this.axis,i=b.pageX-g.left||0,j=b.pageY-g.top||0,k=this._change[h];if(!k)return!1;var l=k.apply(this,[b,i,j]),m=a.browser.msie&&a.browser.version<7,n=this.sizeDiff;this._updateVirtualBoundaries(b.shiftKey);if(this._aspectRatio||b.shiftKey)l=this._updateRatio(l,b);return l=this._respectSize(l,b),this._propagate("resize",b),c.css({top:this.position.top+"px",left:this.position.left+"px",width:this.size.width+"px",height:this.size.height+"px"}),!this._helper&&this._proportionallyResizeElements.length&&this._proportionallyResize(),this._updateCache(l),this._trigger("resize",b,this.ui()),!1},_mouseStop:function(b){this.resizing=!1;var c=this.options,d=this;if(this._helper){var e=this._proportionallyResizeElements,f=e.length&&/textarea/i.test(e[0].nodeName),g=f&&a.ui.hasScroll(e[0],"left")?0:d.sizeDiff.height,h=f?0:d.sizeDiff.width,i={width:d.helper.width()-h,height:d.helper.height()-g},j=parseInt(d.element.css("left"),10)+(d.position.left-d.originalPosition.left)||null,k=parseInt(d.element.css("top"),10)+(d.position.top-d.originalPosition.top)||null;c.animate||this.element.css(a.extend(i,{top:k,left:j})),d.helper.height(d.size.height),d.helper.width(d.size.width),this._helper&&!c.animate&&this._proportionallyResize()}return a("body").css("cursor","auto"),this.element.removeClass("ui-resizable-resizing"),this._propagate("stop",b),this._helper&&this.helper.remove(),!1},_updateVirtualBoundaries:function(a){var b=this.options,c,e,f,g,h;h={minWidth:d(b.minWidth)?b.minWidth:0,maxWidth:d(b.maxWidth)?b.maxWidth:Infinity,minHeight:d(b.minHeight)?b.minHeight:0,maxHeight:d(b.maxHeight)?b.maxHeight:Infinity};if(this._aspectRatio||a)c=h.minHeight*this.aspectRatio,f=h.minWidth/this.aspectRatio,e=h.maxHeight*this.aspectRatio,g=h.maxWidth/this.aspectRatio,c>h.minWidth&&(h.minWidth=c),f>h.minHeight&&(h.minHeight=f),e<h.maxWidth&&(h.maxWidth=e),g<h.maxHeight&&(h.maxHeight=g);this._vBoundaries=h},_updateCache:function(a){var b=this.options;this.offset=this.helper.offset(),d(a.left)&&(this.position.left=a.left),d(a.top)&&(this.position.top=a.top),d(a.height)&&(this.size.height=a.height),d(a.width)&&(this.size.width=a.width)},_updateRatio:function(a,b){var c=this.options,e=this.position,f=this.size,g=this.axis;return d(a.height)?a.width=a.height*this.aspectRatio:d(a.width)&&(a.height=a.width/this.aspectRatio),g=="sw"&&(a.left=e.left+(f.width-a.width),a.top=null),g=="nw"&&(a.top=e.top+(f.height-a.height),a.left=e.left+(f.width-a.width)),a},_respectSize:function(a,b){var c=this.helper,e=this._vBoundaries,f=this._aspectRatio||b.shiftKey,g=this.axis,h=d(a.width)&&e.maxWidth&&e.maxWidth<a.width,i=d(a.height)&&e.maxHeight&&e.maxHeight<a.height,j=d(a.width)&&e.minWidth&&e.minWidth>a.width,k=d(a.height)&&e.minHeight&&e.minHeight>a.height;j&&(a.width=e.minWidth),k&&(a.height=e.minHeight),h&&(a.width=e.maxWidth),i&&(a.height=e.maxHeight);var l=this.originalPosition.left+this.originalSize.width,m=this.position.top+this.size.height,n=/sw|nw|w/.test(g),o=/nw|ne|n/.test(g);j&&n&&(a.left=l-e.minWidth),h&&n&&(a.left=l-e.maxWidth),k&&o&&(a.top=m-e.minHeight),i&&o&&(a.top=m-e.maxHeight);var p=!a.width&&!a.height;return p&&!a.left&&a.top?a.top=null:p&&!a.top&&a.left&&(a.left=null),a},_proportionallyResize:function(){var b=this.options;if(!this._proportionallyResizeElements.length)return;var c=this.helper||this.element;for(var d=0;d<this._proportionallyResizeElements.length;d++){var e=this._proportionallyResizeElements[d];if(!this.borderDif){var f=[e.css("borderTopWidth"),e.css("borderRightWidth"),e.css("borderBottomWidth"),e.css("borderLeftWidth")],g=[e.css("paddingTop"),e.css("paddingRight"),e.css("paddingBottom"),e.css("paddingLeft")];this.borderDif=a.map(f,function(a,b){var c=parseInt(a,10)||0,d=parseInt(g[b],10)||0;return c+d})}if(!a.browser.msie||!a(c).is(":hidden")&&!a(c).parents(":hidden").length)e.css({height:c.height()-this.borderDif[0]-this.borderDif[2]||0,width:c.width()-this.borderDif[1]-this.borderDif[3]||0});else continue}},_renderProxy:function(){var b=this.element,c=this.options;this.elementOffset=b.offset();if(this._helper){this.helper=this.helper||a('<div style="overflow:hidden;"></div>');var d=a.browser.msie&&a.browser.version<7,e=d?1:0,f=d?2:-1;this.helper.addClass(this._helper).css({width:this.element.outerWidth()+f,height:this.element.outerHeight()+f,position:"absolute",left:this.elementOffset.left-e+"px",top:this.elementOffset.top-e+"px",zIndex:++c.zIndex}),this.helper.appendTo("body").disableSelection()}else this.helper=this.element},_change:{e:function(a,b,c){return{width:this.originalSize.width+b}},w:function(a,b,c){var d=this.options,e=this.originalSize,f=this.originalPosition;return{left:f.left+b,width:e.width-b}},n:function(a,b,c){var d=this.options,e=this.originalSize,f=this.originalPosition;return{top:f.top+c,height:e.height-c}},s:function(a,b,c){return{height:this.originalSize.height+c}},se:function(b,c,d){return a.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[b,c,d]))},sw:function(b,c,d){return a.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[b,c,d]))},ne:function(b,c,d){return a.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[b,c,d]))},nw:function(b,c,d){return a.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[b,c,d]))}},_propagate:function(b,c){a.ui.plugin.call(this,b,[c,this.ui()]),b!="resize"&&this._trigger(b,c,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}}),a.extend(a.ui.resizable,{version:"1.8.21"}),a.ui.plugin.add("resizable","alsoResize",{start:function(b,c){var d=a(this).data("resizable"),e=d.options,f=function(b){a(b).each(function(){var b=a(this);b.data("resizable-alsoresize",{width:parseInt(b.width(),10),height:parseInt(b.height(),10),left:parseInt(b.css("left"),10),top:parseInt(b.css("top"),10)})})};typeof e.alsoResize=="object"&&!e.alsoResize.parentNode?e.alsoResize.length?(e.alsoResize=e.alsoResize[0],f(e.alsoResize)):a.each(e.alsoResize,function(a){f(a)}):f(e.alsoResize)},resize:function(b,c){var d=a(this).data("resizable"),e=d.options,f=d.originalSize,g=d.originalPosition,h={height:d.size.height-f.height||0,width:d.size.width-f.width||0,top:d.position.top-g.top||0,left:d.position.left-g.left||0},i=function(b,d){a(b).each(function(){var b=a(this),e=a(this).data("resizable-alsoresize"),f={},g=d&&d.length?d:b.parents(c.originalElement[0]).length?["width","height"]:["width","height","top","left"];a.each(g,function(a,b){var c=(e[b]||0)+(h[b]||0);c&&c>=0&&(f[b]=c||null)}),b.css(f)})};typeof e.alsoResize=="object"&&!e.alsoResize.nodeType?a.each(e.alsoResize,function(a,b){i(a,b)}):i(e.alsoResize)},stop:function(b,c){a(this).removeData("resizable-alsoresize")}}),a.ui.plugin.add("resizable","animate",{stop:function(b,c){var d=a(this).data("resizable"),e=d.options,f=d._proportionallyResizeElements,g=f.length&&/textarea/i.test(f[0].nodeName),h=g&&a.ui.hasScroll(f[0],"left")?0:d.sizeDiff.height,i=g?0:d.sizeDiff.width,j={width:d.size.width-i,height:d.size.height-h},k=parseInt(d.element.css("left"),10)+(d.position.left-d.originalPosition.left)||null,l=parseInt(d.element.css("top"),10)+(d.position.top-d.originalPosition.top)||null;d.element.animate(a.extend(j,l&&k?{top:l,left:k}:{}),{duration:e.animateDuration,easing:e.animateEasing,step:function(){var c={width:parseInt(d.element.css("width"),10),height:parseInt(d.element.css("height"),10),top:parseInt(d.element.css("top"),10),left:parseInt(d.element.css("left"),10)};f&&f.length&&a(f[0]).css({width:c.width,height:c.height}),d._updateCache(c),d._propagate("resize",b)}})}}),a.ui.plugin.add("resizable","containment",{start:function(b,d){var e=a(this).data("resizable"),f=e.options,g=e.element,h=f.containment,i=h instanceof a?h.get(0):/parent/.test(h)?g.parent().get(0):h;if(!i)return;e.containerElement=a(i);if(/document/.test(h)||h==document)e.containerOffset={left:0,top:0},e.containerPosition={left:0,top:0},e.parentData={element:a(document),left:0,top:0,width:a(document).width(),height:a(document).height()||document.body.parentNode.scrollHeight};else{var j=a(i),k=[];a(["Top","Right","Left","Bottom"]).each(function(a,b){k[a]=c(j.css("padding"+b))}),e.containerOffset=j.offset(),e.containerPosition=j.position(),e.containerSize={height:j.innerHeight()-k[3],width:j.innerWidth()-k[1]};var l=e.containerOffset,m=e.containerSize.height,n=e.containerSize.width,o=a.ui.hasScroll(i,"left")?i.scrollWidth:n,p=a.ui.hasScroll(i)?i.scrollHeight:m;e.parentData={element:i,left:l.left,top:l.top,width:o,height:p}}},resize:function(b,c){var d=a(this).data("resizable"),e=d.options,f=d.containerSize,g=d.containerOffset,h=d.size,i=d.position,j=d._aspectRatio||b.shiftKey,k={top:0,left:0},l=d.containerElement;l[0]!=document&&/static/.test(l.css("position"))&&(k=g),i.left<(d._helper?g.left:0)&&(d.size.width=d.size.width+(d._helper?d.position.left-g.left:d.position.left-k.left),j&&(d.size.height=d.size.width/d.aspectRatio),d.position.left=e.helper?g.left:0),i.top<(d._helper?g.top:0)&&(d.size.height=d.size.height+(d._helper?d.position.top-g.top:d.position.top),j&&(d.size.width=d.size.height*d.aspectRatio),d.position.top=d._helper?g.top:0),d.offset.left=d.parentData.left+d.position.left,d.offset.top=d.parentData.top+d.position.top;var m=Math.abs((d._helper?d.offset.left-k.left:d.offset.left-k.left)+d.sizeDiff.width),n=Math.abs((d._helper?d.offset.top-k.top:d.offset.top-g.top)+d.sizeDiff.height),o=d.containerElement.get(0)==d.element.parent().get(0),p=/relative|absolute/.test(d.containerElement.css("position"));o&&p&&(m-=d.parentData.left),m+d.size.width>=d.parentData.width&&(d.size.width=d.parentData.width-m,j&&(d.size.height=d.size.width/d.aspectRatio)),n+d.size.height>=d.parentData.height&&(d.size.height=d.parentData.height-n,j&&(d.size.width=d.size.height*d.aspectRatio))},stop:function(b,c){var d=a(this).data("resizable"),e=d.options,f=d.position,g=d.containerOffset,h=d.containerPosition,i=d.containerElement,j=a(d.helper),k=j.offset(),l=j.outerWidth()-d.sizeDiff.width,m=j.outerHeight()-d.sizeDiff.height;d._helper&&!e.animate&&/relative/.test(i.css("position"))&&a(this).css({left:k.left-h.left-g.left,width:l,height:m}),d._helper&&!e.animate&&/static/.test(i.css("position"))&&a(this).css({left:k.left-h.left-g.left,width:l,height:m})}}),a.ui.plugin.add("resizable","ghost",{start:function(b,c){var d=a(this).data("resizable"),e=d.options,f=d.size;d.ghost=d.originalElement.clone(),d.ghost.css({opacity:.25,display:"block",position:"relative",height:f.height,width:f.width,margin:0,left:0,top:0}).addClass("ui-resizable-ghost").addClass(typeof e.ghost=="string"?e.ghost:""),d.ghost.appendTo(d.helper)},resize:function(b,c){var d=a(this).data("resizable"),e=d.options;d.ghost&&d.ghost.css({position:"relative",height:d.size.height,width:d.size.width})},stop:function(b,c){var d=a(this).data("resizable"),e=d.options;d.ghost&&d.helper&&d.helper.get(0).removeChild(d.ghost.get(0))}}),a.ui.plugin.add("resizable","grid",{resize:function(b,c){var d=a(this).data("resizable"),e=d.options,f=d.size,g=d.originalSize,h=d.originalPosition,i=d.axis,j=e._aspectRatio||b.shiftKey;e.grid=typeof e.grid=="number"?[e.grid,e.grid]:e.grid;var k=Math.round((f.width-g.width)/(e.grid[0]||1))*(e.grid[0]||1),l=Math.round((f.height-g.height)/(e.grid[1]||1))*(e.grid[1]||1);/^(se|s|e)$/.test(i)?(d.size.width=g.width+k,d.size.height=g.height+l):/^(ne)$/.test(i)?(d.size.width=g.width+k,d.size.height=g.height+l,d.position.top=h.top-l):/^(sw)$/.test(i)?(d.size.width=g.width+k,d.size.height=g.height+l,d.position.left=h.left-k):(d.size.width=g.width+k,d.size.height=g.height+l,d.position.top=h.top-l,d.position.left=h.left-k)}});var c=function(a){return parseInt(a,10)||0},d=function(a){return!isNaN(parseInt(a,10))}})(jQuery);;/*! jQuery UI - v1.8.21 - 2012-06-05
+* https://github.com/jquery/jquery-ui
+* Includes: jquery.ui.selectable.js
+* Copyright (c) 2012 AUTHORS.txt; Licensed MIT, GPL */
+(function(a,b){a.widget("ui.selectable",a.ui.mouse,{options:{appendTo:"body",autoRefresh:!0,distance:0,filter:"*",tolerance:"touch"},_create:function(){var b=this;this.element.addClass("ui-selectable"),this.dragged=!1;var c;this.refresh=function(){c=a(b.options.filter,b.element[0]),c.addClass("ui-selectee"),c.each(function(){var b=a(this),c=b.offset();a.data(this,"selectable-item",{element:this,$element:b,left:c.left,top:c.top,right:c.left+b.outerWidth(),bottom:c.top+b.outerHeight(),startselected:!1,selected:b.hasClass("ui-selected"),selecting:b.hasClass("ui-selecting"),unselecting:b.hasClass("ui-unselecting")})})},this.refresh(),this.selectees=c.addClass("ui-selectee"),this._mouseInit(),this.helper=a("<div class='ui-selectable-helper'></div>")},destroy:function(){return this.selectees.removeClass("ui-selectee").removeData("selectable-item"),this.element.removeClass("ui-selectable ui-selectable-disabled").removeData("selectable").unbind(".selectable"),this._mouseDestroy(),this},_mouseStart:function(b){var c=this;this.opos=[b.pageX,b.pageY];if(this.options.disabled)return;var d=this.options;this.selectees=a(d.filter,this.element[0]),this._trigger("start",b),a(d.appendTo).append(this.helper),this.helper.css({left:b.clientX,top:b.clientY,width:0,height:0}),d.autoRefresh&&this.refresh(),this.selectees.filter(".ui-selected").each(function(){var d=a.data(this,"selectable-item");d.startselected=!0,!b.metaKey&&!b.ctrlKey&&(d.$element.removeClass("ui-selected"),d.selected=!1,d.$element.addClass("ui-unselecting"),d.unselecting=!0,c._trigger("unselecting",b,{unselecting:d.element}))}),a(b.target).parents().andSelf().each(function(){var d=a.data(this,"selectable-item");if(d){var e=!b.metaKey&&!b.ctrlKey||!d.$element.hasClass("ui-selected");return d.$element.removeClass(e?"ui-unselecting":"ui-selected").addClass(e?"ui-selecting":"ui-unselecting"),d.unselecting=!e,d.selecting=e,d.selected=e,e?c._trigger("selecting",b,{selecting:d.element}):c._trigger("unselecting",b,{unselecting:d.element}),!1}})},_mouseDrag:function(b){var c=this;this.dragged=!0;if(this.options.disabled)return;var d=this.options,e=this.opos[0],f=this.opos[1],g=b.pageX,h=b.pageY;if(e>g){var i=g;g=e,e=i}if(f>h){var i=h;h=f,f=i}return this.helper.css({left:e,top:f,width:g-e,height:h-f}),this.selectees.each(function(){var i=a.data(this,"selectable-item");if(!i||i.element==c.element[0])return;var j=!1;d.tolerance=="touch"?j=!(i.left>g||i.right<e||i.top>h||i.bottom<f):d.tolerance=="fit"&&(j=i.left>e&&i.right<g&&i.top>f&&i.bottom<h),j?(i.selected&&(i.$element.removeClass("ui-selected"),i.selected=!1),i.unselecting&&(i.$element.removeClass("ui-unselecting"),i.unselecting=!1),i.selecting||(i.$element.addClass("ui-selecting"),i.selecting=!0,c._trigger("selecting",b,{selecting:i.element}))):(i.selecting&&((b.metaKey||b.ctrlKey)&&i.startselected?(i.$element.removeClass("ui-selecting"),i.selecting=!1,i.$element.addClass("ui-selected"),i.selected=!0):(i.$element.removeClass("ui-selecting"),i.selecting=!1,i.startselected&&(i.$element.addClass("ui-unselecting"),i.unselecting=!0),c._trigger("unselecting",b,{unselecting:i.element}))),i.selected&&!b.metaKey&&!b.ctrlKey&&!i.startselected&&(i.$element.removeClass("ui-selected"),i.selected=!1,i.$element.addClass("ui-unselecting"),i.unselecting=!0,c._trigger("unselecting",b,{unselecting:i.element})))}),!1},_mouseStop:function(b){var c=this;this.dragged=!1;var d=this.options;return a(".ui-unselecting",this.element[0]).each(function(){var d=a.data(this,"selectable-item");d.$element.removeClass("ui-unselecting"),d.unselecting=!1,d.startselected=!1,c._trigger("unselected",b,{unselected:d.element})}),a(".ui-selecting",this.element[0]).each(function(){var d=a.data(this,"selectable-item");d.$element.removeClass("ui-selecting").addClass("ui-selected"),d.selecting=!1,d.selected=!0,d.startselected=!0,c._trigger("selected",b,{selected:d.element})}),this._trigger("stop",b),this.helper.remove(),!1}}),a.extend(a.ui.selectable,{version:"1.8.21"})})(jQuery);;/*! jQuery UI - v1.8.21 - 2012-06-05
+* https://github.com/jquery/jquery-ui
+* Includes: jquery.ui.sortable.js
+* Copyright (c) 2012 AUTHORS.txt; Licensed MIT, GPL */
+(function(a,b){a.widget("ui.sortable",a.ui.mouse,{widgetEventPrefix:"sort",ready:!1,options:{appendTo:"parent",axis:!1,connectWith:!1,containment:!1,cursor:"auto",cursorAt:!1,dropOnEmpty:!0,forcePlaceholderSize:!1,forceHelperSize:!1,grid:!1,handle:!1,helper:"original",items:"> *",opacity:!1,placeholder:!1,revert:!1,scroll:!0,scrollSensitivity:20,scrollSpeed:20,scope:"default",tolerance:"intersect",zIndex:1e3},_create:function(){var a=this.options;this.containerCache={},this.element.addClass("ui-sortable"),this.refresh(),this.floating=this.items.length?a.axis==="x"||/left|right/.test(this.items[0].item.css("float"))||/inline|table-cell/.test(this.items[0].item.css("display")):!1,this.offset=this.element.offset(),this._mouseInit(),this.ready=!0},destroy:function(){a.Widget.prototype.destroy.call(this),this.element.removeClass("ui-sortable ui-sortable-disabled"),this._mouseDestroy();for(var b=this.items.length-1;b>=0;b--)this.items[b].item.removeData(this.widgetName+"-item");return this},_setOption:function(b,c){b==="disabled"?(this.options[b]=c,this.widget()[c?"addClass":"removeClass"]("ui-sortable-disabled")):a.Widget.prototype._setOption.apply(this,arguments)},_mouseCapture:function(b,c){var d=this;if(this.reverting)return!1;if(this.options.disabled||this.options.type=="static")return!1;this._refreshItems(b);var e=null,f=this,g=a(b.target).parents().each(function(){if(a.data(this,d.widgetName+"-item")==f)return e=a(this),!1});a.data(b.target,d.widgetName+"-item")==f&&(e=a(b.target));if(!e)return!1;if(this.options.handle&&!c){var h=!1;a(this.options.handle,e).find("*").andSelf().each(function(){this==b.target&&(h=!0)});if(!h)return!1}return this.currentItem=e,this._removeCurrentsFromItems(),!0},_mouseStart:function(b,c,d){var e=this.options,f=this;this.currentContainer=this,this.refreshPositions(),this.helper=this._createHelper(b),this._cacheHelperProportions(),this._cacheMargins(),this.scrollParent=this.helper.scrollParent(),this.offset=this.currentItem.offset(),this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left},a.extend(this.offset,{click:{left:b.pageX-this.offset.left,top:b.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()}),this.helper.css("position","absolute"),this.cssPosition=this.helper.css("position"),this.originalPosition=this._generatePosition(b),this.originalPageX=b.pageX,this.originalPageY=b.pageY,e.cursorAt&&this._adjustOffsetFromHelper(e.cursorAt),this.domPosition={prev:this.currentItem.prev()[0],parent:this.currentItem.parent()[0]},this.helper[0]!=this.currentItem[0]&&this.currentItem.hide(),this._createPlaceholder(),e.containment&&this._setContainment(),e.cursor&&(a("body").css("cursor")&&(this._storedCursor=a("body").css("cursor")),a("body").css("cursor",e.cursor)),e.opacity&&(this.helper.css("opacity")&&(this._storedOpacity=this.helper.css("opacity")),this.helper.css("opacity",e.opacity)),e.zIndex&&(this.helper.css("zIndex")&&(this._storedZIndex=this.helper.css("zIndex")),this.helper.css("zIndex",e.zIndex)),this.scrollParent[0]!=document&&this.scrollParent[0].tagName!="HTML"&&(this.overflowOffset=this.scrollParent.offset()),this._trigger("start",b,this._uiHash()),this._preserveHelperProportions||this._cacheHelperProportions();if(!d)for(var g=this.containers.length-1;g>=0;g--)this.containers[g]._trigger("activate",b,f._uiHash(this));return a.ui.ddmanager&&(a.ui.ddmanager.current=this),a.ui.ddmanager&&!e.dropBehaviour&&a.ui.ddmanager.prepareOffsets(this,b),this.dragging=!0,this.helper.addClass("ui-sortable-helper"),this._mouseDrag(b),!0},_mouseDrag:function(b){this.position=this._generatePosition(b),this.positionAbs=this._convertPositionTo("absolute"),this.lastPositionAbs||(this.lastPositionAbs=this.positionAbs);if(this.options.scroll){var c=this.options,d=!1;this.scrollParent[0]!=document&&this.scrollParent[0].tagName!="HTML"?(this.overflowOffset.top+this.scrollParent[0].offsetHeight-b.pageY<c.scrollSensitivity?this.scrollParent[0].scrollTop=d=this.scrollParent[0].scrollTop+c.scrollSpeed:b.pageY-this.overflowOffset.top<c.scrollSensitivity&&(this.scrollParent[0].scrollTop=d=this.scrollParent[0].scrollTop-c.scrollSpeed),this.overflowOffset.left+this.scrollParent[0].offsetWidth-b.pageX<c.scrollSensitivity?this.scrollParent[0].scrollLeft=d=this.scrollParent[0].scrollLeft+c.scrollSpeed:b.pageX-this.overflowOffset.left<c.scrollSensitivity&&(this.scrollParent[0].scrollLeft=d=this.scrollParent[0].scrollLeft-c.scrollSpeed)):(b.pageY-a(document).scrollTop()<c.scrollSensitivity?d=a(document).scrollTop(a(document).scrollTop()-c.scrollSpeed):a(window).height()-(b.pageY-a(document).scrollTop())<c.scrollSensitivity&&(d=a(document).scrollTop(a(document).scrollTop()+c.scrollSpeed)),b.pageX-a(document).scrollLeft()<c.scrollSensitivity?d=a(document).scrollLeft(a(document).scrollLeft()-c.scrollSpeed):a(window).width()-(b.pageX-a(document).scrollLeft())<c.scrollSensitivity&&(d=a(document).scrollLeft(a(document).scrollLeft()+c.scrollSpeed))),d!==!1&&a.ui.ddmanager&&!c.dropBehaviour&&a.ui.ddmanager.prepareOffsets(this,b)}this.positionAbs=this._convertPositionTo("absolute");if(!this.options.axis||this.options.axis!="y")this.helper[0].style.left=this.position.left+"px";if(!this.options.axis||this.options.axis!="x")this.helper[0].style.top=this.position.top+"px";for(var e=this.items.length-1;e>=0;e--){var f=this.items[e],g=f.item[0],h=this._intersectsWithPointer(f);if(!h)continue;if(g!=this.currentItem[0]&&this.placeholder[h==1?"next":"prev"]()[0]!=g&&!a.ui.contains(this.placeholder[0],g)&&(this.options.type=="semi-dynamic"?!a.ui.contains(this.element[0],g):!0)){this.direction=h==1?"down":"up";if(this.options.tolerance=="pointer"||this._intersectsWithSides(f))this._rearrange(b,f);else break;this._trigger("change",b,this._uiHash());break}}return this._contactContainers(b),a.ui.ddmanager&&a.ui.ddmanager.drag(this,b),this._trigger("sort",b,this._uiHash()),this.lastPositionAbs=this.positionAbs,!1},_mouseStop:function(b,c){if(!b)return;a.ui.ddmanager&&!this.options.dropBehaviour&&a.ui.ddmanager.drop(this,b);if(this.options.revert){var d=this,e=d.placeholder.offset();d.reverting=!0,a(this.helper).animate({left:e.left-this.offset.parent.left-d.margins.left+(this.offsetParent[0]==document.body?0:this.offsetParent[0].scrollLeft),top:e.top-this.offset.parent.top-d.margins.top+(this.offsetParent[0]==document.body?0:this.offsetParent[0].scrollTop)},parseInt(this.options.revert,10)||500,function(){d._clear(b)})}else this._clear(b,c);return!1},cancel:function(){var b=this;if(this.dragging){this._mouseUp({target:null}),this.options.helper=="original"?this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"):this.currentItem.show();for(var c=this.containers.length-1;c>=0;c--)this.containers[c]._trigger("deactivate",null,b._uiHash(this)),this.containers[c].containerCache.over&&(this.containers[c]._trigger("out",null,b._uiHash(this)),this.containers[c].containerCache.over=0)}return this.placeholder&&(this.placeholder[0].parentNode&&this.placeholder[0].parentNode.removeChild(this.placeholder[0]),this.options.helper!="original"&&this.helper&&this.helper[0].parentNode&&this.helper.remove(),a.extend(this,{helper:null,dragging:!1,reverting:!1,_noFinalSort:null}),this.domPosition.prev?a(this.domPosition.prev).after(this.currentItem):a(this.domPosition.parent).prepend(this.currentItem)),this},serialize:function(b){var c=this._getItemsAsjQuery(b&&b.connected),d=[];return b=b||{},a(c).each(function(){var c=(a(b.item||this).attr(b.attribute||"id")||"").match(b.expression||/(.+)[-=_](.+)/);c&&d.push((b.key||c[1]+"[]")+"="+(b.key&&b.expression?c[1]:c[2]))}),!d.length&&b.key&&d.push(b.key+"="),d.join("&")},toArray:function(b){var c=this._getItemsAsjQuery(b&&b.connected),d=[];return b=b||{},c.each(function(){d.push(a(b.item||this).attr(b.attribute||"id")||"")}),d},_intersectsWith:function(a){var b=this.positionAbs.left,c=b+this.helperProportions.width,d=this.positionAbs.top,e=d+this.helperProportions.height,f=a.left,g=f+a.width,h=a.top,i=h+a.height,j=this.offset.click.top,k=this.offset.click.left,l=d+j>h&&d+j<i&&b+k>f&&b+k<g;return this.options.tolerance=="pointer"||this.options.forcePointerForContainers||this.options.tolerance!="pointer"&&this.helperProportions[this.floating?"width":"height"]>a[this.floating?"width":"height"]?l:f<b+this.helperProportions.width/2&&c-this.helperProportions.width/2<g&&h<d+this.helperProportions.height/2&&e-this.helperProportions.height/2<i},_intersectsWithPointer:function(b){var c=this.options.axis==="x"||a.ui.isOverAxis(this.positionAbs.top+this.offset.click.top,b.top,b.height),d=this.options.axis==="y"||a.ui.isOverAxis(this.positionAbs.left+this.offset.click.left,b.left,b.width),e=c&&d,f=this._getDragVerticalDirection(),g=this._getDragHorizontalDirection();return e?this.floating?g&&g=="right"||f=="down"?2:1:f&&(f=="down"?2:1):!1},_intersectsWithSides:function(b){var c=a.ui.isOverAxis(this.positionAbs.top+this.offset.click.top,b.top+b.height/2,b.height),d=a.ui.isOverAxis(this.positionAbs.left+this.offset.click.left,b.left+b.width/2,b.width),e=this._getDragVerticalDirection(),f=this._getDragHorizontalDirection();return this.floating&&f?f=="right"&&d||f=="left"&&!d:e&&(e=="down"&&c||e=="up"&&!c)},_getDragVerticalDirection:function(){var a=this.positionAbs.top-this.lastPositionAbs.top;return a!=0&&(a>0?"down":"up")},_getDragHorizontalDirection:function(){var a=this.positionAbs.left-this.lastPositionAbs.left;return a!=0&&(a>0?"right":"left")},refresh:function(a){return this._refreshItems(a),this.refreshPositions(),this},_connectWith:function(){var a=this.options;return a.connectWith.constructor==String?[a.connectWith]:a.connectWith},_getItemsAsjQuery:function(b){var c=this,d=[],e=[],f=this._connectWith();if(f&&b)for(var g=f.length-1;g>=0;g--){var h=a(f[g]);for(var i=h.length-1;i>=0;i--){var j=a.data(h[i],this.widgetName);j&&j!=this&&!j.options.disabled&&e.push([a.isFunction(j.options.items)?j.options.items.call(j.element):a(j.options.items,j.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),j])}}e.push([a.isFunction(this.options.items)?this.options.items.call(this.element,null,{options:this.options,item:this.currentItem}):a(this.options.items,this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),this]);for(var g=e.length-1;g>=0;g--)e[g][0].each(function(){d.push(this)});return a(d)},_removeCurrentsFromItems:function(){var a=this.currentItem.find(":data("+this.widgetName+"-item)");for(var b=0;b<this.items.length;b++)for(var c=0;c<a.length;c++)a[c]==this.items[b].item[0]&&this.items.splice(b,1)},_refreshItems:function(b){this.items=[],this.containers=[this];var c=this.items,d=this,e=[[a.isFunction(this.options.items)?this.options.items.call(this.element[0],b,{item:this.currentItem}):a(this.options.items,this.element),this]],f=this._connectWith();if(f&&this.ready)for(var g=f.length-1;g>=0;g--){var h=a(f[g]);for(var i=h.length-1;i>=0;i--){var j=a.data(h[i],this.widgetName);j&&j!=this&&!j.options.disabled&&(e.push([a.isFunction(j.options.items)?j.options.items.call(j.element[0],b,{item:this.currentItem}):a(j.options.items,j.element),j]),this.containers.push(j))}}for(var g=e.length-1;g>=0;g--){var k=e[g][1],l=e[g][0];for(var i=0,m=l.length;i<m;i++){var n=a(l[i]);n.data(this.widgetName+"-item",k),c.push({item:n,instance:k,width:0,height:0,left:0,top:0})}}},refreshPositions:function(b){this.offsetParent&&this.helper&&(this.offset.parent=this._getParentOffset());for(var c=this.items.length-1;c>=0;c--){var d=this.items[c];if(d.instance!=this.currentContainer&&this.currentContainer&&d.item[0]!=this.currentItem[0])continue;var e=this.options.toleranceElement?a(this.options.toleranceElement,d.item):d.item;b||(d.width=e.outerWidth(),d.height=e.outerHeight());var f=e.offset();d.left=f.left,d.top=f.top}if(this.options.custom&&this.options.custom.refreshContainers)this.options.custom.refreshContainers.call(this);else for(var c=this.containers.length-1;c>=0;c--){var f=this.containers[c].element.offset();this.containers[c].containerCache.left=f.left,this.containers[c].containerCache.top=f.top,this.containers[c].containerCache.width=this.containers[c].element.outerWidth(),this.containers[c].containerCache.height=this.containers[c].element.outerHeight()}return this},_createPlaceholder:function(b){var c=b||this,d=c.options;if(!d.placeholder||d.placeholder.constructor==String){var e=d.placeholder;d.placeholder={element:function(){var b=a(document.createElement(c.currentItem[0].nodeName)).addClass(e||c.currentItem[0].className+" ui-sortable-placeholder").removeClass("ui-sortable-helper")[0];return e||(b.style.visibility="hidden"),b},update:function(a,b){if(e&&!d.forcePlaceholderSize)return;b.height()||b.height(c.currentItem.innerHeight()-parseInt(c.currentItem.css("paddingTop")||0,10)-parseInt(c.currentItem.css("paddingBottom")||0,10)),b.width()||b.width(c.currentItem.innerWidth()-parseInt(c.currentItem.css("paddingLeft")||0,10)-parseInt(c.currentItem.css("paddingRight")||0,10))}}}c.placeholder=a(d.placeholder.element.call(c.element,c.currentItem)),c.currentItem.after(c.placeholder),d.placeholder.update(c,c.placeholder)},_contactContainers:function(b){var c=null,d=null;for(var e=this.containers.length-1;e>=0;e--){if(a.ui.contains(this.currentItem[0],this.containers[e].element[0]))continue;if(this._intersectsWith(this.containers[e].containerCache)){if(c&&a.ui.contains(this.containers[e].element[0],c.element[0]))continue;c=this.containers[e],d=e}else this.containers[e].containerCache.over&&(this.containers[e]._trigger("out",b,this._uiHash(this)),this.containers[e].containerCache.over=0)}if(!c)return;if(this.containers.length===1)this.containers[d]._trigger("over",b,this._uiHash(this)),this.containers[d].containerCache.over=1;else if(this.currentContainer!=this.containers[d]){var f=1e4,g=null,h=this.positionAbs[this.containers[d].floating?"left":"top"];for(var i=this.items.length-1;i>=0;i--){if(!a.ui.contains(this.containers[d].element[0],this.items[i].item[0]))continue;var j=this.containers[d].floating?this.items[i].item.offset().left:this.items[i].item.offset().top;Math.abs(j-h)<f&&(f=Math.abs(j-h),g=this.items[i],this.direction=j-h>0?"down":"up")}if(!g&&!this.options.dropOnEmpty)return;this.currentContainer=this.containers[d],g?this._rearrange(b,g,null,!0):this._rearrange(b,null,this.containers[d].element,!0),this._trigger("change",b,this._uiHash()),this.containers[d]._trigger("change",b,this._uiHash(this)),this.options.placeholder.update(this.currentContainer,this.placeholder),this.containers[d]._trigger("over",b,this._uiHash(this)),this.containers[d].containerCache.over=1}},_createHelper:function(b){var c=this.options,d=a.isFunction(c.helper)?a(c.helper.apply(this.element[0],[b,this.currentItem])):c.helper=="clone"?this.currentItem.clone():this.currentItem;return d.parents("body").length||a(c.appendTo!="parent"?c.appendTo:this.currentItem[0].parentNode)[0].appendChild(d[0]),d[0]==this.currentItem[0]&&(this._storedCSS={width:this.currentItem[0].style.width,height:this.currentItem[0].style.height,position:this.currentItem.css("position"),top:this.currentItem.css("top"),left:this.currentItem.css("left")}),(d[0].style.width==""||c.forceHelperSize)&&d.width(this.currentItem.width()),(d[0].style.height==""||c.forceHelperSize)&&d.height(this.currentItem.height()),d},_adjustOffsetFromHelper:function(b){typeof b=="string"&&(b=b.split(" ")),a.isArray(b)&&(b={left:+b[0],top:+b[1]||0}),"left"in b&&(this.offset.click.left=b.left+this.margins.left),"right"in b&&(this.offset.click.left=this.helperProportions.width-b.right+this.margins.left),"top"in b&&(this.offset.click.top=b.top+this.margins.top),"bottom"in b&&(this.offset.click.top=this.helperProportions.height-b.bottom+this.margins.top)},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var b=this.offsetParent.offset();this.cssPosition=="absolute"&&this.scrollParent[0]!=document&&a.ui.contains(this.scrollParent[0],this.offsetParent[0])&&(b.left+=this.scrollParent.scrollLeft(),b.top+=this.scrollParent.scrollTop());if(this.offsetParent[0]==document.body||this.offsetParent[0].tagName&&this.offsetParent[0].tagName.toLowerCase()=="html"&&a.browser.msie)b={top:0,left:0};return{top:b.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:b.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if(this.cssPosition=="relative"){var a=this.currentItem.position();return{top:a.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:a.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.currentItem.css("marginLeft"),10)||0,top:parseInt(this.currentItem.css("marginTop"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var b=this.options;b.containment=="parent"&&(b.containment=this.helper[0].parentNode);if(b.containment=="document"||b.containment=="window")this.containment=[0-this.offset.relative.left-this.offset.parent.left,0-this.offset.relative.top-this.offset.parent.top,a(b.containment=="document"?document:window).width()-this.helperProportions.width-this.margins.left,(a(b.containment=="document"?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top];if(!/^(document|window|parent)$/.test(b.containment)){var c=a(b.containment)[0],d=a(b.containment).offset(),e=a(c).css("overflow")!="hidden";this.containment=[d.left+(parseInt(a(c).css("borderLeftWidth"),10)||0)+(parseInt(a(c).css("paddingLeft"),10)||0)-this.margins.left,d.top+(parseInt(a(c).css("borderTopWidth"),10)||0)+(parseInt(a(c).css("paddingTop"),10)||0)-this.margins.top,d.left+(e?Math.max(c.scrollWidth,c.offsetWidth):c.offsetWidth)-(parseInt(a(c).css("borderLeftWidth"),10)||0)-(parseInt(a(c).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left,d.top+(e?Math.max(c.scrollHeight,c.offsetHeight):c.offsetHeight)-(parseInt(a(c).css("borderTopWidth"),10)||0)-(parseInt(a(c).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top]}},_convertPositionTo:function(b,c){c||(c=this.position);var d=b=="absolute"?1:-1,e=this.options,f=this.cssPosition=="absolute"&&(this.scrollParent[0]==document||!a.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,g=/(html|body)/i.test(f[0].tagName);return{top:c.top+this.offset.relative.top*d+this.offset.parent.top*d-(a.browser.safari&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():g?0:f.scrollTop())*d),left:c.left+this.offset.relative.left*d+this.offset.parent.left*d-(a.browser.safari&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():g?0:f.scrollLeft())*d)}},_generatePosition:function(b){var c=this.options,d=this.cssPosition=="absolute"&&(this.scrollParent[0]==document||!a.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,e=/(html|body)/i.test(d[0].tagName);this.cssPosition=="relative"&&(this.scrollParent[0]==document||this.scrollParent[0]==this.offsetParent[0])&&(this.offset.relative=this._getRelativeOffset());var f=b.pageX,g=b.pageY;if(this.originalPosition){this.containment&&(b.pageX-this.offset.click.left<this.containment[0]&&(f=this.containment[0]+this.offset.click.left),b.pageY-this.offset.click.top<this.containment[1]&&(g=this.containment[1]+this.offset.click.top),b.pageX-this.offset.click.left>this.containment[2]&&(f=this.containment[2]+this.offset.click.left),b.pageY-this.offset.click.top>this.containment[3]&&(g=this.containment[3]+this.offset.click.top));if(c.grid){var h=this.originalPageY+Math.round((g-this.originalPageY)/c.grid[1])*c.grid[1];g=this.containment?h-this.offset.click.top<this.containment[1]||h-this.offset.click.top>this.containment[3]?h-this.offset.click.top<this.containment[1]?h+c.grid[1]:h-c.grid[1]:h:h;var i=this.originalPageX+Math.round((f-this.originalPageX)/c.grid[0])*c.grid[0];f=this.containment?i-this.offset.click.left<this.containment[0]||i-this.offset.click.left>this.containment[2]?i-this.offset.click.left<this.containment[0]?i+c.grid[0]:i-c.grid[0]:i:i}}return{top:g-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+(a.browser.safari&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollTop():e?0:d.scrollTop()),left:f-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+(a.browser.safari&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():e?0:d.scrollLeft())}},_rearrange:function(a,b,c,d){c?c[0].appendChild(this.placeholder[0]):b.item[0].parentNode.insertBefore(this.placeholder[0],this.direction=="down"?b.item[0]:b.item[0].nextSibling),this.counter=this.counter?++this.counter:1;var e=this,f=this.counter;window.setTimeout(function(){f==e.counter&&e.refreshPositions(!d)},0)},_clear:function(b,c){this.reverting=!1;var d=[],e=this;!this._noFinalSort&&this.currentItem.parent().length&&this.placeholder.before(this.currentItem),this._noFinalSort=null;if(this.helper[0]==this.currentItem[0]){for(var f in this._storedCSS)if(this._storedCSS[f]=="auto"||this._storedCSS[f]=="static")this._storedCSS[f]="";this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper")}else this.currentItem.show();this.fromOutside&&!c&&d.push(function(a){this._trigger("receive",a,this._uiHash(this.fromOutside))}),(this.fromOutside||this.domPosition.prev!=this.currentItem.prev().not(".ui-sortable-helper")[0]||this.domPosition.parent!=this.currentItem.parent()[0])&&!c&&d.push(function(a){this._trigger("update",a,this._uiHash())});if(!a.ui.contains(this.element[0],this.currentItem[0])){c||d.push(function(a){this._trigger("remove",a,this._uiHash())});for(var f=this.containers.length-1;f>=0;f--)a.ui.contains(this.containers[f].element[0],this.currentItem[0])&&!c&&(d.push(function(a){return function(b){a._trigger("receive",b,this._uiHash(this))}}.call(this,this.containers[f])),d.push(function(a){return function(b){a._trigger("update",b,this._uiHash(this))}}.call(this,this.containers[f])))}for(var f=this.containers.length-1;f>=0;f--)c||d.push(function(a){return function(b){a._trigger("deactivate",b,this._uiHash(this))}}.call(this,this.containers[f])),this.containers[f].containerCache.over&&(d.push(function(a){return function(b){a._trigger("out",b,this._uiHash(this))}}.call(this,this.containers[f])),this.containers[f].containerCache.over=0);this._storedCursor&&a("body").css("cursor",this._storedCursor),this._storedOpacity&&this.helper.css("opacity",this._storedOpacity),this._storedZIndex&&this.helper.css("zIndex",this._storedZIndex=="auto"?"":this._storedZIndex),this.dragging=!1;if(this.cancelHelperRemoval){if(!c){this._trigger("beforeStop",b,this._uiHash());for(var f=0;f<d.length;f++)d[f].call(this,b);this._trigger("stop",b,this._uiHash())}return!1}c||this._trigger("beforeStop",b,this._uiHash()),this.placeholder[0].parentNode.removeChild(this.placeholder[0]),this.helper[0]!=this.currentItem[0]&&this.helper.remove(),this.helper=null;if(!c){for(var f=0;f<d.length;f++)d[f].call(this,b);this._trigger("stop",b,this._uiHash())}return this.fromOutside=!1,!0},_trigger:function(){a.Widget.prototype._trigger.apply(this,arguments)===!1&&this.cancel()},_uiHash:function(b){var c=b||this;return{helper:c.helper,placeholder:c.placeholder||a([]),position:c.position,originalPosition:c.originalPosition,offset:c.positionAbs,item:c.currentItem,sender:b?b.element:null}}}),a.extend(a.ui.sortable,{version:"1.8.21"})})(jQuery);;/*! jQuery UI - v1.8.21 - 2012-06-05
+* https://github.com/jquery/jquery-ui
+* Includes: jquery.ui.accordion.js
+* Copyright (c) 2012 AUTHORS.txt; Licensed MIT, GPL */
+(function(a,b){a.widget("ui.accordion",{options:{active:0,animated:"slide",autoHeight:!0,clearStyle:!1,collapsible:!1,event:"click",fillSpace:!1,header:"> li > :first-child,> :not(li):even",icons:{header:"ui-icon-triangle-1-e",headerSelected:"ui-icon-triangle-1-s"},navigation:!1,navigationFilter:function(){return this.href.toLowerCase()===location.href.toLowerCase()}},_create:function(){var b=this,c=b.options;b.running=0,b.element.addClass("ui-accordion ui-widget ui-helper-reset").children("li").addClass("ui-accordion-li-fix"),b.headers=b.element.find(c.header).addClass("ui-accordion-header ui-helper-reset ui-state-default ui-corner-all").bind("mouseenter.accordion",function(){if(c.disabled)return;a(this).addClass("ui-state-hover")}).bind("mouseleave.accordion",function(){if(c.disabled)return;a(this).removeClass("ui-state-hover")}).bind("focus.accordion",function(){if(c.disabled)return;a(this).addClass("ui-state-focus")}).bind("blur.accordion",function(){if(c.disabled)return;a(this).removeClass("ui-state-focus")}),b.headers.next().addClass("ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom");if(c.navigation){var d=b.element.find("a").filter(c.navigationFilter).eq(0);if(d.length){var e=d.closest(".ui-accordion-header");e.length?b.active=e:b.active=d.closest(".ui-accordion-content").prev()}}b.active=b._findActive(b.active||c.active).addClass("ui-state-default ui-state-active").toggleClass("ui-corner-all").toggleClass("ui-corner-top"),b.active.next().addClass("ui-accordion-content-active"),b._createIcons(),b.resize(),b.element.attr("role","tablist"),b.headers.attr("role","tab").bind("keydown.accordion",function(a){return b._keydown(a)}).next().attr("role","tabpanel"),b.headers.not(b.active||"").attr({"aria-expanded":"false","aria-selected":"false",tabIndex:-1}).next().hide(),b.active.length?b.active.attr({"aria-expanded":"true","aria-selected":"true",tabIndex:0}):b.headers.eq(0).attr("tabIndex",0),a.browser.safari||b.headers.find("a").attr("tabIndex",-1),c.event&&b.headers.bind(c.event.split(" ").join(".accordion ")+".accordion",function(a){b._clickHandler.call(b,a,this),a.preventDefault()})},_createIcons:function(){var b=this.options;b.icons&&(a("<span></span>").addClass("ui-icon "+b.icons.header).prependTo(this.headers),this.active.children(".ui-icon").toggleClass(b.icons.header).toggleClass(b.icons.headerSelected),this.element.addClass("ui-accordion-icons"))},_destroyIcons:function(){this.headers.children(".ui-icon").remove(),this.element.removeClass("ui-accordion-icons")},destroy:function(){var b=this.options;this.element.removeClass("ui-accordion ui-widget ui-helper-reset").removeAttr("role"),this.headers.unbind(".accordion").removeClass("ui-accordion-header ui-accordion-disabled ui-helper-reset ui-state-default ui-corner-all ui-state-active ui-state-disabled ui-corner-top").removeAttr("role").removeAttr("aria-expanded").removeAttr("aria-selected").removeAttr("tabIndex"),this.headers.find("a").removeAttr("tabIndex"),this._destroyIcons();var c=this.headers.next().css("display","").removeAttr("role").removeClass("ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-accordion-disabled ui-state-disabled");return(b.autoHeight||b.fillHeight)&&c.css("height",""),a.Widget.prototype.destroy.call(this)},_setOption:function(b,c){a.Widget.prototype._setOption.apply(this,arguments),b=="active"&&this.activate(c),b=="icons"&&(this._destroyIcons(),c&&this._createIcons()),b=="disabled"&&this.headers.add(this.headers.next())[c?"addClass":"removeClass"]("ui-accordion-disabled ui-state-disabled")},_keydown:function(b){if(this.options.disabled||b.altKey||b.ctrlKey)return;var c=a.ui.keyCode,d=this.headers.length,e=this.headers.index(b.target),f=!1;switch(b.keyCode){case c.RIGHT:case c.DOWN:f=this.headers[(e+1)%d];break;case c.LEFT:case c.UP:f=this.headers[(e-1+d)%d];break;case c.SPACE:case c.ENTER:this._clickHandler({target:b.target},b.target),b.preventDefault()}return f?(a(b.target).attr("tabIndex",-1),a(f).attr("tabIndex",0),f.focus(),!1):!0},resize:function(){var b=this.options,c;if(b.fillSpace){if(a.browser.msie){var d=this.element.parent().css("overflow");this.element.parent().css("overflow","hidden")}c=this.element.parent().height(),a.browser.msie&&this.element.parent().css("overflow",d),this.headers.each(function(){c-=a(this).outerHeight(!0)}),this.headers.next().each(function(){a(this).height(Math.max(0,c-a(this).innerHeight()+a(this).height()))}).css("overflow","auto")}else b.autoHeight&&(c=0,this.headers.next().each(function(){c=Math.max(c,a(this).height("").height())}).height(c));return this},activate:function(a){this.options.active=a;var b=this._findActive(a)[0];return this._clickHandler({target:b},b),this},_findActive:function(b){return b?typeof b=="number"?this.headers.filter(":eq("+b+")"):this.headers.not(this.headers.not(b)):b===!1?a([]):this.headers.filter(":eq(0)")},_clickHandler:function(b,c){var d=this.options;if(d.disabled)return;if(!b.target){if(!d.collapsible)return;this.active.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all").children(".ui-icon").removeClass(d.icons.headerSelected).addClass(d.icons.header),this.active.next().addClass("ui-accordion-content-active");var e=this.active.next(),f={options:d,newHeader:a([]),oldHeader:d.active,newContent:a([]),oldContent:e},g=this.active=a([]);this._toggle(g,e,f);return}var h=a(b.currentTarget||c),i=h[0]===this.active[0];d.active=d.collapsible&&i?!1:this.headers.index(h);if(this.running||!d.collapsible&&i)return;var j=this.active,g=h.next(),e=this.active.next(),f={options:d,newHeader:i&&d.collapsible?a([]):h,oldHeader:this.active,newContent:i&&d.collapsible?a([]):g,oldContent:e},k=this.headers.index(this.active[0])>this.headers.index(h[0]);this.active=i?a([]):h,this._toggle(g,e,f,i,k),j.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all").children(".ui-icon").removeClass(d.icons.headerSelected).addClass(d.icons.header),i||(h.removeClass("ui-state-default ui-corner-all").addClass("ui-state-active ui-corner-top").children(".ui-icon").removeClass(d.icons.header).addClass(d.icons.headerSelected),h.next().addClass("ui-accordion-content-active"));return},_toggle:function(b,c,d,e,f){var g=this,h=g.options;g.toShow=b,g.toHide=c,g.data=d;var i=function(){if(!g)return;return g._completed.apply(g,arguments)};g._trigger("changestart",null,g.data),g.running=c.size()===0?b.size():c.size();if(h.animated){var j={};h.collapsible&&e?j={toShow:a([]),toHide:c,complete:i,down:f,autoHeight:h.autoHeight||h.fillSpace}:j={toShow:b,toHide:c,complete:i,down:f,autoHeight:h.autoHeight||h.fillSpace},h.proxied||(h.proxied=h.animated),h.proxiedDuration||(h.proxiedDuration=h.duration),h.animated=a.isFunction(h.proxied)?h.proxied(j):h.proxied,h.duration=a.isFunction(h.proxiedDuration)?h.proxiedDuration(j):h.proxiedDuration;var k=a.ui.accordion.animations,l=h.duration,m=h.animated;m&&!k[m]&&!a.easing[m]&&(m="slide"),k[m]||(k[m]=function(a){this.slide(a,{easing:m,duration:l||700})}),k[m](j)}else h.collapsible&&e?b.toggle():(c.hide(),b.show()),i(!0);c.prev().attr({"aria-expanded":"false","aria-selected":"false",tabIndex:-1}).blur(),b.prev().attr({"aria-expanded":"true","aria-selected":"true",tabIndex:0}).focus()},_completed:function(a){this.running=a?0:--this.running;if(this.running)return;this.options.clearStyle&&this.toShow.add(this.toHide).css({height:"",overflow:""}),this.toHide.removeClass("ui-accordion-content-active"),this.toHide.length&&(this.toHide.parent()[0].className=this.toHide.parent()[0].className),this._trigger("change",null,this.data)}}),a.extend(a.ui.accordion,{version:"1.8.21",animations:{slide:function(b,c){b=a.extend({easing:"swing",duration:300},b,c);if(!b.toHide.size()){b.toShow.animate({height:"show",paddingTop:"show",paddingBottom:"show"},b);return}if(!b.toShow.size()){b.toHide.animate({height:"hide",paddingTop:"hide",paddingBottom:"hide"},b);return}var d=b.toShow.css("overflow"),e=0,f={},g={},h=["height","paddingTop","paddingBottom"],i,j=b.toShow;i=j[0].style.width,j.width(j.parent().width()-parseFloat(j.css("paddingLeft"))-parseFloat(j.css("paddingRight"))-(parseFloat(j.css("borderLeftWidth"))||0)-(parseFloat(j.css("borderRightWidth"))||0)),a.each(h,function(c,d){g[d]="hide";var e=(""+a.css(b.toShow[0],d)).match(/^([\d+-.]+)(.*)$/);f[d]={value:e[1],unit:e[2]||"px"}}),b.toShow.css({height:0,overflow:"hidden"}).show(),b.toHide.filter(":hidden").each(b.complete).end().filter(":visible").animate(g,{step:function(a,c){c.prop=="height"&&(e=c.end-c.start===0?0:(c.now-c.start)/(c.end-c.start)),b.toShow[0].style[c.prop]=e*f[c.prop].value+f[c.prop].unit},duration:b.duration,easing:b.easing,complete:function(){b.autoHeight||b.toShow.css("height",""),b.toShow.css({width:i,overflow:d}),b.complete()}})},bounceslide:function(a){this.slide(a,{easing:a.down?"easeOutBounce":"swing",duration:a.down?1e3:200})}}})})(jQuery);;/*! jQuery UI - v1.8.21 - 2012-06-05
+* https://github.com/jquery/jquery-ui
+* Includes: jquery.ui.autocomplete.js
+* Copyright (c) 2012 AUTHORS.txt; Licensed MIT, GPL */
+(function(a,b){var c=0;a.widget("ui.autocomplete",{options:{appendTo:"body",autoFocus:!1,delay:300,minLength:1,position:{my:"left top",at:"left bottom",collision:"none"},source:null},pending:0,_create:function(){var b=this,c=this.element[0].ownerDocument,d;this.isMultiLine=this.element.is("textarea"),this.element.addClass("ui-autocomplete-input").attr("autocomplete","off").attr({role:"textbox","aria-autocomplete":"list","aria-haspopup":"true"}).bind("keydown.autocomplete",function(c){if(b.options.disabled||b.element.propAttr("readOnly"))return;d=!1;var e=a.ui.keyCode;switch(c.keyCode){case e.PAGE_UP:b._move("previousPage",c);break;case e.PAGE_DOWN:b._move("nextPage",c);break;case e.UP:b._keyEvent("previous",c);break;case e.DOWN:b._keyEvent("next",c);break;case e.ENTER:case e.NUMPAD_ENTER:b.menu.active&&(d=!0,c.preventDefault());case e.TAB:if(!b.menu.active)return;b.menu.select(c);break;case e.ESCAPE:b.element.val(b.term),b.close(c);break;default:clearTimeout(b.searching),b.searching=setTimeout(function(){b.term!=b.element.val()&&(b.selectedItem=null,b.search(null,c))},b.options.delay)}}).bind("keypress.autocomplete",function(a){d&&(d=!1,a.preventDefault())}).bind("focus.autocomplete",function(){if(b.options.disabled)return;b.selectedItem=null,b.previous=b.element.val()}).bind("blur.autocomplete",function(a){if(b.options.disabled)return;clearTimeout(b.searching),b.closing=setTimeout(function(){b.close(a),b._change(a)},150)}),this._initSource(),this.menu=a("<ul></ul>").addClass("ui-autocomplete").appendTo(a(this.options.appendTo||"body",c)[0]).mousedown(function(c){var d=b.menu.element[0];a(c.target).closest(".ui-menu-item").length||setTimeout(function(){a(document).one("mousedown",function(c){c.target!==b.element[0]&&c.target!==d&&!a.ui.contains(d,c.target)&&b.close()})},1),setTimeout(function(){clearTimeout(b.closing)},13)}).menu({focus:function(a,c){var d=c.item.data("item.autocomplete");!1!==b._trigger("focus",a,{item:d})&&/^key/.test(a.originalEvent.type)&&b.element.val(d.value)},selected:function(a,d){var e=d.item.data("item.autocomplete"),f=b.previous;b.element[0]!==c.activeElement&&(b.element.focus(),b.previous=f,setTimeout(function(){b.previous=f,b.selectedItem=e},1)),!1!==b._trigger("select",a,{item:e})&&b.element.val(e.value),b.term=b.element.val(),b.close(a),b.selectedItem=e},blur:function(a,c){b.menu.element.is(":visible")&&b.element.val()!==b.term&&b.element.val(b.term)}}).zIndex(this.element.zIndex()+1).css({top:0,left:0}).hide().data("menu"),a.fn.bgiframe&&this.menu.element.bgiframe(),b.beforeunloadHandler=function(){b.element.removeAttr("autocomplete")},a(window).bind("beforeunload",b.beforeunloadHandler)},destroy:function(){this.element.removeClass("ui-autocomplete-input").removeAttr("autocomplete").removeAttr("role").removeAttr("aria-autocomplete").removeAttr("aria-haspopup"),this.menu.element.remove(),a(window).unbind("beforeunload",this.beforeunloadHandler),a.Widget.prototype.destroy.call(this)},_setOption:function(b,c){a.Widget.prototype._setOption.apply(this,arguments),b==="source"&&this._initSource(),b==="appendTo"&&this.menu.element.appendTo(a(c||"body",this.element[0].ownerDocument)[0]),b==="disabled"&&c&&this.xhr&&this.xhr.abort()},_initSource:function(){var b=this,c,d;a.isArray(this.options.source)?(c=this.options.source,this.source=function(b,d){d(a.ui.autocomplete.filter(c,b.term))}):typeof this.options.source=="string"?(d=this.options.source,this.source=function(c,e){b.xhr&&b.xhr.abort(),b.xhr=a.ajax({url:d,data:c,dataType:"json",success:function(a,b){e(a)},error:function(){e([])}})}):this.source=this.options.source},search:function(a,b){a=a!=null?a:this.element.val(),this.term=this.element.val();if(a.length<this.options.minLength)return this.close(b);clearTimeout(this.closing);if(this._trigger("search",b)===!1)return;return this._search(a)},_search:function(a){this.pending++,this.element.addClass("ui-autocomplete-loading"),this.source({term:a},this._response())},_response:function(){var a=this,b=++c;return function(d){b===c&&a.__response(d),a.pending--,a.pending||a.element.removeClass("ui-autocomplete-loading")}},__response:function(a){!this.options.disabled&&a&&a.length?(a=this._normalize(a),this._suggest(a),this._trigger("open")):this.close()},close:function(a){clearTimeout(this.closing),this.menu.element.is(":visible")&&(this.menu.element.hide(),this.menu.deactivate(),this._trigger("close",a))},_change:function(a){this.previous!==this.element.val()&&this._trigger("change",a,{item:this.selectedItem})},_normalize:function(b){return b.length&&b[0].label&&b[0].value?b:a.map(b,function(b){return typeof b=="string"?{label:b,value:b}:a.extend({label:b.label||b.value,value:b.value||b.label},b)})},_suggest:function(b){var c=this.menu.element.empty().zIndex(this.element.zIndex()+1);this._renderMenu(c,b),this.menu.deactivate(),this.menu.refresh(),c.show(),this._resizeMenu(),c.position(a.extend({of:this.element},this.options.position)),this.options.autoFocus&&this.menu.next(new a.Event("mouseover"))},_resizeMenu:function(){var a=this.menu.element;a.outerWidth(Math.max(a.width("").outerWidth()+1,this.element.outerWidth()))},_renderMenu:function(b,c){var d=this;a.each(c,function(a,c){d._renderItem(b,c)})},_renderItem:function(b,c){return a("<li></li>").data("item.autocomplete",c).append(a("<a></a>").text(c.label)).appendTo(b)},_move:function(a,b){if(!this.menu.element.is(":visible")){this.search(null,b);return}if(this.menu.first()&&/^previous/.test(a)||this.menu.last()&&/^next/.test(a)){this.element.val(this.term),this.menu.deactivate();return}this.menu[a](b)},widget:function(){return this.menu.element},_keyEvent:function(a,b){if(!this.isMultiLine||this.menu.element.is(":visible"))this._move(a,b),b.preventDefault()}}),a.extend(a.ui.autocomplete,{escapeRegex:function(a){return a.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&")},filter:function(b,c){var d=new RegExp(a.ui.autocomplete.escapeRegex(c),"i");return a.grep(b,function(a){return d.test(a.label||a.value||a)})}})})(jQuery),function(a){a.widget("ui.menu",{_create:function(){var b=this;this.element.addClass("ui-menu ui-widget ui-widget-content ui-corner-all").attr({role:"listbox","aria-activedescendant":"ui-active-menuitem"}).click(function(c){if(!a(c.target).closest(".ui-menu-item a").length)return;c.preventDefault(),b.select(c)}),this.refresh()},refresh:function(){var b=this,c=this.element.children("li:not(.ui-menu-item):has(a)").addClass("ui-menu-item").attr("role","menuitem");c.children("a").addClass("ui-corner-all").attr("tabindex",-1).mouseenter(function(c){b.activate(c,a(this).parent())}).mouseleave(function(){b.deactivate()})},activate:function(a,b){this.deactivate();if(this.hasScroll()){var c=b.offset().top-this.element.offset().top,d=this.element.scrollTop(),e=this.element.height();c<0?this.element.scrollTop(d+c):c>=e&&this.element.scrollTop(d+c-e+b.height())}this.active=b.eq(0).children("a").addClass("ui-state-hover").attr("id","ui-active-menuitem").end(),this._trigger("focus",a,{item:b})},deactivate:function(){if(!this.active)return;this.active.children("a").removeClass("ui-state-hover").removeAttr("id"),this._trigger("blur"),this.active=null},next:function(a){this.move("next",".ui-menu-item:first",a)},previous:function(a){this.move("prev",".ui-menu-item:last",a)},first:function(){return this.active&&!this.active.prevAll(".ui-menu-item").length},last:function(){return this.active&&!this.active.nextAll(".ui-menu-item").length},move:function(a,b,c){if(!this.active){this.activate(c,this.element.children(b));return}var d=this.active[a+"All"](".ui-menu-item").eq(0);d.length?this.activate(c,d):this.activate(c,this.element.children(b))},nextPage:function(b){if(this.hasScroll()){if(!this.active||this.last()){this.activate(b,this.element.children(".ui-menu-item:first"));return}var c=this.active.offset().top,d=this.element.height(),e=this.element.children(".ui-menu-item").filter(function(){var b=a(this).offset().top-c-d+a(this).height();return b<10&&b>-10});e.length||(e=this.element.children(".ui-menu-item:last")),this.activate(b,e)}else this.activate(b,this.element.children(".ui-menu-item").filter(!this.active||this.last()?":first":":last"))},previousPage:function(b){if(this.hasScroll()){if(!this.active||this.first()){this.activate(b,this.element.children(".ui-menu-item:last"));return}var c=this.active.offset().top,d=this.element.height(),e=this.element.children(".ui-menu-item").filter(function(){var b=a(this).offset().top-c+d-a(this).height();return b<10&&b>-10});e.length||(e=this.element.children(".ui-menu-item:first")),this.activate(b,e)}else this.activate(b,this.element.children(".ui-menu-item").filter(!this.active||this.first()?":last":":first"))},hasScroll:function(){return this.element.height()<this.element[a.fn.prop?"prop":"attr"]("scrollHeight")},select:function(a){this._trigger("selected",a,{item:this.active})}})}(jQuery);;/*! jQuery UI - v1.8.21 - 2012-06-05
+* https://github.com/jquery/jquery-ui
+* Includes: jquery.ui.button.js
+* Copyright (c) 2012 AUTHORS.txt; Licensed MIT, GPL */
+(function(a,b){var c,d,e,f,g="ui-button ui-widget ui-state-default ui-corner-all",h="ui-state-hover ui-state-active ",i="ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only",j=function(){var b=a(this).find(":ui-button");setTimeout(function(){b.button("refresh")},1)},k=function(b){var c=b.name,d=b.form,e=a([]);return c&&(d?e=a(d).find("[name='"+c+"']"):e=a("[name='"+c+"']",b.ownerDocument).filter(function(){return!this.form})),e};a.widget("ui.button",{options:{disabled:null,text:!0,label:null,icons:{primary:null,secondary:null}},_create:function(){this.element.closest("form").unbind("reset.button").bind("reset.button",j),typeof this.options.disabled!="boolean"?this.options.disabled=!!this.element.propAttr("disabled"):this.element.propAttr("disabled",this.options.disabled),this._determineButtonType(),this.hasTitle=!!this.buttonElement.attr("title");var b=this,h=this.options,i=this.type==="checkbox"||this.type==="radio",l="ui-state-hover"+(i?"":" ui-state-active"),m="ui-state-focus";h.label===null&&(h.label=this.buttonElement.html()),this.buttonElement.addClass(g).attr("role","button").bind("mouseenter.button",function(){if(h.disabled)return;a(this).addClass("ui-state-hover"),this===c&&a(this).addClass("ui-state-active")}).bind("mouseleave.button",function(){if(h.disabled)return;a(this).removeClass(l)}).bind("click.button",function(a){h.disabled&&(a.preventDefault(),a.stopImmediatePropagation())}),this.element.bind("focus.button",function(){b.buttonElement.addClass(m)}).bind("blur.button",function(){b.buttonElement.removeClass(m)}),i&&(this.element.bind("change.button",function(){if(f)return;b.refresh()}),this.buttonElement.bind("mousedown.button",function(a){if(h.disabled)return;f=!1,d=a.pageX,e=a.pageY}).bind("mouseup.button",function(a){if(h.disabled)return;if(d!==a.pageX||e!==a.pageY)f=!0})),this.type==="checkbox"?this.buttonElement.bind("click.button",function(){if(h.disabled||f)return!1;a(this).toggleClass("ui-state-active"),b.buttonElement.attr("aria-pressed",b.element[0].checked)}):this.type==="radio"?this.buttonElement.bind("click.button",function(){if(h.disabled||f)return!1;a(this).addClass("ui-state-active"),b.buttonElement.attr("aria-pressed","true");var c=b.element[0];k(c).not(c).map(function(){return a(this).button("widget")[0]}).removeClass("ui-state-active").attr("aria-pressed","false")}):(this.buttonElement.bind("mousedown.button",function(){if(h.disabled)return!1;a(this).addClass("ui-state-active"),c=this,a(document).one("mouseup",function(){c=null})}).bind("mouseup.button",function(){if(h.disabled)return!1;a(this).removeClass("ui-state-active")}).bind("keydown.button",function(b){if(h.disabled)return!1;(b.keyCode==a.ui.keyCode.SPACE||b.keyCode==a.ui.keyCode.ENTER)&&a(this).addClass("ui-state-active")}).bind("keyup.button",function(){a(this).removeClass("ui-state-active")}),this.buttonElement.is("a")&&this.buttonElement.keyup(function(b){b.keyCode===a.ui.keyCode.SPACE&&a(this).click()})),this._setOption("disabled",h.disabled),this._resetButton()},_determineButtonType:function(){this.element.is(":checkbox")?this.type="checkbox":this.element.is(":radio")?this.type="radio":this.element.is("input")?this.type="input":this.type="button";if(this.type==="checkbox"||this.type==="radio"){var a=this.element.parents().filter(":last"),b="label[for='"+this.element.attr("id")+"']";this.buttonElement=a.find(b),this.buttonElement.length||(a=a.length?a.siblings():this.element.siblings(),this.buttonElement=a.filter(b),this.buttonElement.length||(this.buttonElement=a.find(b))),this.element.addClass("ui-helper-hidden-accessible");var c=this.element.is(":checked");c&&this.buttonElement.addClass("ui-state-active"),this.buttonElement.attr("aria-pressed",c)}else this.buttonElement=this.element},widget:function(){return this.buttonElement},destroy:function(){this.element.removeClass("ui-helper-hidden-accessible"),this.buttonElement.removeClass(g+" "+h+" "+i).removeAttr("role").removeAttr("aria-pressed").html(this.buttonElement.find(".ui-button-text").html()),this.hasTitle||this.buttonElement.removeAttr("title"),a.Widget.prototype.destroy.call(this)},_setOption:function(b,c){a.Widget.prototype._setOption.apply(this,arguments);if(b==="disabled"){c?this.element.propAttr("disabled",!0):this.element.propAttr("disabled",!1);return}this._resetButton()},refresh:function(){var b=this.element.is(":disabled");b!==this.options.disabled&&this._setOption("disabled",b),this.type==="radio"?k(this.element[0]).each(function(){a(this).is(":checked")?a(this).button("widget").addClass("ui-state-active").attr("aria-pressed","true"):a(this).button("widget").removeClass("ui-state-active").attr("aria-pressed","false")}):this.type==="checkbox"&&(this.element.is(":checked")?this.buttonElement.addClass("ui-state-active").attr("aria-pressed","true"):this.buttonElement.removeClass("ui-state-active").attr("aria-pressed","false"))},_resetButton:function(){if(this.type==="input"){this.options.label&&this.element.val(this.options.label);return}var b=this.buttonElement.removeClass(i),c=a("<span></span>",this.element[0].ownerDocument).addClass("ui-button-text").html(this.options.label).appendTo(b.empty()).text(),d=this.options.icons,e=d.primary&&d.secondary,f=[];d.primary||d.secondary?(this.options.text&&f.push("ui-button-text-icon"+(e?"s":d.primary?"-primary":"-secondary")),d.primary&&b.prepend("<span class='ui-button-icon-primary ui-icon "+d.primary+"'></span>"),d.secondary&&b.append("<span class='ui-button-icon-secondary ui-icon "+d.secondary+"'></span>"),this.options.text||(f.push(e?"ui-button-icons-only":"ui-button-icon-only"),this.hasTitle||b.attr("title",c))):f.push("ui-button-text-only"),b.addClass(f.join(" "))}}),a.widget("ui.buttonset",{options:{items:":button, :submit, :reset, :checkbox, :radio, a, :data(button)"},_create:function(){this.element.addClass("ui-buttonset")},_init:function(){this.refresh()},_setOption:function(b,c){b==="disabled"&&this.buttons.button("option",b,c),a.Widget.prototype._setOption.apply(this,arguments)},refresh:function(){var b=this.element.css("direction")==="rtl";this.buttons=this.element.find(this.options.items).filter(":ui-button").button("refresh").end().not(":ui-button").button().end().map(function(){return a(this).button("widget")[0]}).removeClass("ui-corner-all ui-corner-left ui-corner-right").filter(":first").addClass(b?"ui-corner-right":"ui-corner-left").end().filter(":last").addClass(b?"ui-corner-left":"ui-corner-right").end().end()},destroy:function(){this.element.removeClass("ui-buttonset"),this.buttons.map(function(){return a(this).button("widget")[0]}).removeClass("ui-corner-left ui-corner-right").end().button("destroy"),a.Widget.prototype.destroy.call(this)}})})(jQuery);;/*! jQuery UI - v1.8.21 - 2012-06-05
+* https://github.com/jquery/jquery-ui
+* Includes: jquery.ui.dialog.js
+* Copyright (c) 2012 AUTHORS.txt; Licensed MIT, GPL */
+(function(a,b){var c="ui-dialog ui-widget ui-widget-content ui-corner-all ",d={buttons:!0,height:!0,maxHeight:!0,maxWidth:!0,minHeight:!0,minWidth:!0,width:!0},e={maxHeight:!0,maxWidth:!0,minHeight:!0,minWidth:!0},f=a.attrFn||{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0,click:!0};a.widget("ui.dialog",{options:{autoOpen:!0,buttons:{},closeOnEscape:!0,closeText:"close",dialogClass:"",draggable:!0,hide:null,height:"auto",maxHeight:!1,maxWidth:!1,minHeight:150,minWidth:150,modal:!1,position:{my:"center",at:"center",collision:"fit",using:function(b){var c=a(this).css(b).offset().top;c<0&&a(this).css("top",b.top-c)}},resizable:!0,show:null,stack:!0,title:"",width:300,zIndex:1e3},_create:function(){this.originalTitle=this.element.attr("title"),typeof this.originalTitle!="string"&&(this.originalTitle=""),this.options.title=this.options.title||this.originalTitle;var b=this,d=b.options,e=d.title||" ",f=a.ui.dialog.getTitleId(b.element),g=(b.uiDialog=a("<div></div>")).appendTo(document.body).hide().addClass(c+d.dialogClass).css({zIndex:d.zIndex}).attr("tabIndex",-1).css("outline",0).keydown(function(c){d.closeOnEscape&&!c.isDefaultPrevented()&&c.keyCode&&c.keyCode===a.ui.keyCode.ESCAPE&&(b.close(c),c.preventDefault())}).attr({role:"dialog","aria-labelledby":f}).mousedown(function(a){b.moveToTop(!1,a)}),h=b.element.show().removeAttr("title").addClass("ui-dialog-content ui-widget-content").appendTo(g),i=(b.uiDialogTitlebar=a("<div></div>")).addClass("ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix").prependTo(g),j=a('<a href="#"></a>').addClass("ui-dialog-titlebar-close ui-corner-all").attr("role","button").hover(function(){j.addClass("ui-state-hover")},function(){j.removeClass("ui-state-hover")}).focus(function(){j.addClass("ui-state-focus")}).blur(function(){j.removeClass("ui-state-focus")}).click(function(a){return b.close(a),!1}).appendTo(i),k=(b.uiDialogTitlebarCloseText=a("<span></span>")).addClass("ui-icon ui-icon-closethick").text(d.closeText).appendTo(j),l=a("<span></span>").addClass("ui-dialog-title").attr("id",f).html(e).prependTo(i);a.isFunction(d.beforeclose)&&!a.isFunction(d.beforeClose)&&(d.beforeClose=d.beforeclose),i.find("*").add(i).disableSelection(),d.draggable&&a.fn.draggable&&b._makeDraggable(),d.resizable&&a.fn.resizable&&b._makeResizable(),b._createButtons(d.buttons),b._isOpen=!1,a.fn.bgiframe&&g.bgiframe()},_init:function(){this.options.autoOpen&&this.open()},destroy:function(){var a=this;return a.overlay&&a.overlay.destroy(),a.uiDialog.hide(),a.element.unbind(".dialog").removeData("dialog").removeClass("ui-dialog-content ui-widget-content").hide().appendTo("body"),a.uiDialog.remove(),a.originalTitle&&a.element.attr("title",a.originalTitle),a},widget:function(){return this.uiDialog},close:function(b){var c=this,d,e;if(!1===c._trigger("beforeClose",b))return;return c.overlay&&c.overlay.destroy(),c.uiDialog.unbind("keypress.ui-dialog"),c._isOpen=!1,c.options.hide?c.uiDialog.hide(c.options.hide,function(){c._trigger("close",b)}):(c.uiDialog.hide(),c._trigger("close",b)),a.ui.dialog.overlay.resize(),c.options.modal&&(d=0,a(".ui-dialog").each(function(){this!==c.uiDialog[0]&&(e=a(this).css("z-index"),isNaN(e)||(d=Math.max(d,e)))}),a.ui.dialog.maxZ=d),c},isOpen:function(){return this._isOpen},moveToTop:function(b,c){var d=this,e=d.options,f;return e.modal&&!b||!e.stack&&!e.modal?d._trigger("focus",c):(e.zIndex>a.ui.dialog.maxZ&&(a.ui.dialog.maxZ=e.zIndex),d.overlay&&(a.ui.dialog.maxZ+=1,d.overlay.$el.css("z-index",a.ui.dialog.overlay.maxZ=a.ui.dialog.maxZ)),f={scrollTop:d.element.scrollTop(),scrollLeft:d.element.scrollLeft()},a.ui.dialog.maxZ+=1,d.uiDialog.css("z-index",a.ui.dialog.maxZ),d.element.attr(f),d._trigger("focus",c),d)},open:function(){if(this._isOpen)return;var b=this,c=b.options,d=b.uiDialog;return b.overlay=c.modal?new a.ui.dialog.overlay(b):null,b._size(),b._position(c.position),d.show(c.show),b.moveToTop(!0),c.modal&&d.bind("keydown.ui-dialog",function(b){if(b.keyCode!==a.ui.keyCode.TAB)return;var c=a(":tabbable",this),d=c.filter(":first"),e=c.filter(":last");if(b.target===e[0]&&!b.shiftKey)return d.focus(1),!1;if(b.target===d[0]&&b.shiftKey)return e.focus(1),!1}),a(b.element.find(":tabbable").get().concat(d.find(".ui-dialog-buttonpane :tabbable").get().concat(d.get()))).eq(0).focus(),b._isOpen=!0,b._trigger("open"),b},_createButtons:function(b){var c=this,d=!1,e=a("<div></div>").addClass("ui-dialog-buttonpane ui-widget-content ui-helper-clearfix"),g=a("<div></div>").addClass("ui-dialog-buttonset").appendTo(e);c.uiDialog.find(".ui-dialog-buttonpane").remove(),typeof b=="object"&&b!==null&&a.each(b,function(){return!(d=!0)}),d&&(a.each(b,function(b,d){d=a.isFunction(d)?{click:d,text:b}:d;var e=a('<button type="button"></button>').click(function(){d.click.apply(c.element[0],arguments)}).appendTo(g);a.each(d,function(a,b){if(a==="click")return;a in f?e[a](b):e.attr(a,b)}),a.fn.button&&e.button()}),e.appendTo(c.uiDialog))},_makeDraggable:function(){function f(a){return{position:a.position,offset:a.offset}}var b=this,c=b.options,d=a(document),e;b.uiDialog.draggable({cancel:".ui-dialog-content, .ui-dialog-titlebar-close",handle:".ui-dialog-titlebar",containment:"document",start:function(d,g){e=c.height==="auto"?"auto":a(this).height(),a(this).height(a(this).height()).addClass("ui-dialog-dragging"),b._trigger("dragStart",d,f(g))},drag:function(a,c){b._trigger("drag",a,f(c))},stop:function(g,h){c.position=[h.position.left-d.scrollLeft(),h.position.top-d.scrollTop()],a(this).removeClass("ui-dialog-dragging").height(e),b._trigger("dragStop",g,f(h)),a.ui.dialog.overlay.resize()}})},_makeResizable:function(c){function h(a){return{originalPosition:a.originalPosition,originalSize:a.originalSize,position:a.position,size:a.size}}c=c===b?this.options.resizable:c;var d=this,e=d.options,f=d.uiDialog.css("position"),g=typeof c=="string"?c:"n,e,s,w,se,sw,ne,nw";d.uiDialog.resizable({cancel:".ui-dialog-content",containment:"document",alsoResize:d.element,maxWidth:e.maxWidth,maxHeight:e.maxHeight,minWidth:e.minWidth,minHeight:d._minHeight(),handles:g,start:function(b,c){a(this).addClass("ui-dialog-resizing"),d._trigger("resizeStart",b,h(c))},resize:function(a,b){d._trigger("resize",a,h(b))},stop:function(b,c){a(this).removeClass("ui-dialog-resizing"),e.height=a(this).height(),e.width=a(this).width(),d._trigger("resizeStop",b,h(c)),a.ui.dialog.overlay.resize()}}).css("position",f).find(".ui-resizable-se").addClass("ui-icon ui-icon-grip-diagonal-se")},_minHeight:function(){var a=this.options;return a.height==="auto"?a.minHeight:Math.min(a.minHeight,a.height)},_position:function(b){var c=[],d=[0,0],e;if(b){if(typeof b=="string"||typeof b=="object"&&"0"in b)c=b.split?b.split(" "):[b[0],b[1]],c.length===1&&(c[1]=c[0]),a.each(["left","top"],function(a,b){+c[a]===c[a]&&(d[a]=c[a],c[a]=b)}),b={my:c.join(" "),at:c.join(" "),offset:d.join(" ")};b=a.extend({},a.ui.dialog.prototype.options.position,b)}else b=a.ui.dialog.prototype.options.position;e=this.uiDialog.is(":visible"),e||this.uiDialog.show(),this.uiDialog.css({top:0,left:0}).position(a.extend({of:window},b)),e||this.uiDialog.hide()},_setOptions:function(b){var c=this,f={},g=!1;a.each(b,function(a,b){c._setOption(a,b),a in d&&(g=!0),a in e&&(f[a]=b)}),g&&this._size(),this.uiDialog.is(":data(resizable)")&&this.uiDialog.resizable("option",f)},_setOption:function(b,d){var e=this,f=e.uiDialog;switch(b){case"beforeclose":b="beforeClose";break;case"buttons":e._createButtons(d);break;case"closeText":e.uiDialogTitlebarCloseText.text(""+d);break;case"dialogClass":f.removeClass(e.options.dialogClass).addClass(c+d);break;case"disabled":d?f.addClass("ui-dialog-disabled"):f.removeClass("ui-dialog-disabled");break;case"draggable":var g=f.is(":data(draggable)");g&&!d&&f.draggable("destroy"),!g&&d&&e._makeDraggable();break;case"position":e._position(d);break;case"resizable":var h=f.is(":data(resizable)");h&&!d&&f.resizable("destroy"),h&&typeof d=="string"&&f.resizable("option","handles",d),!h&&d!==!1&&e._makeResizable(d);break;case"title":a(".ui-dialog-title",e.uiDialogTitlebar).html(""+(d||" "))}a.Widget.prototype._setOption.apply(e,arguments)},_size:function(){var b=this.options,c,d,e=this.uiDialog.is(":visible");this.element.show().css({width:"auto",minHeight:0,height:0}),b.minWidth>b.width&&(b.width=b.minWidth),c=this.uiDialog.css({height:"auto",width:b.width}).height(),d=Math.max(0,b.minHeight-c);if(b.height==="auto")if(a.support.minHeight)this.element.css({minHeight:d,height:"auto"});else{this.uiDialog.show();var f=this.element.css("height","auto").height();e||this.uiDialog.hide(),this.element.height(Math.max(f,d))}else this.element.height(Math.max(b.height-c,0));this.uiDialog.is(":data(resizable)")&&this.uiDialog.resizable("option","minHeight",this._minHeight())}}),a.extend(a.ui.dialog,{version:"1.8.21",uuid:0,maxZ:0,getTitleId:function(a){var b=a.attr("id");return b||(this.uuid+=1,b=this.uuid),"ui-dialog-title-"+b},overlay:function(b){this.$el=a.ui.dialog.overlay.create(b)}}),a.extend(a.ui.dialog.overlay,{instances:[],oldInstances:[],maxZ:0,events:a.map("focus,mousedown,mouseup,keydown,keypress,click".split(","),function(a){return a+".dialog-overlay"}).join(" "),create:function(b){this.instances.length===0&&(setTimeout(function(){a.ui.dialog.overlay.instances.length&&a(document).bind(a.ui.dialog.overlay.events,function(b){if(a(b.target).zIndex()<a.ui.dialog.overlay.maxZ)return!1})},1),a(document).bind("keydown.dialog-overlay",function(c){b.options.closeOnEscape&&!c.isDefaultPrevented()&&c.keyCode&&c.keyCode===a.ui.keyCode.ESCAPE&&(b.close(c),c.preventDefault())}),a(window).bind("resize.dialog-overlay",a.ui.dialog.overlay.resize));var c=(this.oldInstances.pop()||a("<div></div>").addClass("ui-widget-overlay")).appendTo(document.body).css({width:this.width(),height:this.height()});return a.fn.bgiframe&&c.bgiframe(),this.instances.push(c),c},destroy:function(b){var c=a.inArray(b,this.instances);c!=-1&&this.oldInstances.push(this.instances.splice(c,1)[0]),this.instances.length===0&&a([document,window]).unbind(".dialog-overlay"),b.remove();var d=0;a.each(this.instances,function(){d=Math.max(d,this.css("z-index"))}),this.maxZ=d},height:function(){var b,c;return a.browser.msie&&a.browser.version<7?(b=Math.max(document.documentElement.scrollHeight,document.body.scrollHeight),c=Math.max(document.documentElement.offsetHeight,document.body.offsetHeight),b<c?a(window).height()+"px":b+"px"):a(document).height()+"px"},width:function(){var b,c;return a.browser.msie?(b=Math.max(document.documentElement.scrollWidth,document.body.scrollWidth),c=Math.max(document.documentElement.offsetWidth,document.body.offsetWidth),b<c?a(window).width()+"px":b+"px"):a(document).width()+"px"},resize:function(){var b=a([]);a.each(a.ui.dialog.overlay.instances,function(){b=b.add(this)}),b.css({width:0,height:0}).css({width:a.ui.dialog.overlay.width(),height:a.ui.dialog.overlay.height()})}}),a.extend(a.ui.dialog.overlay.prototype,{destroy:function(){a.ui.dialog.overlay.destroy(this.$el)}})})(jQuery);;/*! jQuery UI - v1.8.21 - 2012-06-05
+* https://github.com/jquery/jquery-ui
+* Includes: jquery.ui.slider.js
+* Copyright (c) 2012 AUTHORS.txt; Licensed MIT, GPL */
+(function(a,b){var c=5;a.widget("ui.slider",a.ui.mouse,{widgetEventPrefix:"slide",options:{animate:!1,distance:0,max:100,min:0,orientation:"horizontal",range:!1,step:1,value:0,values:null},_create:function(){var b=this,d=this.options,e=this.element.find(".ui-slider-handle").addClass("ui-state-default ui-corner-all"),f="<a class='ui-slider-handle ui-state-default ui-corner-all' href='#'></a>",g=d.values&&d.values.length||1,h=[];this._keySliding=!1,this._mouseSliding=!1,this._animateOff=!0,this._handleIndex=null,this._detectOrientation(),this._mouseInit(),this.element.addClass("ui-slider ui-slider-"+this.orientation+" ui-widget"+" ui-widget-content"+" ui-corner-all"+(d.disabled?" ui-slider-disabled ui-disabled":"")),this.range=a([]),d.range&&(d.range===!0&&(d.values||(d.values=[this._valueMin(),this._valueMin()]),d.values.length&&d.values.length!==2&&(d.values=[d.values[0],d.values[0]])),this.range=a("<div></div>").appendTo(this.element).addClass("ui-slider-range ui-widget-header"+(d.range==="min"||d.range==="max"?" ui-slider-range-"+d.range:"")));for(var i=e.length;i<g;i+=1)h.push(f);this.handles=e.add(a(h.join("")).appendTo(b.element)),this.handle=this.handles.eq(0),this.handles.add(this.range).filter("a").click(function(a){a.preventDefault()}).hover(function(){d.disabled||a(this).addClass("ui-state-hover")},function(){a(this).removeClass("ui-state-hover")}).focus(function(){d.disabled?a(this).blur():(a(".ui-slider .ui-state-focus").removeClass("ui-state-focus"),a(this).addClass("ui-state-focus"))}).blur(function(){a(this).removeClass("ui-state-focus")}),this.handles.each(function(b){a(this).data("index.ui-slider-handle",b)}),this.handles.keydown(function(d){var e=a(this).data("index.ui-slider-handle"),f,g,h,i;if(b.options.disabled)return;switch(d.keyCode){case a.ui.keyCode.HOME:case a.ui.keyCode.END:case a.ui.keyCode.PAGE_UP:case a.ui.keyCode.PAGE_DOWN:case a.ui.keyCode.UP:case a.ui.keyCode.RIGHT:case a.ui.keyCode.DOWN:case a.ui.keyCode.LEFT:d.preventDefault();if(!b._keySliding){b._keySliding=!0,a(this).addClass("ui-state-active"),f=b._start(d,e);if(f===!1)return}}i=b.options.step,b.options.values&&b.options.values.length?g=h=b.values(e):g=h=b.value();switch(d.keyCode){case a.ui.keyCode.HOME:h=b._valueMin();break;case a.ui.keyCode.END:h=b._valueMax();break;case a.ui.keyCode.PAGE_UP:h=b._trimAlignValue(g+(b._valueMax()-b._valueMin())/c);break;case a.ui.keyCode.PAGE_DOWN:h=b._trimAlignValue(g-(b._valueMax()-b._valueMin())/c);break;case a.ui.keyCode.UP:case a.ui.keyCode.RIGHT:if(g===b._valueMax())return;h=b._trimAlignValue(g+i);break;case a.ui.keyCode.DOWN:case a.ui.keyCode.LEFT:if(g===b._valueMin())return;h=b._trimAlignValue(g-i)}b._slide(d,e,h)}).keyup(function(c){var d=a(this).data("index.ui-slider-handle");b._keySliding&&(b._keySliding=!1,b._stop(c,d),b._change(c,d),a(this).removeClass("ui-state-active"))}),this._refreshValue(),this._animateOff=!1},destroy:function(){return this.handles.remove(),this.range.remove(),this.element.removeClass("ui-slider ui-slider-horizontal ui-slider-vertical ui-slider-disabled ui-widget ui-widget-content ui-corner-all").removeData("slider").unbind(".slider"),this._mouseDestroy(),this},_mouseCapture:function(b){var c=this.options,d,e,f,g,h,i,j,k,l;return c.disabled?!1:(this.elementSize={width:this.element.outerWidth(),height:this.element.outerHeight()},this.elementOffset=this.element.offset(),d={x:b.pageX,y:b.pageY},e=this._normValueFromMouse(d),f=this._valueMax()-this._valueMin()+1,h=this,this.handles.each(function(b){var c=Math.abs(e-h.values(b));f>c&&(f=c,g=a(this),i=b)}),c.range===!0&&this.values(1)===c.min&&(i+=1,g=a(this.handles[i])),j=this._start(b,i),j===!1?!1:(this._mouseSliding=!0,h._handleIndex=i,g.addClass("ui-state-active").focus(),k=g.offset(),l=!a(b.target).parents().andSelf().is(".ui-slider-handle"),this._clickOffset=l?{left:0,top:0}:{left:b.pageX-k.left-g.width()/2,top:b.pageY-k.top-g.height()/2-(parseInt(g.css("borderTopWidth"),10)||0)-(parseInt(g.css("borderBottomWidth"),10)||0)+(parseInt(g.css("marginTop"),10)||0)},this.handles.hasClass("ui-state-hover")||this._slide(b,i,e),this._animateOff=!0,!0))},_mouseStart:function(a){return!0},_mouseDrag:function(a){var b={x:a.pageX,y:a.pageY},c=this._normValueFromMouse(b);return this._slide(a,this._handleIndex,c),!1},_mouseStop:function(a){return this.handles.removeClass("ui-state-active"),this._mouseSliding=!1,this._stop(a,this._handleIndex),this._change(a,this._handleIndex),this._handleIndex=null,this._clickOffset=null,this._animateOff=!1,!1},_detectOrientation:function(){this.orientation=this.options.orientation==="vertical"?"vertical":"horizontal"},_normValueFromMouse:function(a){var b,c,d,e,f;return this.orientation==="horizontal"?(b=this.elementSize.width,c=a.x-this.elementOffset.left-(this._clickOffset?this._clickOffset.left:0)):(b=this.elementSize.height,c=a.y-this.elementOffset.top-(this._clickOffset?this._clickOffset.top:0)),d=c/b,d>1&&(d=1),d<0&&(d=0),this.orientation==="vertical"&&(d=1-d),e=this._valueMax()-this._valueMin(),f=this._valueMin()+d*e,this._trimAlignValue(f)},_start:function(a,b){var c={handle:this.handles[b],value:this.value()};return this.options.values&&this.options.values.length&&(c.value=this.values(b),c.values=this.values()),this._trigger("start",a,c)},_slide:function(a,b,c){var d,e,f;this.options.values&&this.options.values.length?(d=this.values(b?0:1),this.options.values.length===2&&this.options.range===!0&&(b===0&&c>d||b===1&&c<d)&&(c=d),c!==this.values(b)&&(e=this.values(),e[b]=c,f=this._trigger("slide",a,{handle:this.handles[b],value:c,values:e}),d=this.values(b?0:1),f!==!1&&this.values(b,c,!0))):c!==this.value()&&(f=this._trigger("slide",a,{handle:this.handles[b],value:c}),f!==!1&&this.value(c))},_stop:function(a,b){var c={handle:this.handles[b],value:this.value()};this.options.values&&this.options.values.length&&(c.value=this.values(b),c.values=this.values()),this._trigger("stop",a,c)},_change:function(a,b){if(!this._keySliding&&!this._mouseSliding){var c={handle:this.handles[b],value:this.value()};this.options.values&&this.options.values.length&&(c.value=this.values(b),c.values=this.values()),this._trigger("change",a,c)}},value:function(a){if(arguments.length){this.options.value=this._trimAlignValue(a),this._refreshValue(),this._change(null,0);return}return this._value()},values:function(b,c){var d,e,f;if(arguments.length>1){this.options.values[b]=this._trimAlignValue(c),this._refreshValue(),this._change(null,b);return}if(!arguments.length)return this._values();if(!a.isArray(arguments[0]))return this.options.values&&this.options.values.length?this._values(b):this.value();d=this.options.values,e=arguments[0];for(f=0;f<d.length;f+=1)d[f]=this._trimAlignValue(e[f]),this._change(null,f);this._refreshValue()},_setOption:function(b,c){var d,e=0;a.isArray(this.options.values)&&(e=this.options.values.length),a.Widget.prototype._setOption.apply(this,arguments);switch(b){case"disabled":c?(this.handles.filter(".ui-state-focus").blur(),this.handles.removeClass("ui-state-hover"),this.handles.propAttr("disabled",!0),this.element.addClass("ui-disabled")):(this.handles.propAttr("disabled",!1),this.element.removeClass("ui-disabled"));break;case"orientation":this._detectOrientation(),this.element.removeClass("ui-slider-horizontal ui-slider-vertical").addClass("ui-slider-"+this.orientation),this._refreshValue();break;case"value":this._animateOff=!0,this._refreshValue(),this._change(null,0),this._animateOff=!1;break;case"values":this._animateOff=!0,this._refreshValue();for(d=0;d<e;d+=1)this._change(null,d);this._animateOff=!1}},_value:function(){var a=this.options.value;return a=this._trimAlignValue(a),a},_values:function(a){var b,c,d;if(arguments.length)return b=this.options.values[a],b=this._trimAlignValue(b),b;c=this.options.values.slice();for(d=0;d<c.length;d+=1)c[d]=this._trimAlignValue(c[d]);return c},_trimAlignValue:function(a){if(a<=this._valueMin())return this._valueMin();if(a>=this._valueMax())return this._valueMax();var b=this.options.step>0?this.options.step:1,c=(a-this._valueMin())%b,d=a-c;return Math.abs(c)*2>=b&&(d+=c>0?b:-b),parseFloat(d.toFixed(5))},_valueMin:function(){return this.options.min},_valueMax:function(){return this.options.max},_refreshValue:function(){var b=this.options.range,c=this.options,d=this,e=this._animateOff?!1:c.animate,f,g={},h,i,j,k;this.options.values&&this.options.values.length?this.handles.each(function(b,i){f=(d.values(b)-d._valueMin())/(d._valueMax()-d._valueMin())*100,g[d.orientation==="horizontal"?"left":"bottom"]=f+"%",a(this).stop(1,1)[e?"animate":"css"](g,c.animate),d.options.range===!0&&(d.orientation==="horizontal"?(b===0&&d.range.stop(1,1)[e?"animate":"css"]({left:f+"%"},c.animate),b===1&&d.range[e?"animate":"css"]({width:f-h+"%"},{queue:!1,duration:c.animate})):(b===0&&d.range.stop(1,1)[e?"animate":"css"]({bottom:f+"%"},c.animate),b===1&&d.range[e?"animate":"css"]({height:f-h+"%"},{queue:!1,duration:c.animate}))),h=f}):(i=this.value(),j=this._valueMin(),k=this._valueMax(),f=k!==j?(i-j)/(k-j)*100:0,g[d.orientation==="horizontal"?"left":"bottom"]=f+"%",this.handle.stop(1,1)[e?"animate":"css"](g,c.animate),b==="min"&&this.orientation==="horizontal"&&this.range.stop(1,1)[e?"animate":"css"]({width:f+"%"},c.animate),b==="max"&&this.orientation==="horizontal"&&this.range[e?"animate":"css"]({width:100-f+"%"},{queue:!1,duration:c.animate}),b==="min"&&this.orientation==="vertical"&&this.range.stop(1,1)[e?"animate":"css"]({height:f+"%"},c.animate),b==="max"&&this.orientation==="vertical"&&this.range[e?"animate":"css"]({height:100-f+"%"},{queue:!1,duration:c.animate}))}}),a.extend(a.ui.slider,{version:"1.8.21"})})(jQuery);;/*! jQuery UI - v1.8.21 - 2012-06-05
+* https://github.com/jquery/jquery-ui
+* Includes: jquery.ui.tabs.js
+* Copyright (c) 2012 AUTHORS.txt; Licensed MIT, GPL */
+(function(a,b){function e(){return++c}function f(){return++d}var c=0,d=0;a.widget("ui.tabs",{options:{add:null,ajaxOptions:null,cache:!1,cookie:null,collapsible:!1,disable:null,disabled:[],enable:null,event:"click",fx:null,idPrefix:"ui-tabs-",load:null,panelTemplate:"<div></div>",remove:null,select:null,show:null,spinner:"<em>Loading…</em>",tabTemplate:"<li><a href='#{href}'><span>#{label}</span></a></li>"},_create:function(){this._tabify(!0)},_setOption:function(a,b){if(a=="selected"){if(this.options.collapsible&&b==this.options.selected)return;this.select(b)}else this.options[a]=b,this._tabify()},_tabId:function(a){return a.title&&a.title.replace(/\s/g,"_").replace(/[^\w\u00c0-\uFFFF-]/g,"")||this.options.idPrefix+e()},_sanitizeSelector:function(a){return a.replace(/:/g,"\\:")},_cookie:function(){var b=this.cookie||(this.cookie=this.options.cookie.name||"ui-tabs-"+f());return a.cookie.apply(null,[b].concat(a.makeArray(arguments)))},_ui:function(a,b){return{tab:a,panel:b,index:this.anchors.index(a)}},_cleanup:function(){this.lis.filter(".ui-state-processing").removeClass("ui-state-processing").find("span:data(label.tabs)").each(function(){var b=a(this);b.html(b.data("label.tabs")).removeData("label.tabs")})},_tabify:function(c){function m(b,c){b.css("display",""),!a.support.opacity&&c.opacity&&b[0].style.removeAttribute("filter")}var d=this,e=this.options,f=/^#.+/;this.list=this.element.find("ol,ul").eq(0),this.lis=a(" > li:has(a[href])",this.list),this.anchors=this.lis.map(function(){return a("a",this)[0]}),this.panels=a([]),this.anchors.each(function(b,c){var g=a(c).attr("href"),h=g.split("#")[0],i;h&&(h===location.toString().split("#")[0]||(i=a("base")[0])&&h===i.href)&&(g=c.hash,c.href=g);if(f.test(g))d.panels=d.panels.add(d.element.find(d._sanitizeSelector(g)));else if(g&&g!=="#"){a.data(c,"href.tabs",g),a.data(c,"load.tabs",g.replace(/#.*$/,""));var j=d._tabId(c);c.href="#"+j;var k=d.element.find("#"+j);k.length||(k=a(e.panelTemplate).attr("id",j).addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").insertAfter(d.panels[b-1]||d.list),k.data("destroy.tabs",!0)),d.panels=d.panels.add(k)}else e.disabled.push(b)}),c?(this.element.addClass("ui-tabs ui-widget ui-widget-content ui-corner-all"),this.list.addClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all"),this.lis.addClass("ui-state-default ui-corner-top"),this.panels.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom"),e.selected===b?(location.hash&&this.anchors.each(function(a,b){if(b.hash==location.hash)return e.selected=a,!1}),typeof e.selected!="number"&&e.cookie&&(e.selected=parseInt(d._cookie(),10)),typeof e.selected!="number"&&this.lis.filter(".ui-tabs-selected").length&&(e.selected=this.lis.index(this.lis.filter(".ui-tabs-selected"))),e.selected=e.selected||(this.lis.length?0:-1)):e.selected===null&&(e.selected=-1),e.selected=e.selected>=0&&this.anchors[e.selected]||e.selected<0?e.selected:0,e.disabled=a.unique(e.disabled.concat(a.map(this.lis.filter(".ui-state-disabled"),function(a,b){return d.lis.index(a)}))).sort(),a.inArray(e.selected,e.disabled)!=-1&&e.disabled.splice(a.inArray(e.selected,e.disabled),1),this.panels.addClass("ui-tabs-hide"),this.lis.removeClass("ui-tabs-selected ui-state-active"),e.selected>=0&&this.anchors.length&&(d.element.find(d._sanitizeSelector(d.anchors[e.selected].hash)).removeClass("ui-tabs-hide"),this.lis.eq(e.selected).addClass("ui-tabs-selected ui-state-active"),d.element.queue("tabs",function(){d._trigger("show",null,d._ui(d.anchors[e.selected],d.element.find(d._sanitizeSelector(d.anchors[e.selected].hash))[0]))}),this.load(e.selected)),a(window).bind("unload",function(){d.lis.add(d.anchors).unbind(".tabs"),d.lis=d.anchors=d.panels=null})):e.selected=this.lis.index(this.lis.filter(".ui-tabs-selected")),this.element[e.collapsible?"addClass":"removeClass"]("ui-tabs-collapsible"),e.cookie&&this._cookie(e.selected,e.cookie);for(var g=0,h;h=this.lis[g];g++)a(h)[a.inArray(g,e.disabled)!=-1&&!a(h).hasClass("ui-tabs-selected")?"addClass":"removeClass"]("ui-state-disabled");e.cache===!1&&this.anchors.removeData("cache.tabs"),this.lis.add(this.anchors).unbind(".tabs");if(e.event!=="mouseover"){var i=function(a,b){b.is(":not(.ui-state-disabled)")&&b.addClass("ui-state-"+a)},j=function(a,b){b.removeClass("ui-state-"+a)};this.lis.bind("mouseover.tabs",function(){i("hover",a(this))}),this.lis.bind("mouseout.tabs",function(){j("hover",a(this))}),this.anchors.bind("focus.tabs",function(){i("focus",a(this).closest("li"))}),this.anchors.bind("blur.tabs",function(){j("focus",a(this).closest("li"))})}var k,l;e.fx&&(a.isArray(e.fx)?(k=e.fx[0],l=e.fx[1]):k=l=e.fx);var n=l?function(b,c){a(b).closest("li").addClass("ui-tabs-selected ui-state-active"),c.hide().removeClass("ui-tabs-hide").animate(l,l.duration||"normal",function(){m(c,l),d._trigger("show",null,d._ui(b,c[0]))})}:function(b,c){a(b).closest("li").addClass("ui-tabs-selected ui-state-active"),c.removeClass("ui-tabs-hide"),d._trigger("show",null,d._ui(b,c[0]))},o=k?function(a,b){b.animate(k,k.duration||"normal",function(){d.lis.removeClass("ui-tabs-selected ui-state-active"),b.addClass("ui-tabs-hide"),m(b,k),d.element.dequeue("tabs")})}:function(a,b,c){d.lis.removeClass("ui-tabs-selected ui-state-active"),b.addClass("ui-tabs-hide"),d.element.dequeue("tabs")};this.anchors.bind(e.event+".tabs",function(){var b=this,c=a(b).closest("li"),f=d.panels.filter(":not(.ui-tabs-hide)"),g=d.element.find(d._sanitizeSelector(b.hash));if(c.hasClass("ui-tabs-selected")&&!e.collapsible||c.hasClass("ui-state-disabled")||c.hasClass("ui-state-processing")||d.panels.filter(":animated").length||d._trigger("select",null,d._ui(this,g[0]))===!1)return this.blur(),!1;e.selected=d.anchors.index(this),d.abort();if(e.collapsible){if(c.hasClass("ui-tabs-selected"))return e.selected=-1,e.cookie&&d._cookie(e.selected,e.cookie),d.element.queue("tabs",function(){o(b,f)}).dequeue("tabs"),this.blur(),!1;if(!f.length)return e.cookie&&d._cookie(e.selected,e.cookie),d.element.queue("tabs",function(){n(b,g)}),d.load(d.anchors.index(this)),this.blur(),!1}e.cookie&&d._cookie(e.selected,e.cookie);if(g.length)f.length&&d.element.queue("tabs",function(){o(b,f)}),d.element.queue("tabs",function(){n(b,g)}),d.load(d.anchors.index(this));else throw"jQuery UI Tabs: Mismatching fragment identifier.";a.browser.msie&&this.blur()}),this.anchors.bind("click.tabs",function(){return!1})},_getIndex:function(a){return typeof a=="string"&&(a=this.anchors.index(this.anchors.filter("[href$='"+a+"']"))),a},destroy:function(){var b=this.options;return this.abort(),this.element.unbind(".tabs").removeClass("ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible").removeData("tabs"),this.list.removeClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all"),this.anchors.each(function(){var b=a.data(this,"href.tabs");b&&(this.href=b);var c=a(this).unbind(".tabs");a.each(["href","load","cache"],function(a,b){c.removeData(b+".tabs")})}),this.lis.unbind(".tabs").add(this.panels).each(function(){a.data(this,"destroy.tabs")?a(this).remove():a(this).removeClass(["ui-state-default","ui-corner-top","ui-tabs-selected","ui-state-active","ui-state-hover","ui-state-focus","ui-state-disabled","ui-tabs-panel","ui-widget-content","ui-corner-bottom","ui-tabs-hide"].join(" "))}),b.cookie&&this._cookie(null,b.cookie),this},add:function(c,d,e){e===b&&(e=this.anchors.length);var f=this,g=this.options,h=a(g.tabTemplate.replace(/#\{href\}/g,c).replace(/#\{label\}/g,d)),i=c.indexOf("#")?this._tabId(a("a",h)[0]):c.replace("#","");h.addClass("ui-state-default ui-corner-top").data("destroy.tabs",!0);var j=f.element.find("#"+i);return j.length||(j=a(g.panelTemplate).attr("id",i).data("destroy.tabs",!0)),j.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide"),e>=this.lis.length?(h.appendTo(this.list),j.appendTo(this.list[0].parentNode)):(h.insertBefore(this.lis[e]),j.insertBefore(this.panels[e])),g.disabled=a.map(g.disabled,function(a,b){return a>=e?++a:a}),this._tabify(),this.anchors.length==1&&(g.selected=0,h.addClass("ui-tabs-selected ui-state-active"),j.removeClass("ui-tabs-hide"),this.element.queue("tabs",function(){f._trigger("show",null,f._ui(f.anchors[0],f.panels[0]))}),this.load(0)),this._trigger("add",null,this._ui(this.anchors[e],this.panels[e])),this},remove:function(b){b=this._getIndex(b);var c=this.options,d=this.lis.eq(b).remove(),e=this.panels.eq(b).remove();return d.hasClass("ui-tabs-selected")&&this.anchors.length>1&&this.select(b+(b+1<this.anchors.length?1:-1)),c.disabled=a.map(a.grep(c.disabled,function(a,c){return a!=b}),function(a,c){return a>=b?--a:a}),this._tabify(),this._trigger("remove",null,this._ui(d.find("a")[0],e[0])),this},enable:function(b){b=this._getIndex(b);var c=this.options;if(a.inArray(b,c.disabled)==-1)return;return this.lis.eq(b).removeClass("ui-state-disabled"),c.disabled=a.grep(c.disabled,function(a,c){return a!=b}),this._trigger("enable",null,this._ui(this.anchors[b],this.panels[b])),this},disable:function(a){a=this._getIndex(a);var b=this,c=this.options;return a!=c.selected&&(this.lis.eq(a).addClass("ui-state-disabled"),c.disabled.push(a),c.disabled.sort(),this._trigger("disable",null,this._ui(this.anchors[a],this.panels[a]))),this},select:function(a){a=this._getIndex(a);if(a==-1)if(this.options.collapsible&&this.options.selected!=-1)a=this.options.selected;else return this;return this.anchors.eq(a).trigger(this.options.event+".tabs"),this},load:function(b){b=this._getIndex(b);var c=this,d=this.options,e=this.anchors.eq(b)[0],f=a.data(e,"load.tabs");this.abort();if(!f||this.element.queue("tabs").length!==0&&a.data(e,"cache.tabs")){this.element.dequeue("tabs");return}this.lis.eq(b).addClass("ui-state-processing");if(d.spinner){var g=a("span",e);g.data("label.tabs",g.html()).html(d.spinner)}return this.xhr=a.ajax(a.extend({},d.ajaxOptions,{url:f,success:function(f,g){c.element.find(c._sanitizeSelector(e.hash)).html(f),c._cleanup(),d.cache&&a.data(e,"cache.tabs",!0),c._trigger("load",null,c._ui(c.anchors[b],c.panels[b]));try{d.ajaxOptions.success(f,g)}catch(h){}},error:function(a,f,g){c._cleanup(),c._trigger("load",null,c._ui(c.anchors[b],c.panels[b]));try{d.ajaxOptions.error(a,f,b,e)}catch(g){}}})),c.element.dequeue("tabs"),this},abort:function(){return this.element.queue([]),this.panels.stop(!1,!0),this.element.queue("tabs",this.element.queue("tabs").splice(-2,2)),this.xhr&&(this.xhr.abort(),delete this.xhr),this._cleanup(),this},url:function(a,b){return this.anchors.eq(a).removeData("cache.tabs").data("load.tabs",b),this},length:function(){return this.anchors.length}}),a.extend(a.ui.tabs,{version:"1.8.21"}),a.extend(a.ui.tabs.prototype,{rotation:null,rotate:function(a,b){var c=this,d=this.options,e=c._rotate||(c._rotate=function(b){clearTimeout(c.rotation),c.rotation=setTimeout(function(){var a=d.selected;c.select(++a<c.anchors.length?a:0)},a),b&&b.stopPropagation()}),f=c._unrotate||(c._unrotate=b?function(a){e()}:function(a){a.clientX&&c.rotate(null)});return a?(this.element.bind("tabsshow",e),this.anchors.bind(d.event+".tabs",f),e()):(clearTimeout(c.rotation),this.element.unbind("tabsshow",e),this.anchors.unbind(d.event+".tabs",f),delete this._rotate,delete this._unrotate),this}})})(jQuery);;/*! jQuery UI - v1.8.21 - 2012-06-05
+* https://github.com/jquery/jquery-ui
+* Includes: jquery.ui.datepicker.js
+* Copyright (c) 2012 AUTHORS.txt; Licensed MIT, GPL */
+(function($,undefined){function Datepicker(){this.debug=!1,this._curInst=null,this._keyEvent=!1,this._disabledInputs=[],this._datepickerShowing=!1,this._inDialog=!1,this._mainDivId="ui-datepicker-div",this._inlineClass="ui-datepicker-inline",this._appendClass="ui-datepicker-append",this._triggerClass="ui-datepicker-trigger",this._dialogClass="ui-datepicker-dialog",this._disableClass="ui-datepicker-disabled",this._unselectableClass="ui-datepicker-unselectable",this._currentClass="ui-datepicker-current-day",this._dayOverClass="ui-datepicker-days-cell-over",this.regional=[],this.regional[""]={closeText:"Done",prevText:"Prev",nextText:"Next",currentText:"Today",monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayNamesMin:["Su","Mo","Tu","We","Th","Fr","Sa"],weekHeader:"Wk",dateFormat:"mm/dd/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},this._defaults={showOn:"focus",showAnim:"fadeIn",showOptions:{},defaultDate:null,appendText:"",buttonText:"...",buttonImage:"",buttonImageOnly:!1,hideIfNoPrevNext:!1,navigationAsDateFormat:!1,gotoCurrent:!1,changeMonth:!1,changeYear:!1,yearRange:"c-10:c+10",showOtherMonths:!1,selectOtherMonths:!1,showWeek:!1,calculateWeek:this.iso8601Week,shortYearCutoff:"+10",minDate:null,maxDate:null,duration:"fast",beforeShowDay:null,beforeShow:null,onSelect:null,onChangeMonthYear:null,onClose:null,numberOfMonths:1,showCurrentAtPos:0,stepMonths:1,stepBigMonths:12,altField:"",altFormat:"",constrainInput:!0,showButtonPanel:!1,autoSize:!1,disabled:!1},$.extend(this._defaults,this.regional[""]),this.dpDiv=bindHover($('<div id="'+this._mainDivId+'" class="ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>'))}function bindHover(a){var b="button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a";return a.bind("mouseout",function(a){var c=$(a.target).closest(b);if(!c.length)return;c.removeClass("ui-state-hover ui-datepicker-prev-hover ui-datepicker-next-hover")}).bind("mouseover",function(c){var d=$(c.target).closest(b);if($.datepicker._isDisabledDatepicker(instActive.inline?a.parent()[0]:instActive.input[0])||!d.length)return;d.parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover"),d.addClass("ui-state-hover"),d.hasClass("ui-datepicker-prev")&&d.addClass("ui-datepicker-prev-hover"),d.hasClass("ui-datepicker-next")&&d.addClass("ui-datepicker-next-hover")})}function extendRemove(a,b){$.extend(a,b);for(var c in b)if(b[c]==null||b[c]==undefined)a[c]=b[c];return a}function isArray(a){return a&&($.browser.safari&&typeof a=="object"&&a.length||a.constructor&&a.constructor.toString().match(/\Array\(\)/))}$.extend($.ui,{datepicker:{version:"1.8.21"}});var PROP_NAME="datepicker",dpuuid=(new Date).getTime(),instActive;$.extend(Datepicker.prototype,{markerClassName:"hasDatepicker",maxRows:4,log:function(){this.debug&&console.log.apply("",arguments)},_widgetDatepicker:function(){return this.dpDiv},setDefaults:function(a){return extendRemove(this._defaults,a||{}),this},_attachDatepicker:function(target,settings){var inlineSettings=null;for(var attrName in this._defaults){var attrValue=target.getAttribute("date:"+attrName);if(attrValue){inlineSettings=inlineSettings||{};try{inlineSettings[attrName]=eval(attrValue)}catch(err){inlineSettings[attrName]=attrValue}}}var nodeName=target.nodeName.toLowerCase(),inline=nodeName=="div"||nodeName=="span";target.id||(this.uuid+=1,target.id="dp"+this.uuid);var inst=this._newInst($(target),inline);inst.settings=$.extend({},settings||{},inlineSettings||{}),nodeName=="input"?this._connectDatepicker(target,inst):inline&&this._inlineDatepicker(target,inst)},_newInst:function(a,b){var c=a[0].id.replace(/([^A-Za-z0-9_-])/g,"\\\\$1");return{id:c,input:a,selectedDay:0,selectedMonth:0,selectedYear:0,drawMonth:0,drawYear:0,inline:b,dpDiv:b?bindHover($('<div class="'+this._inlineClass+' ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>')):this.dpDiv}},_connectDatepicker:function(a,b){var c=$(a);b.append=$([]),b.trigger=$([]);if(c.hasClass(this.markerClassName))return;this._attachments(c,b),c.addClass(this.markerClassName).keydown(this._doKeyDown).keypress(this._doKeyPress).keyup(this._doKeyUp).bind("setData.datepicker",function(a,c,d){b.settings[c]=d}).bind("getData.datepicker",function(a,c){return this._get(b,c)}),this._autoSize(b),$.data(a,PROP_NAME,b),b.settings.disabled&&this._disableDatepicker(a)},_attachments:function(a,b){var c=this._get(b,"appendText"),d=this._get(b,"isRTL");b.append&&b.append.remove(),c&&(b.append=$('<span class="'+this._appendClass+'">'+c+"</span>"),a[d?"before":"after"](b.append)),a.unbind("focus",this._showDatepicker),b.trigger&&b.trigger.remove();var e=this._get(b,"showOn");(e=="focus"||e=="both")&&a.focus(this._showDatepicker);if(e=="button"||e=="both"){var f=this._get(b,"buttonText"),g=this._get(b,"buttonImage");b.trigger=$(this._get(b,"buttonImageOnly")?$("<img/>").addClass(this._triggerClass).attr({src:g,alt:f,title:f}):$('<button type="button"></button>').addClass(this._triggerClass).html(g==""?f:$("<img/>").attr({src:g,alt:f,title:f}))),a[d?"before":"after"](b.trigger),b.trigger.click(function(){return $.datepicker._datepickerShowing&&$.datepicker._lastInput==a[0]?$.datepicker._hideDatepicker():$.datepicker._datepickerShowing&&$.datepicker._lastInput!=a[0]?($.datepicker._hideDatepicker(),$.datepicker._showDatepicker(a[0])):$.datepicker._showDatepicker(a[0]),!1})}},_autoSize:function(a){if(this._get(a,"autoSize")&&!a.inline){var b=new Date(2009,11,20),c=this._get(a,"dateFormat");if(c.match(/[DM]/)){var d=function(a){var b=0,c=0;for(var d=0;d<a.length;d++)a[d].length>b&&(b=a[d].length,c=d);return c};b.setMonth(d(this._get(a,c.match(/MM/)?"monthNames":"monthNamesShort"))),b.setDate(d(this._get(a,c.match(/DD/)?"dayNames":"dayNamesShort"))+20-b.getDay())}a.input.attr("size",this._formatDate(a,b).length)}},_inlineDatepicker:function(a,b){var c=$(a);if(c.hasClass(this.markerClassName))return;c.addClass(this.markerClassName).append(b.dpDiv).bind("setData.datepicker",function(a,c,d){b.settings[c]=d}).bind("getData.datepicker",function(a,c){return this._get(b,c)}),$.data(a,PROP_NAME,b),this._setDate(b,this._getDefaultDate(b),!0),this._updateDatepicker(b),this._updateAlternate(b),b.settings.disabled&&this._disableDatepicker(a),b.dpDiv.css("display","block")},_dialogDatepicker:function(a,b,c,d,e){var f=this._dialogInst;if(!f){this.uuid+=1;var g="dp"+this.uuid;this._dialogInput=$('<input type="text" id="'+g+'" style="position: absolute; top: -100px; width: 0px; z-index: -10;"/>'),this._dialogInput.keydown(this._doKeyDown),$("body").append(this._dialogInput),f=this._dialogInst=this._newInst(this._dialogInput,!1),f.settings={},$.data(this._dialogInput[0],PROP_NAME,f)}extendRemove(f.settings,d||{}),b=b&&b.constructor==Date?this._formatDate(f,b):b,this._dialogInput.val(b),this._pos=e?e.length?e:[e.pageX,e.pageY]:null;if(!this._pos){var h=document.documentElement.clientWidth,i=document.documentElement.clientHeight,j=document.documentElement.scrollLeft||document.body.scrollLeft,k=document.documentElement.scrollTop||document.body.scrollTop;this._pos=[h/2-100+j,i/2-150+k]}return this._dialogInput.css("left",this._pos[0]+20+"px").css("top",this._pos[1]+"px"),f.settings.onSelect=c,this._inDialog=!0,this.dpDiv.addClass(this._dialogClass),this._showDatepicker(this._dialogInput[0]),$.blockUI&&$.blockUI(this.dpDiv),$.data(this._dialogInput[0],PROP_NAME,f),this},_destroyDatepicker:function(a){var b=$(a),c=$.data(a,PROP_NAME);if(!b.hasClass(this.markerClassName))return;var d=a.nodeName.toLowerCase();$.removeData(a,PROP_NAME),d=="input"?(c.append.remove(),c.trigger.remove(),b.removeClass(this.markerClassName).unbind("focus",this._showDatepicker).unbind("keydown",this._doKeyDown).unbind("keypress",this._doKeyPress).unbind("keyup",this._doKeyUp)):(d=="div"||d=="span")&&b.removeClass(this.markerClassName).empty()},_enableDatepicker:function(a){var b=$(a),c=$.data(a,PROP_NAME);if(!b.hasClass(this.markerClassName))return;var d=a.nodeName.toLowerCase();if(d=="input")a.disabled=!1,c.trigger.filter("button").each(function(){this.disabled=!1}).end().filter("img").css({opacity:"1.0",cursor:""});else if(d=="div"||d=="span"){var e=b.children("."+this._inlineClass);e.children().removeClass("ui-state-disabled"),e.find("select.ui-datepicker-month, select.ui-datepicker-year").removeAttr("disabled")}this._disabledInputs=$.map(this._disabledInputs,function(b){return b==a?null:b})},_disableDatepicker:function(a){var b=$(a),c=$.data(a,PROP_NAME);if(!b.hasClass(this.markerClassName))return;var d=a.nodeName.toLowerCase();if(d=="input")a.disabled=!0,c.trigger.filter("button").each(function(){this.disabled=!0}).end().filter("img").css({opacity:"0.5",cursor:"default"});else if(d=="div"||d=="span"){var e=b.children("."+this._inlineClass);e.children().addClass("ui-state-disabled"),e.find("select.ui-datepicker-month, select.ui-datepicker-year").attr("disabled","disabled")}this._disabledInputs=$.map(this._disabledInputs,function(b){return b==a?null:b}),this._disabledInputs[this._disabledInputs.length]=a},_isDisabledDatepicker:function(a){if(!a)return!1;for(var b=0;b<this._disabledInputs.length;b++)if(this._disabledInputs[b]==a)return!0;return!1},_getInst:function(a){try{return $.data(a,PROP_NAME)}catch(b){throw"Missing instance data for this datepicker"}},_optionDatepicker:function(a,b,c){var d=this._getInst(a);if(arguments.length==2&&typeof b=="string")return b=="defaults"?$.extend({},$.datepicker._defaults):d?b=="all"?$.extend({},d.settings):this._get(d,b):null;var e=b||{};typeof b=="string"&&(e={},e[b]=c);if(d){this._curInst==d&&this._hideDatepicker();var f=this._getDateDatepicker(a,!0),g=this._getMinMaxDate(d,"min"),h=this._getMinMaxDate(d,"max");extendRemove(d.settings,e),g!==null&&e.dateFormat!==undefined&&e.minDate===undefined&&(d.settings.minDate=this._formatDate(d,g)),h!==null&&e.dateFormat!==undefined&&e.maxDate===undefined&&(d.settings.maxDate=this._formatDate(d,h)),this._attachments($(a),d),this._autoSize(d),this._setDate(d,f),this._updateAlternate(d),this._updateDatepicker(d)}},_changeDatepicker:function(a,b,c){this._optionDatepicker(a,b,c)},_refreshDatepicker:function(a){var b=this._getInst(a);b&&this._updateDatepicker(b)},_setDateDatepicker:function(a,b){var c=this._getInst(a);c&&(this._setDate(c,b),this._updateDatepicker(c),this._updateAlternate(c))},_getDateDatepicker:function(a,b){var c=this._getInst(a);return c&&!c.inline&&this._setDateFromField(c,b),c?this._getDate(c):null},_doKeyDown:function(a){var b=$.datepicker._getInst(a.target),c=!0,d=b.dpDiv.is(".ui-datepicker-rtl");b._keyEvent=!0;if($.datepicker._datepickerShowing)switch(a.keyCode){case 9:$.datepicker._hideDatepicker(),c=!1;break;case 13:var e=$("td."+$.datepicker._dayOverClass+":not(."+$.datepicker._currentClass+")",b.dpDiv);e[0]&&$.datepicker._selectDay(a.target,b.selectedMonth,b.selectedYear,e[0]);var f=$.datepicker._get(b,"onSelect");if(f){var g=$.datepicker._formatDate(b);f.apply(b.input?b.input[0]:null,[g,b])}else $.datepicker._hideDatepicker();return!1;case 27:$.datepicker._hideDatepicker();break;case 33:$.datepicker._adjustDate(a.target,a.ctrlKey?-$.datepicker._get(b,"stepBigMonths"):-$.datepicker._get(b,"stepMonths"),"M");break;case 34:$.datepicker._adjustDate(a.target,a.ctrlKey?+$.datepicker._get(b,"stepBigMonths"):+$.datepicker._get(b,"stepMonths"),"M");break;case 35:(a.ctrlKey||a.metaKey)&&$.datepicker._clearDate(a.target),c=a.ctrlKey||a.metaKey;break;case 36:(a.ctrlKey||a.metaKey)&&$.datepicker._gotoToday(a.target),c=a.ctrlKey||a.metaKey;break;case 37:(a.ctrlKey||a.metaKey)&&$.datepicker._adjustDate(a.target,d?1:-1,"D"),c=a.ctrlKey||a.metaKey,a.originalEvent.altKey&&$.datepicker._adjustDate(a.target,a.ctrlKey?-$.datepicker._get(b,"stepBigMonths"):-$.datepicker._get(b,"stepMonths"),"M");break;case 38:(a.ctrlKey||a.metaKey)&&$.datepicker._adjustDate(a.target,-7,"D"),c=a.ctrlKey||a.metaKey;break;case 39:(a.ctrlKey||a.metaKey)&&$.datepicker._adjustDate(a.target,d?-1:1,"D"),c=a.ctrlKey||a.metaKey,a.originalEvent.altKey&&$.datepicker._adjustDate(a.target,a.ctrlKey?+$.datepicker._get(b,"stepBigMonths"):+$.datepicker._get(b,"stepMonths"),"M");break;case 40:(a.ctrlKey||a.metaKey)&&$.datepicker._adjustDate(a.target,7,"D"),c=a.ctrlKey||a.metaKey;break;default:c=!1}else a.keyCode==36&&a.ctrlKey?$.datepicker._showDatepicker(this):c=!1;c&&(a.preventDefault(),a.stopPropagation())},_doKeyPress:function(a){var b=$.datepicker._getInst(a.target);if($.datepicker._get(b,"constrainInput")){var c=$.datepicker._possibleChars($.datepicker._get(b,"dateFormat")),d=String.fromCharCode(a.charCode==undefined?a.keyCode:a.charCode);return a.ctrlKey||a.metaKey||d<" "||!c||c.indexOf(d)>-1}},_doKeyUp:function(a){var b=$.datepicker._getInst(a.target);if(b.input.val()!=b.lastVal)try{var c=$.datepicker.parseDate($.datepicker._get(b,"dateFormat"),b.input?b.input.val():null,$.datepicker._getFormatConfig(b));c&&($.datepicker._setDateFromField(b),$.datepicker._updateAlternate(b),$.datepicker._updateDatepicker(b))}catch(d){$.datepicker.log(d)}return!0},_showDatepicker:function(a){a=a.target||a,a.nodeName.toLowerCase()!="input"&&(a=$("input",a.parentNode)[0]);if($.datepicker._isDisabledDatepicker(a)||$.datepicker._lastInput==a)return;var b=$.datepicker._getInst(a);$.datepicker._curInst&&$.datepicker._curInst!=b&&($.datepicker._curInst.dpDiv.stop(!0,!0),b&&$.datepicker._datepickerShowing&&$.datepicker._hideDatepicker($.datepicker._curInst.input[0]));var c=$.datepicker._get(b,"beforeShow"),d=c?c.apply(a,[a,b]):{};if(d===!1)return;extendRemove(b.settings,d),b.lastVal=null,$.datepicker._lastInput=a,$.datepicker._setDateFromField(b),$.datepicker._inDialog&&(a.value=""),$.datepicker._pos||($.datepicker._pos=$.datepicker._findPos(a),$.datepicker._pos[1]+=a.offsetHeight);var e=!1;$(a).parents().each(function(){return e|=$(this).css("position")=="fixed",!e}),e&&$.browser.opera&&($.datepicker._pos[0]-=document.documentElement.scrollLeft,$.datepicker._pos[1]-=document.documentElement.scrollTop);var f={left:$.datepicker._pos[0],top:$.datepicker._pos[1]};$.datepicker._pos=null,b.dpDiv.empty(),b.dpDiv.css({position:"absolute",display:"block",top:"-1000px"}),$.datepicker._updateDatepicker(b),f=$.datepicker._checkOffset(b,f,e),b.dpDiv.css({position:$.datepicker._inDialog&&$.blockUI?"static":e?"fixed":"absolute",display:"none",left:f.left+"px",top:f.top+"px"});if(!b.inline){var g=$.datepicker._get(b,"showAnim"),h=$.datepicker._get(b,"duration"),i=function(){var a=b.dpDiv.find("iframe.ui-datepicker-cover");if(!!a.length){var c=$.datepicker._getBorders(b.dpDiv);a.css({left:-c[0],top:-c[1],width:b.dpDiv.outerWidth(),height:b.dpDiv.outerHeight()})}};b.dpDiv.zIndex($(a).zIndex()+1),$.datepicker._datepickerShowing=!0,$.effects&&$.effects[g]?b.dpDiv.show(g,$.datepicker._get(b,"showOptions"),h,i):b.dpDiv[g||"show"](g?h:null,i),(!g||!h)&&i(),b.input.is(":visible")&&!b.input.is(":disabled")&&b.input.focus(),$.datepicker._curInst=b}},_updateDatepicker:function(a){var b=this;b.maxRows=4;var c=$.datepicker._getBorders(a.dpDiv);instActive=a,a.dpDiv.empty().append(this._generateHTML(a));var d=a.dpDiv.find("iframe.ui-datepicker-cover");!d.length||d.css({left:-c[0],top:-c[1],width:a.dpDiv.outerWidth(),height:a.dpDiv.outerHeight()}),a.dpDiv.find("."+this._dayOverClass+" a").mouseover();var e=this._getNumberOfMonths(a),f=e[1],g=17;a.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width(""),f>1&&a.dpDiv.addClass("ui-datepicker-multi-"+f).css("width",g*f+"em"),a.dpDiv[(e[0]!=1||e[1]!=1?"add":"remove")+"Class"]("ui-datepicker-multi"),a.dpDiv[(this._get(a,"isRTL")?"add":"remove")+"Class"]("ui-datepicker-rtl"),a==$.datepicker._curInst&&$.datepicker._datepickerShowing&&a.input&&a.input.is(":visible")&&!a.input.is(":disabled")&&a.input[0]!=document.activeElement&&a.input.focus();if(a.yearshtml){var h=a.yearshtml;setTimeout(function(){h===a.yearshtml&&a.yearshtml&&a.dpDiv.find("select.ui-datepicker-year:first").replaceWith(a.yearshtml),h=a.yearshtml=null},0)}},_getBorders:function(a){var b=function(a){return{thin:1,medium:2,thick:3}[a]||a};return[parseFloat(b(a.css("border-left-width"))),parseFloat(b(a.css("border-top-width")))]},_checkOffset:function(a,b,c){var d=a.dpDiv.outerWidth(),e=a.dpDiv.outerHeight(),f=a.input?a.input.outerWidth():0,g=a.input?a.input.outerHeight():0,h=document.documentElement.clientWidth+$(document).scrollLeft(),i=document.documentElement.clientHeight+$(document).scrollTop();return b.left-=this._get(a,"isRTL")?d-f:0,b.left-=c&&b.left==a.input.offset().left?$(document).scrollLeft():0,b.top-=c&&b.top==a.input.offset().top+g?$(document).scrollTop():0,b.left-=Math.min(b.left,b.left+d>h&&h>d?Math.abs(b.left+d-h):0),b.top-=Math.min(b.top,b.top+e>i&&i>e?Math.abs(e+g):0),b},_findPos:function(a){var b=this._getInst(a),c=this._get(b,"isRTL");while(a&&(a.type=="hidden"||a.nodeType!=1||$.expr.filters.hidden(a)))a=a[c?"previousSibling":"nextSibling"];var d=$(a).offset();return[d.left,d.top]},_hideDatepicker:function(a){var b=this._curInst;if(!b||a&&b!=$.data(a,PROP_NAME))return;if(this._datepickerShowing){var c=this._get(b,"showAnim"),d=this._get(b,"duration"),e=function(){$.datepicker._tidyDialog(b)};$.effects&&$.effects[c]?b.dpDiv.hide(c,$.datepicker._get(b,"showOptions"),d,e):b.dpDiv[c=="slideDown"?"slideUp":c=="fadeIn"?"fadeOut":"hide"](c?d:null,e),c||e(),this._datepickerShowing=!1;var f=this._get(b,"onClose");f&&f.apply(b.input?b.input[0]:null,[b.input?b.input.val():"",b]),this._lastInput=null,this._inDialog&&(this._dialogInput.css({position:"absolute",left:"0",top:"-100px"}),$.blockUI&&($.unblockUI(),$("body").append(this.dpDiv))),this._inDialog=!1}},_tidyDialog:function(a){a.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar")},_checkExternalClick:function(a){if(!$.datepicker._curInst)return;var b=$(a.target),c=$.datepicker._getInst(b[0]);(b[0].id!=$.datepicker._mainDivId&&b.parents("#"+$.datepicker._mainDivId).length==0&&!b.hasClass($.datepicker.markerClassName)&&!b.closest("."+$.datepicker._triggerClass).length&&$.datepicker._datepickerShowing&&(!$.datepicker._inDialog||!$.blockUI)||b.hasClass($.datepicker.markerClassName)&&$.datepicker._curInst!=c)&&$.datepicker._hideDatepicker()},_adjustDate:function(a,b,c){var d=$(a),e=this._getInst(d[0]);if(this._isDisabledDatepicker(d[0]))return;this._adjustInstDate(e,b+(c=="M"?this._get(e,"showCurrentAtPos"):0),c),this._updateDatepicker(e)},_gotoToday:function(a){var b=$(a),c=this._getInst(b[0]);if(this._get(c,"gotoCurrent")&&c.currentDay)c.selectedDay=c.currentDay,c.drawMonth=c.selectedMonth=c.currentMonth,c.drawYear=c.selectedYear=c.currentYear;else{var d=new Date;c.selectedDay=d.getDate(),c.drawMonth=c.selectedMonth=d.getMonth(),c.drawYear=c.selectedYear=d.getFullYear()}this._notifyChange(c),this._adjustDate(b)},_selectMonthYear:function(a,b,c){var d=$(a),e=this._getInst(d[0]);e["selected"+(c=="M"?"Month":"Year")]=e["draw"+(c=="M"?"Month":"Year")]=parseInt(b.options[b.selectedIndex].value,10),this._notifyChange(e),this._adjustDate(d)},_selectDay:function(a,b,c,d){var e=$(a);if($(d).hasClass(this._unselectableClass)||this._isDisabledDatepicker(e[0]))return;var f=this._getInst(e[0]);f.selectedDay=f.currentDay=$("a",d).html(),f.selectedMonth=f.currentMonth=b,f.selectedYear=f.currentYear=c,this._selectDate(a,this._formatDate(f,f.currentDay,f.currentMonth,f.currentYear))},_clearDate:function(a){var b=$(a),c=this._getInst(b[0]);this._selectDate(b,"")},_selectDate:function(a,b){var c=$(a),d=this._getInst(c[0]);b=b!=null?b:this._formatDate(d),d.input&&d.input.val(b),this._updateAlternate(d);var e=this._get(d,"onSelect");e?e.apply(d.input?d.input[0]:null,[b,d]):d.input&&d.input.trigger("change"),d.inline?this._updateDatepicker(d):(this._hideDatepicker(),this._lastInput=d.input[0],typeof d.input[0]!="object"&&d.input.focus(),this._lastInput=null)},_updateAlternate:function(a){var b=this._get(a,"altField");if(b){var c=this._get(a,"altFormat")||this._get(a,"dateFormat"),d=this._getDate(a),e=this.formatDate(c,d,this._getFormatConfig(a));$(b).each(function(){$(this).val(e)})}},noWeekends:function(a){var b=a.getDay();return[b>0&&b<6,""]},iso8601Week:function(a){var b=new Date(a.getTime());b.setDate(b.getDate()+4-(b.getDay()||7));var c=b.getTime();return b.setMonth(0),b.setDate(1),Math.floor(Math.round((c-b)/864e5)/7)+1},parseDate:function(a,b,c){if(a==null||b==null)throw"Invalid arguments";b=typeof b=="object"?b.toString():b+"";if(b=="")return null;var d=(c?c.shortYearCutoff:null)||this._defaults.shortYearCutoff;d=typeof d!="string"?d:(new Date).getFullYear()%100+parseInt(d,10);var e=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,f=(c?c.dayNames:null)||this._defaults.dayNames,g=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort,h=(c?c.monthNames:null)||this._defaults.monthNames,i=-1,j=-1,k=-1,l=-1,m=!1,n=function(b){var c=s+1<a.length&&a.charAt(s+1)==b;return c&&s++,c},o=function(a){var c=n(a),d=a=="@"?14:a=="!"?20:a=="y"&&c?4:a=="o"?3:2,e=new RegExp("^\\d{1,"+d+"}"),f=b.substring(r).match(e);if(!f)throw"Missing number at position "+r;return r+=f[0].length,parseInt(f[0],10)},p=function(a,c,d){var e=$.map(n(a)?d:c,function(a,b){return[[b,a]]}).sort(function(a,b){return-(a[1].length-b[1].length)}),f=-1;$.each(e,function(a,c){var d=c[1];if(b.substr(r,d.length).toLowerCase()==d.toLowerCase())return f=c[0],r+=d.length,!1});if(f!=-1)return f+1;throw"Unknown name at position "+r},q=function(){if(b.charAt(r)!=a.charAt(s))throw"Unexpected literal at position "+r;r++},r=0;for(var s=0;s<a.length;s++)if(m)a.charAt(s)=="'"&&!n("'")?m=!1:q();else switch(a.charAt(s)){case"d":k=o("d");break;case"D":p("D",e,f);break;case"o":l=o("o");break;case"m":j=o("m");break;case"M":j=p("M",g,h);break;case"y":i=o("y");break;case"@":var t=new Date(o("@"));i=t.getFullYear(),j=t.getMonth()+1,k=t.getDate();break;case"!":var t=new Date((o("!")-this._ticksTo1970)/1e4);i=t.getFullYear(),j=t.getMonth()+1,k=t.getDate();break;case"'":n("'")?q():m=!0;break;default:q()}if(r<b.length)throw"Extra/unparsed characters found in date: "+b.substring(r);i==-1?i=(new Date).getFullYear():i<100&&(i+=(new Date).getFullYear()-(new Date).getFullYear()%100+(i<=d?0:-100));if(l>-1){j=1,k=l;do{var u=this._getDaysInMonth(i,j-1);if(k<=u)break;j++,k-=u}while(!0)}var t=this._daylightSavingAdjust(new Date(i,j-1,k));if(t.getFullYear()!=i||t.getMonth()+1!=j||t.getDate()!=k)throw"Invalid date";return t},ATOM:"yy-mm-dd",COOKIE:"D, dd M yy",ISO_8601:"yy-mm-dd",RFC_822:"D, d M y",RFC_850:"DD, dd-M-y",RFC_1036:"D, d M y",RFC_1123:"D, d M yy",RFC_2822:"D, d M yy",RSS:"D, d M y",TICKS:"!",TIMESTAMP:"@",W3C:"yy-mm-dd",_ticksTo1970:(718685+Math.floor(492.5)-Math.floor(19.7)+Math.floor(4.925))*24*60*60*1e7,formatDate:function(a,b,c){if(!b)return"";var d=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,e=(c?c.dayNames:null)||this._defaults.dayNames,f=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort,g=(c?c.monthNames:null)||this._defaults.monthNames,h=function(b){var c=m+1<a.length&&a.charAt(m+1)==b;return c&&m++,c},i=function(a,b,c){var d=""+b;if(h(a))while(d.length<c)d="0"+d;return d},j=function(a,b,c,d){return h(a)?d[b]:c[b]},k="",l=!1;if(b)for(var m=0;m<a.length;m++)if(l)a.charAt(m)=="'"&&!h("'")?l=!1:k+=a.charAt(m);else switch(a.charAt(m)){case"d":k+=i("d",b.getDate(),2);break;case"D":k+=j("D",b.getDay(),d,e);break;case"o":k+=i("o",Math.round(((new Date(b.getFullYear(),b.getMonth(),b.getDate())).getTime()-(new Date(b.getFullYear(),0,0)).getTime())/864e5),3);break;case"m":k+=i("m",b.getMonth()+1,2);break;case"M":k+=j("M",b.getMonth(),f,g);break;case"y":k+=h("y")?b.getFullYear():(b.getYear()%100<10?"0":"")+b.getYear()%100;break;case"@":k+=b.getTime();break;case"!":k+=b.getTime()*1e4+this._ticksTo1970;break;case"'":h("'")?k+="'":l=!0;break;default:k+=a.charAt(m)}return k},_possibleChars:function(a){var b="",c=!1,d=function(b){var c=e+1<a.length&&a.charAt(e+1)==b;return c&&e++,c};for(var e=0;e<a.length;e++)if(c)a.charAt(e)=="'"&&!d("'")?c=!1:b+=a.charAt(e);else switch(a.charAt(e)){case"d":case"m":case"y":case"@":b+="0123456789";break;case"D":case"M":return null;case"'":d("'")?b+="'":c=!0;break;default:b+=a.charAt(e)}return b},_get:function(a,b){return a.settings[b]!==undefined?a.settings[b]:this._defaults[b]},_setDateFromField:function(a,b){if(a.input.val()==a.lastVal)return;var c=this._get(a,"dateFormat"),d=a.lastVal=a.input?a.input.val():null,e,f;e=f=this._getDefaultDate(a);var g=this._getFormatConfig(a);try{e=this.parseDate(c,d,g)||f}catch(h){this.log(h),d=b?"":d}a.selectedDay=e.getDate(),a.drawMonth=a.selectedMonth=e.getMonth(),a.drawYear=a.selectedYear=e.getFullYear(),a.currentDay=d?e.getDate():0,a.currentMonth=d?e.getMonth():0,a.currentYear=d?e.getFullYear():0,this._adjustInstDate(a)},_getDefaultDate:function(a){return this._restrictMinMax(a,this._determineDate(a,this._get(a,"defaultDate"),new Date))},_determineDate:function(a,b,c){var d=function(a){var b=new Date;return b.setDate(b.getDate()+a),b},e=function(b){try{return $.datepicker.parseDate($.datepicker._get(a,"dateFormat"),b,$.datepicker._getFormatConfig(a))}catch(c){}var d=(b.toLowerCase().match(/^c/)?$.datepicker._getDate(a):null)||new Date,e=d.getFullYear(),f=d.getMonth(),g=d.getDate(),h=/([+-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g,i=h.exec(b);while(i){switch(i[2]||"d"){case"d":case"D":g+=parseInt(i[1],10);break;case"w":case"W":g+=parseInt(i[1],10)*7;break;case"m":case"M":f+=parseInt(i[1],10),g=Math.min(g,$.datepicker._getDaysInMonth(e,f));break;case"y":case"Y":e+=parseInt(i[1],10),g=Math.min(g,$.datepicker._getDaysInMonth(e,f))}i=h.exec(b)}return new Date(e,f,g)},f=b==null||b===""?c:typeof b=="string"?e(b):typeof b=="number"?isNaN(b)?c:d(b):new Date(b.getTime());return f=f&&f.toString()=="Invalid Date"?c:f,f&&(f.setHours(0),f.setMinutes(0),f.setSeconds(0),f.setMilliseconds(0)),this._daylightSavingAdjust(f)},_daylightSavingAdjust:function(a){return a?(a.setHours(a.getHours()>12?a.getHours()+2:0),a):null},_setDate:function(a,b,c){var d=!b,e=a.selectedMonth,f=a.selectedYear,g=this._restrictMinMax(a,this._determineDate(a,b,new Date));a.selectedDay=a.currentDay=g.getDate(),a.drawMonth=a.selectedMonth=a.currentMonth=g.getMonth(),a.drawYear=a.selectedYear=a.currentYear=g.getFullYear(),(e!=a.selectedMonth||f!=a.selectedYear)&&!c&&this._notifyChange(a),this._adjustInstDate(a),a.input&&a.input.val(d?"":this._formatDate(a))},_getDate:function(a){var b=!a.currentYear||a.input&&a.input.val()==""?null:this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay));return b},_generateHTML:function(a){var b=new Date;b=this._daylightSavingAdjust(new Date(b.getFullYear(),b.getMonth(),b.getDate()));var c=this._get(a,"isRTL"),d=this._get(a,"showButtonPanel"),e=this._get(a,"hideIfNoPrevNext"),f=this._get(a,"navigationAsDateFormat"),g=this._getNumberOfMonths(a),h=this._get(a,"showCurrentAtPos"),i=this._get(a,"stepMonths"),j=g[0]!=1||g[1]!=1,k=this._daylightSavingAdjust(a.currentDay?new Date(a.currentYear,a.currentMonth,a.currentDay):new Date(9999,9,9)),l=this._getMinMaxDate(a,"min"),m=this._getMinMaxDate(a,"max"),n=a.drawMonth-h,o=a.drawYear;n<0&&(n+=12,o--);if(m){var p=this._daylightSavingAdjust(new Date(m.getFullYear(),m.getMonth()-g[0]*g[1]+1,m.getDate()));p=l&&p<l?l:p;while(this._daylightSavingAdjust(new Date(o,n,1))>p)n--,n<0&&(n=11,o--)}a.drawMonth=n,a.drawYear=o;var q=this._get(a,"prevText");q=f?this.formatDate(q,this._daylightSavingAdjust(new Date(o,n-i,1)),this._getFormatConfig(a)):q;var r=this._canAdjustMonth(a,-1,o,n)?'<a class="ui-datepicker-prev ui-corner-all" onclick="DP_jQuery_'+dpuuid+".datepicker._adjustDate('#"+a.id+"', -"+i+", 'M');\""+' title="'+q+'"><span class="ui-icon ui-icon-circle-triangle-'+(c?"e":"w")+'">'+q+"</span></a>":e?"":'<a class="ui-datepicker-prev ui-corner-all ui-state-disabled" title="'+q+'"><span class="ui-icon ui-icon-circle-triangle-'+(c?"e":"w")+'">'+q+"</span></a>",s=this._get(a,"nextText");s=f?this.formatDate(s,this._daylightSavingAdjust(new Date(o,n+i,1)),this._getFormatConfig(a)):s;var t=this._canAdjustMonth(a,1,o,n)?'<a class="ui-datepicker-next ui-corner-all" onclick="DP_jQuery_'+dpuuid+".datepicker._adjustDate('#"+a.id+"', +"+i+", 'M');\""+' title="'+s+'"><span class="ui-icon ui-icon-circle-triangle-'+(c?"w":"e")+'">'+s+"</span></a>":e?"":'<a class="ui-datepicker-next ui-corner-all ui-state-disabled" title="'+s+'"><span class="ui-icon ui-icon-circle-triangle-'+(c?"w":"e")+'">'+s+"</span></a>",u=this._get(a,"currentText"),v=this._get(a,"gotoCurrent")&&a.currentDay?k:b;u=f?this.formatDate(u,v,this._getFormatConfig(a)):u;var w=a.inline?"":'<button type="button" class="ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all" onclick="DP_jQuery_'+dpuuid+'.datepicker._hideDatepicker();">'+this._get(a,"closeText")+"</button>",x=d?'<div class="ui-datepicker-buttonpane ui-widget-content">'+(c?w:"")+(this._isInRange(a,v)?'<button type="button" class="ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all" onclick="DP_jQuery_'+dpuuid+".datepicker._gotoToday('#"+a.id+"');\""+">"+u+"</button>":"")+(c?"":w)+"</div>":"",y=parseInt(this._get(a,"firstDay"),10);y=isNaN(y)?0:y;var z=this._get(a,"showWeek"),A=this._get(a,"dayNames"),B=this._get(a,"dayNamesShort"),C=this._get(a,"dayNamesMin"),D=this._get(a,"monthNames"),E=this._get(a,"monthNamesShort"),F=this._get(a,"beforeShowDay"),G=this._get(a,"showOtherMonths"),H=this._get(a,"selectOtherMonths"),I=this._get(a,"calculateWeek")||this.iso8601Week,J=this._getDefaultDate(a),K="";for(var L=0;L<g[0];L++){var M="";this.maxRows=4;for(var N=0;N<g[1];N++){var O=this._daylightSavingAdjust(new Date(o,n,a.selectedDay)),P=" ui-corner-all",Q="";if(j){Q+='<div class="ui-datepicker-group';if(g[1]>1)switch(N){case 0:Q+=" ui-datepicker-group-first",P=" ui-corner-"+(c?"right":"left");break;case g[1]-1:Q+=" ui-datepicker-group-last",P=" ui-corner-"+(c?"left":"right");break;default:Q+=" ui-datepicker-group-middle",P=""}Q+='">'}Q+='<div class="ui-datepicker-header ui-widget-header ui-helper-clearfix'+P+'">'+(/all|left/.test(P)&&L==0?c?t:r:"")+(/all|right/.test(P)&&L==0?c?r:t:"")+this._generateMonthYearHeader(a,n,o,l,m,L>0||N>0,D,E)+'</div><table class="ui-datepicker-calendar"><thead>'+"<tr>";var R=z?'<th class="ui-datepicker-week-col">'+this._get(a,"weekHeader")+"</th>":"";for(var S=0;S<7;S++){var T=(S+y)%7;R+="<th"+((S+y+6)%7>=5?' class="ui-datepicker-week-end"':"")+">"+'<span title="'+A[T]+'">'+C[T]+"</span></th>"}Q+=R+"</tr></thead><tbody>";var U=this._getDaysInMonth(o,n);o==a.selectedYear&&n==a.selectedMonth&&(a.selectedDay=Math.min(a.selectedDay,U));var V=(this._getFirstDayOfMonth(o,n)-y+7)%7,W=Math.ceil((V+U)/7),X=j?this.maxRows>W?this.maxRows:W:W;this.maxRows=X;var Y=this._daylightSavingAdjust(new Date(o,n,1-V));for(var Z=0;Z<X;Z++){Q+="<tr>";var _=z?'<td class="ui-datepicker-week-col">'+this._get(a,"calculateWeek")(Y)+"</td>":"";for(var S=0;S<7;S++){var ba=F?F.apply(a.input?a.input[0]:null,[Y]):[!0,""],bb=Y.getMonth()!=n,bc=bb&&!H||!ba[0]||l&&Y<l||m&&Y>m;_+='<td class="'+((S+y+6)%7>=5?" ui-datepicker-week-end":"")+(bb?" ui-datepicker-other-month":"")+(Y.getTime()==O.getTime()&&n==a.selectedMonth&&a._keyEvent||J.getTime()==Y.getTime()&&J.getTime()==O.getTime()?" "+this._dayOverClass:"")+(bc?" "+this._unselectableClass+" ui-state-disabled":"")+(bb&&!G?"":" "+ba[1]+(Y.getTime()==k.getTime()?" "+this._currentClass:"")+(Y.getTime()==b.getTime()?" ui-datepicker-today":""))+'"'+((!bb||G)&&ba[2]?' title="'+ba[2]+'"':"")+(bc?"":' onclick="DP_jQuery_'+dpuuid+".datepicker._selectDay('#"+a.id+"',"+Y.getMonth()+","+Y.getFullYear()+', this);return false;"')+">"+(bb&&!G?" ":bc?'<span class="ui-state-default">'+Y.getDate()+"</span>":'<a class="ui-state-default'+(Y.getTime()==b.getTime()?" ui-state-highlight":"")+(Y.getTime()==k.getTime()?" ui-state-active":"")+(bb?" ui-priority-secondary":"")+'" href="#">'+Y.getDate()+"</a>")+"</td>",Y.setDate(Y.getDate()+1),Y=this._daylightSavingAdjust(Y)}Q+=_+"</tr>"}n++,n>11&&(n=0,o++),Q+="</tbody></table>"+(j?"</div>"+(g[0]>0&&N==g[1]-1?'<div class="ui-datepicker-row-break"></div>':""):""),M+=Q}K+=M}return K+=x+($.browser.msie&&parseInt($.browser.version,10)<7&&!a.inline?'<iframe src="javascript:false;" class="ui-datepicker-cover" frameborder="0"></iframe>':""),a._keyEvent=!1,K},_generateMonthYearHeader:function(a,b,c,d,e,f,g,h){var i=this._get(a,"changeMonth"),j=this._get(a,"changeYear"),k=this._get(a,"showMonthAfterYear"),l='<div class="ui-datepicker-title">',m="";if(f||!i)m+='<span class="ui-datepicker-month">'+g[b]+"</span>";else{var n=d&&d.getFullYear()==c,o=e&&e.getFullYear()==c;m+='<select class="ui-datepicker-month" onchange="DP_jQuery_'+dpuuid+".datepicker._selectMonthYear('#"+a.id+"', this, 'M');\" "+">";for(var p=0;p<12;p++)(!n||p>=d.getMonth())&&(!o||p<=e.getMonth())&&(m+='<option value="'+p+'"'+(p==b?' selected="selected"':"")+">"+h[p]+"</option>");m+="</select>"}k||(l+=m+(f||!i||!j?" ":""));if(!a.yearshtml){a.yearshtml="";if(f||!j)l+='<span class="ui-datepicker-year">'+c+"</span>";else{var q=this._get(a,"yearRange").split(":"),r=(new Date).getFullYear(),s=function(a){var b=a.match(/c[+-].*/)?c+parseInt(a.substring(1),10):a.match(/[+-].*/)?r+parseInt(a,10):parseInt(a,10);return isNaN(b)?r:b},t=s(q[0]),u=Math.max(t,s(q[1]||""));t=d?Math.max(t,d.getFullYear()):t,u=e?Math.min(u,e.getFullYear()):u,a.yearshtml+='<select class="ui-datepicker-year" onchange="DP_jQuery_'+dpuuid+".datepicker._selectMonthYear('#"+a.id+"', this, 'Y');\" "+">";for(;t<=u;t++)a.yearshtml+='<option value="'+t+'"'+(t==c?' selected="selected"':"")+">"+t+"</option>";a.yearshtml+="</select>",l+=a.yearshtml,a.yearshtml=null}}return l+=this._get(a,"yearSuffix"),k&&(l+=(f||!i||!j?" ":"")+m),l+="</div>",l},_adjustInstDate:function(a,b,c){var d=a.drawYear+(c=="Y"?b:0),e=a.drawMonth+(c=="M"?b:0),f=Math.min(a.selectedDay,this._getDaysInMonth(d,e))+(c=="D"?b:0),g=this._restrictMinMax(a,this._daylightSavingAdjust(new Date(d,e,f)));a.selectedDay=g.getDate(),a.drawMonth=a.selectedMonth=g.getMonth(),a.drawYear=a.selectedYear=g.getFullYear(),(c=="M"||c=="Y")&&this._notifyChange(a)},_restrictMinMax:function(a,b){var c=this._getMinMaxDate(a,"min"),d=this._getMinMaxDate(a,"max"),e=c&&b<c?c:b;return e=d&&e>d?d:e,e},_notifyChange:function(a){var b=this._get(a,"onChangeMonthYear");b&&b.apply(a.input?a.input[0]:null,[a.selectedYear,a.selectedMonth+1,a])},_getNumberOfMonths:function(a){var b=this._get(a,"numberOfMonths");return b==null?[1,1]:typeof b=="number"?[1,b]:b},_getMinMaxDate:function(a,b){return this._determineDate(a,this._get(a,b+"Date"),null)},_getDaysInMonth:function(a,b){return 32-this._daylightSavingAdjust(new Date(a,b,32)).getDate()},_getFirstDayOfMonth:function(a,b){return(new Date(a,b,1)).getDay()},_canAdjustMonth:function(a,b,c,d){var e=this._getNumberOfMonths(a),f=this._daylightSavingAdjust(new Date(c,d+(b<0?b:e[0]*e[1]),1));return b<0&&f.setDate(this._getDaysInMonth(f.getFullYear(),f.getMonth())),this._isInRange(a,f)},_isInRange:function(a,b){var c=this._getMinMaxDate(a,"min"),d=this._getMinMaxDate(a,"max");return(!c||b.getTime()>=c.getTime())&&(!d||b.getTime()<=d.getTime())},_getFormatConfig:function(a){var b=this._get(a,"shortYearCutoff");return b=typeof b!="string"?b:(new Date).getFullYear()%100+parseInt(b,10),{shortYearCutoff:b,dayNamesShort:this._get(a,"dayNamesShort"),dayNames:this._get(a,"dayNames"),monthNamesShort:this._get(a,"monthNamesShort"),monthNames:this._get(a,"monthNames")}},_formatDate:function(a,b,c,d){b||(a.currentDay=a.selectedDay,a.currentMonth=a.selectedMonth,a.currentYear=a.selectedYear);var e=b?typeof b=="object"?b:this._daylightSavingAdjust(new Date(d,c,b)):this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay));return this.formatDate(this._get(a,"dateFormat"),e,this._getFormatConfig(a))}}),$.fn.datepicker=function(a){if(!this.length)return this;$.datepicker.initialized||($(document).mousedown($.datepicker._checkExternalClick).find("body").append($.datepicker.dpDiv),$.datepicker.initialized=!0);var b=Array.prototype.slice.call(arguments,1);return typeof a!="string"||a!="isDisabled"&&a!="getDate"&&a!="widget"?a=="option"&&arguments.length==2&&typeof arguments[1]=="string"?$.datepicker["_"+a+"Datepicker"].apply($.datepicker,[this[0]].concat(b)):this.each(function(){typeof a=="string"?$.datepicker["_"+a+"Datepicker"].apply($.datepicker,[this].concat(b)):$.datepicker._attachDatepicker(this,a)}):$.datepicker["_"+a+"Datepicker"].apply($.datepicker,[this[0]].concat(b))},$.datepicker=new Datepicker,$.datepicker.initialized=!1,$.datepicker.uuid=(new Date).getTime(),$.datepicker.version="1.8.21",window["DP_jQuery_"+dpuuid]=$})(jQuery);;/*! jQuery UI - v1.8.21 - 2012-06-05
+* https://github.com/jquery/jquery-ui
+* Includes: jquery.ui.progressbar.js
+* Copyright (c) 2012 AUTHORS.txt; Licensed MIT, GPL */
+(function(a,b){a.widget("ui.progressbar",{options:{value:0,max:100},min:0,_create:function(){this.element.addClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").attr({role:"progressbar","aria-valuemin":this.min,"aria-valuemax":this.options.max,"aria-valuenow":this._value()}),this.valueDiv=a("<div class='ui-progressbar-value ui-widget-header ui-corner-left'></div>").appendTo(this.element),this.oldValue=this._value(),this._refreshValue()},destroy:function(){this.element.removeClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").removeAttr("role").removeAttr("aria-valuemin").removeAttr("aria-valuemax").removeAttr("aria-valuenow"),this.valueDiv.remove(),a.Widget.prototype.destroy.apply(this,arguments)},value:function(a){return a===b?this._value():(this._setOption("value",a),this)},_setOption:function(b,c){b==="value"&&(this.options.value=c,this._refreshValue(),this._value()===this.options.max&&this._trigger("complete")),a.Widget.prototype._setOption.apply(this,arguments)},_value:function(){var a=this.options.value;return typeof a!="number"&&(a=0),Math.min(this.options.max,Math.max(this.min,a))},_percentage:function(){return 100*this._value()/this.options.max},_refreshValue:function(){var a=this.value(),b=this._percentage();this.oldValue!==a&&(this.oldValue=a,this._trigger("change")),this.valueDiv.toggle(a>this.min).toggleClass("ui-corner-right",a===this.options.max).width(b.toFixed(0)+"%"),this.element.attr("aria-valuenow",a)}}),a.extend(a.ui.progressbar,{version:"1.8.21"})})(jQuery);;/*! jQuery UI - v1.8.21 - 2012-06-05
+* https://github.com/jquery/jquery-ui
+* Includes: jquery.effects.core.js
+* Copyright (c) 2012 AUTHORS.txt; Licensed MIT, GPL */
+jQuery.effects||function(a,b){function c(b){var c;return b&&b.constructor==Array&&b.length==3?b:(c=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(b))?[parseInt(c[1],10),parseInt(c[2],10),parseInt(c[3],10)]:(c=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(b))?[parseFloat(c[1])*2.55,parseFloat(c[2])*2.55,parseFloat(c[3])*2.55]:(c=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(b))?[parseInt(c[1],16),parseInt(c[2],16),parseInt(c[3],16)]:(c=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(b))?[parseInt(c[1]+c[1],16),parseInt(c[2]+c[2],16),parseInt(c[3]+c[3],16)]:(c=/rgba\(0, 0, 0, 0\)/.exec(b))?e.transparent:e[a.trim(b).toLowerCase()]}function d(b,d){var e;do{e=a.curCSS(b,d);if(e!=""&&e!="transparent"||a.nodeName(b,"body"))break;d="backgroundColor"}while(b=b.parentNode);return c(e)}function h(){var a=document.defaultView?document.defaultView.getComputedStyle(this,null):this.currentStyle,b={},c,d;if(a&&a.length&&a[0]&&a[a[0]]){var e=a.length;while(e--)c=a[e],typeof a[c]=="string"&&(d=c.replace(/\-(\w)/g,function(a,b){return b.toUpperCase()}),b[d]=a[c])}else for(c in a)typeof a[c]=="string"&&(b[c]=a[c]);return b}function i(b){var c,d;for(c in b)d=b[c],(d==null||a.isFunction(d)||c in g||/scrollbar/.test(c)||!/color/i.test(c)&&isNaN(parseFloat(d)))&&delete b[c];return b}function j(a,b){var c={_:0},d;for(d in b)a[d]!=b[d]&&(c[d]=b[d]);return c}function k(b,c,d,e){typeof b=="object"&&(e=c,d=null,c=b,b=c.effect),a.isFunction(c)&&(e=c,d=null,c={});if(typeof c=="number"||a.fx.speeds[c])e=d,d=c,c={};return a.isFunction(d)&&(e=d,d=null),c=c||{},d=d||c.duration,d=a.fx.off?0:typeof d=="number"?d:d in a.fx.speeds?a.fx.speeds[d]:a.fx.speeds._default,e=e||c.complete,[b,c,d,e]}function l(b){return!b||typeof b=="number"||a.fx.speeds[b]?!0:typeof b=="string"&&!a.effects[b]?!0:!1}a.effects={},a.each(["backgroundColor","borderBottomColor","borderLeftColor","borderRightColor","borderTopColor","borderColor","color","outlineColor"],function(b,e){a.fx.step[e]=function(a){a.colorInit||(a.start=d(a.elem,e),a.end=c(a.end),a.colorInit=!0),a.elem.style[e]="rgb("+Math.max(Math.min(parseInt(a.pos*(a.end[0]-a.start[0])+a.start[0],10),255),0)+","+Math.max(Math.min(parseInt(a.pos*(a.end[1]-a.start[1])+a.start[1],10),255),0)+","+Math.max(Math.min(parseInt(a.pos*(a.end[2]-a.start[2])+a.start[2],10),255),0)+")"}});var e={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0],transparent:[255,255,255]},f=["add","remove","toggle"],g={border:1,borderBottom:1,borderColor:1,borderLeft:1,borderRight:1,borderTop:1,borderWidth:1,margin:1,padding:1};a.effects.animateClass=function(b,c,d,e){return a.isFunction(d)&&(e=d,d=null),this.queue(function(){var g=a(this),k=g.attr("style")||" ",l=i(h.call(this)),m,n=g.attr("class")||"";a.each(f,function(a,c){b[c]&&g[c+"Class"](b[c])}),m=i(h.call(this)),g.attr("class",n),g.animate(j(l,m),{queue:!1,duration:c,easing:d,complete:function(){a.each(f,function(a,c){b[c]&&g[c+"Class"](b[c])}),typeof g.attr("style")=="object"?(g.attr("style").cssText="",g.attr("style").cssText=k):g.attr("style",k),e&&e.apply(this,arguments),a.dequeue(this)}})})},a.fn.extend({_addClass:a.fn.addClass,addClass:function(b,c,d,e){return c?a.effects.animateClass.apply(this,[{add:b},c,d,e]):this._addClass(b)},_removeClass:a.fn.removeClass,removeClass:function(b,c,d,e){return c?a.effects.animateClass.apply(this,[{remove:b},c,d,e]):this._removeClass(b)},_toggleClass:a.fn.toggleClass,toggleClass:function(c,d,e,f,g){return typeof d=="boolean"||d===b?e?a.effects.animateClass.apply(this,[d?{add:c}:{remove:c},e,f,g]):this._toggleClass(c,d):a.effects.animateClass.apply(this,[{toggle:c},d,e,f])},switchClass:function(b,c,d,e,f){return a.effects.animateClass.apply(this,[{add:c,remove:b},d,e,f])}}),a.extend(a.effects,{version:"1.8.21",save:function(a,b){for(var c=0;c<b.length;c++)b[c]!==null&&a.data("ec.storage."+b[c],a[0].style[b[c]])},restore:function(a,b){for(var c=0;c<b.length;c++)b[c]!==null&&a.css(b[c],a.data("ec.storage."+b[c]))},setMode:function(a,b){return b=="toggle"&&(b=a.is(":hidden")?"show":"hide"),b},getBaseline:function(a,b){var c,d;switch(a[0]){case"top":c=0;break;case"middle":c=.5;break;case"bottom":c=1;break;default:c=a[0]/b.height}switch(a[1]){case"left":d=0;break;case"center":d=.5;break;case"right":d=1;break;default:d=a[1]/b.width}return{x:d,y:c}},createWrapper:function(b){if(b.parent().is(".ui-effects-wrapper"))return b.parent();var c={width:b.outerWidth(!0),height:b.outerHeight(!0),"float":b.css("float")},d=a("<div></div>").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent",border:"none",margin:0,padding:0}),e=document.activeElement;try{e.id}catch(f){e=document.body}return b.wrap(d),(b[0]===e||a.contains(b[0],e))&&a(e).focus(),d=b.parent(),b.css("position")=="static"?(d.css({position:"relative"}),b.css({position:"relative"})):(a.extend(c,{position:b.css("position"),zIndex:b.css("z-index")}),a.each(["top","left","bottom","right"],function(a,d){c[d]=b.css(d),isNaN(parseInt(c[d],10))&&(c[d]="auto")}),b.css({position:"relative",top:0,left:0,right:"auto",bottom:"auto"})),d.css(c).show()},removeWrapper:function(b){var c,d=document.activeElement;return b.parent().is(".ui-effects-wrapper")?(c=b.parent().replaceWith(b),(b[0]===d||a.contains(b[0],d))&&a(d).focus(),c):b},setTransition:function(b,c,d,e){return e=e||{},a.each(c,function(a,c){var f=b.cssUnit(c);f[0]>0&&(e[c]=f[0]*d+f[1])}),e}}),a.fn.extend({effect:function(b,c,d,e){var f=k.apply(this,arguments),g={options:f[1],duration:f[2],callback:f[3]},h=g.options.mode,i=a.effects[b];return a.fx.off||!i?h?this[h](g.duration,g.callback):this.each(function(){g.callback&&g.callback.call(this)}):i.call(this,g)},_show:a.fn.show,show:function(a){if(l(a))return this._show.apply(this,arguments);var b=k.apply(this,arguments);return b[1].mode="show",this.effect.apply(this,b)},_hide:a.fn.hide,hide:function(a){if(l(a))return this._hide.apply(this,arguments);var b=k.apply(this,arguments);return b[1].mode="hide",this.effect.apply(this,b)},__toggle:a.fn.toggle,toggle:function(b){if(l(b)||typeof b=="boolean"||a.isFunction(b))return this.__toggle.apply(this,arguments);var c=k.apply(this,arguments);return c[1].mode="toggle",this.effect.apply(this,c)},cssUnit:function(b){var c=this.css(b),d=[];return a.each(["em","px","%","pt"],function(a,b){c.indexOf(b)>0&&(d=[parseFloat(c),b])}),d}}),a.easing.jswing=a.easing.swing,a.extend(a.easing,{def:"easeOutQuad",swing:function(b,c,d,e,f){return a.easing[a.easing.def](b,c,d,e,f)},easeInQuad:function(a,b,c,d,e){return d*(b/=e)*b+c},easeOutQuad:function(a,b,c,d,e){return-d*(b/=e)*(b-2)+c},easeInOutQuad:function(a,b,c,d,e){return(b/=e/2)<1?d/2*b*b+c:-d/2*(--b*(b-2)-1)+c},easeInCubic:function(a,b,c,d,e){return d*(b/=e)*b*b+c},easeOutCubic:function(a,b,c,d,e){return d*((b=b/e-1)*b*b+1)+c},easeInOutCubic:function(a,b,c,d,e){return(b/=e/2)<1?d/2*b*b*b+c:d/2*((b-=2)*b*b+2)+c},easeInQuart:function(a,b,c,d,e){return d*(b/=e)*b*b*b+c},easeOutQuart:function(a,b,c,d,e){return-d*((b=b/e-1)*b*b*b-1)+c},easeInOutQuart:function(a,b,c,d,e){return(b/=e/2)<1?d/2*b*b*b*b+c:-d/2*((b-=2)*b*b*b-2)+c},easeInQuint:function(a,b,c,d,e){return d*(b/=e)*b*b*b*b+c},easeOutQuint:function(a,b,c,d,e){return d*((b=b/e-1)*b*b*b*b+1)+c},easeInOutQuint:function(a,b,c,d,e){return(b/=e/2)<1?d/2*b*b*b*b*b+c:d/2*((b-=2)*b*b*b*b+2)+c},easeInSine:function(a,b,c,d,e){return-d*Math.cos(b/e*(Math.PI/2))+d+c},easeOutSine:function(a,b,c,d,e){return d*Math.sin(b/e*(Math.PI/2))+c},easeInOutSine:function(a,b,c,d,e){return-d/2*(Math.cos(Math.PI*b/e)-1)+c},easeInExpo:function(a,b,c,d,e){return b==0?c:d*Math.pow(2,10*(b/e-1))+c},easeOutExpo:function(a,b,c,d,e){return b==e?c+d:d*(-Math.pow(2,-10*b/e)+1)+c},easeInOutExpo:function(a,b,c,d,e){return b==0?c:b==e?c+d:(b/=e/2)<1?d/2*Math.pow(2,10*(b-1))+c:d/2*(-Math.pow(2,-10*--b)+2)+c},easeInCirc:function(a,b,c,d,e){return-d*(Math.sqrt(1-(b/=e)*b)-1)+c},easeOutCirc:function(a,b,c,d,e){return d*Math.sqrt(1-(b=b/e-1)*b)+c},easeInOutCirc:function(a,b,c,d,e){return(b/=e/2)<1?-d/2*(Math.sqrt(1-b*b)-1)+c:d/2*(Math.sqrt(1-(b-=2)*b)+1)+c},easeInElastic:function(a,b,c,d,e){var f=1.70158,g=0,h=d;if(b==0)return c;if((b/=e)==1)return c+d;g||(g=e*.3);if(h<Math.abs(d)){h=d;var f=g/4}else var f=g/(2*Math.PI)*Math.asin(d/h);return-(h*Math.pow(2,10*(b-=1))*Math.sin((b*e-f)*2*Math.PI/g))+c},easeOutElastic:function(a,b,c,d,e){var f=1.70158,g=0,h=d;if(b==0)return c;if((b/=e)==1)return c+d;g||(g=e*.3);if(h<Math.abs(d)){h=d;var f=g/4}else var f=g/(2*Math.PI)*Math.asin(d/h);return h*Math.pow(2,-10*b)*Math.sin((b*e-f)*2*Math.PI/g)+d+c},easeInOutElastic:function(a,b,c,d,e){var f=1.70158,g=0,h=d;if(b==0)return c;if((b/=e/2)==2)return c+d;g||(g=e*.3*1.5);if(h<Math.abs(d)){h=d;var f=g/4}else var f=g/(2*Math.PI)*Math.asin(d/h);return b<1?-0.5*h*Math.pow(2,10*(b-=1))*Math.sin((b*e-f)*2*Math.PI/g)+c:h*Math.pow(2,-10*(b-=1))*Math.sin((b*e-f)*2*Math.PI/g)*.5+d+c},easeInBack:function(a,c,d,e,f,g){return g==b&&(g=1.70158),e*(c/=f)*c*((g+1)*c-g)+d},easeOutBack:function(a,c,d,e,f,g){return g==b&&(g=1.70158),e*((c=c/f-1)*c*((g+1)*c+g)+1)+d},easeInOutBack:function(a,c,d,e,f,g){return g==b&&(g=1.70158),(c/=f/2)<1?e/2*c*c*(((g*=1.525)+1)*c-g)+d:e/2*((c-=2)*c*(((g*=1.525)+1)*c+g)+2)+d},easeInBounce:function(b,c,d,e,f){return e-a.easing.easeOutBounce(b,f-c,0,e,f)+d},easeOutBounce:function(a,b,c,d,e){return(b/=e)<1/2.75?d*7.5625*b*b+c:b<2/2.75?d*(7.5625*(b-=1.5/2.75)*b+.75)+c:b<2.5/2.75?d*(7.5625*(b-=2.25/2.75)*b+.9375)+c:d*(7.5625*(b-=2.625/2.75)*b+.984375)+c},easeInOutBounce:function(b,c,d,e,f){return c<f/2?a.easing.easeInBounce(b,c*2,0,e,f)*.5+d:a.easing.easeOutBounce(b,c*2-f,0,e,f)*.5+e*.5+d}})}(jQuery);;/*! jQuery UI - v1.8.21 - 2012-06-05
+* https://github.com/jquery/jquery-ui
+* Includes: jquery.effects.blind.js
+* Copyright (c) 2012 AUTHORS.txt; Licensed MIT, GPL */
+(function(a,b){a.effects.blind=function(b){return this.queue(function(){var c=a(this),d=["position","top","bottom","left","right"],e=a.effects.setMode(c,b.options.mode||"hide"),f=b.options.direction||"vertical";a.effects.save(c,d),c.show();var g=a.effects.createWrapper(c).css({overflow:"hidden"}),h=f=="vertical"?"height":"width",i=f=="vertical"?g.height():g.width();e=="show"&&g.css(h,0);var j={};j[h]=e=="show"?i:0,g.animate(j,b.duration,b.options.easing,function(){e=="hide"&&c.hide(),a.effects.restore(c,d),a.effects.removeWrapper(c),b.callback&&b.callback.apply(c[0],arguments),c.dequeue()})})}})(jQuery);;/*! jQuery UI - v1.8.21 - 2012-06-05
+* https://github.com/jquery/jquery-ui
+* Includes: jquery.effects.bounce.js
+* Copyright (c) 2012 AUTHORS.txt; Licensed MIT, GPL */
+(function(a,b){a.effects.bounce=function(b){return this.queue(function(){var c=a(this),d=["position","top","bottom","left","right"],e=a.effects.setMode(c,b.options.mode||"effect"),f=b.options.direction||"up",g=b.options.distance||20,h=b.options.times||5,i=b.duration||250;/show|hide/.test(e)&&d.push("opacity"),a.effects.save(c,d),c.show(),a.effects.createWrapper(c);var j=f=="up"||f=="down"?"top":"left",k=f=="up"||f=="left"?"pos":"neg",g=b.options.distance||(j=="top"?c.outerHeight({margin:!0})/3:c.outerWidth({margin:!0})/3);e=="show"&&c.css("opacity",0).css(j,k=="pos"?-g:g),e=="hide"&&(g=g/(h*2)),e!="hide"&&h--;if(e=="show"){var l={opacity:1};l[j]=(k=="pos"?"+=":"-=")+g,c.animate(l,i/2,b.options.easing),g=g/2,h--}for(var m=0;m<h;m++){var n={},p={};n[j]=(k=="pos"?"-=":"+=")+g,p[j]=(k=="pos"?"+=":"-=")+g,c.animate(n,i/2,b.options.easing).animate(p,i/2,b.options.easing),g=e=="hide"?g*2:g/2}if(e=="hide"){var l={opacity:0};l[j]=(k=="pos"?"-=":"+=")+g,c.animate(l,i/2,b.options.easing,function(){c.hide(),a.effects.restore(c,d),a.effects.removeWrapper(c),b.callback&&b.callback.apply(this,arguments)})}else{var n={},p={};n[j]=(k=="pos"?"-=":"+=")+g,p[j]=(k=="pos"?"+=":"-=")+g,c.animate(n,i/2,b.options.easing).animate(p,i/2,b.options.easing,function(){a.effects.restore(c,d),a.effects.removeWrapper(c),b.callback&&b.callback.apply(this,arguments)})}c.queue("fx",function(){c.dequeue()}),c.dequeue()})}})(jQuery);;/*! jQuery UI - v1.8.21 - 2012-06-05
+* https://github.com/jquery/jquery-ui
+* Includes: jquery.effects.clip.js
+* Copyright (c) 2012 AUTHORS.txt; Licensed MIT, GPL */
+(function(a,b){a.effects.clip=function(b){return this.queue(function(){var c=a(this),d=["position","top","bottom","left","right","height","width"],e=a.effects.setMode(c,b.options.mode||"hide"),f=b.options.direction||"vertical";a.effects.save(c,d),c.show();var g=a.effects.createWrapper(c).css({overflow:"hidden"}),h=c[0].tagName=="IMG"?g:c,i={size:f=="vertical"?"height":"width",position:f=="vertical"?"top":"left"},j=f=="vertical"?h.height():h.width();e=="show"&&(h.css(i.size,0),h.css(i.position,j/2));var k={};k[i.size]=e=="show"?j:0,k[i.position]=e=="show"?0:j/2,h.animate(k,{queue:!1,duration:b.duration,easing:b.options.easing,complete:function(){e=="hide"&&c.hide(),a.effects.restore(c,d),a.effects.removeWrapper(c),b.callback&&b.callback.apply(c[0],arguments),c.dequeue()}})})}})(jQuery);;/*! jQuery UI - v1.8.21 - 2012-06-05
+* https://github.com/jquery/jquery-ui
+* Includes: jquery.effects.drop.js
+* Copyright (c) 2012 AUTHORS.txt; Licensed MIT, GPL */
+(function(a,b){a.effects.drop=function(b){return this.queue(function(){var c=a(this),d=["position","top","bottom","left","right","opacity"],e=a.effects.setMode(c,b.options.mode||"hide"),f=b.options.direction||"left";a.effects.save(c,d),c.show(),a.effects.createWrapper(c);var g=f=="up"||f=="down"?"top":"left",h=f=="up"||f=="left"?"pos":"neg",i=b.options.distance||(g=="top"?c.outerHeight({margin:!0})/2:c.outerWidth({margin:!0})/2);e=="show"&&c.css("opacity",0).css(g,h=="pos"?-i:i);var j={opacity:e=="show"?1:0};j[g]=(e=="show"?h=="pos"?"+=":"-=":h=="pos"?"-=":"+=")+i,c.animate(j,{queue:!1,duration:b.duration,easing:b.options.easing,complete:function(){e=="hide"&&c.hide(),a.effects.restore(c,d),a.effects.removeWrapper(c),b.callback&&b.callback.apply(this,arguments),c.dequeue()}})})}})(jQuery);;/*! jQuery UI - v1.8.21 - 2012-06-05
+* https://github.com/jquery/jquery-ui
+* Includes: jquery.effects.explode.js
+* Copyright (c) 2012 AUTHORS.txt; Licensed MIT, GPL */
+(function(a,b){a.effects.explode=function(b){return this.queue(function(){var c=b.options.pieces?Math.round(Math.sqrt(b.options.pieces)):3,d=b.options.pieces?Math.round(Math.sqrt(b.options.pieces)):3;b.options.mode=b.options.mode=="toggle"?a(this).is(":visible")?"hide":"show":b.options.mode;var e=a(this).show().css("visibility","hidden"),f=e.offset();f.top-=parseInt(e.css("marginTop"),10)||0,f.left-=parseInt(e.css("marginLeft"),10)||0;var g=e.outerWidth(!0),h=e.outerHeight(!0);for(var i=0;i<c;i++)for(var j=0;j<d;j++)e.clone().appendTo("body").wrap("<div></div>").css({position:"absolute",visibility:"visible",left:-j*(g/d),top:-i*(h/c)}).parent().addClass("ui-effects-explode").css({position:"absolute",overflow:"hidden",width:g/d,height:h/c,left:f.left+j*(g/d)+(b.options.mode=="show"?(j-Math.floor(d/2))*(g/d):0),top:f.top+i*(h/c)+(b.options.mode=="show"?(i-Math.floor(c/2))*(h/c):0),opacity:b.options.mode=="show"?0:1}).animate({left:f.left+j*(g/d)+(b.options.mode=="show"?0:(j-Math.floor(d/2))*(g/d)),top:f.top+i*(h/c)+(b.options.mode=="show"?0:(i-Math.floor(c/2))*(h/c)),opacity:b.options.mode=="show"?1:0},b.duration||500);setTimeout(function(){b.options.mode=="show"?e.css({visibility:"visible"}):e.css({visibility:"visible"}).hide(),b.callback&&b.callback.apply(e[0]),e.dequeue(),a("div.ui-effects-explode").remove()},b.duration||500)})}})(jQuery);;/*! jQuery UI - v1.8.21 - 2012-06-05
+* https://github.com/jquery/jquery-ui
+* Includes: jquery.effects.fade.js
+* Copyright (c) 2012 AUTHORS.txt; Licensed MIT, GPL */
+(function(a,b){a.effects.fade=function(b){return this.queue(function(){var c=a(this),d=a.effects.setMode(c,b.options.mode||"hide");c.animate({opacity:d},{queue:!1,duration:b.duration,easing:b.options.easing,complete:function(){b.callback&&b.callback.apply(this,arguments),c.dequeue()}})})}})(jQuery);;/*! jQuery UI - v1.8.21 - 2012-06-05
+* https://github.com/jquery/jquery-ui
+* Includes: jquery.effects.fold.js
+* Copyright (c) 2012 AUTHORS.txt; Licensed MIT, GPL */
+(function(a,b){a.effects.fold=function(b){return this.queue(function(){var c=a(this),d=["position","top","bottom","left","right"],e=a.effects.setMode(c,b.options.mode||"hide"),f=b.options.size||15,g=!!b.options.horizFirst,h=b.duration?b.duration/2:a.fx.speeds._default/2;a.effects.save(c,d),c.show();var i=a.effects.createWrapper(c).css({overflow:"hidden"}),j=e=="show"!=g,k=j?["width","height"]:["height","width"],l=j?[i.width(),i.height()]:[i.height(),i.width()],m=/([0-9]+)%/.exec(f);m&&(f=parseInt(m[1],10)/100*l[e=="hide"?0:1]),e=="show"&&i.css(g?{height:0,width:f}:{height:f,width:0});var n={},p={};n[k[0]]=e=="show"?l[0]:f,p[k[1]]=e=="show"?l[1]:0,i.animate(n,h,b.options.easing).animate(p,h,b.options.easing,function(){e=="hide"&&c.hide(),a.effects.restore(c,d),a.effects.removeWrapper(c),b.callback&&b.callback.apply(c[0],arguments),c.dequeue()})})}})(jQuery);;/*! jQuery UI - v1.8.21 - 2012-06-05
+* https://github.com/jquery/jquery-ui
+* Includes: jquery.effects.highlight.js
+* Copyright (c) 2012 AUTHORS.txt; Licensed MIT, GPL */
+(function(a,b){a.effects.highlight=function(b){return this.queue(function(){var c=a(this),d=["backgroundImage","backgroundColor","opacity"],e=a.effects.setMode(c,b.options.mode||"show"),f={backgroundColor:c.css("backgroundColor")};e=="hide"&&(f.opacity=0),a.effects.save(c,d),c.show().css({backgroundImage:"none",backgroundColor:b.options.color||"#ffff99"}).animate(f,{queue:!1,duration:b.duration,easing:b.options.easing,complete:function(){e=="hide"&&c.hide(),a.effects.restore(c,d),e=="show"&&!a.support.opacity&&this.style.removeAttribute("filter"),b.callback&&b.callback.apply(this,arguments),c.dequeue()}})})}})(jQuery);;/*! jQuery UI - v1.8.21 - 2012-06-05
+* https://github.com/jquery/jquery-ui
+* Includes: jquery.effects.pulsate.js
+* Copyright (c) 2012 AUTHORS.txt; Licensed MIT, GPL */
+(function(a,b){a.effects.pulsate=function(b){return this.queue(function(){var c=a(this),d=a.effects.setMode(c,b.options.mode||"show"),e=(b.options.times||5)*2-1,f=b.duration?b.duration/2:a.fx.speeds._default/2,g=c.is(":visible"),h=0;g||(c.css("opacity",0).show(),h=1),(d=="hide"&&g||d=="show"&&!g)&&e--;for(var i=0;i<e;i++)c.animate({opacity:h},f,b.options.easing),h=(h+1)%2;c.animate({opacity:h},f,b.options.easing,function(){h==0&&c.hide(),b.callback&&b.callback.apply(this,arguments)}),c.queue("fx",function(){c.dequeue()}).dequeue()})}})(jQuery);;/*! jQuery UI - v1.8.21 - 2012-06-05
+* https://github.com/jquery/jquery-ui
+* Includes: jquery.effects.scale.js
+* Copyright (c) 2012 AUTHORS.txt; Licensed MIT, GPL */
+(function(a,b){a.effects.puff=function(b){return this.queue(function(){var c=a(this),d=a.effects.setMode(c,b.options.mode||"hide"),e=parseInt(b.options.percent,10)||150,f=e/100,g={height:c.height(),width:c.width()};a.extend(b.options,{fade:!0,mode:d,percent:d=="hide"?e:100,from:d=="hide"?g:{height:g.height*f,width:g.width*f}}),c.effect("scale",b.options,b.duration,b.callback),c.dequeue()})},a.effects.scale=function(b){return this.queue(function(){var c=a(this),d=a.extend(!0,{},b.options),e=a.effects.setMode(c,b.options.mode||"effect"),f=parseInt(b.options.percent,10)||(parseInt(b.options.percent,10)==0?0:e=="hide"?0:100),g=b.options.direction||"both",h=b.options.origin;e!="effect"&&(d.origin=h||["middle","center"],d.restore=!0);var i={height:c.height(),width:c.width()};c.from=b.options.from||(e=="show"?{height:0,width:0}:i);var j={y:g!="horizontal"?f/100:1,x:g!="vertical"?f/100:1};c.to={height:i.height*j.y,width:i.width*j.x},b.options.fade&&(e=="show"&&(c.from.opacity=0,c.to.opacity=1),e=="hide"&&(c.from.opacity=1,c.to.opacity=0)),d.from=c.from,d.to=c.to,d.mode=e,c.effect("size",d,b.duration,b.callback),c.dequeue()})},a.effects.size=function(b){return this.queue(function(){var c=a(this),d=["position","top","bottom","left","right","width","height","overflow","opacity"],e=["position","top","bottom","left","right","overflow","opacity"],f=["width","height","overflow"],g=["fontSize"],h=["borderTopWidth","borderBottomWidth","paddingTop","paddingBottom"],i=["borderLeftWidth","borderRightWidth","paddingLeft","paddingRight"],j=a.effects.setMode(c,b.options.mode||"effect"),k=b.options.restore||!1,l=b.options.scale||"both",m=b.options.origin,n={height:c.height(),width:c.width()};c.from=b.options.from||n,c.to=b.options.to||n;if(m){var p=a.effects.getBaseline(m,n);c.from.top=(n.height-c.from.height)*p.y,c.from.left=(n.width-c.from.width)*p.x,c.to.top=(n.height-c.to.height)*p.y,c.to.left=(n.width-c.to.width)*p.x}var q={from:{y:c.from.height/n.height,x:c.from.width/n.width},to:{y:c.to.height/n.height,x:c.to.width/n.width}};if(l=="box"||l=="both")q.from.y!=q.to.y&&(d=d.concat(h),c.from=a.effects.setTransition(c,h,q.from.y,c.from),c.to=a.effects.setTransition(c,h,q.to.y,c.to)),q.from.x!=q.to.x&&(d=d.concat(i),c.from=a.effects.setTransition(c,i,q.from.x,c.from),c.to=a.effects.setTransition(c,i,q.to.x,c.to));(l=="content"||l=="both")&&q.from.y!=q.to.y&&(d=d.concat(g),c.from=a.effects.setTransition(c,g,q.from.y,c.from),c.to=a.effects.setTransition(c,g,q.to.y,c.to)),a.effects.save(c,k?d:e),c.show(),a.effects.createWrapper(c),c.css("overflow","hidden").css(c.from);if(l=="content"||l=="both")h=h.concat(["marginTop","marginBottom"]).concat(g),i=i.concat(["marginLeft","marginRight"]),f=d.concat(h).concat(i),c.find("*[width]").each(function(){var c=a(this);k&&a.effects.save(c,f);var d={height:c.height(),width:c.width()};c.from={height:d.height*q.from.y,width:d.width*q.from.x},c.to={height:d.height*q.to.y,width:d.width*q.to.x},q.from.y!=q.to.y&&(c.from=a.effects.setTransition(c,h,q.from.y,c.from),c.to=a.effects.setTransition(c,h,q.to.y,c.to)),q.from.x!=q.to.x&&(c.from=a.effects.setTransition(c,i,q.from.x,c.from),c.to=a.effects.setTransition(c,i,q.to.x,c.to)),c.css(c.from),c.animate(c.to,b.duration,b.options.easing,function(){k&&a.effects.restore(c,f)})});c.animate(c.to,{queue:!1,duration:b.duration,easing:b.options.easing,complete:function(){c.to.opacity===0&&c.css("opacity",c.from.opacity),j=="hide"&&c.hide(),a.effects.restore(c,k?d:e),a.effects.removeWrapper(c),b.callback&&b.callback.apply(this,arguments),c.dequeue()}})})}})(jQuery);;/*! jQuery UI - v1.8.21 - 2012-06-05
+* https://github.com/jquery/jquery-ui
+* Includes: jquery.effects.shake.js
+* Copyright (c) 2012 AUTHORS.txt; Licensed MIT, GPL */
+(function(a,b){a.effects.shake=function(b){return this.queue(function(){var c=a(this),d=["position","top","bottom","left","right"],e=a.effects.setMode(c,b.options.mode||"effect"),f=b.options.direction||"left",g=b.options.distance||20,h=b.options.times||3,i=b.duration||b.options.duration||140;a.effects.save(c,d),c.show(),a.effects.createWrapper(c);var j=f=="up"||f=="down"?"top":"left",k=f=="up"||f=="left"?"pos":"neg",l={},m={},n={};l[j]=(k=="pos"?"-=":"+=")+g,m[j]=(k=="pos"?"+=":"-=")+g*2,n[j]=(k=="pos"?"-=":"+=")+g*2,c.animate(l,i,b.options.easing);for(var p=1;p<h;p++)c.animate(m,i,b.options.easing).animate(n,i,b.options.easing);c.animate(m,i,b.options.easing).animate(l,i/2,b.options.easing,function(){a.effects.restore(c,d),a.effects.removeWrapper(c),b.callback&&b.callback.apply(this,arguments)}),c.queue("fx",function(){c.dequeue()}),c.dequeue()})}})(jQuery);;/*! jQuery UI - v1.8.21 - 2012-06-05
+* https://github.com/jquery/jquery-ui
+* Includes: jquery.effects.slide.js
+* Copyright (c) 2012 AUTHORS.txt; Licensed MIT, GPL */
+(function(a,b){a.effects.slide=function(b){return this.queue(function(){var c=a(this),d=["position","top","bottom","left","right"],e=a.effects.setMode(c,b.options.mode||"show"),f=b.options.direction||"left";a.effects.save(c,d),c.show(),a.effects.createWrapper(c).css({overflow:"hidden"});var g=f=="up"||f=="down"?"top":"left",h=f=="up"||f=="left"?"pos":"neg",i=b.options.distance||(g=="top"?c.outerHeight({margin:!0}):c.outerWidth({margin:!0}));e=="show"&&c.css(g,h=="pos"?isNaN(i)?"-"+i:-i:i);var j={};j[g]=(e=="show"?h=="pos"?"+=":"-=":h=="pos"?"-=":"+=")+i,c.animate(j,{queue:!1,duration:b.duration,easing:b.options.easing,complete:function(){e=="hide"&&c.hide(),a.effects.restore(c,d),a.effects.removeWrapper(c),b.callback&&b.callback.apply(this,arguments),c.dequeue()}})})}})(jQuery);;/*! jQuery UI - v1.8.21 - 2012-06-05
+* https://github.com/jquery/jquery-ui
+* Includes: jquery.effects.transfer.js
+* Copyright (c) 2012 AUTHORS.txt; Licensed MIT, GPL */
+(function(a,b){a.effects.transfer=function(b){return this.queue(function(){var c=a(this),d=a(b.options.to),e=d.offset(),f={top:e.top,left:e.left,height:d.innerHeight(),width:d.innerWidth()},g=c.offset(),h=a('<div class="ui-effects-transfer"></div>').appendTo(document.body).addClass(b.options.className).css({top:g.top,left:g.left,height:c.innerHeight(),width:c.innerWidth(),position:"absolute"}).animate(f,b.duration,b.options.easing,function(){h.remove(),b.callback&&b.callback.apply(c[0],arguments),c.dequeue()})})}})(jQuery);;
\ 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
// HTML template for the dropdowns
dropdownTemplate = [
- '<div class="dk_container" id="dk_container_{{ id }}" tabindex="{{ tabindex }}">',
+ '<div class="dk_container" id="dk_container_{{ id }}" tabindex="{{ tabindex }}" style="display:inline-block;">',
'<a class="dk_toggle">',
'<span class="dk_label">{{ label }}</span>',
'</a>',
--- /dev/null
+/*
+
+Uniform v1.7.5
+Copyright © 2009 Josh Pyles / Pixelmatrix Design LLC
+http://pixelmatrixdesign.com
+
+Requires jQuery 1.4 or newer
+
+Much thanks to Thomas Reynolds and Buck Wilson for their help and advice on this
+
+Disabling text selection is made possible by Mathias Bynens <http://mathiasbynens.be/>
+and his noSelect plugin. <http://github.com/mathiasbynens/noSelect-jQuery-Plugin>
+
+Also, thanks to David Kaneda and Eugene Bond for their contributions to the plugin
+
+License:
+MIT License - http://www.opensource.org/licenses/mit-license.php
+
+Enjoy!
+
+*/
+
+(function($) {
+ $.uniform = {
+ options: {
+ selectClass: 'selector',
+ radioClass: 'radio',
+ checkboxClass: 'checker',
+ fileClass: 'uploader',
+ filenameClass: 'filename',
+ fileBtnClass: 'action',
+ fileDefaultText: 'No file selected',
+ fileBtnText: 'Choose File',
+ checkedClass: 'checked',
+ focusClass: 'focus',
+ disabledClass: 'disabled',
+ buttonClass: 'button',
+ activeClass: 'active',
+ hoverClass: 'hover',
+ useID: true,
+ idPrefix: 'uniform',
+ resetSelector: false,
+ autoHide: true
+ },
+ elements: []
+ };
+
+ if($.browser.msie && $.browser.version < 7){
+ $.support.selectOpacity = false;
+ }else{
+ $.support.selectOpacity = true;
+ }
+
+ $.fn.uniform = function(options) {
+
+ options = $.extend($.uniform.options, options);
+
+ var el = this;
+ //code for specifying a reset button
+ if(options.resetSelector != false){
+ $(options.resetSelector).mouseup(function(){
+ function resetThis(){
+ $.uniform.update(el);
+ }
+ setTimeout(resetThis, 10);
+ });
+ }
+
+ function doInput(elem){
+ $el = $(elem);
+ $el.addClass($el.attr("type"));
+ storeElement(elem);
+ }
+
+ function doTextarea(elem){
+ $(elem).addClass("uniform");
+ storeElement(elem);
+ }
+
+ function doButton(elem){
+ var $el = $(elem);
+
+ var divTag = $("<div>"),
+ spanTag = $("<span>");
+
+ divTag.addClass(options.buttonClass);
+
+ if(options.useID && $el.attr("id") != "") divTag.attr("id", options.idPrefix+"-"+$el.attr("id"));
+
+ var btnText;
+
+ if($el.is("a") || $el.is("button")){
+ btnText = $el.text();
+ }else if($el.is(":submit") || $el.is(":reset") || $el.is("input[type=button]")){
+ btnText = $el.attr("value");
+ }
+
+ btnText = btnText == "" ? $el.is(":reset") ? "Reset" : "Submit" : btnText;
+
+ spanTag.html(btnText);
+
+ $el.css("opacity", 0);
+ $el.wrap(divTag);
+ $el.wrap(spanTag);
+
+ //redefine variables
+ divTag = $el.closest("div");
+ spanTag = $el.closest("span");
+
+ if($el.is(":disabled")) divTag.addClass(options.disabledClass);
+
+ divTag.bind({
+ "mouseenter.uniform": function(){
+ divTag.addClass(options.hoverClass);
+ },
+ "mouseleave.uniform": function(){
+ divTag.removeClass(options.hoverClass);
+ divTag.removeClass(options.activeClass);
+ },
+ "mousedown.uniform touchbegin.uniform": function(){
+ divTag.addClass(options.activeClass);
+ },
+ "mouseup.uniform touchend.uniform": function(){
+ divTag.removeClass(options.activeClass);
+ },
+ "click.uniform touchend.uniform": function(e){
+ if($(e.target).is("span") || $(e.target).is("div")){
+ if(elem[0].dispatchEvent){
+ var ev = document.createEvent('MouseEvents');
+ ev.initEvent( 'click', true, true );
+ elem[0].dispatchEvent(ev);
+ }else{
+ elem[0].click();
+ }
+ }
+ }
+ });
+
+ elem.bind({
+ "focus.uniform": function(){
+ divTag.addClass(options.focusClass);
+ },
+ "blur.uniform": function(){
+ divTag.removeClass(options.focusClass);
+ }
+ });
+
+ $.uniform.noSelect(divTag);
+ storeElement(elem);
+
+ }
+
+ function doSelect(elem){
+ var $el = $(elem);
+
+ var divTag = $('<div />'),
+ spanTag = $('<span />');
+
+ if(!$el.css("display") == "none" && options.autoHide){
+ divTag.hide();
+ }
+
+ divTag.addClass(options.selectClass);
+
+ if(options.useID && elem.attr("id") != ""){
+ divTag.attr("id", options.idPrefix+"-"+elem.attr("id"));
+ }
+
+ var selected = elem.find(":selected:first");
+ if(selected.length == 0){
+ selected = elem.find("option:first");
+ }
+ spanTag.html(selected.html());
+
+ elem.css('opacity', 0);
+ elem.wrap(divTag);
+ elem.before(spanTag);
+
+ //redefine variables
+ divTag = elem.parent("div");
+ spanTag = elem.siblings("span");
+
+ elem.bind({
+ "change.uniform": function() {
+ spanTag.text(elem.find(":selected").html());
+ divTag.removeClass(options.activeClass);
+ },
+ "focus.uniform": function() {
+ divTag.addClass(options.focusClass);
+ },
+ "blur.uniform": function() {
+ divTag.removeClass(options.focusClass);
+ divTag.removeClass(options.activeClass);
+ },
+ "mousedown.uniform touchbegin.uniform": function() {
+ divTag.addClass(options.activeClass);
+ },
+ "mouseup.uniform touchend.uniform": function() {
+ divTag.removeClass(options.activeClass);
+ },
+ "click.uniform touchend.uniform": function(){
+ divTag.removeClass(options.activeClass);
+ },
+ "mouseenter.uniform": function() {
+ divTag.addClass(options.hoverClass);
+ },
+ "mouseleave.uniform": function() {
+ divTag.removeClass(options.hoverClass);
+ divTag.removeClass(options.activeClass);
+ },
+ "keyup.uniform": function(){
+ spanTag.text(elem.find(":selected").html());
+ }
+ });
+
+ //handle disabled state
+ if($(elem).attr("disabled")){
+ //box is checked by default, check our box
+ divTag.addClass(options.disabledClass);
+ }
+ $.uniform.noSelect(spanTag);
+
+ storeElement(elem);
+
+ }
+
+ function doCheckbox(elem){
+ var $el = $(elem);
+
+ var divTag = $('<div />'),
+ spanTag = $('<span />');
+
+ if(!$el.css("display") == "none" && options.autoHide){
+ divTag.hide();
+ }
+
+ divTag.addClass(options.checkboxClass);
+
+ //assign the id of the element
+ if(options.useID && elem.attr("id") != ""){
+ divTag.attr("id", options.idPrefix+"-"+elem.attr("id"));
+ }
+
+ //wrap with the proper elements
+ $(elem).wrap(divTag);
+ $(elem).wrap(spanTag);
+
+ //redefine variables
+ spanTag = elem.parent();
+ divTag = spanTag.parent();
+
+ //hide normal input and add focus classes
+ $(elem)
+ .css("opacity", 0)
+ .bind({
+ "focus.uniform": function(){
+ divTag.addClass(options.focusClass);
+ },
+ "blur.uniform": function(){
+ divTag.removeClass(options.focusClass);
+ },
+ "click.uniform touchend.uniform": function(){
+ if(!$(elem).attr("checked")){
+ //box was just unchecked, uncheck span
+ spanTag.removeClass(options.checkedClass);
+ }else{
+ //box was just checked, check span.
+ spanTag.addClass(options.checkedClass);
+ }
+ },
+ "mousedown.uniform touchbegin.uniform": function() {
+ divTag.addClass(options.activeClass);
+ },
+ "mouseup.uniform touchend.uniform": function() {
+ divTag.removeClass(options.activeClass);
+ },
+ "mouseenter.uniform": function() {
+ divTag.addClass(options.hoverClass);
+ },
+ "mouseleave.uniform": function() {
+ divTag.removeClass(options.hoverClass);
+ divTag.removeClass(options.activeClass);
+ }
+ });
+
+ //handle defaults
+ if($(elem).attr("checked")){
+ //box is checked by default, check our box
+ spanTag.addClass(options.checkedClass);
+ }
+
+ //handle disabled state
+ if($(elem).attr("disabled")){
+ //box is checked by default, check our box
+ divTag.addClass(options.disabledClass);
+ }
+
+ storeElement(elem);
+ }
+
+ function doRadio(elem){
+ var $el = $(elem);
+
+ var divTag = $('<div />'),
+ spanTag = $('<span />');
+
+ if(!$el.css("display") == "none" && options.autoHide){
+ divTag.hide();
+ }
+
+ divTag.addClass(options.radioClass);
+
+ if(options.useID && elem.attr("id") != ""){
+ divTag.attr("id", options.idPrefix+"-"+elem.attr("id"));
+ }
+
+ //wrap with the proper elements
+ $(elem).wrap(divTag);
+ $(elem).wrap(spanTag);
+
+ //redefine variables
+ spanTag = elem.parent();
+ divTag = spanTag.parent();
+
+ //hide normal input and add focus classes
+ $(elem)
+ .css("opacity", 0)
+ .bind({
+ "focus.uniform": function(){
+ divTag.addClass(options.focusClass);
+ },
+ "blur.uniform": function(){
+ divTag.removeClass(options.focusClass);
+ },
+ "click.uniform touchend.uniform": function(){
+ if(!$(elem).attr("checked")){
+ //box was just unchecked, uncheck span
+ spanTag.removeClass(options.checkedClass);
+ }else{
+ //box was just checked, check span
+ var classes = options.radioClass.split(" ")[0];
+ $("." + classes + " span." + options.checkedClass + ":has([name='" + $(elem).attr('name') + "'])").removeClass(options.checkedClass);
+ spanTag.addClass(options.checkedClass);
+ }
+ },
+ "mousedown.uniform touchend.uniform": function() {
+ if(!$(elem).is(":disabled")){
+ divTag.addClass(options.activeClass);
+ }
+ },
+ "mouseup.uniform touchbegin.uniform": function() {
+ divTag.removeClass(options.activeClass);
+ },
+ "mouseenter.uniform touchend.uniform": function() {
+ divTag.addClass(options.hoverClass);
+ },
+ "mouseleave.uniform": function() {
+ divTag.removeClass(options.hoverClass);
+ divTag.removeClass(options.activeClass);
+ }
+ });
+
+ //handle defaults
+ if($(elem).attr("checked")){
+ //box is checked by default, check span
+ spanTag.addClass(options.checkedClass);
+ }
+ //handle disabled state
+ if($(elem).attr("disabled")){
+ //box is checked by default, check our box
+ divTag.addClass(options.disabledClass);
+ }
+
+ storeElement(elem);
+
+ }
+
+ function doFile(elem){
+ //sanitize input
+ var $el = $(elem);
+
+ var divTag = $('<div />'),
+ filenameTag = $('<span>'+options.fileDefaultText+'</span>'),
+ btnTag = $('<span>'+options.fileBtnText+'</span>');
+
+ if(!$el.css("display") == "none" && options.autoHide){
+ divTag.hide();
+ }
+
+ divTag.addClass(options.fileClass);
+ filenameTag.addClass(options.filenameClass);
+ btnTag.addClass(options.fileBtnClass);
+
+ if(options.useID && $el.attr("id") != ""){
+ divTag.attr("id", options.idPrefix+"-"+$el.attr("id"));
+ }
+
+ //wrap with the proper elements
+ $el.wrap(divTag);
+ $el.after(btnTag);
+ $el.after(filenameTag);
+
+ //redefine variables
+ divTag = $el.closest("div");
+ filenameTag = $el.siblings("."+options.filenameClass);
+ btnTag = $el.siblings("."+options.fileBtnClass);
+
+ //set the size
+ if(!$el.attr("size")){
+ var divWidth = divTag.width();
+ //$el.css("width", divWidth);
+ $el.attr("size", divWidth/10);
+ }
+
+ //actions
+ var setFilename = function()
+ {
+ var filename = $el.val();
+ if (filename === '')
+ {
+ filename = options.fileDefaultText;
+ }
+ else
+ {
+ filename = filename.split(/[\/\\]+/);
+ filename = filename[(filename.length-1)];
+ }
+ filenameTag.text(filename);
+ };
+
+ // Account for input saved across refreshes
+ setFilename();
+
+ $el
+ .css("opacity", 0)
+ .bind({
+ "focus.uniform": function(){
+ divTag.addClass(options.focusClass);
+ },
+ "blur.uniform": function(){
+ divTag.removeClass(options.focusClass);
+ },
+ "mousedown.uniform": function() {
+ if(!$(elem).is(":disabled")){
+ divTag.addClass(options.activeClass);
+ }
+ },
+ "mouseup.uniform": function() {
+ divTag.removeClass(options.activeClass);
+ },
+ "mouseenter.uniform": function() {
+ divTag.addClass(options.hoverClass);
+ },
+ "mouseleave.uniform": function() {
+ divTag.removeClass(options.hoverClass);
+ divTag.removeClass(options.activeClass);
+ }
+ });
+
+ // IE7 doesn't fire onChange until blur or second fire.
+ if ($.browser.msie){
+ // IE considers browser chrome blocking I/O, so it
+ // suspends tiemouts until after the file has been selected.
+ $el.bind('click.uniform.ie7', function() {
+ setTimeout(setFilename, 0);
+ });
+ }else{
+ // All other browsers behave properly
+ $el.bind('change.uniform', setFilename);
+ }
+
+ //handle defaults
+ if($el.attr("disabled")){
+ //box is checked by default, check our box
+ divTag.addClass(options.disabledClass);
+ }
+
+ $.uniform.noSelect(filenameTag);
+ $.uniform.noSelect(btnTag);
+
+ storeElement(elem);
+
+ }
+
+ $.uniform.restore = function(elem){
+ if(elem == undefined){
+ elem = $($.uniform.elements);
+ }
+
+ $(elem).each(function(){
+ if($(this).is(":checkbox")){
+ //unwrap from span and div
+ $(this).unwrap().unwrap();
+ }else if($(this).is("select")){
+ //remove sibling span
+ $(this).siblings("span").remove();
+ //unwrap parent div
+ $(this).unwrap();
+ }else if($(this).is(":radio")){
+ //unwrap from span and div
+ $(this).unwrap().unwrap();
+ }else if($(this).is(":file")){
+ //remove sibling spans
+ $(this).siblings("span").remove();
+ //unwrap parent div
+ $(this).unwrap();
+ }else if($(this).is("button, :submit, :reset, a, input[type='button']")){
+ //unwrap from span and div
+ $(this).unwrap().unwrap();
+ }
+
+ //unbind events
+ $(this).unbind(".uniform");
+
+ //reset inline style
+ $(this).css("opacity", "1");
+
+ //remove item from list of uniformed elements
+ var index = $.inArray($(elem), $.uniform.elements);
+ $.uniform.elements.splice(index, 1);
+ });
+ };
+
+ function storeElement(elem){
+ //store this element in our global array
+ elem = $(elem).get();
+ if(elem.length > 1){
+ $.each(elem, function(i, val){
+ $.uniform.elements.push(val);
+ });
+ }else{
+ $.uniform.elements.push(elem);
+ }
+ }
+
+ //noSelect v1.0
+ $.uniform.noSelect = function(elem) {
+ function f() {
+ return false;
+ };
+ $(elem).each(function() {
+ this.onselectstart = this.ondragstart = f; // Webkit & IE
+ $(this)
+ .mousedown(f) // Webkit & Opera
+ .css({ MozUserSelect: 'none' }); // Firefox
+ });
+ };
+
+ $.uniform.update = function(elem){
+ if(elem == undefined){
+ elem = $($.uniform.elements);
+ }
+ //sanitize input
+ elem = $(elem);
+
+ elem.each(function(){
+ //do to each item in the selector
+ //function to reset all classes
+ var $e = $(this);
+
+ if($e.is("select")){
+ //element is a select
+ var spanTag = $e.siblings("span");
+ var divTag = $e.parent("div");
+
+ divTag.removeClass(options.hoverClass+" "+options.focusClass+" "+options.activeClass);
+
+ //reset current selected text
+ spanTag.html($e.find(":selected").html());
+
+ if($e.is(":disabled")){
+ divTag.addClass(options.disabledClass);
+ }else{
+ divTag.removeClass(options.disabledClass);
+ }
+
+ }else if($e.is(":checkbox")){
+ //element is a checkbox
+ var spanTag = $e.closest("span");
+ var divTag = $e.closest("div");
+
+ divTag.removeClass(options.hoverClass+" "+options.focusClass+" "+options.activeClass);
+ spanTag.removeClass(options.checkedClass);
+
+ if($e.is(":checked")){
+ spanTag.addClass(options.checkedClass);
+ }
+ if($e.is(":disabled")){
+ divTag.addClass(options.disabledClass);
+ }else{
+ divTag.removeClass(options.disabledClass);
+ }
+
+ }else if($e.is(":radio")){
+ //element is a radio
+ var spanTag = $e.closest("span");
+ var divTag = $e.closest("div");
+
+ divTag.removeClass(options.hoverClass+" "+options.focusClass+" "+options.activeClass);
+ spanTag.removeClass(options.checkedClass);
+
+ if($e.is(":checked")){
+ spanTag.addClass(options.checkedClass);
+ }
+
+ if($e.is(":disabled")){
+ divTag.addClass(options.disabledClass);
+ }else{
+ divTag.removeClass(options.disabledClass);
+ }
+ }else if($e.is(":file")){
+ var divTag = $e.parent("div");
+ var filenameTag = $e.siblings(options.filenameClass);
+ btnTag = $e.siblings(options.fileBtnClass);
+
+ divTag.removeClass(options.hoverClass+" "+options.focusClass+" "+options.activeClass);
+
+ filenameTag.text($e.val());
+
+ if($e.is(":disabled")){
+ divTag.addClass(options.disabledClass);
+ }else{
+ divTag.removeClass(options.disabledClass);
+ }
+ }else if($e.is(":submit") || $e.is(":reset") || $e.is("button") || $e.is("a") || elem.is("input[type=button]")){
+ var divTag = $e.closest("div");
+ divTag.removeClass(options.hoverClass+" "+options.focusClass+" "+options.activeClass);
+
+ if($e.is(":disabled")){
+ divTag.addClass(options.disabledClass);
+ }else{
+ divTag.removeClass(options.disabledClass);
+ }
+
+ }
+
+ });
+ };
+
+ return this.each(function() {
+ if($.support.selectOpacity){
+ var elem = $(this);
+
+ if(elem.is("select")){
+ //element is a select
+ if(elem.attr("multiple") != true){
+ //element is not a multi-select
+ if(elem.attr("size") == undefined || elem.attr("size") <= 1){
+ doSelect(elem);
+ }
+ }
+ }else if(elem.is(":checkbox")){
+ //element is a checkbox
+ doCheckbox(elem);
+ }else if(elem.is(":radio")){
+ //element is a radio
+ doRadio(elem);
+ }else if(elem.is(":file")){
+ //element is a file upload
+ doFile(elem);
+ }else if(elem.is(":text, :password, input[type='email']")){
+ doInput(elem);
+ }else if(elem.is("textarea")){
+ doTextarea(elem);
+ }else if(elem.is("a") || elem.is(":submit") || elem.is(":reset") || elem.is("button") || elem.is("input[type=button]")){
+ doButton(elem);
+ }
+
+ }
+ });
+ };
+})(jQuery);
\ No newline at end of file
--- /dev/null
+function group_form_show_resources(el){
+
+ el.addClass('selected');
+ var id = el.attr('id');
+ $('.quotas-form .group').each(function() {
+ if( $(this).hasClass(id) ) {
+ $(this).appendTo('.visible');
+ $(this).show('slow');
+ $(this).find('input')[0].focus()
+ }
+ });
+ if ($('.quotas-form .with-info .with-errors input[type="text"]')){
+ $(this)[0].focus();
+ }
+
+}
+
+
+function bytesToSize2(bytes) {
+ var sizes = [ 'n/a', 'bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
+ var i = +Math.floor(Math.log(bytes) / Math.log(1024));
+ return (bytes / Math.pow(1024, i)).toFixed( 0 ) + sizes[ isNaN( bytes ) ? 0 : i+1 ];
+}
+
+$(document).ready(function() {
+
+
+
+ // ugly fix to transfer data easily
+ $('.with-info input[name^="is_selected_"]').each(function() {
+ $(this).parents('.form-row').hide();
+ });
+
+ $('.quotas-form ul li a').click(function(e){
+
+ // check the hidden input field
+ $(this).siblings('input[type="hidden"]').attr('checked','checked');
+
+ // get the hidden input field without the proxy
+ // and check the python form field
+ hidden_name = $(this).siblings('input[type="hidden"]').attr('name').replace("proxy_","");
+ $("input[name='"+hidden_name+"']").attr('checked','checked');
+
+ // prevent extra actions if it is checked
+ if ( $(this).hasClass('selected')){
+ e.preventDefault();
+ } else {
+
+ // show the relevant fieldsets
+ group_form_show_resources($(this));
+ }
+ });
+
+
+
+
+
+
+ $('.quotas-form .group .delete').click(function(e){
+
+ e.preventDefault();
+
+ // clear form fields
+ $(this).siblings('fieldset').find('input').val('');
+
+ // clear errors
+ $(this).siblings('fieldset').find('.form-row').removeClass('with-errors');
+
+ // hide relevant fieldset
+ $(this).parents('.group').hide('slow', function() {
+ $(this).appendTo('.not-visible');
+ });
+
+ group_class = $(this).parents('.group').attr('class').replace('group ', '');
+
+ // unselect group icon
+ $('.quotas-form ul li a').each(function() {
+ if($(this).attr('id')==group_class) {
+ $(this).removeClass('selected');
+ $(this).siblings('input[type="hidden"]').removeAttr('checked');
+
+ // get the hidden input field without the proxy
+ // and check the python form field
+ hidden_name = $(this).siblings('input[type="hidden"]').attr('name').replace("proxy_","");
+ $("input[name='"+hidden_name+"']").removeAttr('checked');
+
+ }
+ });
+
+ // clear hidden fields
+ $(this).siblings('fieldset').find('input[type="text"]').each(function() {
+ hidden_name = $(this).attr('name').replace("_proxy","");
+ hidden_input = $("input[name='"+hidden_name+"']");
+ hidden_input.val('');
+ });
+
+
+ });
+
+ // if you fill _proxy fields do stuff
+ $('.quotas-form .quota input[type="text"]').change(function () {
+
+
+
+ // get value from input
+ var value = $(this).val();
+
+ //get input name without _proxy
+ hidden_name = $(this).attr('name').replace("_proxy","");
+ var hidden_input = $("input[name='"+hidden_name+"']");
+
+ if (value) {
+ // actions for humanize fields
+ 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"
+ }
+
+ }
+ }
+
+
+
+ }
+
+
+
+
+ if ( flag == '1' ){
+ $(this).parents('.form-row').addClass('with-errors');
+ $(this).parents('.form-row').find('.error-msg').html(msg);
+ bytes = value;
+ $(this).focus();
+
+
+ } else {
+ $(this).parents('.form-row').removeClass('with-errors');
+ }
+
+ hidden_input.val(bytes);
+
+
+ }
+
+ // 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 integer');
+ $(this).parents('.form-row').addClass('with-errors');
+
+ } else {
+ if ( value == '0'){
+ $(this).parents('.form-row').find('.error-msg').html('Ensure this value is greater than or equal to 1');
+ $(this).parents('.form-row').addClass('with-errors');
+ }else {
+ $(this).parents('.form-row').removeClass('with-errors');
+ }
+
+
+ }
+ hidden_input.val(value);
+
+ }
+
+ } else {
+ hidden_input.removeAttr('value');
+ }
+ $('#icons span.info').removeClass('error-msg');
+
+ });
+
+
+ // if hidden checkboxes are checked, the right group is selected
+ $('.with-info input[name^="is_selected_"]').each(function() {
+ if ($(this).attr('checked')){
+
+ // get hidden input name
+ hidden_name = $(this).attr('name');
+ $("input[name='proxy_"+hidden_name+"']").attr('checked','checked');
+
+ // pretend to check the ul li a
+ // show the relevant fieldsets
+ var mock_a = $("input[name='proxy_"+hidden_name+"']").siblings('a');
+ group_form_show_resources(mock_a);
+
+ }
+ });
+
+
+
+ // if input_uplimit fields are filled,
+ // fill the _uplimit_proxy ones
+
+ $('.with-info input[name$="_uplimit"]').each(function() {
+ if ($(this).val()){
+
+ // get value from input
+ var value = $(this).val();
+
+
+ // get hidden input name
+ hidden_name = $(this).attr('name');
+ var field = $("input[name='"+hidden_name+"_proxy']");
+
+
+ if ( (field.hasClass('dehumanize')) && !($(this).parents('.form-row').hasClass('with-errors'))) {
+ // for dehumanize fields transform bytes to KB, MB, etc
+ // unless there is an error
+ field.val(bytesToSize2(value))
+ } else {
+ // else just return the value
+ field.val(value);
+ }
+
+ var group_class = field.parents('div[class^="group"]').attr('class').replace('group ', '');
+
+
+
+
+ // select group icon
+ $('.quotas-form ul li a').each(function() {
+
+ if($(this).attr('id') == group_class) {
+ $(this).addClass('selected');
+ $(this).siblings('input[type="hidden"]').attr('checked', 'checked');
+
+ // get the hidden input field without the proxy
+ // and check the python form field
+ hidden_name = $(this).siblings('input[type="hidden"]').attr('name').replace("proxy_","");
+ $("input[name='"+hidden_name+"']").attr('checked', 'checked');
+
+ group_form_show_resources($(this));
+
+ }
+ });
+
+
+
+ // if the field has class error, transfer error to the proxy fields
+ if ( $(this).parents('.form-row').hasClass('with-errors') ) {
+ field.parents('.form-row').addClass('with-errors');
+ }
+
+
+ }
+ });
+
+});
\ No newline at end of file
"""
installed_apps = [
- {'before': 'django.contrib.admin',
- 'insert': 'astakos.im',},
- 'django.contrib.auth',
- 'django.contrib.contenttypes',
- 'django.contrib.sessions',
- 'django.contrib.messages'
+ {'before': 'django.contrib.admin',
+ 'insert': 'astakos.im', },
+ 'django.contrib.auth',
+ 'django.contrib.contenttypes',
+ 'django.contrib.sessions',
+ 'django.contrib.messages',
+# 'djcelery',
+# 'debug_toolbar',
]
context_processors = [
'astakos.im.context_processors.invitations',
'astakos.im.context_processors.menu',
'astakos.im.context_processors.custom_messages',
+ 'astakos.im.context_processors.group_kinds',
'synnefo.lib.context_processors.cloudbar'
]
'astakos.im.middleware.CookieAuthenticationMiddleware',
'synnefo.lib.middleware.LoggingConfigMiddleware',
'synnefo.lib.middleware.SecureMiddleware',
- 'django.middleware.csrf.CsrfViewMiddleware'
+ 'django.middleware.csrf.CsrfViewMiddleware',
+# 'debug_toolbar.middleware.DebugToolbarMiddleware',
]
loggers = {
- 'astakos': {
- 'handlers': ['console'],
- 'level': 'INFO'
- }
+ 'astakos': {
+ 'handlers': ['console'],
+ 'level': 'INFO'
+ }
}
static_files = {'astakos.im': ''}
# The following settings will replace the default django settings
AUTHENTICATION_BACKENDS = ('astakos.im.auth_backends.EmailBackend',
- 'astakos.im.auth_backends.TokenBackend')
+ 'astakos.im.auth_backends.TokenBackend')
LOGIN_URL = '/im'
CUSTOM_USER_MODEL = 'astakos.im.AstakosUser'
+#SOUTH_TESTS_MIGRATE = False
+
+import djcelery
+djcelery.setup_loader()
+
+BROKER_URL = ''
+
+# INTERNAL_IPS = ('127.0.0.1',)
\ No newline at end of file
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.
-from django.http import HttpResponseBadRequest, HttpResponseRedirect
+from django.http import HttpResponseRedirect
from django.shortcuts import render_to_response
from django.template import RequestContext
-from django.contrib.auth import authenticate
from django.contrib import messages
from django.utils.translation import ugettext as _
from django.views.decorators.csrf import csrf_exempt
from astakos.im.util import prepare_response, get_query
from astakos.im.views import requires_anonymous, signed_terms_required
-from astakos.im.models import AstakosUser, PendingThirdPartyUser
+from astakos.im.models import PendingThirdPartyUser
from astakos.im.forms import LoginForm, ExtendedPasswordChangeForm
from astakos.im.settings import RATELIMIT_RETRIES_ALLOWED
from astakos.im.settings import ENABLE_LOCAL_ACCOUNT_MIGRATION
+import astakos.im.messages as astakos_messages
+
from ratelimit.decorators import ratelimit
-retries = RATELIMIT_RETRIES_ALLOWED-1
-rate = str(retries)+'/m'
+retries = RATELIMIT_RETRIES_ALLOWED - 1
+rate = str(retries) + '/m'
+
@require_http_methods(["GET", "POST"])
@csrf_exempt
on_failure: the template name to render on login failure
"""
was_limited = getattr(request, 'limited', False)
- form = LoginForm(data=request.POST, was_limited=was_limited, request=request)
+ form = LoginForm(data=request.POST,
+ was_limited=was_limited,
+ request=request)
next = get_query(request).get('next', '')
username = get_query(request).get('key')
)
# get the user from the cash
user = form.user_cache
-
+
message = None
if not user:
- message = _('Cannot authenticate account')
+ message = _(astakos_messages.ACCOUNT_AUTHENTICATION_FAILED)
elif not user.is_active:
if not user.activation_sent:
- message = _('Your request is pending activation')
+ message = _(astakos_messages.ACCOUNT_PENDING_ACTIVATION)
else:
- url = reverse('send_activation', kwargs={'user_id':user.id})
- message = _('You have not followed the activation link. \
- <a href="%s">Resend activation email?</a>' % url)
+ send_activation_url = reverse('send_activation', kwargs={'user_id':user.id})
+ message = _(astakos_messages.ACCOUNT_RESEND_ACTIVATION) % locals()
elif user.provider not in ('local', ''):
- message = _(
- 'Local login is not the current authentication method for this account.'
- )
+ message = _(astakos_messages.NO_LOCAL_AUTH)
if message:
messages.error(request, message)
except:
messages.error(
request,
- _('Account failed to switch to %(provider)s' % locals())
+ _(astakos_messages.SWITCH_ACCOUNT_FAILURE)
)
return render_to_response(
on_failure,
new.delete()
messages.success(
request,
- _('Account successfully switched to %(provider)s' % user.__dict__)
+ _(astakos_messages.SWITCH_ACCOUNT_SUCCESS_WITH_PROVIDER) % user.__dict__
)
return prepare_response(request, user, next)
form = password_change_form(user=request.user)
return render_to_response(template_name, {
'form': form,
- }, context_instance=RequestContext(request))
\ No newline at end of file
+ }, context_instance=RequestContext(request))
# or implied, of GRNET S.A.
from django.core.urlresolvers import reverse
-from django.shortcuts import redirect
from django.utils.translation import ugettext as _
-from django.contrib import messages
from django.utils.http import urlencode
from django.contrib.auth import authenticate
from django.http import (
from django.core.exceptions import ValidationError
from django.views.decorators.http import require_http_methods
-from urllib import quote
-from urlparse import urlunsplit, urlsplit, urlparse, parse_qsl
+from urlparse import urlunsplit, urlsplit, parse_qsl
from astakos.im.settings import COOKIE_DOMAIN
from astakos.im.util import restrict_next
from astakos.im.functions import login as auth_login, logout
+import astakos.im.messages as astakos_messages
+
import logging
logger = logging.getLogger(__name__)
+
@require_http_methods(["GET", "POST"])
def login(request):
"""
"""
next = request.GET.get('next')
if not next:
- return HttpResponseBadRequest(_('No next parameter'))
+ return HttpResponseBadRequest(_(astakos_messages.MISSING_NEXT_PARAMETER))
if not restrict_next(
next, domain=COOKIE_DOMAIN, allowed_schemes=('pithos',)
):
- return HttpResponseForbidden(_('Not allowed next parameter'))
+ return HttpResponseForbidden(_(astakos_messages.NOT_ALLOWED_NEXT_PARAM))
force = request.GET.get('force', None)
response = HttpResponse()
if force == '':
if request.user.is_authenticated():
# if user has not signed the approval terms
# redirect to approval terms with next the request path
- if not request.user.signed_terms():
+ if not request.user.signed_terms:
# first build next parameter
parts = list(urlsplit(request.build_absolute_uri()))
params = dict(parse_qsl(parts[3], keep_blank_values=True))
# delete force parameter
parts[3] = urlencode(params)
next = urlunsplit(parts)
-
+
# build url location
parts[2] = reverse('latest_terms')
- params = {'next':next}
+ params = {'next': next}
parts[3] = urlencode(params)
url = urlunsplit(parts)
response['Location'] = url
except ValidationError, e:
return HttpResponseBadRequest(e)
# authenticate before login
- user = authenticate(email=request.user.email, auth_token=request.user.auth_token)
+ user = authenticate(email=request.user.email,
+ auth_token=request.user.auth_token
+ )
auth_login(request, user)
logger.info('Token reset for %s' % request.user.email)
parts = list(urlsplit(next))
- parts[3] = urlencode({'user': request.user.email, 'token': request.user.auth_token})
+ parts[3] = urlencode({'user': request.user.email,
+ 'token': request.user.auth_token
+ }
+ )
url = urlunsplit(parts)
response['Location'] = url
response.status_code = 302
return response
else:
# redirect to login with next the request path
-
+
# first build next parameter
parts = list(urlsplit(request.build_absolute_uri()))
params = dict(parse_qsl(parts[3], keep_blank_values=True))
del params['force']
parts[3] = urlencode(params)
next = urlunsplit(parts)
-
+
# build url location
- parts[2] = reverse('astakos.im.views.index')
- params = {'next':next}
+ parts[2] = reverse('index')
+ params = {'next': next}
parts[3] = urlencode(params)
url = urlunsplit(parts)
response['Location'] = url
response.status_code = 302
- return response
\ No newline at end of file
+ return response
# 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
# 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
from django.contrib import messages
from django.template import RequestContext
from django.views.decorators.http import require_http_methods
-from django.db.models import Q
-from django.core.exceptions import ValidationError
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
-from urlparse import urlunsplit, urlsplit
-from django.utils.http import urlencode
+from django.core.exceptions import ImproperlyConfigured
-from astakos.im.util import prepare_response, get_context, get_invitation
+from astakos.im.util import prepare_response, get_context
from astakos.im.views import requires_anonymous, render_response
-from astakos.im.settings import ENABLE_LOCAL_ACCOUNT_MIGRATION, BASEURL
-
+from astakos.im.settings import ENABLE_LOCAL_ACCOUNT_MIGRATION
from astakos.im.models import AstakosUser, PendingThirdPartyUser
from astakos.im.forms import LoginForm
-from astakos.im.activation_backends import get_backend, SimpleBackend
+from astakos.im.activation_backends import get_backend
+
+import astakos.im.messages as astakos_messages
import logging
class Tokens:
# these are mapped by the Shibboleth SP software
- SHIB_EPPN = "HTTP_EPPN" # eduPersonPrincipalName
+ SHIB_EPPN = "HTTP_EPPN" # eduPersonPrincipalName
SHIB_NAME = "HTTP_SHIB_INETORGPERSON_GIVENNAME"
SHIB_SURNAME = "HTTP_SHIB_PERSON_SURNAME"
SHIB_CN = "HTTP_SHIB_PERSON_COMMONNAME"
SHIB_SESSION_ID = "HTTP_SHIB_SESSION_ID"
SHIB_MAIL = "HTTP_SHIB_MAIL"
+
@require_http_methods(["GET", "POST"])
@requires_anonymous
def login(
extra_context = extra_context or {}
tokens = request.META
-
+
try:
eppn = tokens.get(Tokens.SHIB_EPPN)
if not eppn:
- raise KeyError(_('Missing unique token in request'))
+ raise KeyError(_(astakos_messages.SHIBBOLETH_MISSING_EPPN))
if Tokens.SHIB_DISPLAYNAME in tokens:
realname = tokens[Tokens.SHIB_DISPLAYNAME]
elif Tokens.SHIB_CN in tokens:
elif Tokens.SHIB_NAME in tokens and Tokens.SHIB_SURNAME in tokens:
realname = tokens[Tokens.SHIB_NAME] + ' ' + tokens[Tokens.SHIB_SURNAME]
else:
- raise KeyError(_('Missing user name in request'))
+ raise KeyError(_(astakos_messages.SHIBBOLETH_MISSING_NAME))
except KeyError, e:
extra_context['login_form'] = LoginForm(request=request)
messages.error(request, e)
request.GET.get('next'),
'renew' in request.GET)
elif not user.activation_sent:
- message = _('Your request is pending activation')
+ message = _(astakos_messages.ACCOUNT_PENDING_ACTIVATION)
messages.error(request, message)
else:
urls = {}
- urls['send_activation'] = reverse(
+ urls['send_activation_url'] = reverse(
'send_activation',
kwargs={'user_id':user.id}
)
- urls['signup'] = reverse(
+ urls['signup_url'] = reverse(
'shibboleth_signup',
args= [user.username]
)
- message = _(
- 'You have not followed the activation link. \
- <a href="%(send_activation)s">Resend activation email?</a> or \
- <a href="%(signup)s">Provide new email?</a>' % urls
- )
+ message = _(astakos_messages.INACTIVE_ACCOUNT_CHANGE_EMAIL) % urls
messages.error(request, message)
return render_response(login_template,
login_form = LoginForm(request=request),
logger.exception(e)
template = login_template
extra_context['login_form'] = LoginForm(request=request)
- messages.error(request, _('Something went wrong.'))
+ messages.error(request, _(astakos_messages.GENERIC_ERROR))
else:
if not ENABLE_LOCAL_ACCOUNT_MIGRATION:
url = reverse(
):
extra_context = extra_context or {}
if not username:
- return HttpResponseBadRequest(_('Missing key parameter.'))
+ return HttpResponseBadRequest(_(astakos_messages.MISSING_KEY_PARAMETER))
try:
pending = PendingThirdPartyUser.objects.get(username=username)
except PendingThirdPartyUser.DoesNotExist:
try:
user = AstakosUser.objects.get(username=username)
except AstakosUser.DoesNotExist:
- return HttpResponseBadRequest(_('Invalid key.'))
+ return HttpResponseBadRequest(_(astakos_messages.INVALID_KEY_PARAMETER))
else:
d = pending.__dict__
d.pop('_state', None)
return render_response(
on_creation_template,
context_instance=get_context(request, extra_context)
- )
\ No newline at end of file
+ )
--- /dev/null
+# Copyright 2011 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 celery.task import task, periodic_task
+from celery.schedules import crontab
+
+from functools import wraps
+
+from astakos.im.endpoints.qh import send_quota
+from astakos.im.endpoints.aquarium.producer import (report_credits_event,
+ report_user_event
+ )
+from astakos.im.endpoints.aquarium.client import AquariumClient
+
+import logging
+
+logger = logging.getLogger(__name__)
+
+
+def log(func):
+ @wraps(func)
+ def wrapper(*args, **kwargs):
+ logger.info('Starting the %s with args=%s kwargs=%s' % (
+ func, args, kwargs
+ )
+ )
+ return func(*args, **kwargs)
+ return wrapper
+
+
+@periodic_task(run_every=crontab(day_of_month='1'))
+@log
+def propagate_credits_update():
+ report_credits_event()
+
+
+@task
+@log
+def propagate_groupmembers_quota(group):
+ if group.is_disabled:
+ return
+ send_quota(group.approved_members)
+
+
+@task
+@log
+def request_billing(user, start, end):
+ return AquariumClient().get_billing(user, start, end)
{% block page.quicknav.items %}
<li class="{% block signup_class %}{% endblock %}">
- <a href="{% url astakos.im.views.logout %}">LOGOUT</a>
+ <a href="{% url logout %}">LOGOUT</a>
</li>
{% endblock %}
</li>
{% endfor %}
{% endblock %}
-
+
+{% block page.subnav %}
+ {% for item in menu%}
+ {% if item|lookup:"is_active" %}
+ {% with item|lookup:"submenu" as submenu %}
+ {% for item in submenu %}
+ <li {% if item|lookup:"is_active" %}class="active"{% endif %}>
+ <a href="{{ item|lookup:"url" }}">{{ item|lookup:"name" }}</a>
+ </li>
+ {% endfor %}
+ {% endwith %}
+ {% endif %}
+ {% endfor %}
+{%endblock %}
+
{% block page.body %}
<div class="{% block innerpage.class %}full{% endblock %}">
{% block body %}{% endblock %}
--- A translation in English follows ---
+{% if group_creation %}
Έχει δημιουργηθεί ο παρακάτω λογαριασμός:
+{% else %}
+Έχει ενεργοποιηθεί ο παρακάτω λογαριασμός:
+{% endif %}
Email: {{user.email}}
First name: {{user.first_name}}
Level: {{user.level}}
Invitations: {{user.invitations}}
-Για την ενεργοποίησή του μπορείτε να χρησιμοποιήσετε το command line εργαλείο snf-manage sendactivation
+{% if group_creation %}
+Για την ενεργοποίησή του μπορείτε να χρησιμοποιήσετε το command line εργαλείο snf-manage user_send_activation
+{% endif %}
--
+{% if group_creation %}
The following account has been created:
+{% else %}
+The following account has been activated:
+{% endif %}
Email: {{user.email}}
First name: {{user.first_name}}
Level: {{user.level}}
Invitations: {{user.invitations}}
-For its activation you can use the command line tool snf-manage sendactivation
+{% if group_creation %}
+For its activation you can use the command line tool snf-manage user_send_activation
+{% endif %}
\ No newline at end of file
--- /dev/null
+{% extends "im/account_base.html" %}
+
+{% block body %}
+<div class="projects">
+ <div class="clearfix">
+ <p>You can create the followind type of projects:</p>
+ </div>
+
+ <div class="two-cols clearfix dotted">
+ <div class="rt centered">
+ <img alt="THINK ABOUT IT" src="/static/medialibrary/2012/04/proffessor.png">
+ </div>
+ <div class="lt">
+ <h2 style="color:#B66D00;">COURSE</h2>
+ <div>
+ <p>You wake up one morning and you need a new computer with a specific operating system and hardware requirements (or 10 new computers). You also need 10GB of storage space to store some new content you just got your hands on (or 50GB ;-)).</p>
+ <a href="{% url group_add 'course' %}" class="submit">CREATE COURSE</a>
+ </div>
+ </div>
+ </div>
+ <div class="two-cols clearfix dotted">
+ <div class="rt centered">
+ <img alt="THINK ABOUT IT" src="/static/medialibrary/2012/06/behind_okeanos.png">
+ </div>
+ <div class="lt">
+ <h2 style="color:#4085A6;">PROJECT</h2>
+ <div>
+ <p>You wake up one morning and you need a new computer with a specific operating system and hardware requirements (or 10 new computers). You also need 10GB of storage space to store some new content you just got your hands on (or 50GB ;-)).</p>
+ <a href="{% url group_add 'project' %}" class="submit">CREATE PROJECT</a>
+ </div>
+ </div>
+ </div>
+ <div class="two-cols clearfix dotted">
+ <div class="rt centered">
+ <img alt="THINK ABOUT IT" src="/static/medialibrary/2012/06/from_athens.png">
+ </div>
+ <div class="lt">
+ <h2 style="color:#EF4F54;">ORGANISATION</h2>
+ <div>
+ <p>You wake up one morning and you need a new computer with a specific operating system and hardware requirements (or 10 new computers). You also need 10GB of storage space to store some new content you just got your hands on (or 50GB ;-)).</p>
+ <a href="{% url group_add 'organization' %}" class="submit">CREATE ORGANISATION</a>
+ </div>
+ </div>
+ </div>
+ <div class="two-cols clearfix dotted">
+ <div class="rt centered">
+ <img alt="THINK ABOUT IT" src="/static/medialibrary/2012/04/researcher.png">
+ </div>
+ <div class="lt">
+ <h2 style="color:#FF7CA4;">LAB</h2>
+ <div>
+ <p>You wake up one morning and you need a new computer with a specific operating system and hardware requirements (or 10 new computers). You also need 10GB of storage space to store some new content you just got your hands on (or 50GB ;-)).</p>
+ <a href="{% url group_add 'laboratory' %}" class="submit">CREATE LAB</a>
+ </div>
+ </div>
+ </div>
+</div>
+
+{% endblock body %}
\ No newline at end of file
--- /dev/null
+{% extends "im/account_base.html" %}
+
+{% load filters %}
+
+{% block page.body %}
+{% with object.owners as owners %}
+
+<div class="projects">
+
+ <h2>
+ {% if object.is_member %}
+ <em>
+ {% if object.is_owner %}
+ [ ADMINISTRATOR ]
+ {% else %}
+ [ ENROLLED ]
+ {% endif %}
+ </em>
+ {% endif %}
+ <span>{{ object.name|strip_http|upper }}</span>
+ </h2>
+
+ <div class="details">
+ {% if object.is_owner %}
+ <a href="#" class="edit">EDIT GROUP INFO</a>
+ {% endif %}
+ <div class="data">
+ <p>{{ object.desc }}</p>
+ <dl class="alt-style">
+ <dt>Homepage url</dt>
+ <dd>
+ {% if object.homepage%}
+ <a href="{{ object.homepage }}">{{ object.homepage }}</a>
+ {% else %}
+ Not set yet
+ {% endif %}
+ </dd>
+ </dl>
+ </div>
+ <div class="editable" style="display:none;">
+ <form action="" method="post"
+ class="withlabels">{% csrf_token %}
+ {% with update_form as form %}
+ {% include "im/form_render.html" %}
+ <div class="form-row submit">
+ <input type="submit" class="submit altcol" value="FINISHED EDITING" />
+ </div>
+ {% endwith %}
+ </form>
+ </div>
+ </div>
+ <div class="full-dotted">
+ <h3>DETAILS:</h3>
+ <dl class="alt-style">
+ <dt>Name</dt>
+ <dd>{{ object.name|strip_http }} </dd>
+ <!--<dt>Type</dt>
+ <dd>{{object.kindname|capfirst}} </dd>-->
+ <dt>Issue date:</dt>
+ <dd>{{object.issue_date|date:"d/m/Y"}} </dd>
+ <dt>Expiration Date</dt>
+ <dd>{{object.expiration_date|date:"d/m/Y"}} </dd>
+ <dt>Modaration</dt>
+ <dd>{% if object.moderation_enabled%}Yes{% else %}No{% endif %}</dd>
+ <dt>Activated</dt>
+ <dd>{% if object.is_enabled %}Yes{% else %}No{% endif %}</dd>
+ <dt>Owner</dt>
+ {{ o.owners }}
+ <dd>{% for o in owners %}
+ {% if object.is_owner %}
+ Me
+ {% else%}
+ {{o.realname}} ({{o.email}})
+
+ {% endif %}
+ {% endfor %}
+ </dd>
+ <dt>Max participants</dt>
+ <dd>{% if object.max_participants%}{{object.max_participants}}{% else %} {% endif %}</dd>
+ </dl>
+ </div>
+ <div class="full-dotted">
+ <h3>RESOURCES:</h3>
+ {% if quota %}
+ <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 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="#members-table">
+ <div class="form-row">
+ <select name="sorting" onchange="this.form.submit();" class="dropkicked">
+ <option value="">Sort by</option>
+ <option value="person__email" {% if sorting == 'person__email' %}selected{% endif %}>User Id</option>
+ <option value="person__first_name" {% if sorting == 'person__first_name' %}selected{% endif %}>Name</option>
+ <option value="date_joined" {% if sorting == 'date_joined' %}selected{% endif %}>Status</option>
+ </select>
+ </div>
+ </form>
+ <table class="alt-style" id="members-table">
+ <caption>MEMBERS:</caption>
+ <thead>
+ <tr>
+ <th>User Id</th>
+ <th>Name</th>
+ <th>Status</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for m in membership.object_list %}
+ <tr>
+ <td>{{m.person.email}}</td>
+ <td>{{m.person.realname}}</td>
+ {% if m.person in owners %}
+ <td>Owner</td>
+ {% else %}
+ {% if m.is_approved %}
+ <td>Approved</td>
+ {% else %}
+ <td>Pending
+ {% if object.is_owner %}
+ <a href="{% url approve_member object.id m.person.id %}?{% if page %}page={{ page }}{% endif %}{% if sorting %}&sorting={{sorting}}{% endif %}">Accept</a>
+ <a href="{% url disapprove_member object.id m.person.id %}?{% if page %}page={{ page }}{% endif %}{% if sorting %}&sorting={{sorting}}{% endif %}">Reject</a>
+ {% endif %}
+ </td>
+ {% endif %}
+ {% endif %}
+ </tr>
+ {% endfor %}
+ </tbody>
+ </table>
+ <div class="pagination">
+ <p class="next-prev">
+ {% if membership.has_previous %}
+ <a href="?page={{ membership.previous_page_number }}{% if sorting %}&sorting={{sorting}}{% endif %}">previous</a>
+ {% endif %}
+ {% if membership.has_next %}
+ <a href="?page={{ membership.next_page_number }}{% if sorting %}&sorting={{sorting}}{% endif %}">next</a>
+ {% endif %}
+ </p>
+ <p class="nums">
+ <span class="current">
+ Page {{ membership.number }} of {{ membership.paginator.num_pages }}
+ </span>
+ </p>
+ </div>
+ {% else %}
+ <p>No members yet!</p>
+ {% endif %}
+ {% endwith %}
+ {% endwith %}
+ </div>
+
+
+ {% if object.is_owner %}
+ <div class="full-dotted">
+ <form action="" method="post" class="withlabels">{% csrf_token %}
+ <h2>Enroll more members</h2>
+ {% with addmembers_form as form %}
+ {% include "im/form_render.html" %}
+ {% endwith %}
+ <div class="form-row submit">
+ <input type="submit" class="submit altcol" value="ADD MEMBERS" />
+ </div>
+ </form>
+ </div>
+ {% endif %}
+
+
+</div>
+
+{% endwith %}
+{% endblock %}
--- /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="#top" method="post" class="withlabels quotas-form" id="group_create_form">{% csrf_token %}
+
+ <fieldset class="with-info" id="top">
+ <legend>
+ 1. CREATE GROUP
+ <span class="info">
+ <em>more info</em>
+ <span>Fill in the required fields to create a group. Group details will be visible to the users of the group.</span>
+ </span>
+ </legend>
+
+ {% include "im/form_render.html" %}
+
+ </fieldset>
+
+ <fieldset id="icons">
+ <legend>
+ 2. ADD RESOURCES
+ <span class="info">
+ <em>more info</em>
+ <span>You need to specify at least one resource</span>
+ </span>
+ </legend>
+ <ul class="clearfix">
+ {% 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="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"> </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="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 page.body %}
+
+{% 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>
+<!--
+ {% for k,v in data.iteritems %}
+ <input type="hidden" name="{{ k }}" value="{{ v }}">
+ {% endfor %}
+ -->
+ {% include "im/form_render.html" %}
+
+ <p>{{ data.desc|safe }}</p>
+ <dl class="alt-style">
+ <dt>Homepage Url</dt>
+ <dd>{{ data.homepage }} </dd>
+ </dl>
+
+ <div class="full-dotted">
+ <h3>DETAILS:</h3>
+ <dl class="alt-style">
+ <dt>Name</dt>
+ <dd>{{ data.name|strip_http }} </dd>
+ <!--<dt>Type</dt>
+ <dd>Course </dd>-->
+ <dt>Issue date:</dt>
+ <dd>{{ data.issue_date|date:"d/m/Y"}} </dd>
+ <dt>Expiration Date</dt>
+ <dd>{{ data.expiration_date|date:"d/m/Y"}} </dd>
+ <dt>Modaration</dt>
+ <dd>{{ data.moderation_enabled|yesno:"Yes, No" }}</dd>
+ <dt>Max members per group</dt>
+ <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 p in policies %}
+
+ <dt>
+ Max {% if p.is_abbreviation %}{{ p.name|upper }}{% else %}{{ p.name }}{% endif %}{% if not p.unit %}s {% endif %} per user
+ </dt>
+ <dd>
+ {% if p.uplimit %}
+ {% if p.unit %}
+ {{ p.uplimit|sizeof_fmt }}
+ {% else %}
+ {{ p.uplimit }}
+ {% endif %}
+ {% else %}
+ Unlimited
+ {% endif %}
+ </dd>
+ {% endfor %}
+ </dl>
+ </div>
+
+ <div class="full-dotted">
+
+ </div>
+
+
+ <div class="form-row submit">
+ <input type="submit" value="SUBMIT" class="submit altcol" autocomplete="off">
+ </div>
+ </form>
+
+</div>
+{% endwith %}
+
+{% endblock %}
--- /dev/null
+{% extends "im/account_base.html" %}
+
+{% load filters %}
+
+{% block page.body %}
+<div class="maincol {% block innerpage.class %}{% endblock %}">
+ <div class="projects">
+ <h2>GROUPS</h2>
+ {% if form %}
+ <p>You can search for a group by name</p>
+ <form action="{% url group_search %}" method="post" class="withlabels signup">{% csrf_token %}
+ {% include "im/form_render.html" %}
+ <div class="form-row submit">
+ <input type="submit" class="submit altcol" value="SEARCH" />
+ </div>
+ </form>
+
+ <form action="{% url group_all %}" method="post" class="link-like alone">{% csrf_token %}
+ <div class="form-row submit">
+ <input type="submit" class="submit altcol" value="Show all groups" />
+ </div>
+ </form>
+ {% else %}
+
+ <p>Okeanos grants resources according to activities organized by groups.
+ Join or create a group to get access to more resources.
+ <a href="{% url group_search %}">Join an existing one</a>
+ or
+ <!--<a href="{% url group_create_list %}">Create a new group</a>-->
+ <a href="{% url group_add 'default' %}">create a new group</a>.
+ </p>
+
+ <div class="widjets">
+ <!--<a href="#" class="widjet-x" title="remove boxes">X</a>-->
+ <ul class="clearfix">
+ <li class="create">
+ <div>
+ <div class="wrap">
+ <h2>WELCOME!</h2>
+ <p><a href="{% url group_add 'course' %}"><img alt="THINK ABOUT IT" src="/static/im/images/create.png"></a></p>
+ <p class="txt">Connect with a world of people who share your passions.<br>With millions of groups at your fingertips, it's easy to find the group that's best for you -- no matter your interest.</p>
+ <p><a href="{% url group_add 'default' %}">create a group ></a></p>
+ <!--<p class="btn"><a href="{% url group_create_list %}" class="submit">CREATE</a></p>-->
+ </div>
+ </div>
+ </li>
+ <li class="join">
+ <div>
+ <div class="wrap">
+ <h2>ALREADY AWARE?</h2>
+ <p><a href="{% url group_search %}"><img alt="THINK ABOUT IT" src="/static/im/images/join.png"></a></p>
+ <p class="txt">Well, this is the place to start!<br>sdofuisd ofuaofi usdiof uiofu osifuaoi ufisdfiousf oiusd<br></p>
+
+ <p><a href="{% url group_search %}">join a group ></a></p>
+ </div>
+ </div>
+ </li>
+ </ul>
+ </div>
+
+ {% endif %}
+ {% with page_obj.object_list as object_list %}
+ <!-- Search group -->
+ {% if object_list %}
+ <div class="full-dotted">
+ <form method="GET" class="minimal" action="#searchResults">
+ <div class="form-row">
+ <select name="sorting" onchange="this.form.submit();" class="dropkicked" tabindex="1">
+ <option value="">Sort by Name</option>
+ <option value="issue_date" {% if sorting == 'issue_date' %}selected{% endif %}>Issue date</option>
+ <option value="expiration_date" {% if sorting == 'expiration_date' %}selected{% endif %}>Expiration Date</option>
+ <option value="approved_members_num" {% if sorting == 'approved_members_num' %}selected{% endif %}>Participants</option>
+ </select>
+ <input type="hidden" name="q" value="{{q}}"/>
+ </div>
+ </form>
+ <table class="alt-style complex" id="searchResults">
+ <caption>
+ SEARCH RESULTS
+ </caption>
+ <thead>
+ <tr>
+ <th>Name</th>
+ <!--<th>Type</th>-->
+ <th>Issued</th>
+ <th>Expires</th>
+
+ <th>Enrolled</th>
+
+
+ <th>Status</th>
+ <th> </th>
+
+ </tr>
+ </thead>
+ <tbody>
+ {% for o in object_list %}
+ <tr class="{% cycle 'tr1' 'tr2' %}">
+ <td><a href="{% url group_detail o.id %}" title="visit group page">{{o.groupname|rcut:"/"}}</a></td>
+ <!--td>{{o.kindname|capfirst}}</td-->
+ <td>{{o.issue_date|date:"d/m/Y"}}</td>
+ <td>{{o.expiration_date|date:"d/m/Y"}}</td>
+ <td>{{o.approved_members_num}}</td>
+
+ <td>
+ <div class="msg-wrap">
+
+ {% if o.is_member %}
+ {% if o.membership_approval_date %}
+
+
+ {% if not o.is_owner %}
+ Registered
+ <form action="{% url group_leave o.id %}" method="post" class="link-like">{% csrf_token %}
+ <input type="submit" value="x leave group" class="leave"/>
+ </form>
+ <div class="dialog">
+ Are you sure you what to leave this group?<br>
+ Name: <a href="{% url group_detail o.id %}" title="visit group page">{{o.groupname|rcut:"/" }}</a><br>
+ {% if o.desc %}Description:{{o.desc|truncatewords:30}}{% endif %}<br><br>
+
+ <a href="#" class="yes submit">Yes</a> <a href="#" class="no submit">No</a>
+ </div>
+ {% else %}
+ Owner
+ {% endif %}
+
+
+
+ {% else %}
+ Pending
+ {% endif %}
+ {% else %}
+ Not member
+ <form action="{% url group_join o.id %}" method="post" class="link-like">{% csrf_token %}
+ <input type="submit" value="+ join group" class="join_group join" />
+ </form>
+ <div class="dialog">
+ Are you sure you what to join this group?<br>
+ Name: <a href="{% url group_detail o.id %}" title="visit group page">{{o.groupname|rcut:"/" }}</a><br>
+ {% if o.desc %}Description:{{o.desc|truncatewords:30}}{% endif %}<br><br>
+
+ <a href="#" class="yes submit">Yes</a> <a href="#" class="no submit">No</a>
+ </div>
+
+ {% endif %}
+ </div>
+ </td>
+ <td><a href="#" class="more-info" title="more info">+ more info</a></td>
+ </tr>
+ <tr class="{% cycle 'tmore1' 'tmore2' %}" style="display:none">
+ <td colspan="7" class="info-td">
+ <div>
+ <p>{{o.desc}}</p>
+ <p>{% if o.homepage%}
+ Group's home page: <a target="_blank" href="{{ o.homepage }}">{{ o.homepage }}</a>
+
+ {% endif %}
+ </p>
+ </div>
+ </td>
+ </tr>
+ {% endfor %}
+ </tbody>
+ </table>
+
+ </div>
+
+
+ <div class="pagination">
+ <p class="next-prev">
+ {% if page_obj.has_previous %}
+ <a href="?page={{ page_obj.previous_page_number }}{% if q %}&q={{q}}{% endif %}{% if sorting %}&sorting={{sorting}}{% endif %}">previous</a>
+ {% endif %}
+ {% if page_obj.has_next %}
+ <a href="?page={{ page_obj.next_page_number }}{% if q %}&q={{q}}{% endif %}{% if sorting %}&sorting={{sorting}}{% endif %}">next</a>
+ {% endif %}
+ </p>
+ <p class="nums">
+ <span class="current">
+ Page {{ page_obj.number }} of {{ paginator.num_pages }}
+ </span>
+ </p>
+ </div>
+ <!-- Group listing -->
+ {% else %}
+ {% if not form %}
+ {% with page|concat:sorting as args %}
+ {% with q|paginate:args as page_obj %}
+
+ <div >
+ <form method="GET" class="minimal" action="#allGroups" id="mygroups">
+ <div class="form-row">
+ <select name="sorting" class="dropkicked" tabindex="1">
+ <option value="">Sort by Name</option>
+ <!--<option value="kindname" {% if sorting == 'kindname' %}selected{% endif %}>Type</option>-->
+ <option value="issue_date" {% if sorting == 'issue_date' %}selected{% endif %}>Sort by Issue date</option>
+ <option value="expiration_date" {% if sorting == 'expiration_date' %}selected{% endif %}>Sort by Expiration Date</option>
+ <option value="approved_members_num" {% if sorting == 'approved_members_num' %}selected{% endif %}>Sort by Participants</option>
+ <option value="moderation_enabled" {% if sorting == 'moderation_enabled' %}selected{% endif %}>Sort by Moderation</option>
+ </select>
+ </div>
+ </form>
+ <table class="alt-style complex" id="allGroups">
+ <caption>MY GROUPS</caption>
+ <thead>
+ <tr>
+ <th>Name</th>
+ <!--th>Type</th-->
+ <th>Issued</th>
+ <th>Expires</th>
+ <th>Enrolled</th>
+ <th>Status</th>
+ <th class="centered">Moderated</th>
+ <th> </th>
+
+ </tr>
+ </thead>
+ <tbody>
+ {% for o in page_obj.object_list %}
+ <tr class="{% cycle 'tr1' 'tr2' %}">
+ <td><a href="{% url group_detail o.id %}" title="visit group page">{{o.groupname|rcut:"/" }}</a></td>
+ <!--td>{{o.kindname|capfirst}}</td-->
+ <td>{{o.issue_date|date:"d/m/Y"}}</td>
+ <td>{{o.expiration_date|date:"d/m/Y"}}</td>
+ <td>{{ o.approved_members_num }}</td>
+ <td>
+ <div class="msg-wrap">
+ {% if user.email = o.groupowner %}
+ {% if o.is_enabled %}
+ Owner
+ {% else %}
+ Pending
+ {% endif %}
+ {% else %}
+ {% if o.is_enabled %}
+ {% if o.membership_status %}
+ Registered
+ <form action="{% url group_leave o.id %}" method="post" class="link-like">{% csrf_token %}
+ <input type="submit" value="x leave" class="leave" />
+ </form>
+ <div class="dialog">
+ Are you sure you what to leave this group?<br>
+ Name: <a href="{% url group_detail o.id %}" title="visit group page">{{o.groupname|rcut:"/" }}</a><br>
+ {% if o.desc %}Description:{{o.desc|truncatewords:30}}{% endif %}<br><br>
+
+ <a href="#" class="yes submit">Yes</a> <a href="#" class="no submit">No</a>
+ </div>
+ {% else %}
+ Pending
+ {% endif %}
+ {% else %}
+ -
+ {% endif %}
+ {% endif %}
+
+ </div>
+ </td>
+ <td class="centered">{% if o.moderation_enabled%}Yes{% else %}No{% endif %}</td>
+ <td><a href="#" class="more-info" title="more info">+ more info </a></td>
+ </tr>
+ <tr class="{% cycle 'tmore1' 'tmore2' %}" style="display:none">
+ <td colspan="8" class="info-td">
+ <div>
+ <p>{{o.desc}}</p>
+ <p>{% if o.homepage%}
+ Group's home page: <a href="{{ o.homepage }}">{{ o.homepage }}</a>
+ {% endif %}
+ </p>
+ </div>
+ </td>
+ </tr>
+ {% endfor %}
+ </tbody>
+ </table>
+ </div>
+ <div class="pagination">
+ <p class="next-prev">
+ {% if page_obj.has_previous %}
+ <a href="?page={{ page_obj.previous_page_number }}{% if sorting %}&sorting={{ sorting }}{% endif%}#allGroups">previous</a>
+ {% endif %}
+ {% if page_obj.has_next %}
+ <a href="?page={{ page_obj.next_page_number }}{% if sorting %}&sorting={{ sorting }}{% endif%}#allGroups">next</a>
+ {% endif %}
+ </p>
+ <p class="nums">
+ <span class="current">
+ Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}
+ </span>
+ </p>
+ </div>
+
+ {% endwith %}
+ {% endwith %}
+ {% endif %}
+ {% if form %}
+ {% if q %}
+ <h2>No groups found!</h2>
+ {% endif %}
+ {% endif %}
+ {% endif %}
+ {% endwith %}
+</div>
+</div>
+{% endblock %}
--- /dev/null
+{% extends "im/account_base.html" %}
+
+{% load filters %}
+
+{% block page.body %}
+<div class="maincol {% block innerpage.class %}{% endblock %}">
+ <div class="stats clearfix">
+ <ul>
+ {% for r in data.resources %}
+
+ <li class="clearfix {{ r.load_class }} {{ r.name}}">
+ <div class="img-wrap"> </div>
+ <div class="info">
+ <h3>{{ r.description }} </h3>
+ <p>
+ {{ r.ratio|floatformat }}% Used<br>
+ You are using {{ r.currValue }} {{ r.unit }} out of your {{ r.maxValue }}{{ r.unit }}
+ {% if r.maxValue == '1' %}
+ {{ r.name|capfirst}}
+ {% else %}
+ {{ r.plural|capfirst }}
+ {% endif %}
+ - Aouch!
+ </p>
+ </div>
+ <div class="bar">
+ <div><span style="width:{{ r.ratio|floatformat }}%;">{{ r.ratio|floatformat }}% </span></div>
+ </div>
+ </li>
+ {% endfor %}
+ </ul>
+ </div>
+ <!--
+ <div class="section">
+ {% for k, v in user.quota|items %}
+ <strong>{{k}}</strong>
+ <table class="zebra-striped id-sorted">
+ <thead>
+ <tr>
+ <th>Limit (Group)</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for m in user.membership_set.select_related.all %}
+ {% if m.group.is_enabled %}
+ {% with m.group.quota as quota %}
+ {% if quota %}
+ {% for kk, vv in quota|items %}
+ {% if k == kk %}
+ <tr>
+ <td>{{ vv }} ({{m.group.name}})</td>
+ </tr>
+ {% endif %}
+ {% endfor %}
+ {% endif %}
+ {% endwith %}
+ {% endif %}
+ {% endfor %}
+ <tr>
+ <td><strong>{{ v }}</strong></td>
+ </tr>
+ <tr/>
+ </tbody>
+ </table>
+ {% endfor %}
+ </div>
+ <form action="" method="post"
+ class="withlabels">{% csrf_token %}
+ {% include "im/form_render.html" %}
+ </form>
+ <table>
+ {% for q in object_list %}
+ {% if q.group.is_enabled %}
+ <tr>
+ <td>{{ q.uplimit }}</td><td>({{ q.group }})</td>
+ </tr>
+ {% endif %}
+ {% endfor %}
+ {% if form.cleaned_data %}
+ {% with form.cleaned_data|lookup:'resource' as resource %}
+ {% if resource %}
+ {% for q in user.astakosuserquota_set.all %}
+ {% if q.resource == resource %}
+ <tr>
+ <td>{{ q.uplimit }}</td><td>(assigned to me)</td>
+ </tr>
+ {% endif %}
+ {% endfor %}
+ <tr>
+ <td>{{ user.quota|lookup:resource }}</td><td>(total)</td>
+ </tr>
+ {% endif %}
+ {% endwith %}
+ {% endif %}
+ </table>-->
+ </div>
+</div>
+{% endblock %}
{% block page.css %}
- <link href='https://fonts.googleapis.com/css?family=Didact+Gothic&subset=latin' rel='stylesheet' type='text/css'>
+ <link href='https://fonts.googleapis.com/css?family=Open+Sans:400,600,700&subset=latin,greek-ext,greek' rel='stylesheet' type='text/css'>
<link rel="stylesheet" type="text/css" href="{{ IM_STATIC_URL }}css/global.css">
<link rel="stylesheet" type="text/css" href="{{ IM_STATIC_URL }}css/print.css" media="print">
<!--[if lte IE 7]>
<![endif]-->
<link rel="stylesheet" media="screen and (max-width: 960px)" href="{{ IM_STATIC_URL }}css/max960.css"/>
<link rel="stylesheet" media="screen and (max-width: 768px)" href="{{ IM_STATIC_URL }}css/max768.css"/>
- <link rel="stylesheet" media="screen and (max-width: 480px)" href="{{ IM_STATIC_URL }}css/max480.css"/>
+ <link rel="stylesheet" media="screen and (max-width: 480px)" href="{{ IM_STATIC_URL }}css/max480.css"/>
<link rel="stylesheet" type="text/css" href="{{ IM_STATIC_URL }}css/jquery-ui-1.8.21.custom.css"/>
{% endblock page.css %}
<script src="{{ IM_STATIC_URL }}js/modernizr-2.0.6.js"></script>
<script src="{{ IM_STATIC_URL }}js/jquery.js"></script>
<script src="{{ IM_STATIC_URL }}js/jquery.infieldlabel.js"></script>
- <script src="{{ IM_STATIC_URL }}js/forms.js"></script>
+ <script src="{{ IM_STATIC_URL }}js/forms.js"></script>
+ <script src="{{ IM_STATIC_URL }}js/jquery.dropkick-1.0.0.js"></script>
+ <script src="{{ IM_STATIC_URL }}js/jquery-ui-1.8.21.custom.min.js"></script>
+ <script src="{{ IM_STATIC_URL }}js/jquery.tablesorter.js"></script>
<script src="{{ IM_STATIC_URL }}js/common.js"></script>
{% endblock headjs %}
{% block endhead %}{% endblock endhead %}
--- /dev/null
+{% extends "im/account_base.html" %}
+
+{% load filters %}
+
+
+
+{% block page.body %}
+<div class="billing list">
+ {{ data }}
+ <div class="highlight">
+
+ <em>{{ data.remainingCredits|floatformat:2 }}</em> credits remaining
+ <span class="info foo">
+ <em>more info</em>
+ <span>|<br>This month you are given
+ {% with data|lookup:'bill_addcredits' as bill_addcredits %}
+ {{ bill_addcredits.0.totalCredits|floatformat:0}}
+ {% endwith %} new credits.
+ </span>
+ </span>
+ </div>
+
+ <h2><span>Billing statement for </span><em>[ {{ today.month|month_name|upper }} {{ today.year }} ]</em></h2>
+ {% if data.bill|length %}
+ <p>
+ <em>
+ This page shows billing report for the period 1 {{ today.month|month_name }} {{ today.year }}- {{ month_last_day }} {{ today.month|month_name }} {{ today.year }}
+ </em>
+ </p>
+
+
+
+ <br><br>
+ <table class="alt-style">
+ <tr>
+ <th>Service</th>
+ <th>Monthly Usage</th>
+ <th class="last">Charged Credits</th>
+ </tr>
+ <tr>
+ <td>Cyclades
+ {% with data|lookup:'bill_vmtime' as bill_vmtime %}
+
+ {% if bill_vmtime.0.totalCredits != '0.0' %}
+ <a href="" class="more-info"> </a>
+ {% endif%}
+ {% endwith %}
+ </td>
+ <td>
+ {% with data|lookup:'bill_vmtime' as bill_vmtime %}
+ {{ bill_vmtime.0.totalUnits|floatformat:0}} Hr
+ {% endwith %}
+ </td>
+ <td class="last">
+ {% with data|lookup:'bill_vmtime' as bill_vmtime %}
+ {{ bill_vmtime.0.totalCredits|floatformat:2}}
+ {% endwith %}
+
+ </td>
+ </tr>
+ <tr class="innertable" style="display:none">
+ <td colspan="3">
+ <div class="table-div">
+ <table class="alt-style">
+
+
+ <tr>
+ <th>VM</th>
+ <th>Flavor</th>
+ <th>Vmtime</th>
+ <th class="last">Charged Credits</th>
+ </tr>
+ {% with data|lookup:'bill_vmtime' as bill_vmtime %}
+ {% for d in bill_vmtime %}
+ {% for vm in d.details %}
+ <tr>
+ <td>{{ vm.resourceName }}</td>
+ <td>flavor?</td>
+ <td>{{ vm.totalElapsedTime|todate|timeuntil:zerodate }}</td>
+ <td class="last">{{ vm.totalUnits }}</td>
+ </tr>
+
+ {% endfor %}
+ {% endfor %}
+ {% endwith %}
+
+
+ </table>
+ </div>
+ </td>
+ </tr>
+ <tr>
+ <td>Pithos +</td>
+ <td>
+ {% with data|lookup:'bill_diskspace' as bill_diskspace %}
+ {{ bill_diskspace.0.totalUnits|floatformat:0}} {{ bill_diskspace.0.unitName }}
+ {% endwith %}
+
+ </td>
+ <td class="last">
+ {% with data|lookup:'bill_diskspace' as bill_diskspace %}
+ {{ bill_diskspace.0.totalCredits|floatformat:2}}
+ {% endwith %}
+
+ </td>
+ </tr>
+ <tr>
+ <td>Total Credits</td>
+ <td> </td>
+
+ <td class="sum last">{{ data.deductedCredits|floatformat:2 }}</td>
+ </tr>
+ </table>
+ {% else %}
+ <p>
+ <em>
+ There is no billing statement for this month.
+ </em>
+ </p>
+ {% endif %}
+
+
+ <form action={% url astakos.im.views.billing %} class="withlabels" method="POST">{% csrf_token %}
+ <div class="form-row">
+ <label for="month">Choose another month</label>
+ <select name="datefrom">
+ {% with user.date_joined|monthssince as periods %}
+ {% for period in periods %}
+ <option value="{{ period.2 }}" {% if period.2 == start %}selected="selected"{% endif%}>
+
+ {{ period.1|month_name }} {{period.0}}
+ </option>
+ {% endfor %}
+ {% endwith %}
+ </select>
+ </div>
+ <div class="form-row submit">
+ <input type="submit" value="VIEW">
+ </div>
+ </form>
+
+ <br><br><br>
+ <p>You can download a detailed activity report in Comma Separated Value (CSV) format or as txt file for AUGUST 2012.<br />
+ <a href="#">Download CSV</a>, <a href="#">Download .txt</a>
+ </p>
+
+</div>
+
+
+{% endblock %}
{% extends "im/account_base.html" %}
{% block body %}
-<form action="{% url astakos.im.views.feedback %}" method="post"
+<form action="{% url feedback %}" method="post"
class="withlabels">{% csrf_token %}
{% with feedback_form as form %}
{% block footer_content %}
-<p class="termslink" style="float:right"><a href="{% url latest_terms %}">Terms of use</a></p>
+<p class="termslink" style="float:right"><a href="{% url latest_terms %}">Terms of service</a></p>
<p>Copyright 2011-2012 <a href="http://www.grnet.gr" target="_blank" title="GRNET S.A.">GRNET S.A.</a> All rights reserved.</p>
{% endblock %}
--- /dev/null
+--- A translation in English follows ---
+
+Έχει δημιουργηθεί το παρακάτω group:
+
+Id: {{group.id}}
+Name: {{group.name}}
+Type: {{group.kind}}
+Issue date: {{group.issue_date|date:"d/m/Y"}}
+Expiration date: {{group.expiration_date|date:"d/m/Y"}}
+Moderation: {{group.moderation_enabled}}
+Owner: {{owner}}
+Maximum participant number: {{group.max_participants}}
+Policies:
+{% for p in policies %}
+ {{p.service}}.{{p.resource}}: uplimit:{% if p.uplimit %}{{p.uplimit}}{% else %}inf{% endif %}
+{% endfor %}
+
+Για την ενεργοποίησή του μπορείτε να χρησιμοποιήσετε το command line εργαλείο:
+snf-manage group_update <group_id> --enable
+--
+
+The following account has been created:
+
+Id: {{group.id}}
+Name: {{group.name}}
+Type: {{group.kind}}
+Issue date: {{group.issue_date|date:"d/m/Y"}}
+Expiration date: {{group.expiration_date|date:"d/m/Y"}}
+Moderation: {{group.moderation_enabled}}
+Owner: {{owner}}
+Maximum participant number: {{group.max_participants}}
+Policies:
+{% for p in policies %}
+ {{p.service}}.{{p.resource}}: uplimit:{% if p.uplimit %}{{p.uplimit}}{% else %}inf{% endif %}
+{% endfor %}
+
+For its activation you can use the command line tool:
+snf-manage group_update <group_id> --enable
{% endwith %}
<div class="form-row submit">
- <input type="submit" class="submit altcol" value="SEND" />
+ <input type="submit" class="submit altcol" value="INVITE" />
</div>
</form>
{% endif %}
<h2>You have <em>{{ inviter.invitations }}</em> invitation{{ inviter.invitations|pluralize }} left.</h2>
{% if sent|length %}
- <table class="zebra-striped id-sorted">
+ <table class="alt-style">
<thead>
<tr>
<th>Email</th>
-{% endblock %}
-
+{% endblock %}
\ No newline at end of file
{% block body %}
-<form action={%url astakos.im.views.edit_profile %} method="post" class="withlabels">{% csrf_token %}
+<form action={%url edit_profile %} method="post" class="withlabels">{% csrf_token %}
{% with profile_form as form %}
{% include "im/form_render.html" %}
</div>
</form>
+
+<div class="two-cols-links">
+ <p><a href="{% url password_change %}">Change Password</a></p>
+ <p>
+ <a href="https://okeanos.grnet.gr/home/">Back to ~okeanos</a>
+ <a href="https://cyclades.okeanos.grnet.gr/ui/">Take me to cyclades</a>
+ <a href="https://pithos.okeanos.grnet.gr/ui/">Take me to pithos+</a>
+ </p>
+</div>
{% endblock body %}
--- /dev/null
+{% extends "im/account_base.html" %}
+{% block page.subnav %}
+
+ <li><a href="../">Overview</a></li>
+ <li class="active"><a href="#">Create</a></li>
+ <li><a href="../join/">Join</a></li>
+
+{%endblock %}
+{% block body %}
+<div class="projects">
+ <div class="clearfix">
+ <p>You can create the followind type of projects:</p>
+ </div>
+
+ <div class="two-cols clearfix dotted">
+ <div class="rt centered">
+ <img alt="THINK ABOUT IT" src="/static/medialibrary/2012/04/proffessor.png">
+ </div>
+ <div class="lt">
+ <h2 style="color:#B66D00;">COURSE</h2>
+ <div>
+ <p>You wake up one morning and you need a new computer with a specific operating system and hardware requirements (or 10 new computers). You also need 10GB of storage space to store some new content you just got your hands on (or 50GB ;-)).</p>
+ <a href="course/" class="submit">CREATE COURSE</a>
+ </div>
+ </div>
+ </div>
+ <div class="two-cols clearfix dotted">
+ <div class="rt centered">
+ <img alt="THINK ABOUT IT" src="/static/medialibrary/2012/06/behind_okeanos.png">
+ </div>
+ <div class="lt">
+ <h2 style="color:#4085A6;">PROJECT</h2>
+ <div>
+ <p>You wake up one morning and you need a new computer with a specific operating system and hardware requirements (or 10 new computers). You also need 10GB of storage space to store some new content you just got your hands on (or 50GB ;-)).</p>
+ <a href="#" class="submit">CREATE PROJECT</a>
+ </div>
+ </div>
+ </div>
+ <div class="two-cols clearfix dotted">
+ <div class="rt centered">
+ <img alt="THINK ABOUT IT" src="/static/medialibrary/2012/06/from_athens.png">
+ </div>
+ <div class="lt">
+ <h2 style="color:#EF4F54;">ORGANISATION</h2>
+ <div>
+ <p>You wake up one morning and you need a new computer with a specific operating system and hardware requirements (or 10 new computers). You also need 10GB of storage space to store some new content you just got your hands on (or 50GB ;-)).</p>
+ <a href="#" class="submit">CREATE ORGANISATION</a>
+ </div>
+ </div>
+ </div>
+ <div class="two-cols clearfix dotted">
+ <div class="rt centered">
+ <img alt="THINK ABOUT IT" src="/static/medialibrary/2012/04/researcher.png">
+ </div>
+ <div class="lt">
+ <h2 style="color:#FF7CA4;">LAB</h2>
+ <div>
+ <p>You wake up one morning and you need a new computer with a specific operating system and hardware requirements (or 10 new computers). You also need 10GB of storage space to store some new content you just got your hands on (or 50GB ;-)).</p>
+ <a href="{% url group_add 'laboratory' %}" class="submit">CREATE LAB</a>
+ </div>
+ </div>
+ </div>
+</div>
+
+{% endblock body %}
{% endblock title%}
{% block body %}
-<form action={%url astakos.im.views.register%} method="post">{% csrf_token %}
+<form action={%url register%} method="post">{% csrf_token %}
{{ form.as_p }}
<div>
<button type="submit" class="btn primary">Register</button>
--- /dev/null
+{% extends "im/account_base.html" %}
+
+{% load filters %}
+
+{% block page.body %}
+<div class="maincol {% block innerpage.class %}{% endblock %}">
+ <div class="stats clearfix">
+ <ul>
+ {% for rname, rdata in resource_catalog.resources.items %}
+ <li class="clearfix {{ rdata.load_class }} {{ rdata.verbose_name}}">
+ <div class="img-wrap"> </div>
+ <div class="info">
+ <h3>{{ rdata.report_desc }}</h3>
+ <p>
+ {{ rdata.ratio|floatformat }}% Used<br>
+ You are using
+ {% if rdata.unit == 'bytes' %}
+ {{ rdata.currValue|sizeof_fmt }} out of your {{ rdata.maxValue|sizeof_fmt }}
+ {% else %}
+ {{ rdata.currValue }} {{ rdata.unit }} out of your {{ rdata.maxValue }} {{ rdata.unit }}
+ {% endif %}
+ {% if rdata.is_abbreviation %}{{ rdata.verbose_name|upper }}{% else %}{{ rdata.verbose_name }}{% endif %}{% if rdata.maxValue|floatformat:"0" != "1" and not rdata.unit %}s {% endif %}
+ </p>
+ </div>
+ <div class="bar">
+ <div><span style="width:{{ rdata.ratio|floatformat }}%;">{{ rdata.ratio|floatformat }}% </span></div>
+ </div>
+ </li>
+ {% endfor%}
+ </ul>
+
+ </div>
+</div>
+{% endblock %}
{% block body.right %}
{% if "local" in im_modules %}
<div class="form-stacked">
- <form action="{% url astakos.im.views.signup %}" method="post"
+ <form action="{% url signup %}" method="post"
class="innerlabels signup">{% csrf_token %}
<h2><span>SIGN UP</span></h2>
<input type="hidden" name="next" value="{{ next }}">
</div>
<div class="section">
- <form action="{% url astakos.im.views.signup %}" method="post" class="login innerlabels">{% csrf_token %}
+ <form action="{% url signup %}" method="post" class="login innerlabels">{% csrf_token %}
{% with thirdparty_signup_form as form %}
{% include "im/form_render.html" %}
{% endwith %}
{% block body.right %}
{% if "local" in im_modules %}
<div class="form-stacked">
- <form action="{% url astakos.im.views.signup %}" method="post"
+ <form action="{% url signup %}" method="post"
class="innerlabels signup">{% csrf_token %}
<h2><span>SIGN UP</span></h2>
<input type="hidden" name="next" value="{{ next }}">
--- /dev/null
+{% extends "im/account_base.html" %}
+
+{% block page.body %}
+<div class="projects">
+ <div class="maincol {% block innerpage.class %}{% endblock %}">
+ <form action="" method="post"
+ class="withlabels">{% csrf_token %}
+ {% include "im/form_render.html" %}
+ <div class="form-row submit">
+ <input type="submit" class="submit altcol" value="SUBMIT" />
+ </div>
+ </form>
+ </div>
+</div>
+<div class="timeline">
+<table>
+ <tr style="color:black; font-weight:bold;">
+ {% for i in timeline_header %}
+ <td>{{i}}</td>
+ {% endfor %}
+ </tr>
+{% for o in timeline_body %}
+ <tr style="color:black">
+ {% for i in o %}
+ <td>{{i}}</td>
+ {% endfor %}
+ </tr>
+{% endfor %}
+</table>
+</div>
+{% endblock %}
{% extends "im/account_base.html" %}
{% block body %}
-<form action="{% url astakos.im.views.change_email %}" method="post"
+<form action="{% url email_change %}" method="post"
class="withlabels">{% csrf_token %}
{% include "im/form_render.html" %}
{%block page.title %}Logout{% endblock %}
{% block body %}
<div class="section">
- <h2>You have successfully logged out. <a href="{% url astakos.im.views.index %}">Login</a>.</h2>
+ <h2>You have successfully logged out. <a href="{% url index %}">Login</a>.</h2>
</div>
{% endblock %}
from django import template
-from django.core.urlresolvers import reverse, resolve
+from django.core.urlresolvers import resolve
from django.conf import settings
register = template.Library()
'astakos.im.views.feedback': 'PROFILE_MESSAGES',
})
+
@register.tag(name='display_messages')
def display_messages(parser, token):
return MessagesNode()
+
class DummyMessage(object):
def __init__(self, type, msg):
self.message = msg
def __repr__(self):
return "%s: %s" % (self.tags, self.message)
+
class MessagesNode(template.Node):
def get_view_messages(self, context):
cls = messages[-1].tags
content = '<div class="top-msg active %s">' % cls
for msg in messages:
- content += '<div class="msg %s">%s</div>' % (msg.tags, msg.message)
+ content += '<div class="msg %s">%s</div>' % (
+ msg.tags, msg.message)
content += '<a href="#" title="close" class="close">X</a>'
content += '</div>'
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.
+import calendar
+import datetime
+import math
+
+from collections import defaultdict
+
from django import template
+from django.core.paginator import Paginator, EmptyPage
+from django.db.models.query import QuerySet
+
+
+from astakos.im.settings import PAGINATE_BY
+
register = template.Library()
+DELIM = ','
+
+
+@register.filter
+def monthssince(joined_date):
+ now = datetime.datetime.now()
+ date = datetime.datetime(
+ year=joined_date.year, month=joined_date.month, day=1)
+ months = []
+
+ month = date.month
+ year = date.year
+ timestamp = calendar.timegm(date.utctimetuple())
+
+ while date < now:
+ months.append((year, month, timestamp))
+
+ if date.month < 12:
+ month = date.month + 1
+ year = date.year
+ else:
+ month = 1
+ year = date.year + 1
+
+ date = datetime.datetime(year=year, month=month, day=1)
+ timestamp = calendar.timegm(date.utctimetuple())
+
+ return months
+
+
@register.filter
def lookup(d, key):
- return d[key]
\ No newline at end of file
+ print d, key
+ return d.get(key)
+
+@register.filter
+def lookup_uni(d, key):
+ return d.get(unicode(key))
+
+
+@register.filter
+def dkeys(d):
+ return d.keys()
+
+
+@register.filter
+def month_name(month_number):
+ return calendar.month_name[month_number]
+
+
+@register.filter
+def todate(value, arg=''):
+ secs = int(value) / 1000
+ return datetime.datetime.fromtimestamp(secs)
+
+
+@register.filter
+def rcut(value, chars='/'):
+ return value.rstrip(chars)
+
+
+@register.filter
+def paginate(l, args):
+ l = l or []
+ page, delim, sorting = args.partition(DELIM)
+ if sorting:
+ if isinstance(l, QuerySet):
+ l = l.order_by(sorting)
+ elif isinstance(l, list):
+ default = ''
+ if sorting.endswith('_date'):
+ default = datetime.datetime.utcfromtimestamp(0)
+ l.sort(key=lambda i: getattr(i, sorting)
+ if getattr(i, sorting) else default)
+ paginator = Paginator(l, PAGINATE_BY)
+ try:
+ paginator.len
+ except AttributeError:
+ paginator._count = len(list(l))
+
+ try:
+ page_number = int(page)
+ except ValueError:
+ if page == 'last':
+ page_number = paginator.num_pages
+ else:
+ page_number = 1
+ try:
+ page = paginator.page(page_number)
+ except EmptyPage:
+ page = paginator.page(1)
+ return page
+
+
+@register.filter
+def concat(str1, str2):
+ if not str2:
+ return str(str1)
+ return '%s%s%s' % (str1, DELIM, str2)
+
+
+@register.filter
+def items(d):
+ if isinstance(d, defaultdict):
+ return d.iteritems()
+ return d
+
+
+@register.filter
+def get_value_after_dot(value):
+ return value.split(".")[1]
+
+@register.filter
+def strip_http(value):
+ return value.replace('http://','')[:-1]
+
+
+from math import log
+unit_list = zip(['bytes', 'kB', 'MB', 'GB', 'TB', 'PB'], [0, 0, 0, 0, 0, 0])
+
+@register.filter
+def sizeof_fmt(num):
+
+ """Human friendly file size"""
+ if math.isinf(num):
+ return 'Unlimited'
+ if num > 1:
+ exponent = min(int(log(num, 1024)), len(unit_list) - 1)
+ quotient = float(num) / 1024**exponent
+ unit, num_decimals = unit_list[exponent]
+ format_string = '{0:.%sf} {1}' % (num_decimals)
+ return format_string.format(quotient, unit)
+ if num == 0:
+ return '0 bytes'
+ if num == 1:
+ return '1 byte'
+ else:
+ return '';
+
+@register.filter
+def isinf(v):
+ if math.isinf(v):
+ return 'Unlimited'
+ else:
+ return v
\ No newline at end of file
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.
-from django.conf.urls.defaults import patterns, include, url
-from django.contrib.auth.views import password_change
+from django.conf.urls.defaults import patterns, url
from astakos.im.forms import (ExtendedPasswordResetForm,
ExtendedPasswordChangeForm,
urlpatterns = patterns('astakos.im.views',
url(r'^$', 'index', {}, name='index'),
url(r'^login/?$', 'index', {}, name='login'),
- url(r'^profile/?$', 'edit_profile'),
- url(r'^feedback/?$', 'feedback'),
- url(r'^signup/?$', 'signup', {'on_success':'im/login.html', 'extra_context':{'login_form':LoginForm()}}),
- url(r'^logout/?$', 'logout', {'template':'im/login.html', 'extra_context':{'login_form':LoginForm()}}),
- url(r'^activate/?$', 'activate'),
+ url(r'^profile/?$','edit_profile', {}, name='edit_profile'),
+ url(r'^feedback/?$', 'feedback', {}, name='feedback'),
+ url(r'^signup/?$', 'signup', {'on_success': 'im/login.html', 'extra_context': {'login_form': LoginForm()}}, name='signup'),
+ url(r'^logout/?$', 'logout', {'template': 'im/login.html', 'extra_context': {'login_form': LoginForm()}}, name='logout'),
+ url(r'^activate/?$', 'activate', {}, name='activate'),
url(r'^approval_terms/?$', 'approval_terms', {}, name='latest_terms'),
url(r'^approval_terms/(?P<term_id>\d+)/?$', 'approval_terms'),
- url(r'^send/activation/(?P<user_id>\d+)/?$', 'send_activation', {}, name='send_activation')
+ url(r'^send/activation/(?P<user_id>\d+)/?$', 'send_activation', {}, name='send_activation'),
+ url(r'^resources/?$', 'resource_list', {}, name='resource_list'),
+ url(r'^billing/?$', 'billing', {}, name='billing'),
+ url(r'^timeline/?$', 'timeline', {}, name='timeline'),
+ url(r'^group/add/complete/?$', 'group_add_complete', {}, name='group_add_complete'),
+ url(r'^group/add/(?P<kind_name>\w+)?$', 'group_add', {}, name='group_add'),
+ url(r'^group/list/?$', 'group_list', {}, name='group_list'),
+ url(r'^group/(?P<group_id>\d+)/?$', 'group_detail', {}, name='group_detail'),
+ url(r'^group/search/?$', 'group_search', {}, name='group_search'),
+ url(r'^group/all/?$', 'group_all', {}, name='group_all'),
+ url(r'^group/(?P<group_id>\d+)/join/?$', 'group_join', {}, name='group_join'),
+ url(r'^group/(?P<group_id>\d+)/leave/?$', 'group_leave', {}, name='group_leave'),
+ url(r'^group/(?P<group_id>\d+)/(?P<user_id>\d+)/approve/?$', 'approve_member', {}, name='approve_member'),
+ url(r'^group/(?P<group_id>\d+)/(?P<user_id>\d+)/disapprove/?$', 'disapprove_member', {}, name='disapprove_member'),
+ url(r'^group/create/?$', 'group_create_list', {}, name='group_create_list')
)
+
if EMAILCHANGE_ENABLED:
urlpatterns += patterns('astakos.im.views',
url(r'^email_change/?$', 'change_email', {}, name='email_change'),
)
urlpatterns += patterns('astakos.im.target',
- url(r'^login/redirect/?$', 'redirect.login')
-)
+ url(r'^login/redirect/?$', 'redirect.login')
+ )
if 'local' in IM_MODULES:
urlpatterns += patterns('astakos.im.target',
if INVITATIONS_ENABLED:
urlpatterns += patterns('astakos.im.views',
- url(r'^invite/?$', 'invite')
- )
+ url(r'^invite/?$', 'invite', {}, name='invite')
+ )
if 'shibboleth' in IM_MODULES:
urlpatterns += patterns('astakos.im.target',
if 'twitter' in IM_MODULES:
urlpatterns += patterns('astakos.im.target',
- url(r'^login/twitter/?$', 'twitter.login'),
- url(r'^login/twitter/authenticated/?$', 'twitter.authenticated')
- )
+ url(r'^login/twitter/?$', 'twitter.login'),
+ url(r'^login/twitter/authenticated/?$',
+ 'twitter.authenticated')
+ )
+
+urlpatterns += patterns('astakos.im.api',
+ url(r'^get_services/?$', 'get_services'),
+ url(r'^get_menu/?$', 'get_menu'),
+ )
urlpatterns += patterns('astakos.im.api.admin',
- url(r'^authenticate/?$', 'authenticate_old'),
- #url(r'^authenticate/v2/?$', 'authenticate'),
- url(r'^get_services/?$', 'get_services'),
- url(r'^get_menu/?$', 'get_menu'),
- url(r'^admin/api/v2.0/users/?$', 'get_user_by_email'),
- url(r'^admin/api/v2.0/users/(?P<user_id>.+?)/?$', 'get_user_by_username'),
-)
+ url(r'^authenticate/?$', 'authenticate_old'),
+ #url(r'^authenticate/v2/?$', 'authenticate'),
+ url(r'^admin/api/v2.0/users/?$', 'get_user_by_email'),
+ url(r'^admin/api/v2.0/users/(?P<user_id>.+?)/?$',
+ 'get_user_by_username'),
+ )
urlpatterns += patterns('astakos.im.api.service',
- #url(r'^service/api/v2.0/tokens/(?P<token_id>.+?)/?$', 'validate_token'),
- url(r'^service/api/v2.0/feedback/?$', 'send_feedback'),
- url(r'^service/api/v2.0/users/?$', 'get_user_by_email'),
- url(r'^service/api/v2.0/users/(?P<user_id>.+?)/?$', 'get_user_by_username'),
-)
\ No newline at end of file
+ #url(r'^service/api/v2.0/tokens/(?P<token_id>.+?)/?$', 'validate_token'),
+ url(r'^service/api/v2.0/feedback/?$', 'send_feedback'),
+ url(r'^service/api/v2.0/users/?$',
+ 'get_user_by_email'),
+ url(r'^service/api/v2.0/users/(?P<user_id>.+?)/?$',
+ 'get_user_by_username'),
+ )
# 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
# 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
import datetime
import time
-from urllib import quote
-from urlparse import urlsplit, urlunsplit, urlparse
-
+from urlparse import urlparse
from datetime import tzinfo, timedelta
+
from django.http import HttpResponse, HttpResponseBadRequest, urlencode
from django.template import RequestContext
-from django.utils.translation import ugettext as _
from django.contrib.auth import authenticate
from django.core.urlresolvers import reverse
-from django.core.exceptions import ValidationError
-from django.contrib.sessions.backends.base import SessionBase
+from django.core.exceptions import ValidationError, ObjectDoesNotExist
+from django.utils.translation import ugettext as _
-from astakos.im.models import AstakosUser, Invitation, ApprovalTerms
+from astakos.im.models import AstakosUser, Invitation
from astakos.im.settings import (
- INVITATIONS_PER_LEVEL, COOKIE_DOMAIN, FORCE_PROFILE_UPDATE, LOGGING_LEVEL
+ COOKIE_DOMAIN, FORCE_PROFILE_UPDATE
)
from astakos.im.functions import login
+import astakos.im.messages as astakos_messages
+
logger = logging.getLogger(__name__)
+
class UTC(tzinfo):
- def utcoffset(self, dt):
- return timedelta(0)
+ def utcoffset(self, dt):
+ return timedelta(0)
+
+ def tzname(self, dt):
+ return 'UTC'
- def tzname(self, dt):
- return 'UTC'
+ def dst(self, dt):
+ return timedelta(0)
- def dst(self, dt):
- return timedelta(0)
def isoformat(d):
- """Return an ISO8601 date string that includes a timezone."""
+ """Return an ISO8601 date string that includes a timezone."""
+
+ return d.replace(tzinfo=UTC()).isoformat()
- return d.replace(tzinfo=UTC()).isoformat()
def epoch(datetime):
- return int(time.mktime(datetime.timetuple())*1000)
+ return int(time.mktime(datetime.timetuple()) * 1000)
+
-def get_context(request, extra_context={}, **kwargs):
- if not extra_context:
- extra_context = {}
+def get_context(request, extra_context=None, **kwargs):
+ extra_context = extra_context or {}
extra_context.update(kwargs)
return RequestContext(request, extra_context)
+
def get_invitation(request):
"""
Returns the invitation identified by the ``code``.
-
+
Raises ValueError if the invitation is consumed or there is another account
associated with this email.
"""
code = request.POST.get('code')
if not code:
return
- invitation = Invitation.objects.get(code = code)
+ 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_RESERVED) % locals())
return invitation
def restrict_next(url, domain=None, allowed_schemes=()):
as 'X-Auth-User' and 'X-Auth-Token' headers,
or redirect to the URL provided in 'next'
with the 'user' and 'token' as parameters.
-
+
Reissue the token even if it has not yet
expired, if the 'renew' parameter is present
or user has not a valid token.
"""
renew = renew or (not user.auth_token)
- renew = renew or (user.auth_token_expires and user.auth_token_expires < datetime.datetime.now())
+ renew = renew or (user.auth_token_expires < datetime.datetime.now())
if renew:
user.renew_token(
flush_sessions=True,
params = ''
if next:
params = '?' + urlencode({'next': next})
- next = reverse('astakos.im.views.edit_profile') + params
-
+ next = reverse('edit_profile') + params
+
response = HttpResponse()
-
+
# authenticate before login
user = authenticate(email=user.email, auth_token=user.auth_token)
login(request, user)
request.session.set_expiry(user.auth_token_expires)
-
+
if not next:
next = reverse('astakos.im.views.index')
class lazy_string(object):
def __init__(self, function, *args, **kwargs):
- self.function=function
- self.args=args
- self.kwargs=kwargs
-
+ self.function = function
+ self.args = args
+ self.kwargs = kwargs
+
def __str__(self):
if not hasattr(self, 'str'):
- self.str=self.function(*self.args, **self.kwargs)
+ self.str = self.function(*self.args, **self.kwargs)
return self.str
+
def reverse_lazy(*args, **kwargs):
return lazy_string(reverse, *args, **kwargs)
+
def reserved_email(email):
- return AstakosUser.objects.filter(email = email).count() != 0
+ return AstakosUser.objects.filter(email__iexact=email).count() != 0
+
def get_query(request):
try:
return request.__getattribute__(request.method)
except AttributeError:
- return {}
\ No newline at end of file
+ return {}
+
+
+def model_to_dict(obj, exclude=['AutoField', 'ForeignKey', 'OneToOneField'],
+ include_empty=True):
+ '''
+ serialize model object to dict with related objects
+
+ author: Vadym Zakovinko <vp@zakovinko.com>
+ date: January 31, 2011
+ http://djangosnippets.org/snippets/2342/
+ '''
+ tree = {}
+ for field_name in obj._meta.get_all_field_names():
+ try:
+ field = getattr(obj, field_name)
+ except (ObjectDoesNotExist, AttributeError):
+ continue
+
+ if field.__class__.__name__ in ['RelatedManager', 'ManyRelatedManager']:
+ if field.model.__name__ in exclude:
+ continue
+
+ if field.__class__.__name__ == 'ManyRelatedManager':
+ exclude.append(obj.__class__.__name__)
+ subtree = []
+ for related_obj in getattr(obj, field_name).all():
+ value = model_to_dict(related_obj, exclude=exclude)
+ if value or include_empty:
+ subtree.append(value)
+ if subtree or include_empty:
+ tree[field_name] = subtree
+ continue
+
+ field = obj._meta.get_field_by_name(field_name)[0]
+ if field.__class__.__name__ in exclude:
+ continue
+
+ if field.__class__.__name__ == 'RelatedObject':
+ exclude.append(field.model.__name__)
+ tree[field_name] = model_to_dict(getattr(obj, field_name),
+ exclude=exclude)
+ continue
+
+ value = getattr(obj, field_name)
+ if field.__class__.__name__ == 'ForeignKey':
+ value = unicode(value) if value is not None else value
+ if value or include_empty:
+ tree[field_name] = value
+
+ return tree
# or implied, of GRNET S.A.
import logging
-import socket
+import calendar
+import inflect
+
+engine = inflect.engine()
-from smtplib import SMTPException
from urllib import quote
from functools import wraps
+from datetime import datetime
-from django.core.mail import send_mail
-from django.http import (
- HttpResponse, HttpResponseBadRequest, HttpResponseRedirect
-)
-from django.shortcuts import redirect
-from django.template.loader import render_to_string
-from django.utils.translation import ugettext as _
-from django.core.urlresolvers import reverse
-from django.contrib.auth.decorators import login_required
from django.contrib import messages
+from django.contrib.auth.decorators import login_required
+from django.core.urlresolvers import reverse
from django.db import transaction
-from django.utils.http import urlencode
from django.db.utils import IntegrityError
-from django.contrib.auth.views import password_change
-from django.core.exceptions import ValidationError
-from django.views.decorators.http import require_http_methods
+from django.http import (HttpResponse, HttpResponseBadRequest,
+ HttpResponseForbidden, HttpResponseRedirect,
+ HttpResponseBadRequest, Http404)
+from django.shortcuts import redirect
+from django.template import RequestContext, loader as template_loader
+from django.utils.http import urlencode
+from django.utils.translation import ugettext as _
+from django.views.generic.create_update import (delete_object,
+ get_model_and_form_class)
+from django.views.generic.list_detail import object_list
+from django.core.xheaders import populate_xheaders
-from astakos.im.models import AstakosUser, Invitation, ApprovalTerms
+from django.template.loader import render_to_string
+from django.views.decorators.http import require_http_methods
from astakos.im.activation_backends import get_backend, SimpleBackend
-from astakos.im.util import (
- get_context, prepare_response, get_query, restrict_next
-)
-from astakos.im.forms import *
-from astakos.im.functions import (send_greeting, send_feedback, SendMailError,
- invite as invite_func, logout as auth_logout, activate as activate_func,
- send_activation as send_activation_func
-)
-from astakos.im.settings import (
- DEFAULT_CONTACT_EMAIL, DEFAULT_FROM_EMAIL, COOKIE_DOMAIN, IM_MODULES,
- SITENAME, LOGOUT_NEXT, LOGGING_LEVEL
-)
+
+from astakos.im.models import (AstakosUser, ApprovalTerms, AstakosGroup,
+ EmailChange, GroupKind, Membership,
+ RESOURCE_SEPARATOR)
+from astakos.im.util import get_context, prepare_response, get_query, restrict_next
+from astakos.im.forms import (LoginForm, InvitationForm, ProfileForm,
+ FeedbackForm, SignApprovalTermsForm,
+ EmailChangeForm,
+ AstakosGroupCreationForm, AstakosGroupSearchForm,
+ AstakosGroupUpdateForm, AddGroupMembersForm,
+ MembersSortForm,
+ TimelineForm, PickResourceForm,
+ AstakosGroupCreationSummaryForm)
+from astakos.im.functions import (send_feedback, SendMailError,
+ logout as auth_logout,
+ activate as activate_func,
+ send_activation as send_activation_func,
+ send_group_creation_notification,
+ SendNotificationError)
+from astakos.im.endpoints.qh import timeline_charge
+from astakos.im.settings import (COOKIE_DOMAIN, LOGOUT_NEXT,
+ LOGGING_LEVEL, PAGINATE_BY, RESOURCES_PRESENTATION_DATA)
+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__)
+DB_REPLACE_GROUP_SCHEME = """REPLACE(REPLACE("auth_group".name, 'http://', ''),
+ 'https://', '')"""
+
+callpoint = AstakosCallpoint()
+
def render_response(template, tab=None, status=200, context_instance=None, **kwargs):
"""
Calls ``django.template.loader.render_to_string`` with an additional ``tab``
if tab is None:
tab = template.partition('_')[0].partition('.html')[0]
kwargs.setdefault('tab', tab)
- html = render_to_string(template, kwargs, context_instance=context_instance)
+ html = template_loader.render_to_string(
+ template, kwargs, context_instance=context_instance)
response = HttpResponse(html, status=status)
return response
return func(request, *args)
return wrapper
+
def signed_terms_required(func):
"""
Decorator checkes whether the request.user is Anonymous and in that case
"""
@wraps(func)
def wrapper(request, *args, **kwargs):
- if request.user.is_authenticated() and not request.user.signed_terms():
+ if request.user.is_authenticated() and not request.user.signed_terms:
params = urlencode({'next': request.build_absolute_uri(),
- 'show_form':''})
+ 'show_form': ''})
terms_uri = reverse('latest_terms') + '?' + params
return HttpResponseRedirect(terms_uri)
return func(request, *args, **kwargs)
return wrapper
+
@require_http_methods(["GET", "POST"])
@signed_terms_required
def index(request, login_template_name='im/login.html', profile_template_name='im/profile.html', extra_context=None):
context_instance = get_context(request, extra_context)
)
+
@require_http_methods(["GET", "POST"])
@login_required
@signed_terms_required
The view expectes the following settings are defined:
* LOGIN_URL: login uri
- * ASTAKOS_DEFAULT_CONTACT_EMAIL: service support email
- * ASTAKOS_DEFAULT_FROM_EMAIL: from email
"""
extra_context = extra_context or {}
status = None
message = None
form = InvitationForm()
-
+
inviter = request.user
if request.method == 'POST':
form = InvitationForm(request.POST)
if inviter.invitations > 0:
if form.is_valid():
try:
- invitation = form.save()
- invite_func(invitation, inviter)
- status = messages.SUCCESS
- message = _('Invitation sent to %s' % invitation.username)
+ email = form.cleaned_data.get('username')
+ realname = form.cleaned_data.get('realname')
+ inviter.invite(email, realname)
+ message = _(astakos_messages.INVITATION_SENT) % locals()
+ messages.success(request, message)
except SendMailError, e:
- status = messages.ERROR
message = e.message
+ messages.error(request, message)
transaction.rollback()
except BaseException, e:
- status = messages.ERROR
- message = _('Something went wrong.')
+ message = _(astakos_messages.GENERIC_ERROR)
+ messages.error(request, message)
logger.exception(e)
transaction.rollback()
else:
transaction.commit()
else:
- status = messages.ERROR
- message = _('No invitations left')
- messages.add_message(request, status, message)
+ message = _(astakos_messages.MAX_INVITATION_NUMBER_REACHED)
+ messages.error(request, message)
sent = [{'email': inv.username,
'realname': inv.realname,
'is_consumed': inv.is_consumed}
- for inv in request.user.invitations_sent.all()]
+ for inv in request.user.invitations_sent.all()]
kwargs = {'inviter': inviter,
- 'sent':sent}
+ 'sent': sent}
context = get_context(request, extra_context, **kwargs)
return render_response(template_name,
- invitation_form = form,
- context_instance = context)
+ invitation_form=form,
+ context_instance=context)
+
@require_http_methods(["GET", "POST"])
@login_required
)
if next:
return redirect(next)
- msg = _('<p>Profile has been updated successfully</p>')
- messages.add_message(request, messages.SUCCESS, msg)
+ msg = _(astakos_messages.PROFILE_UPDATED)
+ messages.success(request, msg)
except ValueError, ve:
- messages.add_message(request, messages.ERROR, ve)
+ messages.success(request, ve)
elif request.method == "GET":
- request.user.is_verified = True
- request.user.save()
+ if not request.user.is_verified:
+ request.user.is_verified = True
+ request.user.save()
return render_response(template_name,
profile_form = form,
context_instance = get_context(request,
extra_context))
+
+@transaction.commit_manually
@require_http_methods(["GET", "POST"])
def signup(request, template_name='im/signup.html', on_success='im/signup_complete.html', extra_context=None, backend=None):
"""
if present, otherwise to the ``astakos.im.activation_backends.InvitationBackend``
if settings.ASTAKOS_INVITATIONS_ENABLED is True or ``astakos.im.activation_backends.SimpleBackend`` if not
(see activation_backends);
-
+
Upon successful user creation, if ``next`` url parameter is present the user is redirected there
otherwise renders the same page with a success message.
-
+
On unsuccessful creation, renders ``template_name`` with an error message.
-
+
**Arguments**
-
+
``template_name``
A custom template to render. This is optional;
if not specified, this will default to ``im/signup.html``.
An dictionary of variables to add to the template context.
**Template:**
-
+
im/signup.html or ``template_name`` keyword argument.
- im/signup_complete.html or ``on_success`` keyword argument.
+ im/signup_complete.html or ``on_success`` keyword argument.
"""
extra_context = extra_context or {}
if request.user.is_authenticated():
- return HttpResponseRedirect(reverse('astakos.im.views.edit_profile'))
-
+ return HttpResponseRedirect(reverse('edit_profile'))
+
provider = get_query(request).get('provider', 'local')
id = get_query(request).get('id')
try:
form = backend.get_signup_form(provider, instance)
except Exception, e:
form = SimpleBackend(request).get_signup_form(provider)
- messages.add_message(request, messages.ERROR, e)
+ messages.error(request, e)
if request.method == 'POST':
if form.is_valid():
user = form.save(commit=False)
logger._log(LOGGING_LEVEL, msg, [])
if user and user.is_active:
next = request.POST.get('next', '')
- return prepare_response(request, user, next=next)
+ response = prepare_response(request, user, next=next)
+ transaction.commit()
+ return response
messages.add_message(request, status, message)
return render_response(
on_success,
logger.exception(e)
status = messages.ERROR
message = e.message
- messages.add_message(request, status, message)
+ messages.error(request, message)
+ transaction.rollback()
except BaseException, e:
logger.exception(e)
- status = messages.ERROR
- message = _('Something went wrong.')
- messages.add_message(request, status, message)
+ message = _(astakos_messages.GENERIC_ERROR)
+ messages.error(request, message)
logger.exception(e)
+ transaction.rollback()
return render_response(template_name,
- signup_form = form,
- provider = provider,
+ signup_form=form,
+ provider=provider,
context_instance=get_context(request, extra_context))
+
@require_http_methods(["GET", "POST"])
@login_required
@signed_terms_required
**Settings:**
* LOGIN_URL: login uri
- * ASTAKOS_DEFAULT_CONTACT_EMAIL: List of feedback recipients
"""
extra_context = extra_context or {}
if request.method == 'GET':
try:
send_feedback(msg, data, request.user, email_template_name)
except SendMailError, e:
- message = e.message
- status = messages.ERROR
+ messages.error(request, message)
else:
- message = _('Feedback successfully sent')
- status = messages.SUCCESS
- messages.add_message(request, status, message)
+ message = _(astakos_messages.FEEDBACK_SENT)
+ messages.success(request, message)
return render_response(template_name,
- feedback_form = form,
- context_instance = get_context(request, extra_context))
+ feedback_form=form,
+ context_instance=get_context(request, extra_context))
+
@require_http_methods(["GET"])
+@signed_terms_required
def logout(request, template='registration/logged_out.html', extra_context=None):
"""
Wraps `django.contrib.auth.logout`.
response['Location'] = LOGOUT_NEXT
response.status_code = 301
else:
- messages.add_message(request, messages.SUCCESS, _('<p>You have successfully logged out.</p>'))
+ messages.add_message(request, messages.SUCCESS, _(astakos_messages.LOGOUT_SUCCESS))
context = get_context(request, extra_context)
response.write(render_to_string(template, context_instance=context))
return response
+
@require_http_methods(["GET", "POST"])
@transaction.commit_manually
-def activate(request, greeting_email_template_name='im/welcome_email.txt', helpdesk_email_template_name='im/helpdesk_notification.txt'):
+def activate(request, greeting_email_template_name='im/welcome_email.txt',
+ helpdesk_email_template_name='im/helpdesk_notification.txt'):
"""
Activates the user identified by the ``auth`` request parameter, sends a welcome email
and renews the user token.
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.')
- messages.add_message(request, messages.ERROR, message)
+ message = _(astakos_messages.ACCOUNT_ALREADY_ACTIVE)
+ messages.error(request, message)
return index(request)
-
+
try:
activate_func(user, greeting_email_template_name, helpdesk_email_template_name, verify_email=True)
response = prepare_response(request, user, next, renew=True)
return index(request)
except BaseException, e:
status = messages.ERROR
- message = _('Something went wrong.')
+ message = _(astakos_messages.GENERIC_ERROR)
messages.add_message(request, messages.ERROR, message)
logger.exception(e)
transaction.rollback()
return index(request)
+
@require_http_methods(["GET", "POST"])
def approval_terms(request, term_id=None, template_name='im/approval_terms.html', extra_context=None):
extra_context = extra_context or {}
pass
else:
try:
- term = ApprovalTerms.objects.get(id=term_id)
- except ApprovalTermDoesNotExist, e:
+ term = ApprovalTerms.objects.get(id=term_id)
+ except ApprovalTerms.DoesNotExist, e:
pass
if not term:
- return HttpResponseRedirect(reverse('astakos.im.views.index'))
+ messages.error(request, _(astakos_messages.NO_APPROVAL_TERMS))
+ return HttpResponseRedirect(reverse('index'))
f = open(term.location, 'r')
terms = f.read()
domain=COOKIE_DOMAIN
)
if not next:
- next = reverse('astakos.im.views.index')
+ next = reverse('index')
form = SignApprovalTermsForm(request.POST, instance=request.user)
if not form.is_valid():
return render_response(template_name,
- terms = terms,
- approval_terms_form = form,
- context_instance = get_context(request, extra_context))
+ terms=terms,
+ approval_terms_form=form,
+ context_instance=get_context(request, extra_context))
user = form.save()
return HttpResponseRedirect(next)
else:
form = None
- if request.user.is_authenticated() and not request.user.signed_terms():
+ if request.user.is_authenticated() and not request.user.signed_terms:
form = SignApprovalTermsForm(instance=request.user)
return render_response(template_name,
- terms = terms,
- approval_terms_form = form,
- context_instance = get_context(request, extra_context))
+ terms=terms,
+ approval_terms_form=form,
+ context_instance=get_context(request, extra_context))
+
@require_http_methods(["GET", "POST"])
@login_required
try:
user = EmailChange.objects.change_email(activation_key)
if request.user.is_authenticated() and request.user == user:
- msg = _('Email changed successfully.')
- messages.add_message(request, messages.SUCCESS, msg)
+ msg = _(astakos_messages.EMAIL_CHANGED)
+ messages.success(request, msg)
auth_logout(request)
response = prepare_response(request, user)
transaction.commit()
return response
except ValueError, e:
- messages.add_message(request, messages.ERROR, e)
+ messages.error(request, e)
return render_response(confirm_template_name,
- modified_user = user if 'user' in locals() else None,
- context_instance = get_context(request,
- extra_context))
-
+ modified_user=user if 'user' in locals(
+ ) else None,
+ context_instance=get_context(request,
+ extra_context))
+
if not request.user.is_authenticated():
path = quote(request.get_full_path())
- url = request.build_absolute_uri(reverse('astakos.im.views.index'))
+ url = request.build_absolute_uri(reverse('index'))
return HttpResponseRedirect(url + '?next=' + path)
form = EmailChangeForm(request.POST or None)
if request.method == 'POST' and form.is_valid():
try:
ec = form.save(email_template_name, request)
except SendMailError, e:
- status = messages.ERROR
msg = e
+ messages.error(request, msg)
transaction.rollback()
except IntegrityError, e:
- status = messages.ERROR
- msg = _('There is already a pending change email request.')
+ msg = _(astakos_messages.PENDING_EMAIL_CHANGE_REQUEST)
+ messages.error(request, msg)
else:
- status = messages.SUCCESS
- 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()
- messages.add_message(request, status, msg)
- return render_response(form_template_name,
- form = form,)
+ return render_response(
+ form_template_name,
+ form=form,
+ context_instance=get_context(request, extra_context)
+ )
def send_activation(request, user_id, template_name='im/login.html', extra_context=None):
try:
u = AstakosUser.objects.get(id=user_id)
except AstakosUser.DoesNotExist:
- messages.error(request, _('Invalid user id'))
+ messages.error(request, _(astakos_messages.ACCOUNT_UNKNOWN))
else:
try:
send_activation_func(u)
- msg = _('Activation sent.')
+ msg = _(astakos_messages.ACTIVATION_SENT)
messages.success(request, msg)
except SendMailError, e:
messages.error(request, e)
return render_response(
template_name,
- login_form = LoginForm(request=request),
+ login_form = LoginForm(request=request),
context_instance = get_context(
request,
extra_context
)
)
+
+class ResourcePresentation():
+
+ def __init__(self, data):
+ self.data = data
+
+ def update_from_result(self, result):
+ if result.is_success:
+ for r in result.data:
+ rname = '%s%s%s' % (r.get('service'), RESOURCE_SEPARATOR, r.get('name'))
+ if not rname in self.data['resources']:
+ self.data['resources'][rname] = {}
+
+ self.data['resources'][rname].update(r)
+ self.data['resources'][rname]['id'] = rname
+ group = r.get('group')
+ if not group in self.data['groups']:
+ self.data['groups'][group] = {}
+
+ self.data['groups'][r.get('group')].update({'name': r.get('group')})
+
+ def test(self, quota_dict):
+ for k, v in quota_dict.iteritems():
+ rname = k
+ value = v
+ if not rname in self.data['resources']:
+ self.data['resources'][rname] = {}
+
+
+ self.data['resources'][rname]['value'] = value
+
+
+ def update_from_result_report(self, result):
+ if result.is_success:
+ for r in result.data:
+ rname = r.get('name')
+ if not rname in self.data['resources']:
+ self.data['resources'][rname] = {}
+
+ self.data['resources'][rname].update(r)
+ self.data['resources'][rname]['id'] = rname
+ group = r.get('group')
+ if not group in self.data['groups']:
+ self.data['groups'][group] = {}
+
+ self.data['groups'][r.get('group')].update({'name': r.get('group')})
+
+ def get_group_resources(self, group):
+ return dict(filter(lambda t: t[1].get('group') == group, self.data['resources'].iteritems()))
+
+ def get_groups_resources(self):
+ for g in self.data['groups']:
+ yield g, self.get_group_resources(g)
+
+ def get_quota(self, group_quotas):
+ for r, v in group_quotas.iteritems():
+ rname = str(r)
+ quota = self.data['resources'].get(rname)
+ quota['value'] = v
+ yield quota
+
+
+ def get_policies(self, policies_data):
+ for policy in policies_data:
+ rname = '%s%s%s' % (policy.get('service'), RESOURCE_SEPARATOR, policy.get('resource'))
+ policy.update(self.data['resources'].get(rname))
+ yield policy
+
+ def __repr__(self):
+ return self.data.__repr__()
+
+ def __iter__(self, *args, **kwargs):
+ return self.data.__iter__(*args, **kwargs)
+
+ def __getitem__(self, *args, **kwargs):
+ return self.data.__getitem__(*args, **kwargs)
+
+ def get(self, *args, **kwargs):
+ return self.data.get(*args, **kwargs)
+
+
+
+@require_http_methods(["GET", "POST"])
+@signed_terms_required
+@login_required
+def group_add(request, kind_name='default'):
+
+ result = callpoint.list_resources()
+ resource_catalog = ResourcePresentation(RESOURCES_PRESENTATION_DATA)
+ resource_catalog.update_from_result(result)
+
+ if not result.is_success:
+ messages.error(
+ request,
+ 'Unable to retrieve system resources: %s' % result.reason
+ )
+
+ try:
+ kind = GroupKind.objects.get(name=kind_name)
+ except:
+ return HttpResponseBadRequest(_(astakos_messages.GROUPKIND_UNKNOWN))
+
+
+ post_save_redirect = '/im/group/%(id)s/'
+ context_processors = None
+ model, form_class = get_model_and_form_class(
+ model=None,
+ form_class=AstakosGroupCreationForm
+ )
+
+ if request.method == 'POST':
+ form = form_class(request.POST, request.FILES)
+ if form.is_valid():
+ return render_response(
+ template='im/astakosgroup_form_summary.html',
+ context_instance=get_context(request),
+ form = AstakosGroupCreationSummaryForm(form.cleaned_data),
+ policies = resource_catalog.get_policies(form.policies()),
+ resource_catalog= resource_catalog,
+ )
+
+ else:
+ now = datetime.now()
+ data = {
+ 'kind': kind,
+ }
+ for group, resources in resource_catalog.get_groups_resources():
+ data['is_selected_%s' % group] = False
+ for resource in resources:
+ data['%s_uplimit' % resource] = ''
+
+ form = form_class(data)
+
+ # Create the template, context, response
+ template_name = "%s/%s_form.html" % (
+ model._meta.app_label,
+ model._meta.object_name.lower()
+ )
+ t = template_loader.get_template(template_name)
+ c = RequestContext(request, {
+ 'form': form,
+ 'kind': kind,
+ 'resource_catalog':resource_catalog,
+ }, context_processors)
+ return HttpResponse(t.render(c))
+
+
+#@require_http_methods(["POST"])
+@require_http_methods(["GET", "POST"])
+@signed_terms_required
+@login_required
+def group_add_complete(request):
+ model = AstakosGroup
+ form = AstakosGroupCreationSummaryForm(request.POST)
+ if form.is_valid():
+ d = form.cleaned_data
+ d['owners'] = [request.user]
+ result = callpoint.create_groups((d,)).next()
+ if result.is_success:
+ new_object = result.data[0]
+ msg = _(astakos_messages.OBJECT_CREATED) %\
+ {"verbose_name": model._meta.verbose_name}
+ messages.success(request, msg, fail_silently=True)
+
+ # send notification
+ try:
+ send_group_creation_notification(
+ template_name='im/group_creation_notification.txt',
+ dictionary={
+ 'group': new_object,
+ 'owner': request.user,
+ 'policies': d.get('policies', [])
+ }
+ )
+ except SendNotificationError, e:
+ messages.error(request, e, fail_silently=True)
+ post_save_redirect = '/im/group/%(id)s/'
+ return HttpResponseRedirect(post_save_redirect % new_object)
+ else:
+ d = {"verbose_name": model._meta.verbose_name,
+ "reason":result.reason}
+ msg = _(astakos_messages.OBJECT_CREATED_FAILED) % d
+ messages.error(request, msg, fail_silently=True)
+ return render_response(
+ template='im/astakosgroup_form_summary.html',
+ context_instance=get_context(request),
+ form=form)
+
+
+#@require_http_methods(["GET"])
+@require_http_methods(["GET", "POST"])
+@signed_terms_required
+@login_required
+def group_list(request):
+ none = request.user.astakos_groups.none()
+ sorting = request.GET.get('sorting')
+ query = """
+ SELECT auth_group.id,
+ %s AS groupname,
+ im_groupkind.name AS kindname,
+ im_astakosgroup.*,
+ owner.email AS groupowner,
+ (SELECT COUNT(*) FROM im_membership
+ WHERE group_id = im_astakosgroup.group_ptr_id
+ AND date_joined IS NOT NULL) AS approved_members_num,
+ (SELECT CASE WHEN(
+ SELECT date_joined FROM im_membership
+ WHERE group_id = im_astakosgroup.group_ptr_id
+ AND person_id = %s) IS NULL
+ THEN 0 ELSE 1 END) AS membership_status
+ FROM im_astakosgroup
+ INNER JOIN im_membership ON (
+ im_astakosgroup.group_ptr_id = im_membership.group_id)
+ INNER JOIN auth_group ON(im_astakosgroup.group_ptr_id = auth_group.id)
+ INNER JOIN im_groupkind ON (im_astakosgroup.kind_id = im_groupkind.id)
+ LEFT JOIN im_astakosuser_owner ON (
+ im_astakosuser_owner.astakosgroup_id = im_astakosgroup.group_ptr_id)
+ LEFT JOIN auth_user as owner ON (
+ im_astakosuser_owner.astakosuser_id = owner.id)
+ WHERE im_membership.person_id = %s
+ """ % (DB_REPLACE_GROUP_SCHEME, request.user.id, request.user.id)
+
+ if sorting:
+ query = query+" ORDER BY %s ASC" %sorting
+ else:
+ query = query+" ORDER BY groupname ASC"
+ q = AstakosGroup.objects.raw(query)
+
+
+
+ # Create the template, context, response
+ template_name = "%s/%s_list.html" % (
+ q.model._meta.app_label,
+ q.model._meta.object_name.lower()
+ )
+ extra_context = dict(
+ is_search=False,
+ q=q,
+ sorting=request.GET.get('sorting'),
+ page=request.GET.get('page', 1)
+ )
+ return render_response(template_name,
+ context_instance=get_context(request, extra_context)
+ )
+
+
+@require_http_methods(["GET", "POST"])
+@signed_terms_required
+@login_required
+def group_detail(request, group_id):
+ q = AstakosGroup.objects.select_related().filter(pk=group_id)
+ q = q.extra(select={
+ 'is_member': """SELECT CASE WHEN EXISTS(
+ SELECT id FROM im_membership
+ WHERE group_id = im_astakosgroup.group_ptr_id
+ AND person_id = %s)
+ THEN 1 ELSE 0 END""" % request.user.id,
+ 'is_owner': """SELECT CASE WHEN EXISTS(
+ SELECT id FROM im_astakosuser_owner
+ WHERE astakosgroup_id = im_astakosgroup.group_ptr_id
+ AND astakosuser_id = %s)
+ THEN 1 ELSE 0 END""" % request.user.id,
+ 'kindname': """SELECT name FROM im_groupkind
+ WHERE id = im_astakosgroup.kind_id"""})
+
+ model = q.model
+ context_processors = None
+ mimetype = None
+ try:
+ obj = q.get()
+ except AstakosGroup.DoesNotExist:
+ raise Http404("No %s found matching the query" % (
+ model._meta.verbose_name))
+
+ update_form = AstakosGroupUpdateForm(instance=obj)
+ addmembers_form = AddGroupMembersForm()
+ if request.method == 'POST':
+ update_data = {}
+ addmembers_data = {}
+ for k, v in request.POST.iteritems():
+ if k in update_form.fields:
+ update_data[k] = v
+ if k in addmembers_form.fields:
+ addmembers_data[k] = v
+ update_data = update_data or None
+ addmembers_data = addmembers_data or None
+ update_form = AstakosGroupUpdateForm(update_data, instance=obj)
+ addmembers_form = AddGroupMembersForm(addmembers_data)
+ if update_form.is_valid():
+ update_form.save()
+ if addmembers_form.is_valid():
+ 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" % (
+ model._meta.app_label, model._meta.object_name.lower())
+ t = template_loader.get_template(template_name)
+ c = RequestContext(request, {
+ 'object': obj,
+ }, context_processors)
+
+ # validate sorting
+ sorting = request.GET.get('sorting')
+ if sorting:
+ form = MembersSortForm({'sort_by': sorting})
+ if form.is_valid():
+ sorting = form.cleaned_data.get('sort_by')
+
+
+
+ result = callpoint.list_resources()
+ resource_catalog = ResourcePresentation(RESOURCES_PRESENTATION_DATA)
+ resource_catalog.update_from_result(result)
+
+
+ if not result.is_success:
+ messages.error(
+ request,
+ 'Unable to retrieve system resources: %s' % result.reason
+ )
+
+ extra_context = {'update_form': update_form,
+ 'addmembers_form': addmembers_form,
+ 'page': request.GET.get('page', 1),
+ 'sorting': sorting,
+ 'resource_catalog':resource_catalog,
+ 'quota':resource_catalog.get_quota(obj.quota)}
+ for key, value in extra_context.items():
+ if callable(value):
+ c[key] = value()
+ else:
+ c[key] = value
+ response = HttpResponse(t.render(c), mimetype=mimetype)
+ populate_xheaders(
+ request, response, model, getattr(obj, obj._meta.pk.name))
+ return response
+
+
+@require_http_methods(["GET", "POST"])
+@signed_terms_required
+@login_required
+def group_search(request, extra_context=None, **kwargs):
+ q = request.GET.get('q')
+ sorting = request.GET.get('sorting')
+ if request.method == 'GET':
+ form = AstakosGroupSearchForm({'q': q} if q else None)
+ else:
+ form = AstakosGroupSearchForm(get_query(request))
+ if form.is_valid():
+ q = form.cleaned_data['q'].strip()
+ if q:
+ queryset = AstakosGroup.objects.select_related()
+ queryset = queryset.filter(name__contains=q)
+ queryset = queryset.filter(approval_date__isnull=False)
+ queryset = queryset.extra(select={
+ 'groupname': DB_REPLACE_GROUP_SCHEME,
+ 'kindname': "im_groupkind.name",
+ 'approved_members_num': """
+ SELECT COUNT(*) FROM im_membership
+ WHERE group_id = im_astakosgroup.group_ptr_id
+ AND date_joined IS NOT NULL""",
+ 'membership_approval_date': """
+ SELECT date_joined FROM im_membership
+ WHERE group_id = im_astakosgroup.group_ptr_id
+ AND person_id = %s""" % request.user.id,
+ 'is_member': """
+ SELECT CASE WHEN EXISTS(
+ SELECT date_joined FROM im_membership
+ WHERE group_id = im_astakosgroup.group_ptr_id
+ AND person_id = %s)
+ THEN 1 ELSE 0 END""" % request.user.id,
+ 'is_owner': """
+ SELECT CASE WHEN EXISTS(
+ SELECT id FROM im_astakosuser_owner
+ WHERE astakosgroup_id = im_astakosgroup.group_ptr_id
+ AND astakosuser_id = %s)
+ THEN 1 ELSE 0 END""" % request.user.id,
+ 'is_owner': """SELECT CASE WHEN EXISTS(
+ SELECT id FROM im_astakosuser_owner
+ WHERE astakosgroup_id = im_astakosgroup.group_ptr_id
+ AND astakosuser_id = %s)
+ THEN 1 ELSE 0 END""" % request.user.id,
+ })
+ if sorting:
+ # TODO check sorting value
+ queryset = queryset.order_by(sorting)
+ else:
+ queryset = queryset.order_by("groupname")
+
+ else:
+ queryset = AstakosGroup.objects.none()
+ return object_list(
+ request,
+ queryset,
+ paginate_by=PAGINATE_BY,
+ page=request.GET.get('page') or 1,
+ template_name='im/astakosgroup_list.html',
+ extra_context=dict(form=form,
+ is_search=True,
+ q=q,
+ sorting=sorting))
+
+
+@require_http_methods(["GET", "POST"])
+@signed_terms_required
+@login_required
+def group_all(request, extra_context=None, **kwargs):
+ q = AstakosGroup.objects.select_related()
+ q = q.filter(approval_date__isnull=False)
+ q = q.extra(select={
+ 'groupname': DB_REPLACE_GROUP_SCHEME,
+ 'kindname': "im_groupkind.name",
+ 'approved_members_num': """
+ SELECT COUNT(*) FROM im_membership
+ WHERE group_id = im_astakosgroup.group_ptr_id
+ AND date_joined IS NOT NULL""",
+ 'membership_approval_date': """
+ SELECT date_joined FROM im_membership
+ WHERE group_id = im_astakosgroup.group_ptr_id
+ AND person_id = %s""" % request.user.id,
+ 'is_member': """
+ SELECT CASE WHEN EXISTS(
+ SELECT date_joined FROM im_membership
+ WHERE group_id = im_astakosgroup.group_ptr_id
+ AND person_id = %s)
+ THEN 1 ELSE 0 END""" % request.user.id,
+ 'is_owner': """SELECT CASE WHEN EXISTS(
+ SELECT id FROM im_astakosuser_owner
+ WHERE astakosgroup_id = im_astakosgroup.group_ptr_id
+ AND astakosuser_id = %s)
+ THEN 1 ELSE 0 END""" % request.user.id, })
+ sorting = request.GET.get('sorting')
+ if sorting:
+ # TODO check sorting value
+ q = q.order_by(sorting)
+ else:
+ q = q.order_by("groupname")
+
+ return object_list(
+ request,
+ q,
+ paginate_by=PAGINATE_BY,
+ page=request.GET.get('page') or 1,
+ template_name='im/astakosgroup_list.html',
+ extra_context=dict(form=AstakosGroupSearchForm(),
+ is_search=True,
+ sorting=sorting))
+
+
+#@require_http_methods(["POST"])
+@require_http_methods(["POST", "GET"])
+@signed_terms_required
+@login_required
+def group_join(request, group_id):
+ m = Membership(group_id=group_id,
+ person=request.user,
+ date_requested=datetime.now())
+ try:
+ m.save()
+ post_save_redirect = reverse(
+ 'group_detail',
+ kwargs=dict(group_id=group_id))
+ return HttpResponseRedirect(post_save_redirect)
+ except IntegrityError, e:
+ logger.exception(e)
+ msg = _(astakos_messages.GROUP_JOIN_FAILURE)
+ messages.error(request, msg)
+ return group_search(request)
+
+
+@require_http_methods(["POST"])
+@signed_terms_required
+@login_required
+def group_leave(request, group_id):
+ try:
+ m = Membership.objects.select_related().get(
+ group__id=group_id,
+ person=request.user)
+ except Membership.DoesNotExist:
+ return HttpResponseBadRequest(_(astakos_messages.NOT_MEMBER))
+ if request.user in m.group.owner.all():
+ return HttpResponseForbidden(_(astakos_messages.OWNER_CANNOT_LEAVE_GROUP))
+ return delete_object(
+ request,
+ model=Membership,
+ object_id=m.id,
+ template_name='im/astakosgroup_list.html',
+ post_delete_redirect=reverse(
+ 'group_detail',
+ kwargs=dict(group_id=group_id)))
+
+
+def handle_membership(func):
+ @wraps(func)
+ def wrapper(request, group_id, user_id):
+ try:
+ m = Membership.objects.select_related().get(
+ group__id=group_id,
+ person__id=user_id)
+ except Membership.DoesNotExist:
+ return HttpResponseBadRequest(_(astakos_messages.NOT_MEMBER))
+ else:
+ if request.user not in m.group.owner.all():
+ 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", "GET"])
+@signed_terms_required
+@login_required
+@handle_membership
+def approve_member(request, membership):
+ try:
+ membership.approve()
+ realname = membership.person.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 = _(astakos_messages.GENERIC_ERROR)
+ messages.error(request, msg)
+
+
+@signed_terms_required
+@login_required
+@handle_membership
+def disapprove_member(request, membership):
+ try:
+ membership.disapprove()
+ realname = membership.person.realname
+ msg = astakos_messages.MEMBER_REMOVED % realname
+ messages.success(request, msg)
+ except BaseException, e:
+ logger.exception(e)
+ msg = _(astakos_messages.GENERIC_ERROR)
+ messages.error(request, msg)
+
+
+#@require_http_methods(["GET"])
+@require_http_methods(["POST", "GET"])
+@signed_terms_required
+@login_required
+def resource_list(request):
+ def with_class(entry):
+ entry['load_class'] = 'red'
+ max_value = float(entry['maxValue'])
+ curr_value = float(entry['currValue'])
+ if max_value > 0 :
+ entry['ratio'] = (curr_value / max_value) * 100
+ else:
+ entry['ratio'] = 0
+ if entry['ratio'] < 66:
+ entry['load_class'] = 'yellow'
+ if entry['ratio'] < 33:
+ entry['load_class'] = 'green'
+ return entry
+
+ def pluralize(entry):
+ entry['plural'] = engine.plural(entry.get('name'))
+ return entry
+
+ result = callpoint.get_user_status(request.user.id)
+ if result.is_success:
+ backenddata = map(with_class, result.data)
+ data = map(pluralize, result.data)
+ else:
+ data = None
+ messages.error(request, result.reason)
+ resource_catalog = ResourcePresentation(RESOURCES_PRESENTATION_DATA)
+ resource_catalog.update_from_result_report(result)
+
+
+
+ return render_response('im/resource_list.html',
+ data=data,
+ context_instance=get_context(request),
+ resource_catalog=resource_catalog,
+ result=result)
+
+
+def group_create_list(request):
+ form = PickResourceForm()
+ return render_response(
+ template='im/astakosgroup_create_list.html',
+ context_instance=get_context(request),)
+
+
+#@require_http_methods(["GET"])
+@require_http_methods(["POST", "GET"])
+@signed_terms_required
+@login_required
+def billing(request):
+
+ today = datetime.today()
+ month_last_day = calendar.monthrange(today.year, today.month)[1]
+ start = request.POST.get('datefrom', None)
+ if start:
+ today = datetime.fromtimestamp(int(start))
+ month_last_day = calendar.monthrange(today.year, today.month)[1]
+
+ start = datetime(today.year, today.month, 1).strftime("%s")
+ end = datetime(today.year, today.month, month_last_day).strftime("%s")
+ r = request_billing.apply(args=('pgerakios@grnet.gr',
+ int(start) * 1000,
+ int(end) * 1000))
+ data = {}
+
+ try:
+ status, data = r.result
+ data = _clear_billing_data(data)
+ if status != 200:
+ messages.error(request, _(astakos_messages.BILLING_ERROR) % status)
+ except:
+ messages.error(request, r.result)
+
+ return render_response(
+ template='im/billing.html',
+ context_instance=get_context(request),
+ data=data,
+ zerodate=datetime(month=1, year=1970, day=1),
+ today=today,
+ start=int(start),
+ month_last_day=month_last_day)
+
+
+def _clear_billing_data(data):
+
+ # remove addcredits entries
+ def isnotcredit(e):
+ return e['serviceName'] != "addcredits"
+
+ # separate services
+ def servicefilter(service_name):
+ service = service_name
+
+ def fltr(e):
+ return e['serviceName'] == service
+ return fltr
+
+ data['bill_nocredits'] = filter(isnotcredit, data['bill'])
+ data['bill_vmtime'] = filter(servicefilter('vmtime'), data['bill'])
+ data['bill_diskspace'] = filter(servicefilter('diskspace'), data['bill'])
+ data['bill_addcredits'] = filter(servicefilter('addcredits'), data['bill'])
+
+ return data
+
+
+#@require_http_methods(["GET"])
+@require_http_methods(["POST", "GET"])
+@signed_terms_required
+@login_required
+def timeline(request):
+# data = {'entity':request.user.email}
+ timeline_body = ()
+ timeline_header = ()
+# form = TimelineForm(data)
+ form = TimelineForm()
+ if request.method == 'POST':
+ data = request.POST
+ form = TimelineForm(data)
+ if form.is_valid():
+ data = form.cleaned_data
+ timeline_header = ('entity', 'resource',
+ 'event name', 'event date',
+ 'incremental cost', 'total cost')
+ timeline_body = timeline_charge(
+ data['entity'], data['resource'],
+ data['start_date'], data['end_date'],
+ data['details'], data['operation'])
+
+ return render_response(template='im/timeline.html',
+ context_instance=get_context(request),
+ form=form,
+ timeline_header=timeline_header,
+ timeline_body=timeline_body)
+ return data
\ No newline at end of file
from django import forms
from django.utils.safestring import mark_safe
from django.utils import simplejson as json
-from django.utils.translation import ugettext as _
from django.template.loader import render_to_string
from astakos.im.settings import RECAPTCHA_PUBLIC_KEY, RECAPTCHA_OPTIONS, \
- RECAPTCHA_USE_SSL
+ RECAPTCHA_USE_SSL
+
class RecaptchaWidget(forms.Widget):
""" A Widget which "renders" the output of captcha.displayhtml """
def render(self, *args, **kwargs):
conf = RECAPTCHA_OPTIONS
recaptcha_conf = ('<script type="text/javascript">'
- 'var RecaptchaOptions = %s'
- '</script>') % json.dumps(conf)
- custom_widget_html = render_to_string("im/captcha.html",
- {'conf': 'Bob'})
- return mark_safe(recaptcha_conf + \
- custom_widget_html + \
- captcha.displayhtml(RECAPTCHA_PUBLIC_KEY,
- use_ssl=RECAPTCHA_USE_SSL))
+ 'var RecaptchaOptions = %s'
+ '</script>') % json.dumps(conf)
+ custom_widget_html = render_to_string("im/captcha.html",
+ {'conf': 'Bob'})
+ return mark_safe(recaptcha_conf +
+ custom_widget_html +
+ captcha.displayhtml(RECAPTCHA_PUBLIC_KEY,
+ use_ssl=RECAPTCHA_USE_SSL))
+
class DummyWidget(forms.Widget):
"""
"""
# make sure that labels are not displayed either
- is_hidden=True
+ is_hidden = True
+
def render(self, *args, **kwargs):
return ''
urlpatterns = patterns('',
- (r'^im/', include('astakos.im.urls'))
-)
-
+ (r'^im/', include('astakos.im.urls'))
+ )
#from logging import INFO
#ASTAKOS_LOGGING_LEVEL = INFO
-# Email subjects configuration. For admin/helper notification emails %(user)s
+# Email subjects configuration. For admin/helper notification emails %(user)s
# maps to registered/activated user email.
#ASTAKOS_INVITATION_EMAIL_SUBJECT = 'Invitation to %s alpha2 testing' % SITENAME
#ASTAKOS_GREETING_EMAIL_SUBJECT = 'Welcome to %s alpha2 testing' % SITENAME
#ASTAKOS_FEEDBACK_EMAIL_SUBJECT = 'Feedback from %s alpha2 testing' % SITENAME
#ASTAKOS_VERIFICATION_EMAIL_SUBJECT = '%s alpha2 testing account activation is needed' % SITENAME
-#ASTAKOS_ADMIN_NOTIFICATION_EMAIL_SUBJECT = '%s alpha2 testing account created (%%(user)s)' % SITENAME
+#ASTAKOS_ACCOUNT_CREATION_SUBJECT = '%s alpha2 testing account created (%%(user)s)' % SITENAME)
+#ASTAKOS_GROUP_CREATION_SUBJECT = '%s alpha2 testing group created (%%(group)s)' % SITENAME)
#ASTAKOS_HELPDESK_NOTIFICATION_EMAIL_SUBJECT = '%s alpha2 testing account activated (%%(user)s)' % SITENAME
#ASTAKOS_EMAIL_CHANGE_EMAIL_SUBJECT = 'Email change on %s alpha2 testing' % SITENAME
#ASTAKOS_PASSWORD_RESET_EMAIL_SUBJECT = 'Password reset on %s alpha2 testing' % SITENAME
+# Set the quota holder component URI
+#ASTAKOS_QUOTA_HOLDER_URL = ''
+
+# Set the cloud service properties
+# SERVICES = getattr(settings, 'ASTAKOS_SERVICES',
+# {'cyclades': {'url':'https://node1.example.com/ui/',
+# 'quota': {'vm': 2}},
+# 'pithos+': {'url':'https://node2.example.com/ui/',
+# 'quota': {'diskspace': 50 * 1024 * 1024 * 1024}}})
+
+# Set the billing URI
+#ASTAKOS_AQUARIUM_URL = ''
+
+# Set how many objects should be displayed per page
+#PAGINATE_BY = getattr(settings, 'ASTAKOS_PAGINATE_BY', 10)
+
# Enforce token renewal on password change/reset
# NEWPASSWD_INVALIDATE_TOKEN = getattr(settings, 'ASTAKOS_NEWPASSWD_INVALIDATE_TOKEN', True)
except ImportError:
return _do_download(version, download_base, to_dir, download_delay)
try:
- pkg_resources.require("distribute>="+version)
+ pkg_resources.require("distribute>=" + version)
return
except pkg_resources.VersionConflict:
e = sys.exc_info()[1]
if was_imported:
sys.stderr.write(
- "The required version of distribute (>=%s) is not available,\n"
- "and can't be installed while this script is running. Please\n"
- "install a more recent version first, using\n"
- "'easy_install -U distribute'."
- "\n\n(Currently using %r)\n" % (version, e.args[0]))
+ "The required version of distribute (>=%s) is not available,\n"
+ "and can't be installed while this script is running. Please\n"
+ "install a more recent version first, using\n"
+ "'easy_install -U distribute'."
+ "\n\n(Currently using %r)\n" % (version, e.args[0]))
sys.exit(2)
else:
del pkg_resources, sys.modules['pkg_resources'] # reload ok
if not no_fake:
_create_fake_setuptools_pkg_info(to_dir)
+
def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
to_dir=os.curdir, delay=15):
"""Download distribute from a specified location and return its filename
dst.close()
return os.path.realpath(saveto)
+
def _no_sandbox(function):
def __no_sandbox(*args, **kw):
try:
return __no_sandbox
+
def _patch_file(path, content):
"""Will backup the file then patch it"""
existing_content = open(path).read()
_patch_file = _no_sandbox(_patch_file)
+
def _same_content(path, content):
return open(path).read() == content
+
def _rename_path(path):
new_name = path + '.OLD.%s' % time.time()
log.warn('Renaming %s into %s', path, new_name)
os.rename(path, new_name)
return new_name
+
def _remove_flat_installation(placeholder):
if not os.path.isdir(placeholder):
log.warn('Unkown installation at %s', placeholder)
_remove_flat_installation = _no_sandbox(_remove_flat_installation)
+
def _after_install(dist):
log.warn('After install bootstrap.')
placeholder = dist.get_command_obj('install').install_purelib
_create_fake_setuptools_pkg_info(placeholder)
+
def _create_fake_setuptools_pkg_info(placeholder):
if not placeholder or not os.path.exists(placeholder):
log.warn('Could not find the install location')
return
pyver = '%s.%s' % (sys.version_info[0], sys.version_info[1])
setuptools_file = 'setuptools-%s-py%s.egg-info' % \
- (SETUPTOOLS_FAKED_VERSION, pyver)
+ (SETUPTOOLS_FAKED_VERSION, pyver)
pkg_info = os.path.join(placeholder, setuptools_file)
if os.path.exists(pkg_info):
log.warn('%s already exists', pkg_info)
finally:
f.close()
-_create_fake_setuptools_pkg_info = _no_sandbox(_create_fake_setuptools_pkg_info)
+_create_fake_setuptools_pkg_info = _no_sandbox(
+ _create_fake_setuptools_pkg_info)
+
def _patch_egg_dir(path):
# let's check if it's already patched
_patch_egg_dir = _no_sandbox(_patch_egg_dir)
+
def _before_install():
log.warn('Before install bootstrap.')
_fake_setuptools()
def _under_prefix(location):
if 'install' not in sys.argv:
return True
- args = sys.argv[sys.argv.index('install')+1:]
+ args = sys.argv[sys.argv.index('install') + 1:]
for index, arg in enumerate(args):
for option in ('--root', '--prefix'):
if arg.startswith('%s=' % option):
return location.startswith(top_dir)
elif arg == option:
if len(args) > index:
- top_dir = args[index+1]
+ top_dir = args[index + 1]
return location.startswith(top_dir)
if arg == '--user' and USER_SITE is not None:
return location.startswith(USER_SITE)
replacement=False))
except TypeError:
# old distribute API
- setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools'))
+ setuptools_dist = ws.find(
+ pkg_resources.Requirement.parse('setuptools'))
if setuptools_dist is None:
log.warn('No setuptools distribution found')
log.warn('Egg installation')
pkg_info = os.path.join(setuptools_location, 'EGG-INFO', 'PKG-INFO')
if (os.path.exists(pkg_info) and
- _same_content(pkg_info, SETUPTOOLS_PKG_INFO)):
+ _same_content(pkg_info, SETUPTOOLS_PKG_INFO)):
log.warn('Already patched.')
return
log.warn('Patching...')
# Extract directories with a safe mode.
directories.append(tarinfo)
tarinfo = copy.copy(tarinfo)
- tarinfo.mode = 448 # decimal for oct 0700
+ tarinfo.mode = 448 # decimal for oct 0700
self.extract(tarinfo, path)
# Reverse sort directories.
from setuptools import setup, find_packages
from astakos import get_version
-
HERE = os.path.abspath(os.path.normpath(os.path.dirname(__file__)))
try:
# try to update the version file
# Package meta
CLASSIFIERS = [
- 'Development Status :: 3 - Alpha',
- 'Operating System :: OS Independent',
- 'Programming Language :: Python',
- 'Topic :: Utilities',
- 'License :: OSI Approved :: BSD License',
+ 'Development Status :: 3 - Alpha',
+ 'Operating System :: OS Independent',
+ 'Programming Language :: Python',
+ 'Topic :: Utilities',
+ 'License :: OSI Approved :: BSD License',
]
# Package requirements
'httplib2>=0.6.0',
'snf-common>=0.9.0',
'recaptcha-client>=1.0.5',
- 'django-ratelimit==0.1'
+ 'django-ratelimit==0.1',
+ 'celery',
+ 'requests',
+ 'inflect'
]
EXTRAS_REQUIRES = {
# Note: you may want to copy this into your setup.py file verbatim, as
# you can't import this from another package, when you don't know if
# that package is installed yet.
+
+
def find_package_data(
where=".",
package="",
exclude=standard_exclude,
exclude_directories=standard_exclude_directories,
only_in_packages=True,
- show_ignored=False):
+ show_ignored=False):
"""
Return a dictionary suitable for use in ``package_data``
in a distutils ``setup.py`` file.
bad_name = False
for pattern in exclude_directories:
if (fnmatchcase(name, pattern)
- or fn.lower() == pattern.lower()):
+ or fn.lower() == pattern.lower()):
bad_name = True
if show_ignored:
print >> sys.stderr, (
if bad_name:
continue
if (os.path.isfile(os.path.join(fn, "__init__.py"))
- and not prefix):
+ and not prefix):
if not package:
new_package = name
else:
new_package = package + "." + name
stack.append((fn, "", new_package, False))
else:
- stack.append((fn, prefix + name + "/", package, only_in_packages))
+ stack.append(
+ (fn, prefix + name + "/", package, only_in_packages))
elif package or not only_in_packages:
# is a file
bad_name = False
for pattern in exclude:
if (fnmatchcase(name, pattern)
- or fn.lower() == pattern.lower()):
+ or fn.lower() == pattern.lower()):
bad_name = True
if show_ignored:
print >> sys.stderr, (
break
if bad_name:
continue
- out.setdefault(package, []).append(prefix+name)
+ out.setdefault(package, []).append(prefix + name)
return out
setup(
version=VERSION,
license='BSD',
url='http://code.grnet.gr/projects/astakos',
- description = SHORT_DESCRIPTION,
- long_description=README + '\n\n' + CHANGES,
- classifiers = CLASSIFIERS,
+ description=SHORT_DESCRIPTION,
+ long_description=README + '\n\n' + CHANGES,
+ classifiers=CLASSIFIERS,
author='GRNET',
author_email='astakos@grnet.gr',
package_data=find_package_data('.'),
zip_safe=False,
- install_requires = INSTALL_REQUIRES,
+ install_requires=INSTALL_REQUIRES,
- dependency_links = ['http://docs.dev.grnet.gr/pypi'],
+ dependency_links=['http://docs.dev.grnet.gr/pypi'],
entry_points={
'synnefo': [
- 'default_settings = astakos.im.synnefo_settings',
- 'web_apps = astakos.im.synnefo_settings:installed_apps',
- 'web_middleware = astakos.im.synnefo_settings:middlware_classes',
- 'web_context_processors = astakos.im.synnefo_settings:context_processors',
- 'urls = astakos.urls:urlpatterns',
- 'web_static = astakos.im.synnefo_settings:static_files',
- 'loggers = astakos.im.synnefo_settings:loggers'
+ 'default_settings = astakos.im.synnefo_settings',
+ 'web_apps = astakos.im.synnefo_settings:installed_apps',
+ 'web_middleware = astakos.im.synnefo_settings:middlware_classes',
+ 'web_context_processors = astakos.im.synnefo_settings:context_processors',
+ 'urls = astakos.urls:urlpatterns',
+ 'web_static = astakos.im.synnefo_settings:static_files',
+ 'loggers = astakos.im.synnefo_settings:loggers'
]
}
)
-