Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion common/djangoapps/student/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
COURSE_ENROLLMENT_CREATED,
COURSE_UNENROLLMENT_COMPLETED,
)
from openedx_filters.learning.filters import CourseEnrollmentStarted
from openedx_filters.learning.filters import CourseEnrollmentStarted, CourseUnenrollmentStarted
import openedx.core.djangoapps.django_comment_common.comment_client as cc
from common.djangoapps.course_modes.models import CourseMode, get_cosmetic_verified_display_price
from common.djangoapps.student.emails import send_proctoring_requirements_email
Expand Down Expand Up @@ -1122,6 +1122,10 @@ class EnrollmentNotAllowed(CourseEnrollmentException):
pass


class UnenrollmentNotAllowed(CourseEnrollmentException):
pass


class CourseEnrollmentManager(models.Manager):
"""
Custom manager for CourseEnrollment with Table-level filter methods.
Expand Down Expand Up @@ -1767,6 +1771,12 @@ def unenroll(cls, user, course_id, skip_refund=False):

try:
record = cls.objects.get(user=user, course_id=course_id)

try:
record = CourseUnenrollmentStarted.run_filter(enrollment=record)
except CourseUnenrollmentStarted.PreventUnenrollment as exc:
raise UnenrollmentNotAllowed(str(exc)) from exc

record.update_enrollment(is_active=False, skip_refund=skip_refund)

except cls.DoesNotExist:
Expand Down
24 changes: 23 additions & 1 deletion common/djangoapps/student/views/dashboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from edx_django_utils.plugins import get_plugins_view_context
from edx_toggles.toggles import LegacyWaffleFlag, LegacyWaffleFlagNamespace
from opaque_keys.edx.keys import CourseKey
from openedx_filters.learning.filters import DashboardRenderStarted
from pytz import UTC

from lms.djangoapps.bulk_email.api import is_bulk_email_feature_enabled
Expand Down Expand Up @@ -65,6 +66,19 @@
experiments_namespace = LegacyWaffleFlagNamespace(name='student.experiments')


class DashboardException(Exception):
"""
Exception class that requires redirecting to a URL.
"""
def __init__(self, url):
super().__init__()
self.url = url


class DashboardRenderNotAllowed(DashboardException):
pass


def get_org_black_and_whitelist_for_site():
"""
Returns the org blacklist and whitelist for the current site.
Expand Down Expand Up @@ -863,7 +877,15 @@ def student_dashboard(request): # lint-amnesty, pylint: disable=too-many-statem
'resume_button_urls': resume_button_urls
})

response = render_to_response('dashboard.html', context)
dashboard_template = 'dashboard.html'
try:
context, dashboard_template = DashboardRenderStarted.run_filter(
context=context, template_name=dashboard_template,
)
except DashboardRenderStarted.PreventDashboardRender as exc:
raise DashboardRenderNotAllowed(reverse(exc.redirect_to or 'account_settings')) from exc

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What should happen when interacting with templates?

As it is right now, when filters that interact with templates (i.e they interact with template contexts) fail they raise an exception with the intent to redirect to some constant view (e.g if course about filter fails, then redirect to dashboard). If there's a handler that interprets this kind of exception, then the redirection occurs (as it happens when using CourseAccessRedirect here where this handler catches the exception and manages the redirection). If not, like in this case, then error 500 is thrown. So we can replicate this behavior we should implement a handler that manages exceptions in the student's dashboard, but that's probably out of the scope of this PR.

Either way, should be this way the standard when interacting with templates?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the idea that we should have a consistent way of interacting with templates when filters are involved. Having the ability to redirect somewhere and changing the template_name that should be rendered and naturally updating the context before rendering are the three that come to my mind now.


response = render_to_response(dashboard_template, context)
if show_account_activation_popup:
response.delete_cookie(
settings.SHOW_ACTIVATE_CTA_POPUP_COOKIE_NAME,
Expand Down
17 changes: 16 additions & 1 deletion lms/djangoapps/certificates/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@
from django.db import models, transaction
from django.db.models import Count
from django.dispatch import receiver

from django.utils.translation import gettext_lazy as _
from edx_name_affirmation.api import get_verified_name, should_use_verified_name_for_certs
from model_utils import Choices
from model_utils.models import TimeStampedModel
from opaque_keys.edx.django.models import CourseKeyField
from openedx_filters.learning.filters import CertificateCreationRequested
from simple_history.models import HistoricalRecords

from common.djangoapps.student import models_api as student_api
Expand Down Expand Up @@ -50,6 +50,14 @@ class CertificateSocialNetworks:
twitter = 'Twitter'


class GeneratedCertificateException(Exception):
pass


class CertificateGenerationNotAllowed(GeneratedCertificateException):
pass


class CertificateAllowlist(TimeStampedModel):
"""
Tracks students who are on the certificate allowlist for a given course run.
Expand Down Expand Up @@ -463,6 +471,13 @@ def save(self, *args, **kwargs): # pylint: disable=signature-differs
The COURSE_CERT_AWARDED signal helps determine if a Program Certificate can be awarded to a learner in the
Credentials IDA.
"""
try:
self.user, self.course_id, self.mode, self.status = CertificateCreationRequested.run_filter(
user=self.user, course_id=self.course_id, mode=self.mode, status=self.status,
)
except CertificateCreationRequested.PreventCertificateCreation as exc:
raise CertificateGenerationNotAllowed(str(exc)) from exc

super().save(*args, **kwargs)
COURSE_CERT_CHANGED.send_robust(
sender=self.__class__,
Expand Down
8 changes: 8 additions & 0 deletions lms/djangoapps/certificates/views/webview.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from eventtracking import tracker
from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import CourseKey
from openedx_filters.learning.filters import CertificateRenderStarted
from organizations import api as organizations_api
from edx_django_utils.plugins import pluggable_override

Expand Down Expand Up @@ -643,6 +644,13 @@ def render_html_view(request, course_id, certificate=None):
# Track certificate view events
_track_certificate_events(request, course, user, user_certificate)

try:
context, custom_template = CertificateRenderStarted.run_filter(
context=context, custom_template=custom_template,
)
except CertificateRenderStarted.PreventCertificateRender:
return _render_invalid_certificate(request, course_id, platform_name, configuration)

# Render the certificate
return _render_valid_certificate(request, context, custom_template)

Expand Down
11 changes: 10 additions & 1 deletion lms/djangoapps/courseware/views/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
from markupsafe import escape
from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import CourseKey, UsageKey
from openedx_filters.learning.filters import CourseAboutRenderStarted
from pytz import UTC
from requests.exceptions import ConnectionError, Timeout # pylint: disable=redefined-builtin
from rest_framework import status
Expand Down Expand Up @@ -1025,7 +1026,15 @@ def course_about(request, course_id):
'allow_anonymous': allow_anonymous,
}

return render_to_response('courseware/course_about.html', context)
course_about_template = 'courseware/course_about.html'
try:
context, course_about_template = CourseAboutRenderStarted.run_filter(
context=context, template_name=course_about_template,
)
except CourseAboutRenderStarted.PreventCourseAboutRender as exc:
raise CourseAccessRedirect(reverse(exc.redirect_to or 'dashboard')) from exc

return render_to_response(course_about_template, context)


@ensure_csrf_cookie
Expand Down
18 changes: 17 additions & 1 deletion openedx/core/djangoapps/course_groups/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,23 @@
from django.dispatch import receiver

from opaque_keys.edx.django.models import CourseKeyField
from openedx_filters.learning.filters import CohortChangeRequested

from openedx.core.djangolib.model_mixins import DeletableByUserValue

from openedx_events.learning.data import CohortData, CourseData, UserData, UserPersonalData # lint-amnesty, pylint: disable=wrong-import-order
from openedx_events.learning.signals import COHORT_MEMBERSHIP_CHANGED # lint-amnesty, pylint: disable=wrong-import-order

log = logging.getLogger(__name__)


class CohortMembershipException(Exception):
pass


class CohortChangeNotAllowed(CohortMembershipException):
pass


class CourseUserGroup(models.Model):
"""
This model represents groups of users in a course. Groups may have different types,
Expand Down Expand Up @@ -122,6 +130,14 @@ def assign(cls, cohort, user):
cohort_name=cohort.name))
else:
previous_cohort = membership.course_user_group

try:
membership, cohort = CohortChangeRequested.run_filter(
current_membership=membership, target_cohort=cohort,
)
except CohortChangeRequested.PreventCohortChange as exc:
raise CohortChangeNotAllowed(str(exc)) from exc

previous_cohort.users.remove(user)

membership.course_user_group = cohort
Expand Down
12 changes: 11 additions & 1 deletion openedx/features/course_experience/views/course_home.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from django.views.decorators.cache import cache_control
from django.views.decorators.csrf import ensure_csrf_cookie
from opaque_keys.edx.keys import CourseKey
from openedx_filters.learning.filters import CourseHomeRenderStarted
from web_fragments.fragment import Fragment

from lms.djangoapps.course_home_api.toggles import course_home_legacy_is_active
Expand Down Expand Up @@ -240,5 +241,14 @@ def render_to_fragment(self, request, course_id=None, **kwargs): # lint-amnesty
'has_discount': has_discount,
'show_search': show_search,
}
html = render_to_string('course_experience/course-home-fragment.html', context)

course_home_template = 'course_experience/course-home-fragment.html'
try:
context, course_home_template = CourseHomeRenderStarted.run_filter(
context=context, template_name=course_home_template,
)
except CourseHomeRenderStarted.PreventCourseHomeRender as exc:
raise CourseAccessRedirect(reverse(exc.redirect_to or 'dashboard')) from exc

html = render_to_string(course_home_template, context)
return Fragment(html)
2 changes: 1 addition & 1 deletion requirements/edx/base.txt
Original file line number Diff line number Diff line change
Expand Up @@ -702,7 +702,7 @@ openedx-calc==2.0.1
# -r requirements/edx/base.in
openedx-events==0.7.1
# via -r requirements/edx/base.in
openedx-filters==0.4.3
git+https://github.com/eduNEXT/openedx-filters.git@MJG/2nd_filters_batch#egg=openedx_filters==0.5.0_gamma
# via -r requirements/edx/base.in
ora2==3.8.1
# via -r requirements/edx/base.in
Expand Down
2 changes: 1 addition & 1 deletion requirements/edx/development.txt
Original file line number Diff line number Diff line change
Expand Up @@ -937,7 +937,7 @@ openedx-calc==2.0.1
# -r requirements/edx/testing.txt
openedx-events==0.7.1
# via -r requirements/edx/testing.txt
openedx-filters==0.4.3
git+https://github.com/eduNEXT/openedx-filters.git@MJG/2nd_filters_batch#egg=openedx_filters==0.5.0_gamma
# via -r requirements/edx/testing.txt
ora2==3.8.1
# via -r requirements/edx/testing.txt
Expand Down
2 changes: 1 addition & 1 deletion requirements/edx/testing.txt
Original file line number Diff line number Diff line change
Expand Up @@ -887,7 +887,7 @@ openedx-calc==2.0.1
# -r requirements/edx/base.txt
openedx-events==0.7.1
# via -r requirements/edx/base.txt
openedx-filters==0.4.3
git+https://github.com/eduNEXT/openedx-filters.git@MJG/2nd_filters_batch#egg=openedx_filters==0.5.0_gamma
# via -r requirements/edx/base.txt
ora2==3.8.1
# via -r requirements/edx/base.txt
Expand Down