commit e1a802570a8b919aa0bd9cb0dd32f615fbf75528
Author: Sofia Papagiannaki <papagian@gmail.com>
Date:   Thu Dec 6 15:52:50 2012 +0200

    Progress I

diff --git a/.gitignore b/.gitignore
index a9b3bbb..5b7bab7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,4 +7,315 @@ docs/build
 .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
+snf-astakos-app/distribute-0.6.10.tar.gz
+snf-astakos-app/build/lib/astakos/__init__.py
+snf-astakos-app/build/lib/astakos/im/__init__.py
+snf-astakos-app/build/lib/astakos/im/activation_backends.py
+snf-astakos-app/build/lib/astakos/im/api/__init__.py
+snf-astakos-app/build/lib/astakos/im/api/admin.py
+snf-astakos-app/build/lib/astakos/im/api/backends/__init__.py
+snf-astakos-app/build/lib/astakos/im/api/backends/base.py
+snf-astakos-app/build/lib/astakos/im/api/backends/errors.py
+snf-astakos-app/build/lib/astakos/im/api/backends/lib/__init__.py
+snf-astakos-app/build/lib/astakos/im/api/backends/lib/django/__init__.py
+snf-astakos-app/build/lib/astakos/im/api/backends/notifications.py
+snf-astakos-app/build/lib/astakos/im/api/callpoint.py
+snf-astakos-app/build/lib/astakos/im/api/client.py
+snf-astakos-app/build/lib/astakos/im/api/faults.py
+snf-astakos-app/build/lib/astakos/im/api/service.py
+snf-astakos-app/build/lib/astakos/im/api/spec.py
+snf-astakos-app/build/lib/astakos/im/auth_backends.py
+snf-astakos-app/build/lib/astakos/im/context_processors.py
+snf-astakos-app/build/lib/astakos/im/cookie.py
+snf-astakos-app/build/lib/astakos/im/endpoints/__init__.py
+snf-astakos-app/build/lib/astakos/im/endpoints/aquarium/__init__.py
+snf-astakos-app/build/lib/astakos/im/endpoints/aquarium/client.py
+snf-astakos-app/build/lib/astakos/im/endpoints/aquarium/consumer.py
+snf-astakos-app/build/lib/astakos/im/endpoints/aquarium/producer.py
+snf-astakos-app/build/lib/astakos/im/endpoints/qh.py
+snf-astakos-app/build/lib/astakos/im/forms.py
+snf-astakos-app/build/lib/astakos/im/functions.py
+snf-astakos-app/build/lib/astakos/im/management/__init__.py
+snf-astakos-app/build/lib/astakos/im/management/commands/__init__.py
+snf-astakos-app/build/lib/astakos/im/management/commands/_common.py
+snf-astakos-app/build/lib/astakos/im/management/commands/full-cleanup.py
+snf-astakos-app/build/lib/astakos/im/management/commands/group-add.py
+snf-astakos-app/build/lib/astakos/im/management/commands/group-list.py
+snf-astakos-app/build/lib/astakos/im/management/commands/group-permissions-add.py
+snf-astakos-app/build/lib/astakos/im/management/commands/group-permissions-remove.py~HEAD
+snf-astakos-app/build/lib/astakos/im/management/commands/group-permissions-remove.py~future
+snf-astakos-app/build/lib/astakos/im/management/commands/group-update.py
+snf-astakos-app/build/lib/astakos/im/management/commands/invitation-details.py
+snf-astakos-app/build/lib/astakos/im/management/commands/invitation-list.py
+snf-astakos-app/build/lib/astakos/im/management/commands/quotaholder-sync.py
+snf-astakos-app/build/lib/astakos/im/management/commands/resource-add.py
+snf-astakos-app/build/lib/astakos/im/management/commands/resource-list.py
+snf-astakos-app/build/lib/astakos/im/management/commands/resource-remove.py
+snf-astakos-app/build/lib/astakos/im/management/commands/service-add.py
+snf-astakos-app/build/lib/astakos/im/management/commands/service-list.py
+snf-astakos-app/build/lib/astakos/im/management/commands/service-remove.py
+snf-astakos-app/build/lib/astakos/im/management/commands/service-token-renew.py
+snf-astakos-app/build/lib/astakos/im/management/commands/term-add.py
+snf-astakos-app/build/lib/astakos/im/management/commands/user-activation-send.py
+snf-astakos-app/build/lib/astakos/im/management/commands/user-add.py
+snf-astakos-app/build/lib/astakos/im/management/commands/user-details.py
+snf-astakos-app/build/lib/astakos/im/management/commands/user-invite.py
+snf-astakos-app/build/lib/astakos/im/management/commands/user-list.py
+snf-astakos-app/build/lib/astakos/im/management/commands/user-update.py
+snf-astakos-app/build/lib/astakos/im/messages.py
+snf-astakos-app/build/lib/astakos/im/middleware.py
+snf-astakos-app/build/lib/astakos/im/migrations/0001_initial.py
+snf-astakos-app/build/lib/astakos/im/migrations/0002_auto__add_field_astakosuser_third_party_identifier.py
+snf-astakos-app/build/lib/astakos/im/migrations/0003_auto__add_unique_invitation_username.py
+snf-astakos-app/build/lib/astakos/im/migrations/0004_auto__add_field_astakosuser_email_verified.py
+snf-astakos-app/build/lib/astakos/im/migrations/0005_auto__add_field_astakosuser_has_credits.py
+snf-astakos-app/build/lib/astakos/im/migrations/0006_auto__add_approvalterms__add_field_astakosuser_has_signed_terms__add_f.py
+snf-astakos-app/build/lib/astakos/im/migrations/0007_auto__chg_field_astakosuser_email_verified__chg_field_astakosuser_has_.py
+snf-astakos-app/build/lib/astakos/im/migrations/0007_auto__del_field_invitation_accepted__del_field_invitation_is_accepted.py
+snf-astakos-app/build/lib/astakos/im/migrations/0008_auto__add_emailchange.py
+snf-astakos-app/build/lib/astakos/im/migrations/0009_auto__add_service.py
+snf-astakos-app/build/lib/astakos/im/migrations/0010_auto__add_field_astakosuser_activation_sent__chg_field_service_url.py
+snf-astakos-app/build/lib/astakos/im/migrations/0011_set_old_activation_sent.py
+snf-astakos-app/build/lib/astakos/im/migrations/0012_auto__add_additionalmail.py
+snf-astakos-app/build/lib/astakos/im/migrations/0013_auto__del_unique_additionalmail_email.py
+snf-astakos-app/build/lib/astakos/im/migrations/0014_auto__add_unique_astakosuser_third_party_identifier_provider.py
+snf-astakos-app/build/lib/astakos/im/migrations/0015_auto__add_groupkind__add_astakosgroup__add_resourcemetadata__add_astak.py
+snf-astakos-app/build/lib/astakos/im/migrations/0015_auto__add_pendingthirdpartyuser__add_unique_pendingthirdpartyuser_prov.py
+snf-astakos-app/build/lib/astakos/im/migrations/0016_auto__add_sessioncatalog.py
+snf-astakos-app/build/lib/astakos/im/migrations/0017_populate_resource_data.py
+snf-astakos-app/build/lib/astakos/im/migrations/0018_auto__add_field_astakosgroup_homepage.py
+snf-astakos-app/build/lib/astakos/im/migrations/0020_auto__chg_field_astakosgroup_homepage.py
+snf-astakos-app/build/lib/astakos/im/migrations/0021_auto__add_field_astakosgroupquota_uplimit__add_field_astakosuserquota_.py
+snf-astakos-app/build/lib/astakos/im/migrations/0022_copy_limit_to_uplimit.py
+snf-astakos-app/build/lib/astakos/im/migrations/0024_auto__chg_field_astakosgroupquota_lim.py
+snf-astakos-app/build/lib/astakos/im/migrations/0025_case_insensitive_emails.py
+snf-astakos-app/build/lib/astakos/im/migrations/0026_auto__add_field_resource_desc__add_field_resource_unit.py
+snf-astakos-app/build/lib/astakos/im/migrations/0027_auto__add_field_resource_group.py
+snf-astakos-app/build/lib/astakos/im/migrations/0028_auto__add_field_astakosuser_disturbed_quota.py
+snf-astakos-app/build/lib/astakos/im/migrations/0029_auto__add_field_astakosgroup_max_participants.py
+snf-astakos-app/build/lib/astakos/im/migrations/0030_populate_resource_data.py
+snf-astakos-app/build/lib/astakos/im/migrations/0031_populate_group_data.py
+snf-astakos-app/build/lib/astakos/im/migrations/__init__.py
+snf-astakos-app/build/lib/astakos/im/models.py
+snf-astakos-app/build/lib/astakos/im/settings.py
+snf-astakos-app/build/lib/astakos/im/static/im/cloudbar/README.rst
+snf-astakos-app/build/lib/astakos/im/static/im/cloudbar/cloudbar.css
+snf-astakos-app/build/lib/astakos/im/static/im/cloudbar/cloudbar.js
+snf-astakos-app/build/lib/astakos/im/static/im/cloudbar/cloudbar.less
+snf-astakos-app/build/lib/astakos/im/static/im/cloudbar/home-icon.png
+snf-astakos-app/build/lib/astakos/im/static/im/cloudbar/service_cloud.css
+snf-astakos-app/build/lib/astakos/im/static/im/cloudbar/service_cyclades.css
+snf-astakos-app/build/lib/astakos/im/static/im/cloudbar/service_pithos.css
+snf-astakos-app/build/lib/astakos/im/static/im/css/boxsizing.htc
+snf-astakos-app/build/lib/astakos/im/static/im/css/browser-fixes.css
+snf-astakos-app/build/lib/astakos/im/static/im/css/colorbox.css
+snf-astakos-app/build/lib/astakos/im/static/im/css/custom.css
+snf-astakos-app/build/lib/astakos/im/static/im/css/dropkick.css
+snf-astakos-app/build/lib/astakos/im/static/im/css/formating.css
+snf-astakos-app/build/lib/astakos/im/static/im/css/forms.css
+snf-astakos-app/build/lib/astakos/im/static/im/css/global.css
+snf-astakos-app/build/lib/astakos/im/static/im/css/ie7.css
+snf-astakos-app/build/lib/astakos/im/static/im/css/images/ui-bg_flat_0_aaaaaa_40x100.png
+snf-astakos-app/build/lib/astakos/im/static/im/css/images/ui-bg_glass_55_fbf9ee_1x400.png
+snf-astakos-app/build/lib/astakos/im/static/im/css/images/ui-bg_glass_65_ffffff_1x400.png
+snf-astakos-app/build/lib/astakos/im/static/im/css/images/ui-bg_glass_75_dadada_1x400.png
+snf-astakos-app/build/lib/astakos/im/static/im/css/images/ui-bg_glass_75_e6e6e6_1x400.png
+snf-astakos-app/build/lib/astakos/im/static/im/css/images/ui-bg_glass_75_ffffff_1x400.png
+snf-astakos-app/build/lib/astakos/im/static/im/css/images/ui-bg_highlight-soft_75_cccccc_1x100.png
+snf-astakos-app/build/lib/astakos/im/static/im/css/images/ui-bg_inset-soft_95_fef1ec_1x100.png
+snf-astakos-app/build/lib/astakos/im/static/im/css/images/ui-icons_222222_256x240.png
+snf-astakos-app/build/lib/astakos/im/static/im/css/images/ui-icons_2e83ff_256x240.png
+snf-astakos-app/build/lib/astakos/im/static/im/css/images/ui-icons_454545_256x240.png
+snf-astakos-app/build/lib/astakos/im/static/im/css/images/ui-icons_888888_256x240.png
+snf-astakos-app/build/lib/astakos/im/static/im/css/images/ui-icons_cd0a0a_256x240.png
+snf-astakos-app/build/lib/astakos/im/static/im/css/jquery-ui-1.8.21.custom.css
+snf-astakos-app/build/lib/astakos/im/static/im/css/max480.css
+snf-astakos-app/build/lib/astakos/im/static/im/css/max768.css
+snf-astakos-app/build/lib/astakos/im/static/im/css/max960.css
+snf-astakos-app/build/lib/astakos/im/static/im/css/modules.css
+snf-astakos-app/build/lib/astakos/im/static/im/css/print.css
+snf-astakos-app/build/lib/astakos/im/static/im/css/uniform.default.css
+snf-astakos-app/build/lib/astakos/im/static/im/images/_double-dots.jpg
+snf-astakos-app/build/lib/astakos/im/static/im/images/accounts-logo.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/arrow-down.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/arrow-down_black.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/arrow-down_blue.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/arrow-down_grey.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/arrow-lt.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/arrow-up.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/arrow-up_blue.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/arrow-white-reverse.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/arrow-white.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/arrow_01.jpg
+snf-astakos-app/build/lib/astakos/im/static/im/images/bandwidth-stats.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/banner.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/black-line.jpg
+snf-astakos-app/build/lib/astakos/im/static/im/images/checkbox.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/cpu-stats.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/create-compute.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/create-network.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/create-resource-cpu.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/create-resource-disk.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/create-resource-network.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/create-resource-ram.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/create-resource-storage.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/create-resource-vm.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/create-storage.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/create.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/dashed_border.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/disk-stats.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/dots.jpg
+snf-astakos-app/build/lib/astakos/im/static/im/images/double-dots-unequal.jpg
+snf-astakos-app/build/lib/astakos/im/static/im/images/join.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/local.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/network-stats.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/openid.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/pictures/accounts_3.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/pictures/login_pic.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/pictures/ring_bell_beige.gif
+snf-astakos-app/build/lib/astakos/im/static/im/images/pictures/signup_pic.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/plus-minus-hover.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/plus-minus.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/porta.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/question-mark.jpg
+snf-astakos-app/build/lib/astakos/im/static/im/images/quota-related-bg.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/ram-stats.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/red-stats-vm.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/refresh_g.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/refresh_lg.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/service_okeanos_logo.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/service_pithos_logo.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/shibboleth.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/sound_g.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/sound_lg.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/sprite.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/statistics_icons.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/stats-line.jpg
+snf-astakos-app/build/lib/astakos/im/static/im/images/storage-stats.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/symbols.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/symbols2.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/twitter.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/vm-stats.png
+snf-astakos-app/build/lib/astakos/im/static/im/js/common.js
+snf-astakos-app/build/lib/astakos/im/static/im/js/forms.js
+snf-astakos-app/build/lib/astakos/im/static/im/js/html5.js
+snf-astakos-app/build/lib/astakos/im/static/im/js/html5shiv-printshiv.js
+snf-astakos-app/build/lib/astakos/im/static/im/js/jqModal.js
+snf-astakos-app/build/lib/astakos/im/static/im/js/jquery-1.7.1.min.js
+snf-astakos-app/build/lib/astakos/im/static/im/js/jquery-ui-1.8.21.custom.min.js
+snf-astakos-app/build/lib/astakos/im/static/im/js/jquery.alerts.js
+snf-astakos-app/build/lib/astakos/im/static/im/js/jquery.colorbox.js
+snf-astakos-app/build/lib/astakos/im/static/im/js/jquery.dropkick-1.0.0.js
+snf-astakos-app/build/lib/astakos/im/static/im/js/jquery.infieldlabel.js
+snf-astakos-app/build/lib/astakos/im/static/im/js/jquery.js
+snf-astakos-app/build/lib/astakos/im/static/im/js/jquery.labelify.js
+snf-astakos-app/build/lib/astakos/im/static/im/js/jquery.tablesorter.js
+snf-astakos-app/build/lib/astakos/im/static/im/js/jquery.uniform.js
+snf-astakos-app/build/lib/astakos/im/static/im/js/main.js
+snf-astakos-app/build/lib/astakos/im/static/im/js/modernizr-2.0.6.js
+snf-astakos-app/build/lib/astakos/im/static/im/js/os.js
+snf-astakos-app/build/lib/astakos/im/static/im/js/quotas.js
+snf-astakos-app/build/lib/astakos/im/static/im/js/resources_list.js
+snf-astakos-app/build/lib/astakos/im/static/im/js/underscore.js
+snf-astakos-app/build/lib/astakos/im/static/im/less/bootstrap.less
+snf-astakos-app/build/lib/astakos/im/static/im/less/django_forms.less
+snf-astakos-app/build/lib/astakos/im/static/im/less/forms.less
+snf-astakos-app/build/lib/astakos/im/static/im/less/mixins.less
+snf-astakos-app/build/lib/astakos/im/static/im/less/patterns.less
+snf-astakos-app/build/lib/astakos/im/static/im/less/reset.less
+snf-astakos-app/build/lib/astakos/im/static/im/less/scaffolding.less
+snf-astakos-app/build/lib/astakos/im/static/im/less/tables.less
+snf-astakos-app/build/lib/astakos/im/static/im/less/type.less
+snf-astakos-app/build/lib/astakos/im/static/im/less/variables.less
+snf-astakos-app/build/lib/astakos/im/static/im/less/xtra.less
+snf-astakos-app/build/lib/astakos/im/synnefo_settings.py
+snf-astakos-app/build/lib/astakos/im/target/__init__.py
+snf-astakos-app/build/lib/astakos/im/target/local.py
+snf-astakos-app/build/lib/astakos/im/target/redirect.py
+snf-astakos-app/build/lib/astakos/im/target/shibboleth.py
+snf-astakos-app/build/lib/astakos/im/tasks.py
+snf-astakos-app/build/lib/astakos/im/templates/im/account_base.html
+snf-astakos-app/build/lib/astakos/im/templates/im/account_notification.txt
+snf-astakos-app/build/lib/astakos/im/templates/im/activation_email.txt
+snf-astakos-app/build/lib/astakos/im/templates/im/approval_terms.html
+snf-astakos-app/build/lib/astakos/im/templates/im/astakosgroup_create_list.html
+snf-astakos-app/build/lib/astakos/im/templates/im/astakosgroup_detail.html
+snf-astakos-app/build/lib/astakos/im/templates/im/astakosgroup_form.html
+snf-astakos-app/build/lib/astakos/im/templates/im/astakosgroup_form_summary.html
+snf-astakos-app/build/lib/astakos/im/templates/im/astakosgroup_list.html
+snf-astakos-app/build/lib/astakos/im/templates/im/astakosuserquota_list.html
+snf-astakos-app/build/lib/astakos/im/templates/im/base.html
+snf-astakos-app/build/lib/astakos/im/templates/im/base_two_cols.html
+snf-astakos-app/build/lib/astakos/im/templates/im/billing.html
+snf-astakos-app/build/lib/astakos/im/templates/im/captcha.html
+snf-astakos-app/build/lib/astakos/im/templates/im/email.txt
+snf-astakos-app/build/lib/astakos/im/templates/im/feedback.html
+snf-astakos-app/build/lib/astakos/im/templates/im/feedback_mail.txt
+snf-astakos-app/build/lib/astakos/im/templates/im/footer.html
+snf-astakos-app/build/lib/astakos/im/templates/im/form_render.html
+snf-astakos-app/build/lib/astakos/im/templates/im/group_creation_notification.txt
+snf-astakos-app/build/lib/astakos/im/templates/im/helpdesk_notification.txt
+snf-astakos-app/build/lib/astakos/im/templates/im/invitation.txt
+snf-astakos-app/build/lib/astakos/im/templates/im/invitations.html
+snf-astakos-app/build/lib/astakos/im/templates/im/login.html
+snf-astakos-app/build/lib/astakos/im/templates/im/login_base.html
+snf-astakos-app/build/lib/astakos/im/templates/im/one_col_base.html
+snf-astakos-app/build/lib/astakos/im/templates/im/profile.html
+snf-astakos-app/build/lib/astakos/im/templates/im/projects/list_types.html
+snf-astakos-app/build/lib/astakos/im/templates/im/register.html
+snf-astakos-app/build/lib/astakos/im/templates/im/resource_list.html
+snf-astakos-app/build/lib/astakos/im/templates/im/services_description.html
+snf-astakos-app/build/lib/astakos/im/templates/im/signup.html
+snf-astakos-app/build/lib/astakos/im/templates/im/signup_complete.html
+snf-astakos-app/build/lib/astakos/im/templates/im/switch_accounts_email.txt
+snf-astakos-app/build/lib/astakos/im/templates/im/third_party_check_local.html
+snf-astakos-app/build/lib/astakos/im/templates/im/third_party_registration.html
+snf-astakos-app/build/lib/astakos/im/templates/im/timeline.html
+snf-astakos-app/build/lib/astakos/im/templates/im/welcome_email.txt
+snf-astakos-app/build/lib/astakos/im/templates/registration/email_change_confirm.html
+snf-astakos-app/build/lib/astakos/im/templates/registration/email_change_done.html
+snf-astakos-app/build/lib/astakos/im/templates/registration/email_change_email.txt
+snf-astakos-app/build/lib/astakos/im/templates/registration/email_change_form.html
+snf-astakos-app/build/lib/astakos/im/templates/registration/logged_out.html
+snf-astakos-app/build/lib/astakos/im/templates/registration/password_change_form.html
+snf-astakos-app/build/lib/astakos/im/templates/registration/password_email.txt
+snf-astakos-app/build/lib/astakos/im/templates/registration/password_reset_complete.html
+snf-astakos-app/build/lib/astakos/im/templates/registration/password_reset_confirm.html
+snf-astakos-app/build/lib/astakos/im/templates/registration/password_reset_done.html
+snf-astakos-app/build/lib/astakos/im/templates/registration/password_reset_form.html
+snf-astakos-app/build/lib/astakos/im/templatetags/__init__.py
+snf-astakos-app/build/lib/astakos/im/templatetags/astakos_tags.py
+snf-astakos-app/build/lib/astakos/im/templatetags/filters.py
+snf-astakos-app/build/lib/astakos/im/templatetags/formatters.py
+snf-astakos-app/build/lib/astakos/im/urls.py
+snf-astakos-app/build/lib/astakos/im/util.py
+snf-astakos-app/build/lib/astakos/im/views.py
+snf-astakos-app/build/lib/astakos/im/widgets.py
+snf-astakos-app/build/lib/astakos/urls.py
+snf-astakos-app/build/lib/astakos/version.py
+snf-astakos-app/dist/snf_astakos_app-0.7.6_279_gc0b2660-py2.6.egg
+snf-astakos-app/build/lib/astakos/im/auth_providers.py
+snf-astakos-app/build/lib/astakos/im/management/commands/astakos-qh-sync.py
+snf-astakos-app/build/lib/astakos/im/management/commands/group-details.py
+snf-astakos-app/build/lib/astakos/im/management/commands/group-permissions-remove.py
+snf-astakos-app/build/lib/astakos/im/management/commands/user-remove.py
+snf-astakos-app/build/lib/astakos/im/migrations/0016_populate_group_data.py
+snf-astakos-app/build/lib/astakos/im/migrations/0031_auto__add_sessioncatalog__add_pendingthirdpartyuser__add_unique_pendin.py
+snf-astakos-app/build/lib/astakos/im/migrations/0032_auto__add_astakosuserauthprovider__add_unique_astakosuserauthprovider_.py
+snf-astakos-app/build/lib/astakos/im/migrations/0033_multiple_auth_providers.py
+snf-astakos-app/build/lib/astakos/im/static/im/images/arrow-down_green.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/arrow-down_yellow.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/arrow-up_green.png
+snf-astakos-app/build/lib/astakos/im/static/im/images/how_it_works.png
+snf-astakos-app/build/lib/astakos/im/templates/im/account_creation_notification.txt
+snf-astakos-app/build/lib/astakos/im/templates/im/auth/local_login_form.html
+snf-astakos-app/build/lib/astakos/im/templates/im/auth/local_login_prompt.html
+snf-astakos-app/build/lib/astakos/im/templates/im/auth/local_signup_prompt.html
+snf-astakos-app/build/lib/astakos/im/templates/im/auth/shibboleth_login.html
+snf-astakos-app/build/lib/astakos/im/templates/im/auth/shibboleth_login_prompt.html
+snf-astakos-app/build/lib/astakos/im/templates/im/auth/shibboleth_quick_login.html
+snf-astakos-app/build/lib/astakos/im/templates/im/how_it_works.html
+snf-astakos-app/build/lib/astakos/im/templates/im/profile_bak.html
+snf-astakos-app/build/lib/astakos/im/templates/im/resource_usage.html
+snf-astakos-app/build/lib/astakos/im/tests.py
+snf-astakos-app/dist/snf_astakos_app-0.7.6_343_g666c749-py2.6.egg
\ No newline at end of file
diff --git a/snf-astakos-app/astakos/im/api/backends/lib/django/__init__.py b/snf-astakos-app/astakos/im/api/backends/lib/django/__init__.py
index 9988b73..76b08e2 100644
--- a/snf-astakos-app/astakos/im/api/backends/lib/django/__init__.py
+++ b/snf-astakos-app/astakos/im/api/backends/lib/django/__init__.py
@@ -33,19 +33,28 @@
 
 from django.db import IntegrityError, transaction
 from django.core.exceptions import ObjectDoesNotExist
+from django.conf import settings
 
 from functools import wraps
 from smtplib import SMTPException
 
 from astakos.im.models import (
-    AstakosUser, AstakosGroup, GroupKind, Resource, Service, RESOURCE_SEPARATOR
+    AstakosUser, AstakosGroup, GroupKind, Resource, Service, RESOURCE_SEPARATOR,
+    Project, ProjectApplication, ProjectMembership, filter_queryset_by_property
 )
 from astakos.im.api.backends.base import BaseBackend, SuccessResult, FailureResult
 from astakos.im.api.backends.errors import (
     ItemNotExists, ItemExists, MissingIdentifier, MultipleItemsExist
 )
+# from astakos.im.api.backends.lib.notifications import EmailNotification
+
 from astakos.im.util import reserved_email, model_to_dict
-from astakos.im.endpoints.qh import get_quota
+from astakos.im.endpoints.qh import get_quota, send_quota
+from astakos.im.settings import SITENAME
+try:
+    from astakos.im.messages import astakos_messages
+except:
+    pass
 
 import logging
 
@@ -56,7 +65,6 @@ 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))
@@ -64,10 +72,8 @@ def safe(func):
             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
 
@@ -107,7 +113,10 @@ class DjangoBackend(BaseBackend):
         q = model.objects.all()
         if filter:
             q = q.filter(id__in=filter)
-        return map(lambda o: model_to_dict(o, exclude=[]), q)
+        return map(lambda o: self._details(o), q)
+
+    def _details(self, obj):
+        return model_to_dict(obj, exclude=[])
 
     def _create_object(self, model, **kwargs):
         o = model.objects.create(**kwargs)
@@ -136,6 +145,7 @@ class DjangoBackend(BaseBackend):
         permissions = kwargs.pop('permissions', ())
         groups = kwargs.pop('groups', ())
         password = kwargs.pop('password', None)
+        provider = kwargs.pop('provider', 'local')
 
         u = self._create_object(AstakosUser, **kwargs)
 
@@ -145,10 +155,10 @@ class DjangoBackend(BaseBackend):
         u.policies = policies
         u.extended_groups = groups
 
-        if not u.has_auth_provider('local'):
-            u.add_auth_provider('local')
+        if not u.has_auth_provider(provider):
+            u.add_auth_provider(provider)
 
-        return self._list(AstakosUser, filter=(u.id,))
+        return self._details(u)
 
     @safe
     def add_policies(self, user_id, update=False, policies=()):
@@ -180,6 +190,7 @@ class DjangoBackend(BaseBackend):
             except ObjectDoesNotExist, e:
                 append((service, resource, e))
         return rejected
+    
     @safe
     def add_permissions(self, user_id, permissions=()):
         user = self._lookup_user(user_id)
@@ -252,7 +263,7 @@ class DjangoBackend(BaseBackend):
         resources = kwargs.pop('resources', ())
         s = self._create_object(Service, **kwargs)
         s.resources = resources
-        return self._list(Service, filter=(s.id,))
+        return self._details(s)
 
     @safe
     def remove_services(self, ids=()):
@@ -308,4 +319,152 @@ class DjangoBackend(BaseBackend):
         g.policies = policies
 #        g.members = members
         g.owners = owners
-        return self._list(AstakosGroup, filter=(g.id,))
+        return self._details(g)
+    
+    
+    @safe
+    def submit_application(self, **kwargs):
+        app = self._create_object(ProjectApplication, **kwargs)
+        notification = build_notification(
+            settings.SERVER_EMAIL,
+            [settings.ADMINS],
+            _(GROUP_CREATION_SUBJECT) % {'group':app.definition.name},
+            _('An new project application identified by %(serial)s has been submitted.') % app.serial
+        )
+        notification.send()
+    
+    @safe
+    def list_applications(self):
+        return self._list(ProjectAppication)
+    
+    @safe
+    def approve_application(self, serial):
+        app = self._lookup_object(ProjectAppication, serial=serial)
+        notify = False
+        if not app.precursor_application:
+            kwargs = {
+                'application':app,
+                'creation_date':datetime.now(),
+                'last_approval_date':datetime.now(),
+            }
+            project = self._create_object(Project, **kwargs)
+        else:
+            project = app.precursor_application.project
+            last_approval_date = project.last_approval_date
+            if project.is_valid:
+                project.application = app
+                project.last_approval_date = datetime.now()
+                project.save()
+            else:
+                raise Exception(_(astakos_messages.INVALID_PROJECT) % project.__dict__)
+        
+        r = _synchonize_project(project.serial)
+        if not r.is_success:
+            # revert to precursor
+            project.appication = app.precursor_application
+            if project.application:
+                project.last_approval_date = last_approval_date
+            project.save()
+            r = synchonize_project(project.serial)
+            if not r.is_success:
+                raise Exception(_(astakos_messages.QH_SYNC_ERROR))
+        else:
+            project.last_application_synced = app
+            project.save()
+            sender, recipients, subject, message
+            notification = build_notification(
+                settings.SERVER_EMAIL,
+                [project.owner.email],
+                _('Project application has been approved on %s alpha2 testing' % SITENAME),
+                _('Your application request %(serial)s has been apporved.')
+            )
+            notification.send()
+    
+    
+    @safe
+    def list_projects(self, filter_property=None):
+        if filter_property:
+            q = filter_queryset_by_property(
+                Project.objects.all(),
+                filter_property
+            )
+            return map(lambda o: self._details(o), q)
+        return self._list(Project)
+        
+
+    
+    @safe
+    def add_project_member(self, serial, user_id, request_user):
+        project = self._lookup_object(Project, serial=serial)
+        user = self.lookup_user(user_id)
+        if not project.owner == request_user:
+            raise Exception(_(astakos_messages.NOT_PROJECT_OWNER))
+        
+        if not project.is_alive:
+            raise Exception(_(astakos_messages.NOT_ALIVE_PROJECT) % project.__dict__)
+        if len(project.members) + 1 > project.limit_on_members_number:
+            raise Exception(_(astakos_messages.MEMBER_NUMBER_LIMIT_REACHED))
+        m = self._lookup_object(ProjectMembership, person=user, project=project)
+        if m.is_accepted:
+            return
+        m.is_accepted = True
+        m.decision_date = datetime.now()
+        m.save()
+        notification = build_notification(
+            settings.SERVER_EMAIL,
+            [user.email],
+            _('Your membership on project %(name)s has been accepted.') % project.definition.__dict__, 
+            _('Your membership on project %(name)s has been accepted.') % project.definition.__dict__,
+        )
+        notification.send()
+    
+    @safe
+    def remove_project_member(self, serial, user_id, request_user):
+        project = self._lookup_object(Project, serial=serial)
+        if not project.is_alive:
+            raise Exception(_(astakos_messages.NOT_ALIVE_PROJECT) % project.__dict__)
+        if not project.owner == request_user:
+            raise Exception(_(astakos_messages.NOT_PROJECT_OWNER))
+        user = self.lookup_user(user_id)
+        m = self._lookup_object(ProjectMembership, person=user, project=project)
+        if not m.is_accepted:
+            return
+        m.is_accepted = False
+        m.decision_date = datetime.now()
+        m.save()
+        notification = build_notification(
+            settings.SERVER_EMAIL,
+            [user.email],
+            _('Your membership on project %(name)s has been removed.') % project.definition.__dict__, 
+            _('Your membership on project %(name)s has been removed.') % project.definition.__dict__,
+        )
+        notification.send()    
+    
+    @safe
+    def suspend_project(self, serial):
+        project = self._lookup_object(Project, serial=serial)
+        project.suspend()
+        notification = build_notification(
+            settings.SERVER_EMAIL,
+            [project.owner.email],
+            _('Project %(name)s has been suspended on %s alpha2 testing' % SITENAME),
+            _('Project %(name)s has been suspended on %s alpha2 testing' % SITENAME)
+        )
+        notification.send()
+    
+    @safe
+    def terminate_project(self, serial):
+        project = self._lookup_object(Project, serial=serial)
+        project.termination()
+        notification = build_notification(
+            settings.SERVER_EMAIL,
+            [project.owner.email],
+            _('Project %(name)s has been terminated on %s alpha2 testing' % SITENAME),
+            _('Project %(name)s has been terminated on %s alpha2 testing' % SITENAME)
+        )
+        notification.send()
+    
+    @safe
+    def synchonize_project(self, serial):
+        project = self._lookup_object(Project, serial=serial)
+        project.sync()
\ No newline at end of file
diff --git a/snf-astakos-app/astakos/im/api/backends/notifications.py b/snf-astakos-app/astakos/im/api/backends/notifications.py
deleted file mode 100644
index cd72179..0000000
--- a/snf-astakos-app/astakos/im/api/backends/notifications.py
+++ /dev/null
@@ -1,97 +0,0 @@
-# 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__()
diff --git a/snf-astakos-app/astakos/im/fixtures/member_accept_policies b/snf-astakos-app/astakos/im/fixtures/member_accept_policies
new file mode 100644
index 0000000..80398a3
--- /dev/null
+++ b/snf-astakos-app/astakos/im/fixtures/member_accept_policies
@@ -0,0 +1,26 @@
+[
+    {
+        "model": "im.memberacceptpolicy",
+        "pk": 1,
+        "fields": {
+            "policy": "auto_accept",
+            "description": "new join requests are automatically accepted by the system"
+        }
+    },
+    {
+        "model": "im.memberacceptpolicy",
+        "pk": 2,
+        "fields": {
+            "policy": "owner_accepts",
+            "description": "new join requests must be accepted by the owner of the project"
+        }
+    },
+    {
+        "model": "im.memberacceptpolicy",
+        "pk": 3,
+        "fields": {
+            "policy": "closed",
+            "description": "no new members can join the project, even if old ones leave"
+        }
+    }
+]
diff --git a/snf-astakos-app/astakos/im/forms.py b/snf-astakos-app/astakos/im/forms.py
index 8fafc10..d3bba91 100644
--- a/snf-astakos-app/astakos/im/forms.py
+++ b/snf-astakos-app/astakos/im/forms.py
@@ -35,9 +35,11 @@ from random import random
 
 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
@@ -50,10 +52,12 @@ from django.forms.models import fields_for_model
 from django.db import transaction
 from django.utils.encoding import smart_unicode
 from django.core import validators
+from django.contrib.auth.models import AnonymousUser
 
 from astakos.im.models import (
     AstakosUser, EmailChange, AstakosGroup, Invitation, GroupKind,
-    Resource, PendingThirdPartyUser, get_latest_terms, RESOURCE_SEPARATOR
+    Resource, PendingThirdPartyUser, get_latest_terms, RESOURCE_SEPARATOR,
+    ProjectDefinition, ProjectApplication, create_application
 )
 from astakos.im.settings import (
     INVITATIONS_PER_LEVEL, BASEURL, SITENAME, RECAPTCHA_PRIVATE_KEY,
@@ -902,3 +906,104 @@ class ExtendedSetPasswordForm(SetPasswordForm):
         except BaseException, e:
             logger.exception(e)
         return super(ExtendedSetPasswordForm, self).save(commit=commit)
+
+
+class ProjectApplicationForm(forms.ModelForm):
+    name = forms.CharField(
+        validators=[validators.RegexValidator(
+            DOMAIN_VALUE_REGEX,
+            _(astakos_messages.DOMAIN_VALUE_ERR),
+            'invalid'
+        )],
+        widget=forms.TextInput(attrs={'placeholder': 'eg. foo.ece.ntua.gr'}),
+        help_text="Name should be in the form of dns"
+    )
+    comments = forms.CharField(widget=forms.Textarea, required=False)
+    
+    class Meta:
+        model = ProjectDefinition
+        exclude = ('resource_grants')
+    
+    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)
+
+        #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 clean(self):
+        userid = self.data.get('user', None)[0]
+        self.user = None
+        if userid:
+            try:
+                self.user = AstakosUser.objects.get(id=userid)
+            except AstakosUser.DoesNotExist:
+                pass
+        if not self.user:
+            raise forms.ValidationError(_(astakos_messages.NO_APPLICANT))
+        super(ProjectApplicationForm, self).clean()
+        return self.cleaned_data
+    
+    def resource_policies(self, clean=True):
+        if clean:
+            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
+
+    def save(self, commit=True):
+        definition = super(ProjectApplicationForm, self).save(commit=commit)
+        definition.resource_policies=self.resource_policies(clean=False)
+        applicant = self.user
+        comments = self.cleaned_data.pop('comments', None)
+        try:
+            precursor_application = self.instance.projectapplication
+        except:
+            precursor_application = None
+        return create_application(
+            definition,
+            applicant,
+            comments,
+            precursor_application,
+            commit
+        )
diff --git a/snf-astakos-app/astakos/im/management/commands/astakos-qh-sync.py b/snf-astakos-app/astakos/im/management/commands/astakos-qh-sync.py
new file mode 100644
index 0000000..f045eb8
--- /dev/null
+++ b/snf-astakos-app/astakos/im/management/commands/astakos-qh-sync.py
@@ -0,0 +1,52 @@
+# 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 NoArgsCommand, 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(NoArgsCommand):
+    help = "Send user information and resource quota in the Quotaholder"
+
+    def handle_noargs(self, **options):
+        try:
+            register_resources(Resource.objects.all())
+            register_users(AstakosUser.objects.all())
+        except BaseException, e:
+            logger.exception(e)
+            raise CommandError("Syncing failed.")
diff --git a/snf-astakos-app/astakos/im/management/commands/group-details.py b/snf-astakos-app/astakos/im/management/commands/group-details.py
index 6e601fc..4276615 100644
--- a/snf-astakos-app/astakos/im/management/commands/group-details.py
+++ b/snf-astakos-app/astakos/im/management/commands/group-details.py
@@ -39,12 +39,12 @@ from django.db import models
 from astakos.im.models import AstakosGroup
 
 class Command(BaseCommand):
-    args = "<group name>"
+    args = "<group id or name>"
     help = "Show group info"
 
     def handle(self, *args, **options):
         if len(args) != 1:
-            raise CommandError("Please provide a group name")
+            raise CommandError("Please provide a group id or name")
 
         group = AstakosGroup.objects
         name_or_id = args[0].decode('utf8')
diff --git a/snf-astakos-app/astakos/im/management/commands/invitation-list.py b/snf-astakos-app/astakos/im/management/commands/invitation-list.py
index 67b5b61..9112a2f 100644
--- a/snf-astakos-app/astakos/im/management/commands/invitation-list.py
+++ b/snf-astakos-app/astakos/im/management/commands/invitation-list.py
@@ -33,17 +33,17 @@
 
 from optparse import make_option
 
-from django.core.management.base import BaseCommand, CommandError
+from django.core.management.base import NoArgsCommand, CommandError
 
 from astakos.im.models import Invitation
 
 from ._common import format_bool
 
 
-class Command(BaseCommand):
+class Command(NoArgsCommand):
     help = "List invitations"
 
-    option_list = BaseCommand.option_list + (
+    option_list = NoArgsCommand.option_list + (
         make_option('-c',
                     action='store_true',
                     dest='csv',
@@ -51,10 +51,7 @@ class Command(BaseCommand):
                     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):
         invitations = Invitation.objects.all().order_by('id')
         
         labels = ('id', 'inviter', 'email', 'real name', 'code', 'consumed')
diff --git a/snf-astakos-app/astakos/im/management/commands/quotaholder-sync.py b/snf-astakos-app/astakos/im/management/commands/quotaholder-sync.py
deleted file mode 100644
index 307a271..0000000
--- a/snf-astakos-app/astakos/im/management/commands/quotaholder-sync.py
+++ /dev/null
@@ -1,52 +0,0 @@
-# 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.")
diff --git a/snf-astakos-app/astakos/im/management/commands/resource-list.py b/snf-astakos-app/astakos/im/management/commands/resource-list.py
index 93bd736..0ba6799 100644
--- a/snf-astakos-app/astakos/im/management/commands/resource-list.py
+++ b/snf-astakos-app/astakos/im/management/commands/resource-list.py
@@ -33,15 +33,15 @@
 
 from optparse import make_option
 
-from django.core.management.base import BaseCommand
+from django.core.management.base import NoArgsCommand
 
 from astakos.im.models import Resource
 
 
-class Command(BaseCommand):
+class Command(NoArgsCommand):
     help = "List resources"
 
-    option_list = BaseCommand.option_list + (
+    option_list = NoArgsCommand.option_list + (
         make_option('-c',
                     action='store_true',
                     dest='csv',
@@ -49,7 +49,7 @@ class Command(BaseCommand):
                     help="Use pipes to separate values"),
     )
 
-    def handle(self, *args, **options):
+    def handle_noargs(self, **options):
         resources = Resource.objects.select_related().all()
 
         labels = ('id', 'service', 'name')
diff --git a/snf-astakos-app/astakos/im/management/commands/user-update.py b/snf-astakos-app/astakos/im/management/commands/user-update.py
index d1b6d23..d1b267b 100644
--- a/snf-astakos-app/astakos/im/management/commands/user-update.py
+++ b/snf-astakos-app/astakos/im/management/commands/user-update.py
@@ -58,10 +58,6 @@ class Command(BaseCommand):
                     dest='password',
                     metavar='PASSWORD',
                     help="Set user's password"),
-        make_option('--provider',
-                    dest='provider',
-                    metavar='PROVIDER',
-                    help="Set user's provider"),
         make_option('--renew-token',
                     action='store_true',
                     dest='renew_token',
@@ -189,10 +185,6 @@ class Command(BaseCommand):
         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()
diff --git a/snf-astakos-app/astakos/im/messages.py b/snf-astakos-app/astakos/im/messages.py
index 2f85d32..1c413cf 100644
--- a/snf-astakos-app/astakos/im/messages.py
+++ b/snf-astakos-app/astakos/im/messages.py
@@ -113,11 +113,11 @@ 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.'
+                                             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.'
+                                             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.'
@@ -126,4 +126,11 @@ 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.'
-DOMAIN_VALUE_ERR                        =   'Enter a valid domain.'
\ No newline at end of file
+DOMAIN_VALUE_ERR                        =   'Enter a valid domain.'
+QH_SYNC_ERROR                           =   'Failed to get synchronized with quotaholder.'
+UNIQUE_PROJECT_NAME_CONSTRAIN_ERR       =   'The project name (as specified in its application\'s definition) must be unique among all active projects.'
+INVALID_PROJECT                         =   'Project %(serial)s is invalid.'
+NOT_ALIVE_PROJECT                       =   'Project %(serial)s is not alive.'
+NOT_PROJECT_OWNER                       =   'Only project owner can perform this action.'
+MEMBER_NUMBER_LIMIT_REACHED             =   'Maximum participant number has been reached.'
+NO_APPLICANT                            =   'Project application requires an applicant. None found.'
\ No newline at end of file
diff --git a/snf-astakos-app/astakos/im/migrations/0017_populate_resource_data.py b/snf-astakos-app/astakos/im/migrations/0017_populate_resource_data.py
index 04b8ded..1611ba0 100644
--- a/snf-astakos-app/astakos/im/migrations/0017_populate_resource_data.py
+++ b/snf-astakos-app/astakos/im/migrations/0017_populate_resource_data.py
@@ -9,70 +9,14 @@ import logging
 logger = logging.getLogger(__name__)
 
 class Migration(DataMigration):
-
+    "Obsolete migration."
+        
     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())
+        pass
 
     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())
-
+        pass
+    
     models = {
         'auth.group': {
             'Meta': {'object_name': 'Group'},
diff --git a/snf-astakos-app/astakos/im/migrations/0024_auto__chg_field_astakosgroupquota_lim.py b/snf-astakos-app/astakos/im/migrations/0024_auto__chg_field_astakosgroupquota_lim.py
index ff4cba2..3a318e3 100644
--- a/snf-astakos-app/astakos/im/migrations/0024_auto__chg_field_astakosgroupquota_lim.py
+++ b/snf-astakos-app/astakos/im/migrations/0024_auto__chg_field_astakosgroupquota_lim.py
@@ -16,13 +16,7 @@ class Migration(SchemaMigration):
 
 
     def backwards(self, orm):
-        
-        # 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))
-
+        pass
 
     models = {
         'auth.group': {
diff --git a/snf-astakos-app/astakos/im/migrations/0034_auto__add_projectapplication__add_project__add_projectmembership__add_.py b/snf-astakos-app/astakos/im/migrations/0034_auto__add_projectapplication__add_project__add_projectmembership__add_.py
new file mode 100644
index 0000000..28e2a60
--- /dev/null
+++ b/snf-astakos-app/astakos/im/migrations/0034_auto__add_projectapplication__add_project__add_projectmembership__add_.py
@@ -0,0 +1,357 @@
+# 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 'ProjectApplication'
+        db.create_table('im_projectapplication', (
+            ('serial', self.gf('django.db.models.fields.CharField')(default='a3e0784bfd924d4ba9a1181332d5e9', unique=True, max_length=30, primary_key=True)),
+            ('applicant', self.gf('django.db.models.fields.related.ForeignKey')(related_name='my_project_applications', to=orm['im.AstakosUser'])),
+            ('owner', self.gf('django.db.models.fields.related.ForeignKey')(related_name='own_project_applications', to=orm['im.AstakosUser'])),
+            ('comments', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
+            ('definition', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['im.ProjectDefinition'], unique=True)),
+            ('issue_date', self.gf('django.db.models.fields.DateTimeField')()),
+            ('precursor_application', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['im.ProjectApplication'], unique=True, null=True, blank=True)),
+        ))
+        db.send_create_signal('im', ['ProjectApplication'])
+
+        # Adding model 'Project'
+        db.create_table('im_project', (
+            ('serial', self.gf('django.db.models.fields.CharField')(default='ab6c5646977c4e678daff27ff3c7cb', unique=True, max_length=30, primary_key=True)),
+            ('application', self.gf('django.db.models.fields.related.OneToOneField')(related_name='project', unique=True, to=orm['im.ProjectApplication'])),
+            ('creation_date', self.gf('django.db.models.fields.DateTimeField')()),
+            ('last_approval_date', self.gf('django.db.models.fields.DateTimeField')()),
+            ('termination_date', self.gf('django.db.models.fields.DateTimeField')()),
+            ('last_synced_application', self.gf('django.db.models.fields.related.OneToOneField')(blank=True, related_name='last_project', unique=True, null=True, to=orm['im.ProjectApplication'])),
+        ))
+        db.send_create_signal('im', ['Project'])
+
+        # Adding model 'ProjectMembership'
+        db.create_table('im_projectmembership', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('person', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['im.AstakosUser'])),
+            ('project', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['im.Project'])),
+            ('issue_date', self.gf('django.db.models.fields.DateField')(default=datetime.datetime(2012, 12, 6, 10, 11, 33, 387204))),
+            ('decision_date', self.gf('django.db.models.fields.DateField')(null=True, db_index=True)),
+            ('is_accepted', self.gf('django.db.models.fields.BooleanField')(default=False)),
+        ))
+        db.send_create_signal('im', ['ProjectMembership'])
+
+        # Adding unique constraint on 'ProjectMembership', fields ['person', 'project']
+        db.create_unique('im_projectmembership', ['person_id', 'project_id'])
+
+        # Adding model 'MemberAcceptPolicy'
+        db.create_table('im_memberacceptpolicy', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('policy', self.gf('django.db.models.fields.CharField')(unique=True, max_length=255, db_index=True)),
+            ('description', self.gf('django.db.models.fields.CharField')(max_length=80)),
+        ))
+        db.send_create_signal('im', ['MemberAcceptPolicy'])
+
+        # Adding model 'ProjectDefinition'
+        db.create_table('im_projectdefinition', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('name', self.gf('django.db.models.fields.CharField')(max_length=80)),
+            ('homepage', self.gf('django.db.models.fields.URLField')(max_length=255, null=True, blank=True)),
+            ('description', self.gf('django.db.models.fields.TextField')(null=True)),
+            ('start_date', self.gf('django.db.models.fields.DateTimeField')()),
+            ('end_date', self.gf('django.db.models.fields.DateTimeField')()),
+            ('member_accept_policy', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['im.MemberAcceptPolicy'])),
+            ('limit_on_members_number', self.gf('django.db.models.fields.PositiveIntegerField')(null=True, blank=True)),
+        ))
+        db.send_create_signal('im', ['ProjectDefinition'])
+
+        # Adding model 'ProjectResourceGrant'
+        db.create_table('im_projectresourcegrant', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('member_limit', self.gf('django.db.models.fields.BigIntegerField')(null=True)),
+            ('project_limit', self.gf('django.db.models.fields.BigIntegerField')(null=True)),
+            ('resource', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['im.Resource'])),
+            ('project_definition', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['im.ProjectDefinition'], blank=True)),
+        ))
+        db.send_create_signal('im', ['ProjectResourceGrant'])
+
+        # Adding unique constraint on 'ProjectResourceGrant', fields ['resource', 'project_definition']
+        db.create_unique('im_projectresourcegrant', ['resource_id', 'project_definition_id'])
+
+
+    def backwards(self, orm):
+        
+        # Removing unique constraint on 'ProjectResourceGrant', fields ['resource', 'project_definition']
+        db.delete_unique('im_projectresourcegrant', ['resource_id', 'project_definition_id'])
+
+        # Removing unique constraint on 'ProjectMembership', fields ['person', 'project']
+        db.delete_unique('im_projectmembership', ['person_id', 'project_id'])
+
+        # Deleting model 'ProjectApplication'
+        db.delete_table('im_projectapplication')
+
+        # Deleting model 'Project'
+        db.delete_table('im_project')
+
+        # Deleting model 'ProjectMembership'
+        db.delete_table('im_projectmembership')
+
+        # Deleting model 'MemberAcceptPolicy'
+        db.delete_table('im_memberacceptpolicy')
+
+        # Deleting model 'ProjectDefinition'
+        db.delete_table('im_projectdefinition')
+
+        # Deleting model 'ProjectResourceGrant'
+        db.delete_table('im_projectresourcegrant')
+
+
+    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, 12, 6, 10, 11, 33, 376205)', '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, 12, 6, 10, 11, 33, 368608)'}),
+            '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', 'null': 'True', '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', 'null': 'True', '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.astakosuserauthprovider': {
+            'Meta': {'unique_together': "(('identifier', 'module', 'user'),)", 'object_name': 'AstakosUserAuthProvider'},
+            'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'affiliation': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'auth_backend': ('django.db.models.fields.CharField', [], {'default': "'astakos'", 'max_length': '255'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'identifier': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'module': ('django.db.models.fields.CharField', [], {'default': "'local'", 'max_length': '255'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'auth_providers'", 'to': "orm['im.AstakosUser']"})
+        },
+        '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, 12, 6, 10, 11, 33, 378081)'}),
+            '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.memberacceptpolicy': {
+            'Meta': {'object_name': 'MemberAcceptPolicy'},
+            'description': ('django.db.models.fields.CharField', [], {'max_length': '80'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'policy': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'})
+        },
+        '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, 12, 6, 10, 11, 33, 373957)', '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.pendingthirdpartyuser': {
+            'Meta': {'unique_together': "(('provider', 'third_party_identifier'),)", 'object_name': 'PendingThirdPartyUser'},
+            'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            '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'}),
+            'token': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'im.project': {
+            'Meta': {'object_name': 'Project'},
+            'application': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'project'", 'unique': 'True', 'to': "orm['im.ProjectApplication']"}),
+            'creation_date': ('django.db.models.fields.DateTimeField', [], {}),
+            'last_approval_date': ('django.db.models.fields.DateTimeField', [], {}),
+            'last_synced_application': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'last_project'", 'unique': 'True', 'null': 'True', 'to': "orm['im.ProjectApplication']"}),
+            'members': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['im.AstakosUser']", 'through': "orm['im.ProjectMembership']", 'symmetrical': 'False'}),
+            'serial': ('django.db.models.fields.CharField', [], {'default': "'ab6c5646977c4e678daff27ff3c7cb'", 'unique': 'True', 'max_length': '30', 'primary_key': 'True'}),
+            'termination_date': ('django.db.models.fields.DateTimeField', [], {})
+        },
+        'im.projectapplication': {
+            'Meta': {'object_name': 'ProjectApplication'},
+            'applicant': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'my_project_applications'", 'to': "orm['im.AstakosUser']"}),
+            'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'definition': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['im.ProjectDefinition']", 'unique': 'True'}),
+            'issue_date': ('django.db.models.fields.DateTimeField', [], {}),
+            'owner': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'own_project_applications'", 'to': "orm['im.AstakosUser']"}),
+            'precursor_application': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['im.ProjectApplication']", 'unique': 'True', 'null': 'True', 'blank': 'True'}),
+            'serial': ('django.db.models.fields.CharField', [], {'default': "'a3e0784bfd924d4ba9a1181332d5e9'", 'unique': 'True', 'max_length': '30', 'primary_key': 'True'})
+        },
+        'im.projectdefinition': {
+            'Meta': {'object_name': 'ProjectDefinition'},
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+            'end_date': ('django.db.models.fields.DateTimeField', [], {}),
+            'homepage': ('django.db.models.fields.URLField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'limit_on_members_number': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
+            'member_accept_policy': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.MemberAcceptPolicy']"}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '80'}),
+            'resource_grants': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['im.Resource']", 'null': 'True', 'through': "orm['im.ProjectResourceGrant']", 'blank': 'True'}),
+            'start_date': ('django.db.models.fields.DateTimeField', [], {})
+        },
+        'im.projectmembership': {
+            'Meta': {'unique_together': "(('person', 'project'),)", 'object_name': 'ProjectMembership'},
+            'decision_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'db_index': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_accepted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'issue_date': ('django.db.models.fields.DateField', [], {'default': 'datetime.datetime(2012, 12, 6, 10, 11, 33, 387204)'}),
+            'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"}),
+            'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Project']"})
+        },
+        'im.projectresourcegrant': {
+            'Meta': {'unique_together': "(('resource', 'project_definition'),)", 'object_name': 'ProjectResourceGrant'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'member_limit': ('django.db.models.fields.BigIntegerField', [], {'null': 'True'}),
+            'project_definition': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.ProjectDefinition']", 'blank': 'True'}),
+            'project_limit': ('django.db.models.fields.BigIntegerField', [], {'null': 'True'}),
+            'resource': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Resource']"})
+        },
+        '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'})
+        },
+        'im.sessioncatalog': {
+            'Meta': {'object_name': 'SessionCatalog'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'session_key': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'to': "orm['im.AstakosUser']"})
+        }
+    }
+
+    complete_apps = ['im']
diff --git a/snf-astakos-app/astakos/im/migrations/0035_populate_resource_data.py b/snf-astakos-app/astakos/im/migrations/0035_populate_resource_data.py
new file mode 100644
index 0000000..cf99572
--- /dev/null
+++ b/snf-astakos-app/astakos/im/migrations/0035_populate_resource_data.py
@@ -0,0 +1,321 @@
+# 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, 12, 6, 9, 57, 4, 932883)', '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, 12, 6, 9, 57, 4, 925144)'}),
+            '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', 'null': 'True', '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', 'null': 'True', '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.astakosuserauthprovider': {
+            'Meta': {'unique_together': "(('identifier', 'module', 'user'),)", 'object_name': 'AstakosUserAuthProvider'},
+            'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'affiliation': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'auth_backend': ('django.db.models.fields.CharField', [], {'default': "'astakos'", 'max_length': '255'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'identifier': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'module': ('django.db.models.fields.CharField', [], {'default': "'local'", 'max_length': '255'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'auth_providers'", 'to': "orm['im.AstakosUser']"})
+        },
+        '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, 12, 6, 9, 57, 4, 934567)'}),
+            '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.memberacceptpolicy': {
+            'Meta': {'object_name': 'MemberAcceptPolicy'},
+            'description': ('django.db.models.fields.CharField', [], {'max_length': '80'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'policy': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'})
+        },
+        '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, 12, 6, 9, 57, 4, 930535)', '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.pendingthirdpartyuser': {
+            'Meta': {'unique_together': "(('provider', 'third_party_identifier'),)", 'object_name': 'PendingThirdPartyUser'},
+            'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            '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'}),
+            'token': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'im.project': {
+            'Meta': {'object_name': 'Project'},
+            'application': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'project'", 'unique': 'True', 'to': "orm['im.ProjectApplication']"}),
+            'creation_date': ('django.db.models.fields.DateTimeField', [], {}),
+            'last_approval_date': ('django.db.models.fields.DateTimeField', [], {}),
+            'last_synced_application': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'last_project'", 'unique': 'True', 'null': 'True', 'to': "orm['im.ProjectApplication']"}),
+            'members': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['im.AstakosUser']", 'through': "orm['im.ProjectMembership']", 'symmetrical': 'False'}),
+            'serial': ('django.db.models.fields.CharField', [], {'default': "'3853dff39e254f1cac9f1e76a235df'", 'unique': 'True', 'max_length': '30', 'primary_key': 'True'}),
+            'termination_date': ('django.db.models.fields.DateTimeField', [], {})
+        },
+        'im.projectapplication': {
+            'Meta': {'object_name': 'ProjectApplication'},
+            'applicant': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'my_project_applications'", 'to': "orm['im.AstakosUser']"}),
+            'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'definition': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['im.ProjectDefinition']", 'unique': 'True'}),
+            'issue_date': ('django.db.models.fields.DateTimeField', [], {}),
+            'owner': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'own_project_applications'", 'to': "orm['im.AstakosUser']"}),
+            'precursor_application': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['im.ProjectApplication']", 'unique': 'True', 'null': 'True', 'blank': 'True'}),
+            'serial': ('django.db.models.fields.CharField', [], {'default': "'84b9d92532ef4258a6980a046f9126'", 'unique': 'True', 'max_length': '30', 'primary_key': 'True'})
+        },
+        'im.projectdefinition': {
+            'Meta': {'object_name': 'ProjectDefinition'},
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+            'end_date': ('django.db.models.fields.DateTimeField', [], {}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'limit_on_members_number': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
+            'member_accept_policy': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.MemberAcceptPolicy']"}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '80'}),
+            'resource_grants': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['im.Resource']", 'null': 'True', 'through': "orm['im.ProjectResourceGrant']", 'blank': 'True'}),
+            'start_date': ('django.db.models.fields.DateTimeField', [], {})
+        },
+        'im.projectmembership': {
+            'Meta': {'unique_together': "(('person', 'project'),)", 'object_name': 'ProjectMembership'},
+            'decision_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'db_index': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_accepted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'issue_date': ('django.db.models.fields.DateField', [], {'default': 'datetime.datetime(2012, 12, 6, 9, 57, 4, 944049)'}),
+            'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"}),
+            'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Project']"})
+        },
+        'im.projectresourcegrant': {
+            'Meta': {'unique_together': "(('resource', 'project_definition'),)", 'object_name': 'ProjectResourceGrant'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'member_limit': ('django.db.models.fields.BigIntegerField', [], {'null': 'True'}),
+            'project_definition': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.ProjectDefinition']", 'blank': 'True'}),
+            'project_limit': ('django.db.models.fields.BigIntegerField', [], {'null': 'True'}),
+            'resource': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Resource']"})
+        },
+        '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'})
+        },
+        'im.sessioncatalog': {
+            'Meta': {'object_name': 'SessionCatalog'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'session_key': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'to': "orm['im.AstakosUser']"})
+        }
+    }
+
+    complete_apps = ['im']
diff --git a/snf-astakos-app/astakos/im/models.py b/snf-astakos-app/astakos/im/models.py
index 3a4748c..9ed9ef9 100644
--- a/snf-astakos-app/astakos/im/models.py
+++ b/snf-astakos-app/astakos/im/models.py
@@ -62,9 +62,11 @@ from django.conf import settings
 from django.utils.importlib import import_module
 from django.core.validators import email_re
 
-from astakos.im.settings import (DEFAULT_USER_LEVEL, INVITATIONS_PER_LEVEL,
-                                 AUTH_TOKEN_DURATION, BILLING_FIELDS,
-                                 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
 )
@@ -73,6 +75,8 @@ 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
 
+from astakos.im.notifications import build_notification
+
 import astakos.im.messages as astakos_messages
 
 logger = logging.getLogger(__name__)
@@ -88,14 +92,14 @@ RESOURCE_SEPARATOR = '.'
 inf = float('inf')
 
 class Service(models.Model):
-    name = models.CharField('Name', max_length=255, unique=True, db_index=True)
+    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,
+    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_created = models.DateTimeField(_('Token creation date'), null=True)
     auth_token_expires = models.DateTimeField(
-        'Token expiration date', null=True)
+        _('Token expiration date'), null=True)
 
     def renew_token(self):
         md5 = hashlib.md5()
@@ -133,24 +137,24 @@ class Service(models.Model):
 
 
 class ResourceMetadata(models.Model):
-    key = models.CharField('Name', max_length=255, unique=True, db_index=True)
-    value = models.CharField('Value', max_length=255)
+    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)
+    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)
+    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)
+    name = models.CharField(_('Name'), max_length=255, unique=True, db_index=True)
 
     def __str__(self):
         return self.name
@@ -159,8 +163,8 @@ class GroupKind(models.Model):
 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)
+        _('Homepage Url'), max_length=255, null=True, blank=True)
+    desc = models.TextField(_('Description'), null=True)
     policy = models.ManyToManyField(
         Resource,
         null=True,
@@ -168,30 +172,31 @@ class AstakosGroup(Group):
         through='AstakosGroupQuota'
     )
     creation_date = models.DateTimeField(
-        'Creation date',
+        _('Creation date'),
         default=datetime.now()
     )
     issue_date = models.DateTimeField('Start date', null=True)
     expiration_date = models.DateTimeField(
-        'End date',
+        _('Expiration date'),
+        null=True
          null=True
     )
     moderation_enabled = models.BooleanField(
-        'Moderated membership?',
+        _('Moderated membership?'),
         default=True
     )
     approval_date = models.DateTimeField(
-        'Activation date',
+        _('Activation date'),
         null=True,
         blank=True
     )
     estimated_participants = models.PositiveIntegerField(
-        'Estimated #members',
+        _('Estimated #members'),
         null=True,
         blank=True,
     )
     max_participants = models.PositiveIntegerField(
-        'Maximum numder of participants',
+        _('Maximum numder of participants'),
         null=True,
         blank=True
     )
@@ -235,19 +240,9 @@ class AstakosGroup(Group):
         self.save()
         quota_disturbed.send(sender=self, users=self.approved_members)
 
-    @transaction.commit_manually
     def approve_member(self, person):
         m, created = self.membership_set.get_or_create(person=person)
-        try:
-            m.approve()
-        except:
-            transaction.rollback()
-            raise
-        else:
-            transaction.commit()
-
-#     def disapprove_member(self, person):
-#         self.membership_set.remove(person=person)
+        m.approve()
 
     @property
     def members(self):
@@ -322,44 +317,44 @@ class AstakosUser(User):
     """
     Extends ``django.contrib.auth.models.User`` by defining additional fields.
     """
-    affiliation = models.CharField('Affiliation', max_length=255, blank=True,
+    affiliation = models.CharField(_('Affiliation'), max_length=255, blank=True,
                                    null=True)
 
     # DEPRECATED FIELDS: provider, third_party_identifier moved in
     #                    AstakosUserProvider model.
-    provider = models.CharField('Provider', max_length=255, blank=True,
+    provider = models.CharField(_('Provider'), max_length=255, blank=True,
                                 null=True)
     # ex. screen_name for twitter, eppn for shibboleth
-    third_party_identifier = models.CharField('Third-party identifier',
+    third_party_identifier = models.CharField(_('Third-party identifier'),
                                               max_length=255, null=True,
                                               blank=True)
 
 
     #for invitations
     user_level = DEFAULT_USER_LEVEL
-    level = models.IntegerField('Inviter 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 left'), default=INVITATIONS_PER_LEVEL.get(user_level, 0))
 
-    auth_token = models.CharField('Authentication Token', max_length=32,
+    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_created = models.DateTimeField(_('Token creation date'), null=True)
     auth_token_expires = models.DateTimeField(
-        'Token expiration date', null=True)
+        _('Token expiration date'), null=True)
 
-    updated = models.DateTimeField('Update date')
-    is_verified = models.BooleanField('Is verified?', default=False)
+    updated = models.DateTimeField(_('Update date'))
+    is_verified = models.BooleanField(_('Is verified?'), default=False)
 
-    email_verified = models.BooleanField('Email verified?', default=False)
+    email_verified = models.BooleanField(_('Email verified?'), default=False)
 
-    has_credits = models.BooleanField('Has credits?', default=False)
+    has_credits = models.BooleanField(_('Has credits?'), default=False)
     has_signed_terms = models.BooleanField(
-        'I agree with the terms', default=False)
+        _('I agree with the terms'), default=False)
     date_signed_terms = models.DateTimeField(
-        'Signed terms date', null=True, blank=True)
+        _('Signed terms date'), null=True, blank=True)
 
     activation_sent = models.DateTimeField(
-        'Activation sent data', null=True, blank=True)
+        _('Activation sent data'), null=True, blank=True)
 
     policy = models.ManyToManyField(
         Resource, null=True, through='AstakosUserQuota')
@@ -370,7 +365,7 @@ class AstakosUser(User):
         through='Membership')
 
     __has_signed_terms = False
-    disturbed_quota = models.BooleanField('Needs quotaholder syncing',
+    disturbed_quota = models.BooleanField(_('Needs quotaholder syncing'),
                                            default=False, db_index=True)
 
     objects = AstakosUserManager()
@@ -700,16 +695,16 @@ class AstakosUserAuthProvider(models.Model):
     """
     Available user authentication methods.
     """
-    affiliation = models.CharField('Affiliation', max_length=255, blank=True,
+    affiliation = models.CharField(_('Affiliation'), max_length=255, blank=True,
                                    null=True, default=None)
     user = models.ForeignKey(AstakosUser, related_name='auth_providers')
-    module = models.CharField('Provider', max_length=255, blank=False,
+    module = models.CharField(_('Provider'), max_length=255, blank=False,
                                 default='local')
-    identifier = models.CharField('Third-party identifier',
+    identifier = models.CharField(_('Third-party identifier'),
                                               max_length=255, null=True,
                                               blank=True)
     active = models.BooleanField(default=True)
-    auth_backend = models.CharField('Backend', max_length=255, blank=False,
+    auth_backend = models.CharField(_('Backend'), max_length=255, blank=False,
                                    default='astakos')
 
     objects = AstakosUserAuthProviderManager()
@@ -779,10 +774,12 @@ class Membership(models.Model):
         quota_disturbed.send(sender=self, users=(self.person,))
 
     def disapprove(self):
+        approved = self.is_approved()
         self.delete()
-        quota_disturbed.send(sender=self, users=(self.person,))
+        if approved:
+            quota_disturbed.send(sender=self, users=(self.person,))
 
-class AstakosQuotaManager(models.Manager):
+class ExtendedManager(models.Manager):
     def _update_or_create(self, **kwargs):
         assert kwargs, \
             'update_or_create() must be passed at least one keyword argument'
@@ -812,9 +809,9 @@ class AstakosQuotaManager(models.Manager):
     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)
+    objects = ExtendedManager()
+    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)
 
@@ -822,9 +819,9 @@ class AstakosGroupQuota(models.Model):
         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)
+    objects = ExtendedManager()
+    limit = models.PositiveIntegerField(_('Limit'), null=True)    # obsolete field
+    uplimit = models.BigIntegerField(_('Up limit'), null=True)
     resource = models.ForeignKey(Resource)
     user = models.ForeignKey(AstakosUser)
 
@@ -838,8 +835,8 @@ class ApprovalTerms(models.Model):
     """
 
     date = models.DateTimeField(
-        'Issue date', db_index=True, default=datetime.now())
-    location = models.CharField('Terms location', max_length=255)
+        _('Issue date'), db_index=True, default=datetime.now())
+    location = models.CharField(_('Terms location'), max_length=255)
 
 
 class Invitation(models.Model):
@@ -848,12 +845,12 @@ class Invitation(models.Model):
     """
     inviter = models.ForeignKey(AstakosUser, related_name='invitations_sent',
                                 null=True)
-    realname = models.CharField('Real name', max_length=255)
-    username = models.CharField('Unique ID', max_length=255, unique=True)
-    code = models.BigIntegerField('Invitation code', db_index=True)
-    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)
+    realname = models.CharField(_('Real name'), max_length=255)
+    username = models.CharField(_('Unique ID'), max_length=255, unique=True)
+    code = models.BigIntegerField(_('Invitation code'), db_index=True)
+    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)
@@ -957,14 +954,14 @@ class PendingThirdPartyUser(models.Model):
     """
     Model for registring successful third party user authentications
     """
-    third_party_identifier = models.CharField('Third-party identifier', max_length=255, null=True, blank=True)
-    provider = models.CharField('Provider', max_length=255, blank=True)
+    third_party_identifier = models.CharField(_('Third-party identifier'), max_length=255, null=True, blank=True)
+    provider = models.CharField(_('Provider'), max_length=255, blank=True)
     email = models.EmailField(_('e-mail address'), blank=True, null=True)
     first_name = models.CharField(_('first name'), max_length=30, blank=True)
     last_name = models.CharField(_('last name'), max_length=30, blank=True)
     affiliation = models.CharField('Affiliation', max_length=255, blank=True)
     username = models.CharField(_('username'), max_length=30, unique=True, help_text=_("Required. 30 characters or fewer. Letters, numbers and @/./+/-/_ characters"))
-    token = models.CharField('Token', max_length=255, null=True, blank=True)
+    token = models.CharField(_('Token'), max_length=255, null=True, blank=True)
     created = models.DateTimeField(auto_now_add=True, null=True, blank=True)
 
     class Meta:
@@ -1003,7 +1000,406 @@ class SessionCatalog(models.Model):
     session_key = models.CharField(_('session key'), max_length=40)
     user = models.ForeignKey(AstakosUser, related_name='sessions', null=True)
 
+class MemberAcceptPolicy(models.Model):
+    policy = models.CharField(_('Policy'), max_length=255, unique=True, db_index=True)
+    description = models.CharField(_('Description'), max_length=80)
+
+    def __str__(self):
+        return self.policy
+
+try:
+    auto_accept = MemberAcceptPolicy.objects.get(policy='auto_accept')
+except:
+    auto_accept = None
+
+class ProjectDefinition(models.Model):
+    name = models.CharField(max_length=80)
+    homepage = models.URLField(max_length=255, null=True, blank=True)
+    description = models.TextField(null=True)
+    start_date = models.DateTimeField()
+    end_date = models.DateTimeField()
+    member_accept_policy = models.ForeignKey(MemberAcceptPolicy)
+    limit_on_members_number = models.PositiveIntegerField(null=True,blank=True)
+    resource_grants = models.ManyToManyField(
+        Resource,
+        null=True,
+        blank=True,
+        through='ProjectResourceGrant'
+    )
+    
+    def save(self):
+        self.validate_name()
+        super(ProjectDefinition, self).save()
+    
+    def validate_name(self):
+        """
+        Validate name uniqueness among all active projects.
+        """
+        alive_projects = list(get_alive_projects())
+        q = filter(lambda p: p.definition.name==self.name, alive_projects)
+        if q:
+            raise ValidationError({'name': [_(astakos_messages.UNIQUE_PROJECT_NAME_CONSTRAIN_ERR)]})
+    
+    @property
+    def violated_resource_grants(self):
+        return False
+    
+    def add_resource_policy(self, service, resource, uplimit, update=True):
+        """Raises ObjectDoesNotExist, IntegrityError"""
+        resource = Resource.objects.get(service__name=service, name=resource)
+        if update:
+            ResourceGrant.objects.update_or_create(
+                project=self,
+                resource=resource,
+                defaults={'uplimit': uplimit}
+            )
+        else:
+            q = self.resource_grants_set
+            q.create(resource=resource, uplimit=uplimit)
+
+    @property
+    def resource_policies(self):
+        return self.resource_grants_set.select_related().all()
+
+    @resource_policies.setter
+    def resource_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_resource_policy(service, resource, uplimit, update)
+
+
+class ProjectResourceGrant(models.Model):
+    objects = ExtendedManager()
+    member_limit = models.BigIntegerField(null=True)
+    project_limit = models.BigIntegerField(null=True)
+    resource = models.ForeignKey(Resource)
+    project_definition = models.ForeignKey(ProjectDefinition, blank=True)
+
+    class Meta:
+        unique_together = ("resource", "project_definition")
+
+class ProjectApplication(models.Model):
+    serial = models.CharField(
+        primary_key=True,
+        max_length=30,
+        unique=True,
+        default=uuid.uuid4().hex[:30]
+    )
+    applicant = models.ForeignKey(AstakosUser, related_name='my_project_applications')
+    owner = models.ForeignKey(AstakosUser, related_name='own_project_applications')
+    comments = models.TextField(null=True, blank=True)
+    definition = models.OneToOneField(ProjectDefinition)
+    issue_date = models.DateTimeField()
+    precursor_application = models.OneToOneField('ProjectApplication',
+        null=True,
+        blank=True
+    )
+
+class Project(models.Model):
+    serial = models.CharField(
+        _('username'),
+        primary_key=True,
+        max_length=30,
+        unique=True,
+        default=uuid.uuid4().hex[:30]
+    )
+    application = models.OneToOneField(ProjectApplication, related_name='project')
+    creation_date = models.DateTimeField()
+    last_approval_date = models.DateTimeField()
+    termination_date = models.DateTimeField()
+    members = models.ManyToManyField(AstakosUser, through='ProjectMembership')
+    last_synced_application = models.OneToOneField(
+        ProjectApplication, related_name='last_project', null=True, blank=True
+    )
+    
+    @property
+    def definition(self):
+        return self.application.definition
+    
+    @property
+    def is_valid(self):
+        try:
+            self.application.definition.validate_name()
+        except ValidationError:
+            return False
+        else:
+            return True
+    
+    @property
+    def is_active(self):
+        if not self.is_valid:
+            return False
+        if not self.last_approval_date:
+            return False
+        if self.termination_date:
+            return False
+        if self.definition.violated_resource_grants:
+            return False
+        return True
+    
+    @property
+    def is_terminated(self):
+        if not self.is_valid:
+            return False
+        if not self.termination_date:
+            return False
+        return True
+    
+    @property
+    def is_suspended(self):
+        if not self.is_valid:
+            return False
+        if not self.termination_date:
+            return False
+        if not self.last_approval_date:
+            if not self.definition.violated_resource_grants:
+                return False
+        return True
+    
+    @property
+    def is_alive(self):
+        return self.is_active or self.is_suspended
+    
+    @property
+    def is_inconsistent(self):
+        now = datetime.now()
+        if self.creation_date > now:
+            return True
+        if self.last_approval_date > now:
+            return True
+        if self.terminaton_date > now:
+            return True
+        return False
+    
+    @property
+    def approved_members(self):
+        return self.members.filter(is_accepted=True)
+    
+    def suspend(self):
+        self.last_approval_date = None
+        self.save()
+    
+    def terminate(self):
+        self.terminaton_date = datetime.now()
+        self.save()
+    
+    def sync(self):
+        c, rejected = send_quota(self.approved_members)
+        return rejected
+
+class ProjectMembership(models.Model):
+    person = models.ForeignKey(AstakosUser)
+    project = models.ForeignKey(Project)
+    issue_date = models.DateField(default=datetime.now())
+    decision_date = models.DateField(null=True, db_index=True)
+    is_accepted = models.BooleanField(
+        _('Whether the membership application is accepted'),
+        default=False
+    )
+
+    class Meta:
+        unique_together = ("person", "project")
+
+def filter_queryset_by_property(q, property):
+    """
+    Incorporate list comprehension for filtering querysets by property
+    since Queryset.filter() operates on the database level.
+    """
+    return (p for p in q if getattr(p, property, False))
+
+def get_alive_projects():
+    return filter_queryset_by_property(
+        Project.objects.all(),
+        'is_alive'
+    )
+
+def get_active_projects():
+    return filter_queryset_by_property(
+        Project.objects.all(),
+        'is_active'
+    )
+
+def _lookup_object(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 _create_object(model, **kwargs):
+    o = model.objects.create(**kwargs)
+    o.save()
+    return o
+
+def _update_object(model, id, save=True, **kwargs):
+    o = self._lookup_object(model, id=id)
+    if kwargs:
+        o.__dict__.update(kwargs)
+    if save:
+        o.save()
+    return o
+
+def submit_application(**kwargs):
+    app = self._create_object(ProjectApplication, **kwargs)
+    notification = build_notification(
+        settings.SERVER_EMAIL,
+        [settings.ADMINS],
+        _(GROUP_CREATION_SUBJECT) % {'group':app.definition.name},
+        _('An new project application identified by %(serial)s has been submitted.') % app.serial
+    )
+    notification.send()
+
+def list_applications():
+    return ProjectAppication.objects.all()
+
+def create_application(definition, applicant, comments, precursor_application=None, commit=True):
+    if precursor_application:
+        application = precursor_application.copy()
+        application.precursor_application = precursor_application
+    else:
+        application = ProjectApplication(owner=applicant)
+    application.definition = definition
+    application.applicant = applicant
+    application.comments = comments
+    application.issue_date = datetime.now()
+    if commit:
+        definition.save()
+        application.save()
+    return application
+    
+def approve_application(serial):
+    app = _lookup_object(ProjectAppication, serial=serial)
+    notify = False
+    if not app.precursor_application:
+        kwargs = {
+            'application':app,
+            'creation_date':datetime.now(),
+            'last_approval_date':datetime.now(),
+        }
+        project = _create_object(Project, **kwargs)
+    else:
+        project = app.precursor_application.project
+        last_approval_date = project.last_approval_date
+        if project.is_valid:
+            project.application = app
+            project.last_approval_date = datetime.now()
+            project.save()
+        else:
+            raise Exception(_(astakos_messages.INVALID_PROJECT) % project.__dict__)
+    
+    rejected = synchonize_project(project.serial)
+    if rejected:
+        # revert to precursor
+        project.appication = app.precursor_application
+        if project.application:
+            project.last_approval_date = last_approval_date
+        project.save()
+        rejected = synchonize_project(project.serial)
+        if rejected:
+            raise Exception(_(astakos_messages.QH_SYNC_ERROR))
+    else:
+        project.last_application_synced = app
+        project.save()
+        sender, recipients, subject, message
+        notification = build_notification(
+            settings.SERVER_EMAIL,
+            [project.owner.email],
+            _('Project application has been approved on %s alpha2 testing' % SITENAME),
+            _('Your application request %(serial)s has been apporved.')
+        )
+        notification.send()
+
+
+def list_projects(filter_property=None):
+    if filter_property:
+        return filter_queryset_by_property(
+            Project.objects.all(),
+            filter_property
+        )
+    return Project.objects.all()
+
+def add_project_member(serial, user_id, request_user):
+    project = _lookup_object(Project, serial=serial)
+    user = _lookup_object(AstakosUser, id=user_id)
+    if not project.owner == request_user:
+        raise Exception(_(astakos_messages.NOT_PROJECT_OWNER))
+    
+    if not project.is_alive:
+        raise Exception(_(astakos_messages.NOT_ALIVE_PROJECT) % project.__dict__)
+    if len(project.members) + 1 > project.limit_on_members_number:
+        raise Exception(_(astakos_messages.MEMBER_NUMBER_LIMIT_REACHED))
+    m = self._lookup_object(ProjectMembership, person=user, project=project)
+    if m.is_accepted:
+        return
+    m.is_accepted = True
+    m.decision_date = datetime.now()
+    m.save()
+    notification = build_notification(
+        settings.SERVER_EMAIL,
+        [user.email],
+        _('Your membership on project %(name)s has been accepted.') % project.definition.__dict__, 
+        _('Your membership on project %(name)s has been accepted.') % project.definition.__dict__,
+    )
+    notification.send()
+
+def remove_project_member(serial, user_id, request_user):
+    project = _lookup_object(Project, serial=serial)
+    if not project.is_alive:
+        raise Exception(_(astakos_messages.NOT_ALIVE_PROJECT) % project.__dict__)
+    if not project.owner == request_user:
+        raise Exception(_(astakos_messages.NOT_PROJECT_OWNER))
+    user = self.lookup_user(user_id)
+    m = _lookup_object(ProjectMembership, person=user, project=project)
+    if not m.is_accepted:
+        return
+    m.is_accepted = False
+    m.decision_date = datetime.now()
+    m.save()
+    notification = build_notification(
+        settings.SERVER_EMAIL,
+        [user.email],
+        _('Your membership on project %(name)s has been removed.') % project.definition.__dict__,
+        _('Your membership on project %(name)s has been removed.') % project.definition.__dict__
+    )
+    notification.send()    
+
+def suspend_project(serial):
+    project = _lookup_object(Project, serial=serial)
+    project.suspend()
+    notification = build_notification(
+        settings.SERVER_EMAIL,
+        [project.owner.email],
+        _('Project %(name)s has been suspended.') %  project.definition.__dict__,
+        _('Project %(name)s has been suspended.') %  project.definition.__dict__
+    )
+    notification.send()
+
+def terminate_project(serial):
+    project = _lookup_object(Project, serial=serial)
+    project.termination()
+    notification = build_notification(
+        settings.SERVER_EMAIL,
+        [project.owner.email],
+        _('Project %(name)s has been terminated.') %  project.definition.__dict__,
+        _('Project %(name)s has been terminated.') %  project.definition.__dict__
+    )
+    notification.send()
 
+def synchonize_project(serial):
+    project = _lookup_object(Project, serial=serial)
+    if project.app != project.last_application_synced:
+        return project.sync()
+     
 def create_astakos_user(u):
     try:
         AstakosUser.objects.get(user_ptr=u.pk)
diff --git a/snf-astakos-app/astakos/im/notifications.py b/snf-astakos-app/astakos/im/notifications.py
new file mode 100644
index 0000000..4713fdf
--- /dev/null
+++ b/snf-astakos-app/astakos/im/notifications.py
@@ -0,0 +1,75 @@
+# 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 django.conf import settings
+from django.core.mail import send_mail
+
+import astakos.im.messages as astakos_messages
+
+logger = logging.getLogger(__name__)
+
+def build_notification():
+    return EmailNotification(sender, recipients, subject, message)
+
+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 EmailNotification(Notification):
+    def send(self):
+        try:
+            send_mail(
+                subject,
+                message,
+                sender,
+                recipients
+            )
+        except:
+            raise SendNotificationError()
+
+class SendMailError(Exception):
+    pass
+
+class SendNotificationError(SendMailError):
+    def __init__(self):
+        self.message = _(astakos_messages.NOTIFICATION_SEND_ERR)
+        super(SendNotificationError, self).__init__()
diff --git a/snf-astakos-app/astakos/im/static/im/css/global.css b/snf-astakos-app/astakos/im/static/im/css/global.css
index 709ac42..a6bb170 100644
--- a/snf-astakos-app/astakos/im/static/im/css/global.css
+++ b/snf-astakos-app/astakos/im/static/im/css/global.css
@@ -3,4 +3,4 @@
 @import url(colorbox.css);
 @import url(browser-fixes.css);
 @import url(forms.css);
-@import url(dropkick.css);
\ No newline at end of file
+/* @import url(dropkick.css); */
\ No newline at end of file
diff --git a/snf-astakos-app/astakos/im/templates/im/astakosgroup_detail.html b/snf-astakos-app/astakos/im/templates/im/astakosgroup_detail.html
index 0a64e82..ac6d05d 100644
--- a/snf-astakos-app/astakos/im/templates/im/astakosgroup_detail.html
+++ b/snf-astakos-app/astakos/im/templates/im/astakosgroup_detail.html
@@ -169,7 +169,7 @@
                         <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>
+                                <a href="{% url disapprove_member object.id m.person.id  %}?{% if page %}page={{ page }}{% endif %}{% if sorting %}&sorting={{sorting}}{% endif %}">Remove</a>
                             {% endif %}
                         </td>    
                         {% endif %}
diff --git a/snf-astakos-app/astakos/im/templates/im/projects/project_detail.html b/snf-astakos-app/astakos/im/templates/im/projects/project_detail.html
new file mode 100644
index 0000000..3b2d35f
--- /dev/null
+++ b/snf-astakos-app/astakos/im/templates/im/projects/project_detail.html
@@ -0,0 +1,231 @@
+{% 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 %}
+					{% if object.is_enabled %}
+						[ ADMINISTRATOR - ACTIVE ]
+					{% else %}
+						[ ADMINISTRATOR - PENDING ]
+					{% endif %}
+				{%  else %}
+					{% if  object.is_active_member %}
+						[ ENROLLED - ACTIVE ]
+					{% else %}
+						[ ENROLLED - PENDING ]
+					{% endif %}
+				{% endif %}
+			</em>		
+		{% endif %}
+	 	<span>{{ object.name|upper }}</span>
+	 </h2>
+	 
+	 <div class="details">
+	 	<h3>
+	 		GENERAL INFO
+		 	{% if object.is_owner %}
+		 		<a href="#" class="edit">EDIT</a>
+		 	{% endif %}
+	 	</h3>
+	 	
+	 	<div class="data">
+		 	<p class="restricted">{{ object.desc|safe }}</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>
+			 	<dt>Moderation</dt>
+			 	<dd>
+			 		{{ object.moderation_enabled|yesno:"Yes, No" }}
+			 	</dd>
+			 </dl>
+		</div>
+		<div class="editable" style="display:none;">
+		<form action="{% url astakos.im.views.group_detail object.id %}" 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="SAVE" />
+                    </div>
+	            {% endwith %}
+	    </form>
+		</div>
+	 </div>
+	 <div class="full-dotted">
+		 <h3>DETAILS</h3>
+		 <dl class="alt-style">
+		 	<dt>Name</dt>
+		 	<dd>{{ object.name }}&nbsp;</dd>
+		 	<!--<dt>Type</dt>
+		 	<dd>{{object.kindname|capfirst}}&nbsp;</dd>-->
+		 	<dt>Issue date:</dt>
+		 	<dd>{{object.issue_date|date:"d/m/Y"}}&nbsp;</dd>
+		 	<dt>Expiration Date</dt>
+		 	<dd>{{object.expiration_date|date:"d/m/Y"}}&nbsp;</dd>
+<!--
+		 	<dt>Moderation</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 %}&nbsp;
+            </dd>
+            <dt>Max participants</dt>
+		 	<dd>{% if object.max_participants%}{{object.max_participants}}{% else %}&nbsp;{% 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>
+       		{% empty %}
+       			No resources
+	 		{% endfor %}
+	 		
+	 		
+	 	</dl>
+		{% else %}
+            <p>No resources</p>
+        {% endif %} 
+	 </div>
+     {% if object.is_owner %}
+	 <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="person__email" {% if sorting == 'person__email' %}selected{% endif %}>Sort by User Id</option>
+                        <option value="person__first_name" {% if sorting == 'person__first_name' %}selected{% endif %}>Sort by Name</option>
+                        <option value="date_joined" {% if sorting == 'date_joined' %}selected{% endif %}>Sort by 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
+                            {% if object.is_owner %}
+                                <a href="{% url disapprove_member object.id m.person.id  %}?{% if page %}page={{ page }}{% endif %}{% if sorting %}&sorting={{sorting}}{% endif %}">Remove</a>
+                            {% endif %}
+                        </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 %}">Remove</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>
+     
+     
+    
+    <div class="full-dotted">
+        <form action="{% url astakos.im.views.group_detail object.id %}#members-table" 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 class="full-dotted">
+    	<p>
+    		<a href="{% url group_all %}">back to All Projects &gt;</a></li>
+    	</p>
+        <p>
+        	<a href="{% url group_list %}">back to My Projects &gt;</a>
+        </p>
+        </ul>
+    </div>
+     
+    
+</div>
+ 
+{% endwith %}
+{% endblock %}
diff --git a/snf-astakos-app/astakos/im/templates/im/projects/project_form_summary.html b/snf-astakos-app/astakos/im/templates/im/projects/project_form_summary.html
new file mode 100644
index 0000000..4f4a6b0
--- /dev/null
+++ b/snf-astakos-app/astakos/im/templates/im/projects/project_form_summary.html
@@ -0,0 +1,84 @@
+{% 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>CONFIRMATION REQUEST</legend>
+		<P>These are the specifications of the Project you want to create. If you hit the "Submit" button this form will be officially sent to GRNET for review. Please make sure the following reflect exactly your request. After submitting, there is no way to modify your Project request. </P>
+<!-- 
+ 		{% for k,v in data.iteritems %}
+ 			<input type="hidden" name="{{ k }}" value="{{ v }}">
+ 		{% endfor %}
+ -->
+        {% include "im/form_render.html" %}
+		<div class="full-dotted">
+			<h3>GENERAL INFO</h3>
+			<p class="restricted">{{ data.desc|safe }}</p>
+			<dl class="alt-style">			    
+				<dt>Homepage Url</dt>
+				<dd>{{ data.homepage }}&nbsp;</dd>
+				<dt>Modaration</dt>
+			 	<dd>{{ data.moderation_enabled|yesno:"Yes, No" }}</dd>
+			</dl>
+		</div>
+		<div class="full-dotted">
+			 <h3>DETAILS</h3>
+			 <dl class="alt-style">			    
+			    <dt>Name</dt>
+			 	<dd>{{ data.name }}&nbsp;</dd>
+			 	<!--<dt>Type</dt>
+			 	<dd>Course&nbsp;</dd>-->
+			 	<dt>Issue date:</dt>
+			 	<dd>{{ data.issue_date|date:"d/m/Y"}}&nbsp;</dd>
+			 	<dt>Expiration Date</dt>
+			 	<dd>{{ data.expiration_date|date:"d/m/Y"}}&nbsp;</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>
+			<p>The following ~okeanos resources will be granted to each member of this Project:</p>
+		 	<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>
+           		{% empty %}
+           			No resources
+		 		{% 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 %}
diff --git a/snf-astakos-app/astakos/im/templates/im/projects/project_list.html b/snf-astakos-app/astakos/im/templates/im/projects/project_list.html
new file mode 100644
index 0000000..d60ce99
--- /dev/null
+++ b/snf-astakos-app/astakos/im/templates/im/projects/project_list.html
@@ -0,0 +1,362 @@
+{% extends "im/account_base.html" %}
+
+{% load filters %}
+
+{% block page.body %}
+<div class="maincol {% block innerpage.class %}{% endblock %}">
+    <div class="projects">
+	    <h2>PROJECTS</h2>
+	    {% if form %}
+		    <p>Search for existing Projects and join the ones you like. Please search by Project name. </p>
+		    <form action="{% url group_search %}" method="post" class="withlabels signup submit-inline">{% csrf_token %}
+		            {% include "im/form_render.html" %}
+		        <div class="form-row submit">
+		                <input type="submit" class="submit altcol" value="SEARCH" />
+		                {% if q %}<a href="{% url group_all %}">clear</a>{% endif %}
+		        </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="clear" />
+		        </div>
+		    </form>
+		   <-->
+	    {% else %}
+    	<div class="two-cols clearfix">
+			<div class="rt">
+				 &nbsp;
+			</div>
+			<div class="lt">
+				 <p>~okeanos gives the opportunity to Greek Academic or Research Organizations/Institutions/Faculty to run their own projects remotely on virtual infrastructure. Simple, fast and with minimal to no cost at all.</p>
+				 <p><a href="{% url how_it_works %}" style="font-size:1.154em;">How it works ></a></p>
+			</div>
+		</div>
+		
+		
+		<div class="widjets"> 
+			<!--<a href="#" class="widjet-x" title="remove boxes">X</a>-->
+			<ul class="clearfix">	
+				<li class="create">
+					<div>
+						<div class="wrap">
+
+							<p class="centered"><a href="{% url group_add 'project' %}"><img alt="THINK ABOUT IT" src="/static/im/images/create.png"></a></p>
+							<p class="txt">Create a new Project in seconds. Specify how many members it will have, which and how many virtual resources it will provide to its members. Describe its purpose. Submit your request and if accepted, you and your colleagues are ready to deploy!<br><br> </p>
+							<p><a href="{% url group_add 'project' %}">create a project ></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">
+							<p class="centered"><a href="{% url group_all %}"><img alt="THINK ABOUT IT" src="/static/im/images/join.png"></a></p>
+							<p class="txt">Become a member of an existing Project and instantly gain access to the resources it has to offer you. Search for open Projects and join for free. Contact the closed Projects administrators, if you think they will accept you. In two words: try to Join now. </p>
+							
+							<p><a href="{% url group_all %}">join a project ></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="groupname">Sort by Name</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>
+					<input type="hidden" name="q" value="{{q}}"/>
+				</div>
+			</form>
+            <table class="alt-style complex" id="searchResults">
+                <caption>
+                    {% if q %}SEARCH RESULTS{% else %}ALL PROJECTS{% endif %}
+                </caption>
+                <thead>
+                  <tr>
+                    <th>Name</th>
+                    <!--<th>Type</th>-->
+                    <th>Issued</th>
+                    <th>Expires</th>
+                     
+                    <th>Enrolled</th>
+                   
+                     
+                    <th>Status</th>
+                    <th>&nbsp;</th>
+			        <th>Moderated</th>
+                   <!-- <th>&nbsp;</th>-->
+                  
+                  </tr>
+                </thead>
+                <tbody>
+                  {% for o in object_list %}
+                   <tr class="{% cycle 'tr1' 'tr2' %}">
+	                    <td style="width:22%"><a href="{% url group_detail o.id %}" title="visit group page">{{o.groupname|truncatename}}</a></td>
+	                    <!--td>{{o.kindname|capfirst}}</td-->
+	                    <td style="width:13%">{{o.issue_date|date:"d/m/Y"}}</td>
+	                    <td style="width:13%">{{o.expiration_date|date:"d/m/Y"}}</td>
+	                    <td style="width:11%">{{o.approved_members_num}}</td>
+	                    
+	                    <td style="width:17%">
+	                    	<div class="msg-wrap">
+	                    		 
+		                    {% if o.is_member %}
+		                        {% if o.membership_approval_date %}
+		    
+		                        
+			                       	{% if not o.is_owner %}
+			                            Registered
+			                             
+			                        {% else %}
+			                        	Owner
+			                        {% endif %}
+		                        
+		                            
+		
+		                        {% else %}
+		                            Activation pending
+		                        {% endif %}
+		                    {% else %}
+		                            Not member 
+		                                 
+		                            
+		                    {% endif %}
+	                    	</div>
+	                    </td>
+	                    <td style="width:15%">
+	                    	<div class="msg-wrap">
+	                    		 
+		                    {% if o.is_member %}
+		                        {% if o.membership_approval_date %}
+		    
+		                        
+			                       	{% if not o.is_owner %}
+			                             
+			                            <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}}</a><br>
+					                		{% if o.desc %}Description:{{o.desc|truncatewords:30}}{% endif %}<br><br>
+					                		
+					                		<a href="#" class="yes submit">Yes</a>&nbsp;&nbsp;&nbsp;<a href="#" class="no submit">No</a>
+					                	</div>
+			                        {% else %}
+			                        	 &nbsp;
+			                        {% endif %}
+		                        
+		                            
+		
+		                        {% else %}
+		                            &nbsp;
+		                        {% endif %}
+		                    {% else %}
+		                             
+		                                <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}}</a><br>
+					                		{% if o.desc %}Description:{{o.desc|truncatewords:30}}{% endif %}<br><br>
+					                		
+					                		<a href="#" class="yes submit">Yes</a>&nbsp;&nbsp;&nbsp;<a href="#" class="no submit">No</a>
+					                	</div>
+		                            
+		                    {% endif %}
+	                    	</div>
+	                    </td>
+			            <td class="centered" style="width:9%">{% 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="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 %}
+        	 	{% if page_obj.object_list  %}
+	      	    <div>
+					<form method="GET" class="minimal" action="#allGroups" id="mygroups">
+						<div class="form-row">
+						    <select name="sorting"  class="dropkicked"  tabindex="1">
+							    <option value="groupname">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 PROJECTS</caption>
+			            <thead>
+			              <tr>
+			                <th>Name</th>
+			                <!--th>Type</th-->
+			                <th>Issued</th>
+			                <th>Expires</th>
+			                <th>Enrolled</th>
+			                <th>Status</th>
+			                <th>&nbsp;</th>
+			                <th class="centered">Moderated</th>
+			               <!-- <th>&nbsp;</th>-->
+			                
+			              </tr>
+			            </thead>
+			            <tbody>
+			              {% for o in page_obj.object_list %}
+			              <tr class="{% cycle 'tr1' 'tr2' %}">
+			                <td style="width:22%"><a  href="{% url group_detail o.id %}" title="visit group page">{{o.groupname|truncatename }}</a></td>
+			                <!--td>{{o.kindname|capfirst}}</td-->
+			                <td style="width:13%">{{o.issue_date|date:"d/m/Y"}}</td>
+			                <td style="width:13%">{{o.expiration_date|date:"d/m/Y"}}</td>
+			                <td style="width:11%">{{ o.approved_members_num }}</td>
+			                <td style="width:17%">
+			                	<div class="msg-wrap">
+			                	{% if user.email = o.groupowner %}
+			                		{% if o.is_enabled %}
+			                			Active (Owner)
+			                		{% else %}
+			                			Pending
+			                		{% endif %}
+			                	{% else %}
+			                		{% if o.is_enabled %}
+			                			{% if o.membership_status %}
+						                    Registered
+					                    	 
+						                {% else %}
+						                    Activation Pending
+						                {% endif %}
+			                		{% else %}
+			                			-
+			                		{% endif %}
+			                	{% endif %}
+			                	
+			                	</div>
+			                </td>
+			                <td style="width:15%">
+			                	<div class="msg-wrap">
+			                 	{% if user.email = o.groupowner %}
+			                		{% if o.is_enabled %}
+			                			&nbsp;
+			                		{% else %}
+			                			&nbsp;
+			                		{% endif %}
+			                	{% else %}
+			                		{% if o.is_enabled %}
+			                			{% if o.membership_status %}
+						                    
+					                    	<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}}</a><br>
+						                		{% if o.desc %}Description:{{o.desc|truncatewords:30}}{% endif %}<br><br>
+						                		
+						                		<a href="#" class="yes submit">Yes</a>&nbsp;&nbsp;&nbsp;<a href="#" class="no submit">No</a>
+						                	</div>
+						                {% else %}
+						                    &nbsp;
+						                {% endif %}
+			                		{% else %}
+			                			&nbsp;
+			                		{% endif %}
+			                	{% endif %}
+			                	
+			                	</div>
+			                </td>
+			                <td class="centered" style="width:9%">{% 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>
+		 	{% endif %}
+        	{% endwith %} 
+	      	{% endwith %} 
+	      	{% endif %}
+	      	{% if form %}
+	            {% if q %}
+	                <h2>No projects found!</h2>
+	            {% endif %}
+	        {% endif %}
+      {% endif %}
+    {% endwith %}
+</div>
+</div>  
+{% endblock %}
diff --git a/snf-astakos-app/astakos/im/templates/im/projects/projectapplication_detail.html b/snf-astakos-app/astakos/im/templates/im/projects/projectapplication_detail.html
new file mode 100644
index 0000000..3b2d35f
--- /dev/null
+++ b/snf-astakos-app/astakos/im/templates/im/projects/projectapplication_detail.html
@@ -0,0 +1,231 @@
+{% 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 %}
+					{% if object.is_enabled %}
+						[ ADMINISTRATOR - ACTIVE ]
+					{% else %}
+						[ ADMINISTRATOR - PENDING ]
+					{% endif %}
+				{%  else %}
+					{% if  object.is_active_member %}
+						[ ENROLLED - ACTIVE ]
+					{% else %}
+						[ ENROLLED - PENDING ]
+					{% endif %}
+				{% endif %}
+			</em>		
+		{% endif %}
+	 	<span>{{ object.name|upper }}</span>
+	 </h2>
+	 
+	 <div class="details">
+	 	<h3>
+	 		GENERAL INFO
+		 	{% if object.is_owner %}
+		 		<a href="#" class="edit">EDIT</a>
+		 	{% endif %}
+	 	</h3>
+	 	
+	 	<div class="data">
+		 	<p class="restricted">{{ object.desc|safe }}</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>
+			 	<dt>Moderation</dt>
+			 	<dd>
+			 		{{ object.moderation_enabled|yesno:"Yes, No" }}
+			 	</dd>
+			 </dl>
+		</div>
+		<div class="editable" style="display:none;">
+		<form action="{% url astakos.im.views.group_detail object.id %}" 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="SAVE" />
+                    </div>
+	            {% endwith %}
+	    </form>
+		</div>
+	 </div>
+	 <div class="full-dotted">
+		 <h3>DETAILS</h3>
+		 <dl class="alt-style">
+		 	<dt>Name</dt>
+		 	<dd>{{ object.name }}&nbsp;</dd>
+		 	<!--<dt>Type</dt>
+		 	<dd>{{object.kindname|capfirst}}&nbsp;</dd>-->
+		 	<dt>Issue date:</dt>
+		 	<dd>{{object.issue_date|date:"d/m/Y"}}&nbsp;</dd>
+		 	<dt>Expiration Date</dt>
+		 	<dd>{{object.expiration_date|date:"d/m/Y"}}&nbsp;</dd>
+<!--
+		 	<dt>Moderation</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 %}&nbsp;
+            </dd>
+            <dt>Max participants</dt>
+		 	<dd>{% if object.max_participants%}{{object.max_participants}}{% else %}&nbsp;{% 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>
+       		{% empty %}
+       			No resources
+	 		{% endfor %}
+	 		
+	 		
+	 	</dl>
+		{% else %}
+            <p>No resources</p>
+        {% endif %} 
+	 </div>
+     {% if object.is_owner %}
+	 <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="person__email" {% if sorting == 'person__email' %}selected{% endif %}>Sort by User Id</option>
+                        <option value="person__first_name" {% if sorting == 'person__first_name' %}selected{% endif %}>Sort by Name</option>
+                        <option value="date_joined" {% if sorting == 'date_joined' %}selected{% endif %}>Sort by 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
+                            {% if object.is_owner %}
+                                <a href="{% url disapprove_member object.id m.person.id  %}?{% if page %}page={{ page }}{% endif %}{% if sorting %}&sorting={{sorting}}{% endif %}">Remove</a>
+                            {% endif %}
+                        </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 %}">Remove</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>
+     
+     
+    
+    <div class="full-dotted">
+        <form action="{% url astakos.im.views.group_detail object.id %}#members-table" 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 class="full-dotted">
+    	<p>
+    		<a href="{% url group_all %}">back to All Projects &gt;</a></li>
+    	</p>
+        <p>
+        	<a href="{% url group_list %}">back to My Projects &gt;</a>
+        </p>
+        </ul>
+    </div>
+     
+    
+</div>
+ 
+{% endwith %}
+{% endblock %}
diff --git a/snf-astakos-app/astakos/im/templates/im/projects/projectapplication_form.html b/snf-astakos-app/astakos/im/templates/im/projects/projectapplication_form.html
new file mode 100644
index 0000000..a3665e6
--- /dev/null
+++ b/snf-astakos-app/astakos/im/templates/im/projects/projectapplication_form.html
@@ -0,0 +1,102 @@
+{% 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="{{ gname }}">
+	    		<a href="#icons" class="delete">X remove resource</a>	
+	    		{% for rname, rdata in resources.items %}
+	    		<fieldset class="quota">
+			    	
+			    	<legend>
+			    		{% if rdata.is_abbreviation %}
+			    			{{ rdata.verbose_name|upper }}
+			    		{% else %}
+			    			{{ rdata.verbose_name|capfirst }}
+			    		{% endif %}
+			    		<span class="info"> 
+					    	<em>more info</em>
+					    	<span>{{ rdata.help_text }}</span>
+				    	</span>  
+			    	</legend>
+			    	<div class="form-row">
+			    		<p class="clearfix">
+			    			<label for="{{'id_'|add:rname|add:'_uplimit'}}_proxy" >
+								Max {% if rdata.is_abbreviation %}{{ rdata.verbose_name|upper }}{% else %}{{ rdata.verbose_name }}{% endif %}{% if not rdata.unit %}s {% endif  %} per user
+							</label>
+			    			<input 	type="text" 
+			       						id="{{'id_'|add:rname|add:'_uplimit'}}_proxy" 
+			       						name="{{rname|add:'_uplimit'}}_proxy" 
+			       						placeholder="{{ rdata.placeholder}} " 
+			       						{% if rdata.unit == 'bytes' %} 
+										 	class="dehumanize"
+										{% endif  %}
+			       						/> 
+			    			<span class="extra-img">&nbsp;</span>
+			         		<span class="info"><em>more info</em><span>Leave this field blank if you don't want to specify this resource</span></span>
+			         		<p class="error-msg">Invalid format</p>
+			    		</p>
+			    		<p class="msg"></p>
+			    	</div>
+				</fieldset>	
+				{% endfor %}
+	    	</div>
+	    	 
+	    {% endfor %}
+    </div>
+    <div class="form-row submit">
+   		<input type="submit" value="CONTINUE" class="submit altcol" autocomplete="off">
+	</div>  
+	<input type="hidden" name="user"  id="id_user" value="{{user.id}}">   
+</form>
+	 
+<script>
+	
+</script>	 
+ 
+{% endblock %}
\ No newline at end of file
diff --git a/snf-astakos-app/astakos/im/templates/im/projects/projectapplication_list.html b/snf-astakos-app/astakos/im/templates/im/projects/projectapplication_list.html
new file mode 100644
index 0000000..0b31485
--- /dev/null
+++ b/snf-astakos-app/astakos/im/templates/im/projects/projectapplication_list.html
@@ -0,0 +1,14 @@
+{% extends "im/account_base.html" %}
+
+{% load filters %}
+
+{% block page.body %}
+<div class="maincol {% block innerpage.class %}{% endblock %}">
+    <div class="projects">
+	    <h2>PROJECTS</h2>
+    {% with page_obj.object_list as object_list %}
+        {{object_list}}
+    {% endwith %}
+    </div>
+</div>  
+{% endblock %}
diff --git a/snf-astakos-app/astakos/im/urls.py b/snf-astakos-app/astakos/im/urls.py
index 8904774..96d7ef4 100644
--- a/snf-astakos-app/astakos/im/urls.py
+++ b/snf-astakos-app/astakos/im/urls.py
@@ -50,8 +50,10 @@ urlpatterns = patterns('astakos.im.views',
     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'^resources/?$', 'resource_usage', {}, name='resource_usage'),
+
 #    url(r'^billing/?$', 'billing', {}, name='billing'),
-    url(r'^timeline/?$', 'timeline', {}, name='timeline'),
+#    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'),
@@ -63,9 +65,21 @@ urlpatterns = patterns('astakos.im.views',
     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'),
-    url(r'^remove_auth_provider/(?P<pk>\d+)?$', 'remove_auth_provider', {},
-        name='remove_auth_provider'),
-    url(r'^group/how_it_works/?$', 'how_it_works', {}, name='how_it_works')
+    url(r'^group/how_it_works/?$', 'how_it_works', {}, name='how_it_works'),
+    
+    url(r'^project/add/?$', 'project_add', {}, name='project_add'),
+    url(r'^project/application/list/?$', 'project_application_list', {}, name='project_application_list'),
+    url(r'^project/list/?$', 'project_list', {}, name='project_list'),
+    url(r'^project/(?P<serial>\w+)/?$', 'project_detail', {}, name='project_detail'),
+    url(r'^project/search/?$', 'project_search', {}, name='project_search'),
+    url(r'^project/all/?$', 'project_all', {}, name='project_all'),
+    url(r'^project/(?P<serial>\w+)/join/?$', 'project_join', {}, name='project_join'),
+    url(r'^project/(?P<serial>\w+)/leave/?$', 'project_leave', {}, name='project_leave'),
+    url(r'^project/(?P<serial>\w+)/(?P<user_id>\d+)/approve/?$', 'project_approve_member', {}, name='project_approve_member'),
+    url(r'^project/(?P<serial>\w+)/(?P<user_id>\d+)/disapprove/?$', 'project_remove_member', {}, name='project_remove_member'),
+    
+    url(r'^group/how_it_works/?$', 'how_it_works', {}, name='how_it_works'),
+    url(r'^remove_auth_provider/(?P<pk>\d+)?$', 'remove_auth_provider', {}, name='remove_auth_provider'),
 )
 
 
diff --git a/snf-astakos-app/astakos/im/views.py b/snf-astakos-app/astakos/im/views.py
index be905e5..87126a4 100644
--- a/snf-astakos-app/astakos/im/views.py
+++ b/snf-astakos-app/astakos/im/views.py
@@ -53,8 +53,9 @@ 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.create_update import (
+    create_object, delete_object, get_model_and_form_class
+)
 from django.views.generic.list_detail import object_list
 from django.core.xheaders import populate_xheaders
 from django.core.exceptions import ValidationError, PermissionDenied
@@ -63,29 +64,36 @@ from django.views.decorators.http import require_http_methods
 from django.db.models import Q
 
 from astakos.im.activation_backends import get_backend, SimpleBackend
-from astakos.im.models import (AstakosUser, ApprovalTerms, AstakosGroup,
-                               EmailChange, GroupKind, Membership,
-                               RESOURCE_SEPARATOR, AstakosUserAuthProvider)
+from astakos.im.models import (
+    AstakosUser, ApprovalTerms, AstakosGroup,
+    EmailChange, GroupKind, Membership,
+    RESOURCE_SEPARATOR, AstakosUserAuthProvider,
+    ProjectApplication
+)
 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, AstakosGroupSortForm,
-                              TimelineForm, PickResourceForm,
-                              AstakosGroupCreationSummaryForm)
+from astakos.im.forms import (
+    LoginForm, InvitationForm, ProfileForm,
+    FeedbackForm, SignApprovalTermsForm,
+    EmailChangeForm,
+    AstakosGroupCreationForm, AstakosGroupSearchForm,
+    AstakosGroupUpdateForm, AddGroupMembersForm,
+    MembersSortForm, AstakosGroupSortForm,
+    TimelineForm, PickResourceForm,
+    AstakosGroupCreationSummaryForm,
+    ProjectApplicationForm
+)
 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,
+#                                   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, PAGINATE_BY_ALL)
 #from astakos.im.tasks import request_billing
 from astakos.im.api.callpoint import AstakosCallpoint
+# from .generic_views import create_object
 
 import astakos.im.messages as astakos_messages
 from astakos.im import settings
@@ -802,7 +810,6 @@ class ResourcePresentation():
 @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)
@@ -863,10 +870,11 @@ def group_add(request, kind_name='default'):
     return HttpResponse(t.render(c))
 
 
-#@require_http_methods(["POST"])
+#@require_hsttp_methods(["POST"])
 @require_http_methods(["GET", "POST"])
 @signed_terms_required
 @login_required
+@transaction.commit_manually
 def group_add_complete(request):
     model = AstakosGroup
     form = AstakosGroupCreationSummaryForm(request.POST)
@@ -876,10 +884,6 @@ def group_add_complete(request):
         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(
@@ -892,8 +896,13 @@ def group_add_complete(request):
                 )
             except SendNotificationError, e:
                 messages.error(request, e, fail_silently=True)
-            post_save_redirect = '/im/group/%(id)s/'
-            return HttpResponseRedirect(post_save_redirect % new_object)
+                transaction.rollback()
+            else:
+                msg = _(astakos_messages.OBJECT_CREATED) %\
+                    {"verbose_name": model._meta.verbose_name}
+                message = _(astakos_messages.NOTIFICATION_SENT) % 'a project'
+                messages.success(request, msg, fail_silently=True)
+                transaction.commit()
         else:
             d = {"verbose_name": model._meta.verbose_name,
                  "reason":result.reason}
@@ -1389,35 +1398,35 @@ def group_create_list(request):
 #    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
+# #@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
 
 
 # TODO: action only on POST and user should confirm the removal
@@ -1441,4 +1450,83 @@ def how_it_works(request):
     return render_response(
         template='im/how_it_works.html',
         context_instance=get_context(request),)
+
+@require_http_methods(["GET", "POST"])
+@signed_terms_required
+@login_required
+def project_add(request):
+    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 = {'resource_catalog':resource_catalog}
+    return create_object(request, template_name='im/projects/projectapplication_form.html',
+        extra_context=extra_context, post_save_redirect=request.path,
+        form_class=ProjectApplicationForm)
+
+@require_http_methods(["GET"])
+@signed_terms_required
+@login_required
+def project_application_list(request):
+    queryset = ProjectApplication.objects.all()
+    return object_list(
+        request,
+        queryset,
+        paginate_by=PAGINATE_BY_ALL,
+        page=request.GET.get('page') or 1,
+        template_name='im/projects/projectapplication_list.html')
+
+
+@require_http_methods(["GET"])
+@signed_terms_required
+@login_required
+def project_list(request):
+    pass
+
+@require_http_methods(["GET", "POST"])
+@signed_terms_required
+@login_required
+def project_detail(request, serial):
+    pass
+
+@require_http_methods(["GET", "POST"])
+@signed_terms_required
+@login_required
+def project_search(request):
+    pass
+
+@require_http_methods(["GET"])
+@signed_terms_required
+@login_required
+def project_all(request):
+    pass
+
+@require_http_methods(["GET", "POST"])
+@signed_terms_required
+@login_required
+def project_join(request, serial):
+    pass
+
+@require_http_methods(["GET", "POST"])
+@signed_terms_required
+@login_required
+def project_leave(request, serial):
+    pass
+
+@require_http_methods(["POST"])
+@signed_terms_required
+@login_required
+def project_approve_member(request, serial, user_id):
+    pass
+
+@require_http_methods(["POST"])
+@signed_terms_required
+@login_required
+def project_remove_member(request, serial, user_id):
+    pass
     
\ No newline at end of file
