diff --git a/course/apps.py b/course/apps.py index 233c608a914bf4d2262c62589a12b36ab1c19197..136c657f2b7cc1dfd983f6b576bb7a69ce278d72 100644 --- a/course/apps.py +++ b/course/apps.py @@ -5,4 +5,8 @@ from django.utils.translation import ugettext_lazy as _ class CourseConfig(AppConfig): name = 'course' # for translation of the name of "Course" app displayed in admin. - verbose_name = _("Course module") \ No newline at end of file + verbose_name = _("Course module") + + def ready(self): + import course.signals + import course.receivers \ No newline at end of file diff --git a/course/enrollment.py b/course/enrollment.py index bcbce104d22979dd132aab903740c67f5f876018..b91b2ca2b62974ba516794c12289d61a269f2934 100644 --- a/course/enrollment.py +++ b/course/enrollment.py @@ -201,17 +201,42 @@ def decide_enrollment(approved, modeladmin, request, queryset): _("%d requests processed.") % count) -def send_enrollment_decision(participation, approved, request): +def send_enrollment_decision(participation, approved, request=None): with translation.override(settings.RELATE_ADMIN_EMAIL_LOCALE): course = participation.course + if request: + course_uri = request.build_absolute_uri( + reverse("relate-course_page", + args=(course.identifier,))) + else: + site_domain = None + if "django.contrib.sites" in settings.INSTALLED_APPS: + from django.contrib.sites.models import Site + try: + site_domain = Site.objects.get_current().domain + except: + # sites framework may failed due to unresolved + # migrations issue for PostgreSql + # http://stackoverflow.com/q/30356963/3437454 + pass + else: + site_domain = getattr(settings, "RELATE_SITE_DOMAIN","") + + if site_domain: + course_uri = 'http://%s%s' % ( + site_domain, + course.get_absolute_url()) + else: + # FIXME : For full url, RELATE_SITE_DOMAIN is need + # in settings When sending emails triggered by signal. + course_uri = course.get_absolute_url() + from django.template.loader import render_to_string message = render_to_string("course/enrollment-decision-email.txt", { "user": participation.user, "approved": approved, "course": course, - "course_uri": request.build_absolute_uri( - reverse("relate-course_page", - args=(course.identifier,))) + "course_uri": course_uri }) from django.core.mail import EmailMessage @@ -333,16 +358,7 @@ def create_preapprovals(pctx): try: preapproval = ParticipationPreapproval.objects.get( course=pctx.course, institutional_id__iexact=l) - # FIXME : - """ - When an exist preapproval is submit, and if the tutor changed the - requirement of preapproval_require_verified_inst_id of the - course from True to False, some pending requests which had not - provided validated inst_id will still be pending. - - BTW, it is also the case when the tutor changed the - enrollment_required_email_suffix from "@what.com" to "". - """ + except ParticipationPreapproval.DoesNotExist: # approve if l is requesting enrollment diff --git a/course/receivers.py b/course/receivers.py new file mode 100644 index 0000000000000000000000000000000000000000..603a3208e6f9388383930ba3dd506e6c26522bc6 --- /dev/null +++ b/course/receivers.py @@ -0,0 +1,93 @@ +# -*- coding: utf-8 -*- + +from __future__ import division + +__copyright__ = "Copyright (C) 2016 Dong Zhuang, Andreas Kloeckner" + +__license__ = """ +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" + +from django.db.models.signals import post_save +from django.dispatch import receiver +from accounts.models import User + +@receiver(post_save, sender=User) +def update_enrollment_status(sender, created, instance, **kwargs): + if not created: + user = instance + from course.models import ( + Participation, participation_status, + ParticipationPreapproval, + ) + + requested_qset = Participation.objects.filter( + user=user, status=participation_status.requested) + if requested_qset is None: + pass + + for requested in requested_qset: + course = requested.course + + preapproval = None + if user.email: + try: + preapproval = ParticipationPreapproval.objects.get( + course=course, email__iexact=user.email) + except ParticipationPreapproval.DoesNotExist: + if user.institutional_id: + if not (course.preapproval_require_verified_inst_id + and not user.institutional_id_verified): + try: + preapproval = ( + ParticipationPreapproval.objects.get( + course=course, + institutional_id__iexact\ + =user.institutional_id)) + except ParticipationPreapproval.DoesNotExist: + pass + pass + + def enroll(status, role): + participations = Participation.objects.filter( + course=course, user=user) + + assert participations.count() <= 1 + if participations.count() == 0: + participation = Participation() + participation.user = user + participation.course = course + participation.role = role + participation.status = status + participation.save() + else: + (participation,) = participations + participation.status = status + participation.save() + + return participation + + if preapproval is not None: + role = preapproval.role + enroll(participation_status.active, role) + + from course.enrollment import send_enrollment_decision + send_enrollment_decision(requested, True) + +# vim: foldmethod=marker diff --git a/course/signals.py b/course/signals.py new file mode 100644 index 0000000000000000000000000000000000000000..f939ba93cc7d8b2704a388a7f3d5a5f8944cf1c9 --- /dev/null +++ b/course/signals.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- + +from __future__ import division + +__copyright__ = "Copyright (C) 2016 Dong Zhuang, Andreas Kloeckner" + +__license__ = """ +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" + + +# TODO + +# vim: foldmethod=marker \ No newline at end of file diff --git a/local_settings.py.example b/local_settings.py.example index 4f02a8a644d9008a5bf3b966b2e0eccc4eb23585..232158a714880771c5897384841f8ef85a49e1b6 100644 --- a/local_settings.py.example +++ b/local_settings.py.example @@ -8,6 +8,11 @@ ALLOWED_HOSTS = [ "relate.example.com", ] +# If sites framework is not enabled. For returning the +# site domain of your relate instance, replace the +# string with your own domain for production. +RELATE_SITE_DOMAIN = "http://example.com" + # Uncomment this to use a real database. If left commented out, a local SQLite3 # database will be used, which is not recommended for production use. # diff --git a/relate/settings.py b/relate/settings.py index c443133e62903adfbeb7b8bd45e5ed85a542267e..6e69248ec306021b574714dc4d4f84a47c7f0cd5 100644 --- a/relate/settings.py +++ b/relate/settings.py @@ -49,6 +49,11 @@ INSTALLED_APPS = ( "accounts", "course", + ## Seem sites should be put after other + ## apps so as to migrate successfully + ## for PostgreSql + + #"django.contrib.sites", ) if local_settings["RELATE_SIGN_IN_BY_SAML2_ENABLED"]: @@ -277,4 +282,7 @@ SAML_ATTRIBUTE_MAPPING = { # }}} +# Enabling the sites framework, need migration +# SITE_ID = 1 + # vim: foldmethod=marker