From c746a4e6305584873e20d2b99179fa7b27787700 Mon Sep 17 00:00:00 2001 From: Bill Filler Date: Wed, 11 Apr 2018 22:19:35 -0400 Subject: [PATCH] Add Journals support Enable the journal app in LMS. Add support to display Journals on index and courseware pages and dashboard. --- common/djangoapps/student/views/dashboard.py | 2 + common/djangoapps/student/views/management.py | 4 + common/test/acceptance/pages/lms/discovery.py | 2 +- lms/djangoapps/courseware/views/views.py | 4 +- lms/djangoapps/learner_dashboard/views.py | 2 + lms/envs/common.py | 1 - lms/static/sass/_build-lms-v1.scss | 1 + lms/static/sass/_build-lms-v2.scss | 1 + lms/static/sass/features/_journals.scss | 173 ++++++++++ lms/templates/courses_list.html | 14 + lms/templates/courseware/courses.html | 16 +- .../header/navbar-authenticated.html | 7 + .../_dashboard_navigation_journals.html | 8 + .../bootstrap/navbar-authenticated.html | 7 + .../navigation/navbar-authenticated.html | 7 + .../create_sites_and_configurations.py | 26 +- openedx/features/journals/README.rst | 37 +++ openedx/features/journals/__init__.py | 0 openedx/features/journals/api.py | 310 ++++++++++++++++++ openedx/features/journals/apps.py | 30 ++ .../features/journals/settings/__init__.py | 0 openedx/features/journals/settings/aws.py | 11 + openedx/features/journals/settings/common.py | 12 + .../features/journals/settings/devstack.py | 11 + openedx/features/journals/settings/test.py | 9 + .../templates/journals/bundle_about.html | 164 +++++++++ .../templates/journals/bundle_card.html | 48 +++ .../templates/journals/journal_card.html | 48 +++ .../learner_dashboard/journal_dashboard.html | 135 ++++++++ openedx/features/journals/tests/__init__.py | 0 .../journals/tests/test_learner_dashboard.py | 74 +++++ .../journals/tests/test_marketing_views.py | 128 ++++++++ openedx/features/journals/tests/utils.py | 139 ++++++++ openedx/features/journals/urls.py | 19 ++ openedx/features/journals/views/__init__.py | 0 .../journals/views/learner_dashboard.py | 103 ++++++ openedx/features/journals/views/marketing.py | 75 +++++ .../learner_profile/views/learner_profile.py | 2 + setup.py | 1 + .../header/navbar-authenticated.html | 7 + 40 files changed, 1633 insertions(+), 5 deletions(-) create mode 100644 lms/static/sass/features/_journals.scss create mode 100644 lms/templates/learner_dashboard/_dashboard_navigation_journals.html create mode 100644 openedx/features/journals/README.rst create mode 100644 openedx/features/journals/__init__.py create mode 100644 openedx/features/journals/api.py create mode 100644 openedx/features/journals/apps.py create mode 100644 openedx/features/journals/settings/__init__.py create mode 100644 openedx/features/journals/settings/aws.py create mode 100644 openedx/features/journals/settings/common.py create mode 100644 openedx/features/journals/settings/devstack.py create mode 100644 openedx/features/journals/settings/test.py create mode 100644 openedx/features/journals/templates/journals/bundle_about.html create mode 100644 openedx/features/journals/templates/journals/bundle_card.html create mode 100644 openedx/features/journals/templates/journals/journal_card.html create mode 100644 openedx/features/journals/templates/journals/learner_dashboard/journal_dashboard.html create mode 100644 openedx/features/journals/tests/__init__.py create mode 100644 openedx/features/journals/tests/test_learner_dashboard.py create mode 100644 openedx/features/journals/tests/test_marketing_views.py create mode 100644 openedx/features/journals/tests/utils.py create mode 100644 openedx/features/journals/urls.py create mode 100644 openedx/features/journals/views/__init__.py create mode 100644 openedx/features/journals/views/learner_dashboard.py create mode 100644 openedx/features/journals/views/marketing.py diff --git a/common/djangoapps/student/views/dashboard.py b/common/djangoapps/student/views/dashboard.py index 27e89f6a7a..1576db34d3 100644 --- a/common/djangoapps/student/views/dashboard.py +++ b/common/djangoapps/student/views/dashboard.py @@ -42,6 +42,7 @@ from openedx.core.djangoapps.util.maintenance_banner import add_maintenance_bann from openedx.core.djangoapps.waffle_utils import WaffleFlag, WaffleFlagNamespace from openedx.core.djangolib.markup import HTML, Text from openedx.features.enterprise_support.api import get_dashboard_consent_notification +from openedx.features.journals.api import journals_enabled from shoppingcart.api import order_history from shoppingcart.models import CourseRegistrationCode, DonationConfiguration from student.cookies import set_user_info_cookie @@ -821,6 +822,7 @@ def student_dashboard(request): 'nav_hidden': True, 'inverted_programs': inverted_programs, 'show_program_listing': ProgramsApiConfig.is_enabled(), + 'show_journal_listing': journals_enabled(), # TODO: Dashboard Plugin required 'show_dashboard_tabs': True, 'disable_courseware_js': True, 'display_course_modes_on_dashboard': enable_verified_certificates and display_course_modes_on_dashboard, diff --git a/common/djangoapps/student/views/management.py b/common/djangoapps/student/views/management.py index 3e3f03995e..61fcbedc48 100644 --- a/common/djangoapps/student/views/management.py +++ b/common/djangoapps/student/views/management.py @@ -70,6 +70,7 @@ from openedx.core.djangoapps.user_api.models import UserRetirementRequest from openedx.core.djangoapps.user_api.preferences import api as preferences_api from openedx.core.djangoapps.user_api.config.waffle import PREVENT_AUTH_USER_WRITES, SYSTEM_MAINTENANCE_MSG, waffle from openedx.core.djangolib.markup import HTML, Text +from openedx.features.journals.api import get_journals_context from student.cookies import set_logged_in_cookies from student.forms import AccountCreationForm, PasswordResetFormNoActive, get_registration_extension_form from student.helpers import ( @@ -195,6 +196,9 @@ def index(request, extra_context=None, user=AnonymousUser()): # Add marketable programs to the context. context['programs_list'] = get_programs_with_type(request.site, include_hidden=False) + # TODO: Course Listing Plugin required + context['journal_info'] = get_journals_context(request) + return render_to_response('index.html', context) diff --git a/common/test/acceptance/pages/lms/discovery.py b/common/test/acceptance/pages/lms/discovery.py index 35c414e329..010fd2e03f 100644 --- a/common/test/acceptance/pages/lms/discovery.py +++ b/common/test/acceptance/pages/lms/discovery.py @@ -31,7 +31,7 @@ class CourseDiscoveryPage(PageObject): """ Return search result items. """ - return self.q(css=".courses-listing-item") + return self.q(css=".courses-list .courses-listing-item") @property def clear_button(self): diff --git a/lms/djangoapps/courseware/views/views.py b/lms/djangoapps/courseware/views/views.py index f795034a78..609a0604c7 100644 --- a/lms/djangoapps/courseware/views/views.py +++ b/lms/djangoapps/courseware/views/views.py @@ -93,6 +93,7 @@ from openedx.features.course_experience.views.course_dates import CourseDatesFra from openedx.features.course_experience.waffle import waffle as course_experience_waffle from openedx.features.course_experience.waffle import ENABLE_COURSE_ABOUT_SIDEBAR_HTML from openedx.features.enterprise_support.api import data_sharing_consent_required +from openedx.features.journals.api import get_journals_context from shoppingcart.utils import is_shopping_cart_enabled from student.models import CourseEnrollment, UserTestGroup from util.cache import cache, cache_if_anonymous @@ -231,7 +232,8 @@ def courses(request): { 'courses': courses_list, 'course_discovery_meanings': course_discovery_meanings, - 'programs_list': programs_list + 'programs_list': programs_list, + 'journal_info': get_journals_context(request), # TODO: Course Listing Plugin required } ) diff --git a/lms/djangoapps/learner_dashboard/views.py b/lms/djangoapps/learner_dashboard/views.py index 38a6da820d..43e8605e1d 100644 --- a/lms/djangoapps/learner_dashboard/views.py +++ b/lms/djangoapps/learner_dashboard/views.py @@ -6,6 +6,7 @@ from edxmako.shortcuts import render_to_response from lms.djangoapps.learner_dashboard.programs import ProgramsFragmentView, ProgramDetailsFragmentView from openedx.core.djangoapps.programs.models import ProgramsApiConfig +from openedx.features.journals.api import journals_enabled @login_required @@ -21,6 +22,7 @@ def program_listing(request): 'nav_hidden': True, 'show_dashboard_tabs': True, 'show_program_listing': programs_config.enabled, + 'show_journal_listing': journals_enabled(), # TODO: Dashboard Plugin required 'uses_pattern_library': True, } diff --git a/lms/envs/common.py b/lms/envs/common.py index 93db504abc..ac0b6e0a07 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -3457,7 +3457,6 @@ FERNET_KEYS = [ # Maximum number of rows to fetch in XBlockUserStateClient calls. Adjust for performance USER_STATE_BATCH_SIZE = 5000 - ############## Plugin Django Apps ######################### from openedx.core.djangoapps.plugins import plugin_apps, plugin_settings, constants as plugin_constants diff --git a/lms/static/sass/_build-lms-v1.scss b/lms/static/sass/_build-lms-v1.scss index 3081a4b704..aa2c05fc90 100644 --- a/lms/static/sass/_build-lms-v1.scss +++ b/lms/static/sass/_build-lms-v1.scss @@ -69,6 +69,7 @@ // features @import 'features/bookmarks-v1'; @import 'features/learner-profile'; +@import 'features/journals'; // search @import 'search/search'; diff --git a/lms/static/sass/_build-lms-v2.scss b/lms/static/sass/_build-lms-v2.scss index 180b029bca..e18e0184e9 100644 --- a/lms/static/sass/_build-lms-v2.scss +++ b/lms/static/sass/_build-lms-v2.scss @@ -33,6 +33,7 @@ @import 'features/course-sock'; @import 'features/course-upgrade-message'; @import 'features/learner-analytics-dashboard'; +@import 'features/journals'; // Responsive Design @import 'header'; diff --git a/lms/static/sass/features/_journals.scss b/lms/static/sass/features/_journals.scss new file mode 100644 index 0000000000..784248d7e9 --- /dev/null +++ b/lms/static/sass/features/_journals.scss @@ -0,0 +1,173 @@ +// journal catalog listing +.journals-listing-item { + box-sizing: border-box; + box-shadow: 1px 2px 5px #ccc; + position: relative; + height: 360px; + overflow: visible; + min-height: 0; + border: none; + display: block; + margin: 0 auto 40px; + background: white; + border-radius: 0; + + .journal-image { + height: 142px; + position: relative; + overflow: hidden; + + .cover-image { + height: 142px; + + img { + width: 100%; + height: auto; + min-height: 100%; + } + + &::before { + content: ''; + position: absolute; + left: 0; + right: 0; + top: 0; + bottom: 0; + margin: auto; + background: black; + opacity: 0; + transition: all 0.2s ease-out; + } + + .learn-more { + left: calc(50% - 100px); + box-sizing: border-box; + position: absolute; + z-index: 100; + top: 62px; + padding: 0 20px; + width: 200px; + height: 50px; + border-color: #0074b4; + border-radius: 3px; + background: #0074b4; + color: #fff; + line-height: 50px; + text-align: center; + opacity: 0; + text-transform: none; + transition: all 0.25s ease; + } + } + } + + .banner { + background: #065784; + color: white; + + @include padding-right(15px); + + line-height: 18px; + font-weight: bold; + font-size: 0.7em; + text-align: right; + text-transform: uppercase; + } + + .journal-info { + padding: 12px 15px 5px; + + .journal-org { + font-weight: normal; + font-size: 0.9em; + color: #3d3e3f; + margin: 0; + line-height: 16px; + } + + .journal-title { + max-height: 55px; + overflow: hidden; + color: #222; + font-size: 1.25em; + line-height: 1.333; + margin-bottom: 5px; + } + + .journal-subtitle { + font-size: 1em; + margin-bottom: 33px; + line-height: 1.25em; + height: 40px; + color: #646464; + overflow: hidden; + } + } + + .journal-footer { + display: table; + width: 100%; + padding: 0 15px 15px; + position: absolute; + bottom: 0; + + .availability, + .journal-logo { + display: table-cell; + vertical-align: middle; + } + + .availability { + text-align: left; + font-size: 0.9em; + line-height: 20px; + color: #3d3e3f; + } + + .journal-logo { + text-align: right; + width: 75px; + } + } + + &::before, + &::after { + box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.2); + content: ''; + position: absolute; + width: 100%; + height: 100%; + background: #d5d5d5; + border: 1px solid #b5b5b5; + } + + &::before { + @include left(-5px); + + top: -5px; + z-index: -1; + } + + &::after { + @include left(-10px); + + top: -10px; + z-index: -2; + } + + &:hover { + opacity: 1; + + .journal-image { + .cover-image { + .learn-more { + opacity: 1; + } + + &::before { + opacity: 0.6; + } + } + } + } +} diff --git a/lms/templates/courses_list.html b/lms/templates/courses_list.html index 18b8851b7c..7362d9e1cd 100644 --- a/lms/templates/courses_list.html +++ b/lms/templates/courses_list.html @@ -7,6 +7,20 @@ % if settings.FEATURES.get('COURSES_ARE_BROWSABLE'):
+ +