Revision 39b2cb50 snf-astakos-app/astakos/im/views.py

b/snf-astakos-app/astakos/im/views.py
80 80
    AstakosUser, ApprovalTerms,
81 81
    EmailChange, RESOURCE_SEPARATOR,
82 82
    AstakosUserAuthProvider, PendingThirdPartyUser,
83
    PendingMembershipError,
84 83
    ProjectApplication, ProjectMembership, Project)
85 84
from astakos.im.util import (
86 85
    get_context, prepare_response, get_query, restrict_next)
......
115 114
from astakos.im import settings as astakos_settings
116 115
from astakos.im.api.callpoint import AstakosCallpoint
117 116
from astakos.im import auth_providers as auth
118
from astakos.im.project_xctx import project_transaction_context
119
from astakos.im.retry_xctx import RetryException
117
from synnefo.lib.db.transaction import commit_on_success_strict
118
from astakos.im.ctx import ExceptionHandler
120 119

  
121 120
logger = logging.getLogger(__name__)
122 121

  
......
931 930
        'im/how_it_works.html',
932 931
        context_instance=get_context(request))
933 932

  
934
@project_transaction_context()
933

  
934
@commit_on_success_strict()
935 935
def _create_object(request, model=None, template_name=None,
936 936
        template_loader=template_loader, extra_context=None, post_save_redirect=None,
937 937
        login_required=False, context_processors=None, form_class=None,
938
        msg=None, ctx=None):
938
        msg=None):
939 939
    """
940 940
    Based of django.views.generic.create_update.create_object which displays a
941 941
    summary page before creating the object.
......
968 968
                    response = redirect(post_save_redirect, new_object)
969 969
        else:
970 970
            form = form_class()
971
    except BaseException, e:
972
        logger.exception(e)
973
        messages.error(request, _(astakos_messages.GENERIC_ERROR))
974
        if ctx:
975
            ctx.mark_rollback()
976
    finally:
971
    except (IOError, PermissionDenied), e:
972
        messages.error(request, e)
973
        return None
974
    else:
977 975
        if response == None:
978 976
            # Create the template, context, response
979 977
            if not template_name:
......
987 985
            response = HttpResponse(t.render(c))
988 986
        return response
989 987

  
990
@project_transaction_context()
988
@commit_on_success_strict()
991 989
def _update_object(request, model=None, object_id=None, slug=None,
992 990
        slug_field='slug', template_name=None, template_loader=template_loader,
993 991
        extra_context=None, post_save_redirect=None, login_required=False,
994 992
        context_processors=None, template_object_name='object',
995
        form_class=None, msg=None, ctx=None):
993
        form_class=None, msg=None):
996 994
    """
997 995
    Based of django.views.generic.create_update.update_object which displays a
998 996
    summary page before updating the object.
......
1026 1024
                    response = redirect(post_save_redirect, obj)
1027 1025
        else:
1028 1026
            form = form_class(instance=obj)
1029
    except BaseException, e:
1030
        logger.exception(e)
1031
        messages.error(request, _(astakos_messages.GENERIC_ERROR))
1032
        ctx.mark_rollback()
1033
    finally:
1027
    except (IOError, PermissionDenied), e:
1028
        messages.error(request, e)
1029
        return None
1030
    else:
1034 1031
        if response == None:
1035 1032
            if not template_name:
1036 1033
                template_name = "%s/%s_form.html" %\
......
1094 1091
        'show_form':True,
1095 1092
        'details_fields':details_fields,
1096 1093
        'membership_fields':membership_fields}
1097
    return _create_object(
1098
        request,
1099
        template_name='im/projects/projectapplication_form.html',
1100
        extra_context=extra_context,
1101
        post_save_redirect=reverse('project_list'),
1102
        form_class=ProjectApplicationForm,
1103
        msg=_("The %(verbose_name)s has been received and \
1104
                 is under consideration."))
1094

  
1095
    response = None
1096
    with ExceptionHandler(request):
1097
        response = _create_object(
1098
            request,
1099
            template_name='im/projects/projectapplication_form.html',
1100
            extra_context=extra_context,
1101
            post_save_redirect=reverse('project_list'),
1102
            form_class=ProjectApplicationForm,
1103
            msg=_("The %(verbose_name)s has been received and "
1104
                  "is under consideration."),
1105
            )
1106

  
1107
    if response is not None:
1108
        return response
1109

  
1110
    next = reverse('astakos.im.views.project_list')
1111
    next = restrict_next(next, domain=COOKIE_DOMAIN)
1112
    return redirect(next)
1105 1113

  
1106 1114

  
1107 1115
@require_http_methods(["GET"])
......
1124 1132

  
1125 1133
@require_http_methods(["POST"])
1126 1134
@valid_astakos_user_required
1127
@project_transaction_context()
1128
def project_app_cancel(request, application_id, ctx=None):
1135
def project_app_cancel(request, application_id):
1136
    next = request.GET.get('next')
1129 1137
    chain_id = None
1130
    try:
1131
        application_id = int(application_id)
1132
        chain_id = get_related_project_id(application_id)
1133
        cancel_application(application_id, request.user)
1134
    except (IOError, PermissionDenied), e:
1135
        messages.error(request, e)
1136
    except BaseException, e:
1137
        logger.exception(e)
1138
        messages.error(request, _(astakos_messages.GENERIC_ERROR))
1139
        if ctx:
1140
            ctx.mark_rollback()
1141
    else:
1142
        msg = _(astakos_messages.APPLICATION_CANCELLED)
1143
        messages.success(request, msg)
1144 1138

  
1145
    next = request.GET.get('next')
1139
    with ExceptionHandler(request):
1140
        chain_id = _project_app_cancel(request, application_id)
1141

  
1146 1142
    if not next:
1147 1143
        if chain_id:
1148 1144
            next = reverse('astakos.im.views.project_detail', args=(chain_id,))
......
1152 1148
    next = restrict_next(next, domain=COOKIE_DOMAIN)
1153 1149
    return redirect(next)
1154 1150

  
1151
@commit_on_success_strict()
1152
def _project_app_cancel(request, application_id):
1153
    chain_id = None
1154
    try:
1155
        application_id = int(application_id)
1156
        chain_id = get_related_project_id(application_id)
1157
        cancel_application(application_id, request.user)
1158
    except (IOError, PermissionDenied), e:
1159
        messages.error(request, e)
1160
    else:
1161
        msg = _(astakos_messages.APPLICATION_CANCELLED)
1162
        messages.success(request, msg)
1163
        return chain_id
1164

  
1155 1165

  
1156 1166
@require_http_methods(["GET", "POST"])
1157 1167
@valid_astakos_user_required
......
1199 1209
        'details_fields':details_fields,
1200 1210
        'update_form': True,
1201 1211
        'membership_fields':membership_fields}
1202
    return _update_object(
1203
        request,
1204
        object_id=application_id,
1205
        template_name='im/projects/projectapplication_form.html',
1206
        extra_context=extra_context, post_save_redirect=reverse('project_list'),
1207
        form_class=ProjectApplicationForm,
1208
        msg = _("The %(verbose_name)s has been received and \
1209
                    is under consideration."))
1210 1212

  
1213
    response = None
1214
    with ExceptionHandler(request):
1215
        response =_update_object(
1216
            request,
1217
            object_id=application_id,
1218
            template_name='im/projects/projectapplication_form.html',
1219
            extra_context=extra_context, post_save_redirect=reverse('project_list'),
1220
            form_class=ProjectApplicationForm,
1221
            msg = _("The %(verbose_name)s has been received and "
1222
                    "is under consideration."),
1223
            )
1224

  
1225
    if response is not None:
1226
        return response
1227

  
1228
    next = reverse('astakos.im.views.project_list')
1229
    next = restrict_next(next, domain=COOKIE_DOMAIN)
1230
    return redirect(next)
1211 1231

  
1212 1232
@require_http_methods(["GET", "POST"])
1213 1233
@valid_astakos_user_required
......
1219 1239
def project_detail(request, chain_id):
1220 1240
    return common_detail(request, chain_id)
1221 1241

  
1222
@project_transaction_context(sync=True)
1223
def addmembers(request, chain_id, addmembers_form, ctx=None):
1242
@commit_on_success_strict()
1243
def addmembers(request, chain_id, addmembers_form):
1224 1244
    if addmembers_form.is_valid():
1225 1245
        try:
1226 1246
            chain_id = int(chain_id)
......
1231 1251
                addmembers_form.valid_users)
1232 1252
        except (IOError, PermissionDenied), e:
1233 1253
            messages.error(request, e)
1234
        except BaseException, e:
1235
            if ctx:
1236
                ctx.mark_rollback()
1237
            messages.error(request, e)
1238 1254

  
1239 1255
def common_detail(request, chain_or_app_id, project_view=True):
1240 1256
    project = None
......
1245 1261
                request.POST,
1246 1262
                chain_id=int(chain_id),
1247 1263
                request_user=request.user)
1248
            addmembers(request, chain_id, addmembers_form)
1264
            with ExceptionHandler(request):
1265
                addmembers(request, chain_id, addmembers_form)
1266

  
1249 1267
            if addmembers_form.is_valid():
1250 1268
                addmembers_form = AddProjectMembersForm()  # clear form data
1251 1269
        else:
......
1357 1375

  
1358 1376
@require_http_methods(["POST"])
1359 1377
@valid_astakos_user_required
1360
@project_transaction_context(sync=True)
1361
def project_join(request, chain_id, ctx=None):
1378
def project_join(request, chain_id):
1362 1379
    next = request.GET.get('next')
1363 1380
    if not next:
1364 1381
        next = reverse('astakos.im.views.project_detail',
1365 1382
                       args=(chain_id,))
1366 1383

  
1384
    with ExceptionHandler(request):
1385
        _project_join(request, chain_id)
1386

  
1387

  
1388
    next = restrict_next(next, domain=COOKIE_DOMAIN)
1389
    return redirect(next)
1390

  
1391

  
1392
@commit_on_success_strict()
1393
def _project_join(request, chain_id):
1367 1394
    try:
1368 1395
        chain_id = int(chain_id)
1369 1396
        auto_accepted = join_project(chain_id, request.user)
......
1374 1401
        messages.success(request, m)
1375 1402
    except (IOError, PermissionDenied), e:
1376 1403
        messages.error(request, e)
1377
    except BaseException, e:
1378
        logger.exception(e)
1379
        messages.error(request, _(astakos_messages.GENERIC_ERROR))
1380
        if ctx:
1381
            ctx.mark_rollback()
1382
    next = restrict_next(next, domain=COOKIE_DOMAIN)
1383
    return redirect(next)
1404

  
1384 1405

  
1385 1406
@require_http_methods(["POST"])
1386 1407
@valid_astakos_user_required
1387
@project_transaction_context(sync=True)
1388
def project_leave(request, chain_id, ctx=None):
1408
def project_leave(request, chain_id):
1389 1409
    next = request.GET.get('next')
1390 1410
    if not next:
1391 1411
        next = reverse('astakos.im.views.project_list')
1392 1412

  
1413
    with ExceptionHandler(request):
1414
        _project_leave(request, chain_id)
1415

  
1416
    next = restrict_next(next, domain=COOKIE_DOMAIN)
1417
    return redirect(next)
1418

  
1419

  
1420
@commit_on_success_strict()
1421
def _project_leave(request, chain_id):
1393 1422
    try:
1394 1423
        chain_id = int(chain_id)
1395 1424
        auto_accepted = leave_project(chain_id, request.user)
......
1400 1429
        messages.success(request, m)
1401 1430
    except (IOError, PermissionDenied), e:
1402 1431
        messages.error(request, e)
1403
    except PendingMembershipError as e:
1404
        raise RetryException()
1405
    except BaseException, e:
1406
        logger.exception(e)
1407
        messages.error(request, _(astakos_messages.GENERIC_ERROR))
1408
        if ctx:
1409
            ctx.mark_rollback()
1410
    next = restrict_next(next, domain=COOKIE_DOMAIN)
1411
    return redirect(next)
1432

  
1412 1433

  
1413 1434
@require_http_methods(["POST"])
1414 1435
@valid_astakos_user_required
1415
@project_transaction_context()
1416
def project_cancel(request, chain_id, ctx=None):
1436
def project_cancel(request, chain_id):
1417 1437
    next = request.GET.get('next')
1418 1438
    if not next:
1419 1439
        next = reverse('astakos.im.views.project_list')
1420 1440

  
1441
    with ExceptionHandler(request):
1442
        _project_cancel(request, chain_id)
1443

  
1444
    next = restrict_next(next, domain=COOKIE_DOMAIN)
1445
    return redirect(next)
1446

  
1447

  
1448
@commit_on_success_strict()
1449
def _project_cancel(request, chain_id):
1421 1450
    try:
1422 1451
        chain_id = int(chain_id)
1423 1452
        cancel_membership(chain_id, request.user)
......
1425 1454
        messages.success(request, m)
1426 1455
    except (IOError, PermissionDenied), e:
1427 1456
        messages.error(request, e)
1428
    except PendingMembershipError as e:
1429
        raise RetryException()
1430
    except BaseException, e:
1431
        logger.exception(e)
1432
        messages.error(request, _(astakos_messages.GENERIC_ERROR))
1433
        if ctx:
1434
            ctx.mark_rollback()
1435 1457

  
1436
    next = restrict_next(next, domain=COOKIE_DOMAIN)
1437
    return redirect(next)
1438 1458

  
1439 1459
@require_http_methods(["POST"])
1440 1460
@valid_astakos_user_required
1441
@project_transaction_context(sync=True)
1442
def project_accept_member(request, chain_id, user_id, ctx=None):
1461
def project_accept_member(request, chain_id, user_id):
1462

  
1463
    with ExceptionHandler(request):
1464
        _project_accept_member(request, chain_id, user_id)
1465

  
1466
    return redirect(reverse('project_detail', args=(chain_id,)))
1467

  
1468

  
1469
@commit_on_success_strict()
1470
def _project_accept_member(request, chain_id, user_id):
1443 1471
    try:
1444 1472
        chain_id = int(chain_id)
1445 1473
        user_id = int(user_id)
1446 1474
        m = accept_membership(chain_id, user_id, request.user)
1447 1475
    except (IOError, PermissionDenied), e:
1448 1476
        messages.error(request, e)
1449
    except PendingMembershipError as e:
1450
        raise RetryException()
1451
    except BaseException, e:
1452
        logger.exception(e)
1453
        messages.error(request, _(astakos_messages.GENERIC_ERROR))
1454
        if ctx:
1455
            ctx.mark_rollback()
1456 1477
    else:
1457 1478
        email = escape(m.person.email)
1458 1479
        msg = _(astakos_messages.USER_MEMBERSHIP_ACCEPTED) % email
1459 1480
        messages.success(request, msg)
1460
    return redirect(reverse('project_detail', args=(chain_id,)))
1481

  
1461 1482

  
1462 1483
@require_http_methods(["POST"])
1463 1484
@valid_astakos_user_required
1464
@project_transaction_context(sync=True)
1465
def project_remove_member(request, chain_id, user_id, ctx=None):
1485
def project_remove_member(request, chain_id, user_id):
1486

  
1487
    with ExceptionHandler(request):
1488
        _project_remove_member(request, chain_id, user_id)
1489

  
1490
    return redirect(reverse('project_detail', args=(chain_id,)))
1491

  
1492

  
1493
@commit_on_success_strict()
1494
def _project_remove_member(request, chain_id, user_id):
1466 1495
    try:
1467 1496
        chain_id = int(chain_id)
1468 1497
        user_id = int(user_id)
1469 1498
        m = remove_membership(chain_id, user_id, request.user)
1470 1499
    except (IOError, PermissionDenied), e:
1471 1500
        messages.error(request, e)
1472
    except PendingMembershipError as e:
1473
        raise RetryException()
1474
    except BaseException, e:
1475
        logger.exception(e)
1476
        messages.error(request, _(astakos_messages.GENERIC_ERROR))
1477
        if ctx:
1478
            ctx.mark_rollback()
1479 1501
    else:
1480 1502
        email = escape(m.person.email)
1481 1503
        msg = _(astakos_messages.USER_MEMBERSHIP_REMOVED) % email
1482 1504
        messages.success(request, msg)
1483
    return redirect(reverse('project_detail', args=(chain_id,)))
1505

  
1484 1506

  
1485 1507
@require_http_methods(["POST"])
1486 1508
@valid_astakos_user_required
1487
@project_transaction_context()
1488
def project_reject_member(request, chain_id, user_id, ctx=None):
1509
def project_reject_member(request, chain_id, user_id):
1510

  
1511
    with ExceptionHandler(request):
1512
        _project_reject_member(request, chain_id, user_id)
1513

  
1514
    return redirect(reverse('project_detail', args=(chain_id,)))
1515

  
1516

  
1517
@commit_on_success_strict()
1518
def _project_reject_member(request, chain_id, user_id):
1489 1519
    try:
1490 1520
        chain_id = int(chain_id)
1491 1521
        user_id = int(user_id)
1492 1522
        m = reject_membership(chain_id, user_id, request.user)
1493 1523
    except (IOError, PermissionDenied), e:
1494 1524
        messages.error(request, e)
1495
    except PendingMembershipError as e:
1496
        raise RetryException()
1497
    except BaseException, e:
1498
        logger.exception(e)
1499
        messages.error(request, _(astakos_messages.GENERIC_ERROR))
1500
        if ctx:
1501
            ctx.mark_rollback()
1502 1525
    else:
1503 1526
        email = escape(m.person.email)
1504 1527
        msg = _(astakos_messages.USER_MEMBERSHIP_REJECTED) % email
1505 1528
        messages.success(request, msg)
1506
    return redirect(reverse('project_detail', args=(chain_id,)))
1529

  
1507 1530

  
1508 1531
@require_http_methods(["POST"])
1509 1532
@signed_terms_required
1510 1533
@login_required
1511
@project_transaction_context(sync=True)
1512
def project_app_approve(request, application_id, ctx=None):
1534
def project_app_approve(request, application_id):
1513 1535

  
1514 1536
    if not request.user.is_project_admin():
1515 1537
        m = _(astakos_messages.NOT_ALLOWED)
......
1520 1542
    except ProjectApplication.DoesNotExist:
1521 1543
        raise Http404
1522 1544

  
1523
    approve_application(application_id)
1545
    with ExceptionHandler(request):
1546
        _project_app_approve(request, application_id)
1547

  
1524 1548
    chain_id = get_related_project_id(application_id)
1525 1549
    return redirect(reverse('project_detail', args=(chain_id,)))
1526 1550

  
1551

  
1552
@commit_on_success_strict()
1553
def _project_app_approve(request, application_id):
1554
    approve_application(application_id)
1555

  
1556

  
1527 1557
@require_http_methods(["POST"])
1528 1558
@signed_terms_required
1529 1559
@login_required
1530
@project_transaction_context()
1531
def project_app_deny(request, application_id, ctx=None):
1560
def project_app_deny(request, application_id):
1532 1561

  
1533 1562
    reason = request.POST.get('reason', None)
1534 1563
    if not reason:
......
1543 1572
    except ProjectApplication.DoesNotExist:
1544 1573
        raise Http404
1545 1574

  
1546
    deny_application(application_id, reason=reason)
1575
    with ExceptionHandler(request):
1576
        _project_app_deny(request, application_id, reason)
1577

  
1547 1578
    return redirect(reverse('project_list'))
1548 1579

  
1580

  
1581
@commit_on_success_strict()
1582
def _project_app_deny(request, application_id, reason):
1583
    deny_application(application_id, reason=reason)
1584

  
1585

  
1549 1586
@require_http_methods(["POST"])
1550 1587
@signed_terms_required
1551 1588
@login_required
1552
@project_transaction_context()
1553
def project_app_dismiss(request, application_id, ctx=None):
1589
def project_app_dismiss(request, application_id):
1554 1590
    try:
1555 1591
        app = ProjectApplication.objects.get(id=application_id)
1556 1592
    except ProjectApplication.DoesNotExist:
......
1560 1596
        m = _(astakos_messages.NOT_ALLOWED)
1561 1597
        raise PermissionDenied(m)
1562 1598

  
1563
    # XXX: dismiss application also does authorization
1564
    dismiss_application(application_id, request_user=request.user)
1599
    with ExceptionHandler(request):
1600
        _project_app_dismiss(request, application_id)
1565 1601

  
1566 1602
    chain_id = None
1567 1603
    chain_id = get_related_project_id(application_id)
......
1571 1607
        next = reverse('project_list')
1572 1608
    return redirect(next)
1573 1609

  
1610

  
1611
def _project_app_dismiss(request, application_id):
1612
    # XXX: dismiss application also does authorization
1613
    dismiss_application(application_id, request_user=request.user)
1614

  
1615

  
1574 1616
@require_http_methods(["GET"])
1575 1617
@required_auth_methods_assigned(allow_access=True)
1576 1618
@login_required

Also available in: Unified diff