Merge pull request #10645 from CourseTalk/coursetalk_widget
Integration CourseTalk widgets with OpenEdx.
This commit is contained in:
@@ -788,6 +788,7 @@ INSTALLED_APPS = (
|
||||
'external_auth',
|
||||
'student', # misleading name due to sharing with lms
|
||||
'openedx.core.djangoapps.course_groups', # not used in cms (yet), but tests run
|
||||
'openedx.core.djangoapps.coursetalk', # not used in cms (yet), but tests run
|
||||
'xblock_config',
|
||||
|
||||
# Tracking
|
||||
|
||||
@@ -269,7 +269,7 @@ class SelfPacedCourseInfoTestCase(LoginEnrollmentTestCase, SharedModuleStoreTest
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
|
||||
def test_num_queries_instructor_paced(self):
|
||||
self.fetch_course_info_with_queries(self.instructor_paced_course, 19, 4)
|
||||
self.fetch_course_info_with_queries(self.instructor_paced_course, 20, 4)
|
||||
|
||||
def test_num_queries_self_paced(self):
|
||||
self.fetch_course_info_with_queries(self.self_paced_course, 19, 4)
|
||||
self.fetch_course_info_with_queries(self.self_paced_course, 20, 4)
|
||||
|
||||
@@ -65,6 +65,8 @@ from courseware.user_state_client import DjangoXBlockUserStateClient
|
||||
from edxmako.shortcuts import render_to_response, render_to_string, marketing_link
|
||||
from instructor.enrollment import uses_shib
|
||||
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
|
||||
from openedx.core.djangoapps.coursetalk.helpers import inject_coursetalk_keys_into_context
|
||||
from openedx.core.djangoapps.user_api.preferences.api import get_user_preference
|
||||
from openedx.core.djangoapps.credit.api import (
|
||||
get_credit_requirement_status,
|
||||
is_user_eligible_for_credit,
|
||||
@@ -97,8 +99,6 @@ from .entrance_exams import (
|
||||
from .module_render import toc_for_course, get_module_for_descriptor, get_module, get_module_by_usage_id
|
||||
|
||||
from lang_pref import LANGUAGE_KEY
|
||||
from openedx.core.djangoapps.user_api.preferences.api import get_user_preference
|
||||
|
||||
|
||||
log = logging.getLogger("edx.courseware")
|
||||
|
||||
@@ -727,6 +727,9 @@ def course_info(request, course_id):
|
||||
# course is not yet visible to students.
|
||||
context['disable_student_access'] = True
|
||||
|
||||
if CourseEnrollment.is_enrolled(request.user, course.id):
|
||||
inject_coursetalk_keys_into_context(context, course_key)
|
||||
|
||||
return render_to_response('courseware/info.html', context)
|
||||
|
||||
|
||||
@@ -935,7 +938,7 @@ def course_about(request, course_id):
|
||||
# Overview
|
||||
overview = CourseOverview.get_from_id(course.id)
|
||||
|
||||
return render_to_response('courseware/course_about.html', {
|
||||
context = {
|
||||
'course': course,
|
||||
'staff_access': staff_access,
|
||||
'studio_url': studio_url,
|
||||
@@ -961,7 +964,10 @@ def course_about(request, course_id):
|
||||
'cart_link': reverse('shoppingcart.views.show_cart'),
|
||||
'pre_requisite_courses': pre_requisite_courses,
|
||||
'course_image_urls': overview.image_urls,
|
||||
})
|
||||
}
|
||||
inject_coursetalk_keys_into_context(context, course_key)
|
||||
|
||||
return render_to_response('courseware/course_about.html', context)
|
||||
|
||||
|
||||
@transaction.non_atomic_requests
|
||||
|
||||
@@ -1976,6 +1976,9 @@ INSTALLED_APPS = (
|
||||
|
||||
# Static i18n support
|
||||
'statici18n',
|
||||
|
||||
# Review widgets
|
||||
'openedx.core.djangoapps.coursetalk',
|
||||
)
|
||||
|
||||
# Migrations which are not in the standard module "migrations"
|
||||
|
||||
@@ -424,6 +424,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
>.coursetalk-read-reviews {
|
||||
margin-top: -200px;
|
||||
margin-bottom: 220px;
|
||||
}
|
||||
|
||||
header {
|
||||
margin-bottom: 30px;
|
||||
padding-bottom: 16px;
|
||||
|
||||
@@ -17,7 +17,10 @@ from openedx.core.lib.courses import course_image_url
|
||||
</%block>
|
||||
|
||||
<%block name="js_extra">
|
||||
|
||||
## CourseTalk widget js script
|
||||
% if show_coursetalk_widget:
|
||||
<script src="//d3q6qq2zt8nhwv.cloudfront.net/s/js/widgets/coursetalk-read-reviews.js"></script>
|
||||
% endif
|
||||
<script type="text/javascript">
|
||||
(function() {
|
||||
$(".register").click(function(event) {
|
||||
@@ -283,6 +286,12 @@ from openedx.core.lib.courses import course_image_url
|
||||
</ol>
|
||||
</section>
|
||||
|
||||
## CourseTalk widget
|
||||
% if show_coursetalk_widget:
|
||||
<div class="coursetalk-read-reviews">
|
||||
<div id="ct-custom-read-review-widget" data-provider="${platform_key}" data-course="${course_review_key}"></div>
|
||||
</div>
|
||||
% endif
|
||||
|
||||
## For now, ocw links are the only thing that goes in additional resources
|
||||
% if get_course_about_section(request, course, "ocw_links"):
|
||||
@@ -298,6 +307,7 @@ from openedx.core.lib.courses import course_image_url
|
||||
</section>
|
||||
</section>
|
||||
%endif
|
||||
|
||||
</section>
|
||||
|
||||
</section>
|
||||
|
||||
@@ -45,6 +45,10 @@ from openedx.core.djangolib.markup import Text, HTML
|
||||
</%static:require_module>
|
||||
|
||||
<%block name="js_extra">
|
||||
## CourseTalk widget js script
|
||||
% if show_coursetalk_widget:
|
||||
<script src="//d3q6qq2zt8nhwv.cloudfront.net/s/js/widgets/coursetalk-write-reviews.js"></script>
|
||||
% endif
|
||||
<script type="text/javascript" src="${static.url('js/jquery.treeview.js')}"></script>
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
$(document).ready(function(){
|
||||
@@ -79,6 +83,13 @@ from openedx.core.djangolib.markup import Text, HTML
|
||||
|
||||
<h1>${_("Course Updates and News")}</h1>
|
||||
${HTML(get_course_info_section(request, masquerade_user, course, 'updates'))}
|
||||
|
||||
## CourseTalk widget
|
||||
% if show_coursetalk_widget:
|
||||
<div class="coursetalk-write-reviews">
|
||||
<div id="ct-custom-read-review-widget" data-provider="${platform_key}" data-course="${course_review_key}"></div>
|
||||
</div>
|
||||
% endif
|
||||
</section>
|
||||
<section aria-label="${_('Handout Navigation')}" class="handouts">
|
||||
% if SelfPacedConfiguration.current().enable_course_home_improvements:
|
||||
|
||||
0
openedx/core/djangoapps/coursetalk/__init__.py
Normal file
0
openedx/core/djangoapps/coursetalk/__init__.py
Normal file
8
openedx/core/djangoapps/coursetalk/admin.py
Normal file
8
openedx/core/djangoapps/coursetalk/admin.py
Normal file
@@ -0,0 +1,8 @@
|
||||
"""Manage coursetalk configuration. """
|
||||
from django.contrib import admin
|
||||
|
||||
from config_models.admin import ConfigurationModelAdmin
|
||||
from openedx.core.djangoapps.coursetalk.models import CourseTalkWidgetConfiguration
|
||||
|
||||
|
||||
admin.site.register(CourseTalkWidgetConfiguration, ConfigurationModelAdmin)
|
||||
35
openedx/core/djangoapps/coursetalk/helpers.py
Normal file
35
openedx/core/djangoapps/coursetalk/helpers.py
Normal file
@@ -0,0 +1,35 @@
|
||||
"""
|
||||
CourseTalk widget helpers
|
||||
"""
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from openedx.core.djangoapps.coursetalk import models
|
||||
|
||||
|
||||
def get_coursetalk_course_key(course_key):
|
||||
"""
|
||||
Return course key for coursetalk widget
|
||||
|
||||
CourseTalk unique key for a course contains only organization and course code.
|
||||
:param course_key: SlashSeparatedCourseKey instance
|
||||
:type course_key: SlashSeparatedCourseKey
|
||||
:return: CourseTalk course key
|
||||
:rtype: str
|
||||
"""
|
||||
return '{0.org}_{0.course}'.format(course_key)
|
||||
|
||||
|
||||
def inject_coursetalk_keys_into_context(context, course_key):
|
||||
"""
|
||||
Set params to view context based on course_key and CourseTalkWidgetConfiguration
|
||||
|
||||
:param context: view context
|
||||
:type context: dict
|
||||
:param course_key: SlashSeparatedCourseKey instance
|
||||
:type course_key: SlashSeparatedCourseKey
|
||||
"""
|
||||
show_coursetalk_widget = models.CourseTalkWidgetConfiguration.is_enabled()
|
||||
if show_coursetalk_widget:
|
||||
context['show_coursetalk_widget'] = True
|
||||
context['platform_key'] = models.CourseTalkWidgetConfiguration.get_platform_key()
|
||||
context['course_review_key'] = get_coursetalk_course_key(course_key)
|
||||
@@ -0,0 +1,30 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='CourseTalkWidgetConfiguration',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('change_date', models.DateTimeField(auto_now_add=True, verbose_name='Change date')),
|
||||
('enabled', models.BooleanField(default=False, verbose_name='Enabled')),
|
||||
('platform_key', models.CharField(help_text="This key needs to associate CourseTalk reviews with your platform. Better to use domain name Ex: for 'http://edx.org' platform_key will be 'edx'", max_length=50)),
|
||||
('changed_by', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, editable=False, to=settings.AUTH_USER_MODEL, null=True, verbose_name='Changed by')),
|
||||
],
|
||||
options={
|
||||
'ordering': ('-change_date',),
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
]
|
||||
39
openedx/core/djangoapps/coursetalk/models.py
Normal file
39
openedx/core/djangoapps/coursetalk/models.py
Normal file
@@ -0,0 +1,39 @@
|
||||
"""
|
||||
Models for CourseTalk configurations
|
||||
"""
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from config_models.models import ConfigurationModel
|
||||
|
||||
|
||||
class CourseTalkWidgetConfiguration(ConfigurationModel):
|
||||
"""
|
||||
This model represents Enable Configuration for CourseTalk widget.
|
||||
If the setting enabled, widget will will be available on course
|
||||
info page and on course about page.
|
||||
"""
|
||||
platform_key = models.fields.CharField(
|
||||
max_length=50,
|
||||
help_text=_(
|
||||
"The platform key associates CourseTalk widgets with your platform. "
|
||||
"Generally, it is the domain name for your platform. For example, "
|
||||
"if your platform is http://edx.org, the platform key is \"edx\"."
|
||||
)
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_platform_key(cls):
|
||||
"""
|
||||
Return platform_key for current active configuration.
|
||||
If current configuration is not enabled - return empty string
|
||||
|
||||
:return: Platform key
|
||||
:rtype: unicode
|
||||
"""
|
||||
return cls.current().platform_key if cls.is_enabled() else ''
|
||||
|
||||
def __unicode__(self):
|
||||
return 'CourseTalkWidgetConfiguration - {0}'.format(self.enabled)
|
||||
58
openedx/core/djangoapps/coursetalk/tests/test_helpers.py
Normal file
58
openedx/core/djangoapps/coursetalk/tests/test_helpers.py
Normal file
@@ -0,0 +1,58 @@
|
||||
""" CourseTalk widget helpers tests """
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from unittest import skipUnless
|
||||
|
||||
from django import test
|
||||
from django.conf import settings
|
||||
|
||||
from opaque_keys.edx.locations import SlashSeparatedCourseKey
|
||||
from openedx.core.djangoapps.coursetalk import helpers
|
||||
from openedx.core.djangoapps.coursetalk import models
|
||||
|
||||
|
||||
@skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Tests only valid in LMS')
|
||||
class CourseTalkKeyTests(test.TestCase):
|
||||
"""
|
||||
CourseTalkKeyTests:
|
||||
tests for function get_coursetalk_course_key
|
||||
tests for function inject_coursetalk_keys_into_context
|
||||
"""
|
||||
|
||||
PLATFORM_KEY = 'some_platform'
|
||||
|
||||
def setUp(self):
|
||||
super(CourseTalkKeyTests, self).setUp()
|
||||
self.course_key = SlashSeparatedCourseKey('org', 'course', 'run')
|
||||
self.context = {}
|
||||
|
||||
def db_set_up(self, enabled):
|
||||
"""
|
||||
Setup database for this test:
|
||||
Create CourseTalkWidgetConfiguration
|
||||
"""
|
||||
config = models.CourseTalkWidgetConfiguration.current()
|
||||
config.enabled = enabled
|
||||
config.platform_key = self.PLATFORM_KEY
|
||||
config.save()
|
||||
|
||||
def test_simple_key(self):
|
||||
coursetalk_course_key = helpers.get_coursetalk_course_key(self.course_key)
|
||||
self.assertEqual(coursetalk_course_key, 'org_course')
|
||||
|
||||
def test_inject_coursetalk_keys_when_widget_not_enabled(self):
|
||||
self.db_set_up(False)
|
||||
helpers.inject_coursetalk_keys_into_context(self.context, self.course_key)
|
||||
self.assertNotIn('show_coursetalk_widget', self.context)
|
||||
self.assertNotIn('platform_key', self.context)
|
||||
self.assertNotIn('course_review_key', self.context)
|
||||
|
||||
def test_inject_coursetalk_keys_when_widget_enabled(self):
|
||||
self.db_set_up(True)
|
||||
helpers.inject_coursetalk_keys_into_context(self.context, self.course_key)
|
||||
self.assertIn('show_coursetalk_widget', self.context)
|
||||
self.assertIn('platform_key', self.context)
|
||||
self.assertIn('course_review_key', self.context)
|
||||
self.assertEqual(self.context.get('show_coursetalk_widget'), True)
|
||||
self.assertEqual(self.context.get('platform_key'), self.PLATFORM_KEY)
|
||||
self.assertEqual(self.context.get('course_review_key'), 'org_course')
|
||||
Reference in New Issue
Block a user