From 88c6d412e55a3dfceb3b43e1e214df8a7d6db197 Mon Sep 17 00:00:00 2001 From: Awais Jibran Date: Mon, 9 Mar 2020 17:12:39 +0500 Subject: [PATCH] Add switch to disable forum digest in prod. --- lms/djangoapps/discussion/config/__init__.py | 12 --- lms/djangoapps/discussion/config/waffle.py | 39 +++++++++ lms/djangoapps/discussion/plugins.py | 5 +- .../templates/discussion-home.underscore | 28 ++++--- lms/djangoapps/discussion/views.py | 8 +- lms/envs/common.py | 5 ++ .../discussion/_underscore_templates.html | 14 +++- lms/urls.py | 79 ++++++++----------- 8 files changed, 112 insertions(+), 78 deletions(-) create mode 100644 lms/djangoapps/discussion/config/waffle.py diff --git a/lms/djangoapps/discussion/config/__init__.py b/lms/djangoapps/discussion/config/__init__.py index 09a8c721f2..e69de29bb2 100644 --- a/lms/djangoapps/discussion/config/__init__.py +++ b/lms/djangoapps/discussion/config/__init__.py @@ -1,12 +0,0 @@ -""" -Discussion settings and flags. -""" - - -from openedx.core.djangoapps.waffle_utils import WaffleFlag, WaffleFlagNamespace - -# Namespace for course experience waffle flags. -WAFFLE_FLAG_NAMESPACE = WaffleFlagNamespace(name='edx_discussions') - -# Waffle flag to enable the use of Bootstrap -USE_BOOTSTRAP_FLAG = WaffleFlag(WAFFLE_FLAG_NAMESPACE, 'use_bootstrap', flag_undefined_default=True) diff --git a/lms/djangoapps/discussion/config/waffle.py b/lms/djangoapps/discussion/config/waffle.py new file mode 100644 index 0000000000..e672926703 --- /dev/null +++ b/lms/djangoapps/discussion/config/waffle.py @@ -0,0 +1,39 @@ +""" +Discussion settings and flags. +""" + +from openedx.core.djangoapps.waffle_utils import WaffleFlag, WaffleFlagNamespace + +USE_BOOTSTRAP = 'use_bootstrap' +ENABLE_FORUM_DAILY_DIGEST = 'enable_forum_daily_digest' + + +def waffle_flags(): + """ + Returns the namespaced, cached, audited Waffle flags dictionary for Grades. + """ + namespace = WaffleFlagNamespace(name='edx_discussions') + return { + # Waffle flag to enable the use of Bootstrap + USE_BOOTSTRAP: WaffleFlag( + namespace, + USE_BOOTSTRAP, + flag_undefined_default=True + ), + # By default, enable forum notifications. Can be disabled platform wide. + ENABLE_FORUM_DAILY_DIGEST: WaffleFlag( + namespace, + ENABLE_FORUM_DAILY_DIGEST, + flag_undefined_default=True + ), + } + + +def use_bootstrap_flag_enabled(): + """Returns whether use of bootstrap is enabled.""" + return waffle_flags()[USE_BOOTSTRAP].is_enabled() + + +def is_forum_daily_digest_enabled(): + """Returns whether forum notification features should be visible""" + return waffle_flags()[ENABLE_FORUM_DAILY_DIGEST].is_enabled() diff --git a/lms/djangoapps/discussion/plugins.py b/lms/djangoapps/discussion/plugins.py index b5ce9f1dde..c9ea2101eb 100644 --- a/lms/djangoapps/discussion/plugins.py +++ b/lms/djangoapps/discussion/plugins.py @@ -8,10 +8,9 @@ from django.utils.translation import ugettext_noop import lms.djangoapps.discussion.django_comment_client.utils as utils from lms.djangoapps.courseware.tabs import EnrolledTab +from lms.djangoapps.discussion.config.waffle import use_bootstrap_flag_enabled from xmodule.tabs import TabFragmentViewMixin -from .config import USE_BOOTSTRAP_FLAG - class DiscussionTab(TabFragmentViewMixin, EnrolledTab): """ @@ -39,4 +38,4 @@ class DiscussionTab(TabFragmentViewMixin, EnrolledTab): """ Returns true if this tab is rendered with Bootstrap. """ - return USE_BOOTSTRAP_FLAG.is_enabled() + return use_bootstrap_flag_enabled() diff --git a/lms/djangoapps/discussion/static/discussion/templates/discussion-home.underscore b/lms/djangoapps/discussion/static/discussion/templates/discussion-home.underscore index 6a55a37706..f9ce1111f9 100644 --- a/lms/djangoapps/discussion/static/discussion/templates/discussion-home.underscore +++ b/lms/djangoapps/discussion/static/discussion/templates/discussion-home.underscore @@ -44,19 +44,21 @@ <%- gettext("Follow or unfollow posts") %> - - <%- gettext('Receive updates') %> - - - <%- gettext("Check this box to receive an email digest once a day notifying you about new, unread activity from posts you are following.") %> - - + <% if (window.ENABLE_FORUM_DAILY_DIGEST) {%> + + <%- gettext('Receive updates') %> + + + <%- gettext("Check this box to receive an email digest once a day notifying you about new, unread activity from posts you are following.") %> + + + <% } %> <% } %> diff --git a/lms/djangoapps/discussion/views.py b/lms/djangoapps/discussion/views.py index a4bb67aab4..65ec5e92c8 100644 --- a/lms/djangoapps/discussion/views.py +++ b/lms/djangoapps/discussion/views.py @@ -30,6 +30,7 @@ import openedx.core.djangoapps.django_comment_common.comment_client as cc from lms.djangoapps.courseware.access import has_access from lms.djangoapps.courseware.courses import get_course_with_access from lms.djangoapps.courseware.views.views import CourseTabView +from lms.djangoapps.discussion.config.waffle import is_forum_daily_digest_enabled, use_bootstrap_flag_enabled from lms.djangoapps.discussion.django_comment_client.base.views import track_thread_viewed_event from lms.djangoapps.discussion.django_comment_client.constants import TYPE_ENTRY from lms.djangoapps.discussion.django_comment_client.permissions import has_permission @@ -59,8 +60,6 @@ from student.models import CourseEnrollment from util.json_request import JsonResponse, expect_json from xmodule.modulestore.django import modulestore -from .config import USE_BOOTSTRAP_FLAG - log = logging.getLogger("edx.discussions") @@ -433,7 +432,7 @@ def _create_base_discussion_view_context(request, course_key): user_info = cc_user.to_dict() course = get_course_with_access(user, 'load', course_key, check_if_enrolled=True) course_settings = make_course_settings(course, user) - uses_bootstrap = USE_BOOTSTRAP_FLAG.is_enabled() + uses_bootstrap = use_bootstrap_flag_enabled() return { 'csrf': csrf(request)['csrf_token'], 'course': course, @@ -519,6 +518,7 @@ def _create_discussion_board_context(request, base_context, thread=None): 'is_commentable_divided': is_commentable_divided(course_key, discussion_id, course_discussion_settings), # If the default topic id is None the front-end code will look for a topic that contains "General" 'discussion_default_topic_id': _get_discussion_default_topic_id(course), + 'enable_daily_digest': is_forum_daily_digest_enabled() }) context.update( get_experiment_user_metadata_context( @@ -818,7 +818,7 @@ class DiscussionBoardFragmentView(EdxFragmentView): the files are loaded individually, but in production just the single bundle is loaded. """ is_right_to_left = get_language_bidi() - if USE_BOOTSTRAP_FLAG.is_enabled(): + if use_bootstrap_flag_enabled(): css_file = BOOTSTRAP_DISCUSSION_CSS_PATH if is_right_to_left: css_file = css_file.replace('.css', '-rtl.css') diff --git a/lms/envs/common.py b/lms/envs/common.py index 071672918f..ff678e5acc 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -100,6 +100,11 @@ FEATURES = { # this should remain off in production until digest notifications are online. 'ENABLE_DISCUSSION_HOME_PANEL': False, + # settings for forums/discussions to on/off daily digest feature. Set this to True if you want to + # enable the UI for the users to subscribe and unsubscribe for daily digest. This setting is the + # part of deprecation of daily digest in production. + 'ENABLE_FORUM_DAILY_DIGEST': True, + # Set this to True if you want the discussion digest emails enabled automatically for new users. # This will be set on all new account registrations. # It is not recommended to enable this feature if ENABLE_DISCUSSION_HOME_PANEL is not enabled, since diff --git a/lms/templates/discussion/_underscore_templates.html b/lms/templates/discussion/_underscore_templates.html index 1d43ba979b..fde2766bce 100644 --- a/lms/templates/discussion/_underscore_templates.html +++ b/lms/templates/discussion/_underscore_templates.html @@ -1,13 +1,23 @@ <%page expression_filter="h"/> <%namespace name='static' file='../static_content.html'/> -<%! from openedx.core.djangolib.js_utils import js_escaped_string %> +<% +from openedx.core.djangolib.js_utils import dump_js_escaped_json, js_escaped_string +enable_discussion_home_panel = settings.FEATURES.get('ENABLE_DISCUSSION_HOME_PANEL', False) +%> <%static:include path="common/templates/discussion/templates.underscore" /> diff --git a/lms/urls.py b/lms/urls.py index d141946a33..3fd43db7a4 100644 --- a/lms/urls.py +++ b/lms/urls.py @@ -2,7 +2,6 @@ URLs for LMS """ - from config_models.views import ConfigurationModelCurrentAPIView from django.conf import settings from django.conf.urls import include, url @@ -10,26 +9,29 @@ from django.conf.urls.static import static from django.contrib.admin import autodiscover as django_autodiscover from django.utils.translation import ugettext_lazy as _ from django.views.generic.base import RedirectView +from edx_api_doc_tools import make_docs_urls from ratelimitbackend import admin -from edx_api_doc_tools import make_docs_urls - from branding import views as branding_views +from debug import views as debug_views +from lms.djangoapps.certificates import views as certificates_views from lms.djangoapps.courseware.masquerade import handle_ajax as courseware_masquerade_handle_ajax from lms.djangoapps.courseware.module_render import ( - handle_xblock_callback, handle_xblock_callback_noauth, xblock_view, xqueue_callback, + handle_xblock_callback, + handle_xblock_callback_noauth, + xblock_view, + xqueue_callback ) from lms.djangoapps.courseware.views import views as courseware_views from lms.djangoapps.courseware.views.index import CoursewareIndex from lms.djangoapps.courseware.views.views import CourseTabView, EnrollStaffView, StaticCourseTabView -from debug import views as debug_views -from lms.djangoapps.certificates import views as certificates_views from lms.djangoapps.discussion import views as discussion_views from lms.djangoapps.discussion.notification_prefs import views as notification_prefs_views from lms.djangoapps.instructor.views import coupons as instructor_coupons_views from lms.djangoapps.instructor.views import instructor_dashboard as instructor_dashboard_views from lms.djangoapps.instructor.views import registration_codes as instructor_registration_codes_views from lms.djangoapps.instructor_task import views as instructor_task_views +from openedx.core.apidocs import api_info from openedx.core.djangoapps.auth_exchange.views import LoginWithAccessTokenView from openedx.core.djangoapps.catalog.models import CatalogIntegration from openedx.core.djangoapps.common_views.xblock import xblock_resource @@ -47,7 +49,6 @@ from openedx.core.djangoapps.self_paced.models import SelfPacedConfiguration from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers from openedx.core.djangoapps.user_authn.views.login import redirect_to_lms_login from openedx.core.djangoapps.verified_track_content import views as verified_track_content_views -from openedx.core.apidocs import api_info from openedx.features.enterprise_support.api import enterprise_enabled from static_template_view import views as static_template_view_views from staticbook import views as staticbook_views @@ -68,8 +69,28 @@ if settings.DEBUG or settings.FEATURES.get('ENABLE_DJANGO_ADMIN_SITE'): handler404 = static_template_view_views.render_404 handler500 = static_template_view_views.render_500 +notification_prefs_urls = [ + url(r'^notification_prefs/enable/', notification_prefs_views.ajax_enable), + url(r'^notification_prefs/disable/', notification_prefs_views.ajax_disable), + url(r'^notification_prefs/status/', notification_prefs_views.ajax_status), + + url( + r'^notification_prefs/unsubscribe/(?P[a-zA-Z0-9-_=]+)/', + notification_prefs_views.set_subscription, + {'subscribe': False}, + name='unsubscribe_forum_update', + ), + url( + r'^notification_prefs/resubscribe/(?P[a-zA-Z0-9-_=]+)/', + notification_prefs_views.set_subscription, + {'subscribe': True}, + name='resubscribe_forum_update', + ), +] + + urlpatterns = [ - url(r'^$', branding_views.index, name='root'), # Main marketing page, or redirect to courseware + url(r'^$', branding_views.index, name='root'), # Main marketing page, or redirect to courseware url(r'', include('student.urls')), # TODO: Move lms specific student views out of common code @@ -108,7 +129,6 @@ urlpatterns = [ # subsumed by api/user listed above. url(r'', include('openedx.core.djangoapps.user_api.legacy_urls')), - # Profile Images API endpoints url(r'^api/profile_images/', include('openedx.core.djangoapps.profile_images.urls')), @@ -164,7 +184,6 @@ urlpatterns += [ url(r'^openassessment/fileupload/', include('openassessment.fileupload.urls')), ] - # sysadmin dashboard, to see what courses are loaded, to delete & load courses if settings.FEATURES.get('ENABLE_SYSADMIN_DASHBOARD'): urlpatterns += [ @@ -299,7 +318,7 @@ urlpatterns += [ url(r'^courses/?$', branding_views.courses, name='courses'), - #About the course + # About the course url( r'^courses/{}/about$'.format( settings.COURSE_ID_PATTERN, @@ -320,7 +339,7 @@ urlpatterns += [ name='enroll_staff', ), - #Inside the course + # Inside the course url( r'^courses/{}/$'.format( settings.COURSE_ID_PATTERN, @@ -486,7 +505,6 @@ urlpatterns += [ name='instructor_dashboard', ), - url( r'^courses/{}/set_course_mode_price$'.format( settings.COURSE_ID_PATTERN, @@ -714,36 +732,11 @@ if settings.FEATURES.get('ENABLE_DISCUSSION_SERVICE'): ), include('lms.djangoapps.discussion.django_comment_client.urls') ), - url( - r'^notification_prefs/enable/', - notification_prefs_views.ajax_enable - ), - url( - r'^notification_prefs/disable/', - notification_prefs_views.ajax_disable - ), - url( - r'^notification_prefs/status/', - notification_prefs_views.ajax_status - ), - url( - r'^notification_prefs/unsubscribe/(?P[a-zA-Z0-9-_=]+)/', - notification_prefs_views.set_subscription, - { - 'subscribe': False, - }, - name='unsubscribe_forum_update', - ), - url( - r'^notification_prefs/resubscribe/(?P[a-zA-Z0-9-_=]+)/', - notification_prefs_views.set_subscription, - { - 'subscribe': True, - }, - name='resubscribe_forum_update', - ), ] +if settings.FEATURES.get('ENABLE_FORUM_DAILY_DIGEST'): + urlpatterns += notification_prefs_urls + urlpatterns += [ url(r'^bulk_email/', include('bulk_email.urls')), ] @@ -804,7 +797,6 @@ if configuration_helpers.get_value('ENABLE_BULK_ENROLLMENT_VIEW', settings.FEATU url(r'^api/bulk_enroll/v1/', include('bulk_enroll.urls')), ] - # Shopping cart urlpatterns += [ url(r'^shoppingcart/', include('shoppingcart.urls')), @@ -842,7 +834,6 @@ if settings.FEATURES.get('ENABLE_OAUTH2_PROVIDER'): url(r'^_o/', include('oauth2_provider.urls', namespace='oauth2_provider')), ] - if settings.FEATURES.get('ENABLE_SERVICE_STATUS'): urlpatterns += [ url(r'^status/', include('openedx.core.djangoapps.service_status.urls')), @@ -866,7 +857,6 @@ urlpatterns += [ url(r'^debug/show_parameters$', debug_views.show_parameters), ] - # Third-party auth. if settings.FEATURES.get('ENABLE_THIRD_PARTY_AUTH'): urlpatterns += [ @@ -948,6 +938,7 @@ urlpatterns += [ if 'debug_toolbar' in settings.INSTALLED_APPS: import debug_toolbar + urlpatterns += [ url(r'^__debug__/', include(debug_toolbar.urls)), ]