Commit 44c27bb1 by Michael Katz

Merge pull request #11606 from edx/rc/2016-02-23

Release Candidate rc/2016-02-23
parents 5f0d969b 67b128df
......@@ -9,6 +9,9 @@ from mock import patch, Mock
import ddt
from django.test import RequestFactory
from django.test.client import Client
from common.test.utils import XssTestMixin
from xmodule.course_module import CourseSummary
from contentstore.views.course import (_accessible_courses_list, _accessible_courses_list_from_groups,
......@@ -30,7 +33,7 @@ USER_COURSES_COUNT = 50
@ddt.ddt
class TestCourseListing(ModuleStoreTestCase):
class TestCourseListing(ModuleStoreTestCase, XssTestMixin):
"""
Unit tests for getting the list of courses for a logged in user
"""
......@@ -72,6 +75,30 @@ class TestCourseListing(ModuleStoreTestCase):
self.client.logout()
ModuleStoreTestCase.tearDown(self)
def test_course_listing_is_escaped(self):
"""
Tests course listing returns escaped data.
"""
escaping_content = "<script>alert('ESCAPE')</script>"
# Make user staff to access course listing
self.user.is_staff = True
self.user.save() # pylint: disable=no-member
self.client = Client()
self.client.login(username=self.user.username, password='test')
# Change 'display_coursenumber' field and update the course.
course = CourseFactory.create()
course.display_coursenumber = escaping_content
course = self.store.update_item(course, self.user.id) # pylint: disable=no-member
self.assertEqual(course.display_coursenumber, escaping_content)
# Check if response is escaped
response = self.client.get('/home')
self.assertEqual(response.status_code, 200)
self.assert_no_xss(response, escaping_content)
def test_get_course_list(self):
"""
Test getting courses with new access group format e.g. 'instructor_edx.course.run'
......
......@@ -20,14 +20,12 @@ from xmodule.library_tools import normalize_key_for_search
from xmodule.modulestore import ModuleStoreEnum
from xmodule.modulestore.django import SignalHandler, modulestore
from xmodule.modulestore.edit_info import EditInfoMixin
from xmodule.modulestore.exceptions import ItemNotFoundError
from xmodule.modulestore.inheritance import InheritanceMixin
from xmodule.modulestore.mixed import MixedModuleStore
from xmodule.modulestore.tests.django_utils import (
ModuleStoreTestCase,
TEST_DATA_MONGO_MODULESTORE,
TEST_DATA_SPLIT_MODULESTORE
)
TEST_DATA_SPLIT_MODULESTORE,
SharedModuleStoreTestCase)
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory, LibraryFactory
from xmodule.modulestore.tests.mongo_connection import MONGO_PORT_NUM, MONGO_HOST
from xmodule.modulestore.tests.utils import (
......@@ -692,7 +690,7 @@ class TestLargeCourseDeletions(MixedWithOptionsTestCase):
self._perform_test_using_store(store_type, self._test_large_course_deletion)
class TestTaskExecution(ModuleStoreTestCase):
class TestTaskExecution(SharedModuleStoreTestCase):
"""
Set of tests to ensure that the task code will do the right thing when
executed directly. The test course and library gets created without the listeners
......@@ -700,52 +698,53 @@ class TestTaskExecution(ModuleStoreTestCase):
executed, it is done as expected.
"""
def setUp(self):
super(TestTaskExecution, self).setUp()
@classmethod
def setUpClass(cls):
super(TestTaskExecution, cls).setUpClass()
SignalHandler.course_published.disconnect(listen_for_course_publish)
SignalHandler.library_updated.disconnect(listen_for_library_update)
self.course = CourseFactory.create(start=datetime(2015, 3, 1, tzinfo=UTC))
cls.course = CourseFactory.create(start=datetime(2015, 3, 1, tzinfo=UTC))
self.chapter = ItemFactory.create(
parent_location=self.course.location,
cls.chapter = ItemFactory.create(
parent_location=cls.course.location,
category='chapter',
display_name="Week 1",
publish_item=True,
start=datetime(2015, 3, 1, tzinfo=UTC),
)
self.sequential = ItemFactory.create(
parent_location=self.chapter.location,
cls.sequential = ItemFactory.create(
parent_location=cls.chapter.location,
category='sequential',
display_name="Lesson 1",
publish_item=True,
start=datetime(2015, 3, 1, tzinfo=UTC),
)
self.vertical = ItemFactory.create(
parent_location=self.sequential.location,
cls.vertical = ItemFactory.create(
parent_location=cls.sequential.location,
category='vertical',
display_name='Subsection 1',
publish_item=True,
start=datetime(2015, 4, 1, tzinfo=UTC),
)
# unspecified start - should inherit from container
self.html_unit = ItemFactory.create(
parent_location=self.vertical.location,
cls.html_unit = ItemFactory.create(
parent_location=cls.vertical.location,
category="html",
display_name="Html Content",
publish_item=False,
)
self.library = LibraryFactory.create()
cls.library = LibraryFactory.create()
self.library_block1 = ItemFactory.create(
parent_location=self.library.location,
cls.library_block1 = ItemFactory.create(
parent_location=cls.library.location,
category="html",
display_name="Html Content",
publish_item=False,
)
self.library_block2 = ItemFactory.create(
parent_location=self.library.location,
cls.library_block2 = ItemFactory.create(
parent_location=cls.library.location,
category="html",
display_name="Html Content 2",
publish_item=False,
......
......@@ -14,7 +14,7 @@ from nose.plugins.skip import SkipTest
from xmodule.modulestore.tests.factories import CourseFactory
from xmodule.contentstore.content import StaticContent
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
from xmodule.exceptions import NotFoundError
from xmodule.contentstore.django import contentstore
from xmodule.video_module import transcripts_utils
......@@ -77,7 +77,7 @@ class TestGenerateSubs(unittest.TestCase):
@override_settings(CONTENTSTORE=TEST_DATA_CONTENTSTORE)
class TestSaveSubsToStore(ModuleStoreTestCase):
class TestSaveSubsToStore(SharedModuleStoreTestCase):
"""Tests for `save_subs_to_store` function."""
org = 'MITx'
......@@ -92,13 +92,13 @@ class TestSaveSubsToStore(ModuleStoreTestCase):
except NotFoundError:
pass
def setUp(self):
super(TestSaveSubsToStore, self).setUp()
self.course = CourseFactory.create(
org=self.org, number=self.number, display_name=self.display_name)
@classmethod
def setUpClass(cls):
super(TestSaveSubsToStore, cls).setUpClass()
cls.course = CourseFactory.create(
org=cls.org, number=cls.number, display_name=cls.display_name)
self.subs = {
cls.subs = {
'start': [100, 200, 240, 390, 1000],
'end': [200, 240, 380, 1000, 1500],
'text': [
......@@ -110,18 +110,20 @@ class TestSaveSubsToStore(ModuleStoreTestCase):
]
}
self.subs_id = str(uuid4())
filename = 'subs_{0}.srt.sjson'.format(self.subs_id)
self.content_location = StaticContent.compute_location(self.course.id, filename)
self.addCleanup(self.clear_subs_content)
cls.subs_id = str(uuid4())
filename = 'subs_{0}.srt.sjson'.format(cls.subs_id)
cls.content_location = StaticContent.compute_location(cls.course.id, filename)
# incorrect subs
self.unjsonable_subs = set([1]) # set can't be serialized
cls.unjsonable_subs = {1} # set can't be serialized
self.unjsonable_subs_id = str(uuid4())
filename_unjsonable = 'subs_{0}.srt.sjson'.format(self.unjsonable_subs_id)
self.content_location_unjsonable = StaticContent.compute_location(self.course.id, filename_unjsonable)
cls.unjsonable_subs_id = str(uuid4())
filename_unjsonable = 'subs_{0}.srt.sjson'.format(cls.unjsonable_subs_id)
cls.content_location_unjsonable = StaticContent.compute_location(cls.course.id, filename_unjsonable)
def setUp(self):
super(TestSaveSubsToStore, self).setUp()
self.addCleanup(self.clear_subs_content)
self.clear_subs_content()
def test_save_subs_to_store(self):
......@@ -154,7 +156,7 @@ class TestSaveSubsToStore(ModuleStoreTestCase):
@override_settings(CONTENTSTORE=TEST_DATA_CONTENTSTORE)
class TestDownloadYoutubeSubs(ModuleStoreTestCase):
class TestDownloadYoutubeSubs(SharedModuleStoreTestCase):
"""Tests for `download_youtube_subs` function."""
org = 'MITx'
......@@ -182,10 +184,11 @@ class TestDownloadYoutubeSubs(ModuleStoreTestCase):
for subs_id in youtube_subs.values():
self.clear_sub_content(subs_id)
def setUp(self):
super(TestDownloadYoutubeSubs, self).setUp()
self.course = CourseFactory.create(
org=self.org, number=self.number, display_name=self.display_name)
@classmethod
def setUpClass(cls):
super(TestDownloadYoutubeSubs, cls).setUpClass()
cls.course = CourseFactory.create(
org=cls.org, number=cls.number, display_name=cls.display_name)
def test_success_downloading_subs(self):
......
......@@ -9,7 +9,7 @@ from django.test import TestCase
from django.test.utils import override_settings
from xmodule.modulestore import ModuleStoreEnum
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase, SharedModuleStoreTestCase
from opaque_keys.edx.locations import SlashSeparatedCourseKey
from xmodule.modulestore.django import modulestore
from xmodule.partitions.partitions import UserPartition, Group
......@@ -110,16 +110,17 @@ class ExtraPanelTabTestCase(TestCase):
return course
class XBlockVisibilityTestCase(ModuleStoreTestCase):
class XBlockVisibilityTestCase(SharedModuleStoreTestCase):
"""Tests for xblock visibility for students."""
def setUp(self):
super(XBlockVisibilityTestCase, self).setUp()
@classmethod
def setUpClass(cls):
super(XBlockVisibilityTestCase, cls).setUpClass()
self.dummy_user = ModuleStoreEnum.UserID.test
self.past = datetime(1970, 1, 1, tzinfo=UTC)
self.future = datetime.now(UTC) + timedelta(days=1)
self.course = CourseFactory.create()
cls.dummy_user = ModuleStoreEnum.UserID.test
cls.past = datetime(1970, 1, 1, tzinfo=UTC)
cls.future = datetime.now(UTC) + timedelta(days=1)
cls.course = CourseFactory.create()
def test_private_unreleased_xblock(self):
"""Verifies that a private unreleased xblock is not visible"""
......@@ -484,18 +485,18 @@ class GetUserPartitionInfoTest(ModuleStoreTestCase):
expected = [
{
"id": 0,
"name": "Cohort user partition",
"scheme": "cohort",
"name": u"Cohort user partition",
"scheme": u"cohort",
"groups": [
{
"id": 0,
"name": "Group A",
"name": u"Group A",
"selected": False,
"deleted": False,
},
{
"id": 1,
"name": "Group B",
"name": u"Group B",
"selected": False,
"deleted": False,
},
......@@ -503,12 +504,12 @@ class GetUserPartitionInfoTest(ModuleStoreTestCase):
},
{
"id": 1,
"name": "Random user partition",
"scheme": "random",
"name": u"Random user partition",
"scheme": u"random",
"groups": [
{
"id": 0,
"name": "Group C",
"name": u"Group C",
"selected": False,
"deleted": False,
},
......
......@@ -13,6 +13,8 @@ from django.utils.translation import ugettext as _
from django_comment_common.models import assign_default_role
from django_comment_common.utils import seed_permissions_roles
from openedx.core.djangoapps.self_paced.models import SelfPacedConfiguration
from xmodule.modulestore import ModuleStoreEnum
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.exceptions import ItemNotFoundError
......@@ -455,3 +457,10 @@ def get_visibility_partition_info(xblock):
"has_selected_groups": has_selected_groups,
"selected_verified_partition_id": selected_verified_partition_id,
}
def is_self_paced(course):
"""
Returns True if course is self-paced, False otherwise.
"""
return course and course.self_paced and SelfPacedConfiguration.current().enabled
......@@ -31,6 +31,8 @@ from contentstore.utils import (
from contentstore.views.helpers import is_unit, xblock_studio_url, xblock_primary_child_category, \
xblock_type_display_name, get_parent_xblock, create_xblock, usage_key_with_run
from contentstore.views.preview import get_preview_fragment
from contentstore.utils import is_self_paced
from openedx.core.lib.gating import api as gating_api
from edxmako.shortcuts import render_to_string
from models.settings.course_grading import CourseGradingModel
......@@ -855,7 +857,9 @@ def create_xblock_info(xblock, data=None, metadata=None, include_ancestor_info=F
release_date = _get_release_date(xblock, user)
if xblock.category != 'course':
visibility_state = _compute_visibility_state(xblock, child_info, is_xblock_unit and has_changes)
visibility_state = _compute_visibility_state(
xblock, child_info, is_xblock_unit and has_changes, is_self_paced(course)
)
else:
visibility_state = None
published = modulestore().has_published_version(xblock) if not is_library_block else None
......@@ -1017,7 +1021,7 @@ class VisibilityState(object):
gated = 'gated'
def _compute_visibility_state(xblock, child_info, is_unit_with_changes):
def _compute_visibility_state(xblock, child_info, is_unit_with_changes, is_course_self_paced=False):
"""
Returns the current publish state for the specified xblock and its children
"""
......@@ -1027,10 +1031,10 @@ def _compute_visibility_state(xblock, child_info, is_unit_with_changes):
# Note that a unit that has never been published will fall into this category,
# as well as previously published units with draft content.
return VisibilityState.needs_attention
is_unscheduled = xblock.start == DEFAULT_START_DATE
is_live = datetime.now(UTC) > xblock.start
children = child_info and child_info.get('children', [])
if children and len(children) > 0:
is_live = is_course_self_paced or datetime.now(UTC) > xblock.start
if child_info and child_info.get('children', []):
all_staff_only = True
all_unscheduled = True
all_live = True
......
#-*- coding: utf-8 -*-
"""
Group Configuration Tests.
Certificates Tests.
"""
import json
import mock
......
......@@ -14,6 +14,7 @@ from django.test.client import RequestFactory
from django.core.urlresolvers import reverse
from contentstore.utils import reverse_usage_url, reverse_course_url
from openedx.core.djangoapps.self_paced.models import SelfPacedConfiguration
from contentstore.views.component import (
component_handler, get_component_templates
)
......@@ -1847,6 +1848,7 @@ class TestLibraryXBlockCreation(ItemTest):
self.assertFalse(lib.children)
@ddt.ddt
class TestXBlockPublishingInfo(ItemTest):
"""
Unit tests for XBlock's outline handling.
......@@ -2169,3 +2171,31 @@ class TestXBlockPublishingInfo(ItemTest):
self._verify_has_staff_only_message(xblock_info, True)
self._verify_has_staff_only_message(xblock_info, True, path=self.FIRST_SUBSECTION_PATH)
self._verify_has_staff_only_message(xblock_info, True, path=self.FIRST_UNIT_PATH)
@ddt.data(ModuleStoreEnum.Type.mongo, ModuleStoreEnum.Type.split)
def test_self_paced_item_visibility_state(self, store_type):
"""
Test that in self-paced course, item has `live` visibility state.
Test that when item was initially in `scheduled` state in instructor mode, change course pacing to self-paced,
now in self-paced course, item should have `live` visibility state.
"""
SelfPacedConfiguration(enabled=True).save()
# Create course, chapter and setup future release date to make chapter in scheduled state
course = CourseFactory.create(default_store=store_type)
chapter = self._create_child(course, 'chapter', "Test Chapter")
self._set_release_date(chapter.location, datetime.now(UTC) + timedelta(days=1))
# Check that chapter has scheduled state
xblock_info = self._get_xblock_info(chapter.location)
self._verify_visibility_state(xblock_info, VisibilityState.ready)
self.assertFalse(course.self_paced)
# Change course pacing to self paced
course.self_paced = True
self.store.update_item(course, self.user.id)
self.assertTrue(course.self_paced)
# Check that in self paced course content has live state now
xblock_info = self._get_xblock_info(chapter.location)
self._verify_visibility_state(xblock_info, VisibilityState.live)
......@@ -10,6 +10,7 @@ from provider.constants import CONFIDENTIAL
from openedx.core.djangoapps.programs.models import ProgramsApiConfig
from openedx.core.djangoapps.programs.tests.mixins import ProgramsApiConfigMixin, ProgramsDataMixin
from openedx.core.djangolib.markup import escape
from student.tests.factories import UserFactory
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
......@@ -63,7 +64,7 @@ class TestProgramListing(ProgramsApiConfigMixin, ProgramsDataMixin, SharedModule
self.mock_programs_api(data={'results': []})
response = self.client.get(self.studio_home)
self.assertIn("You haven't created any programs yet.", response.content)
self.assertIn(escape("You haven't created any programs yet."), response.content)
# When data is provided, expect a program listing.
self.mock_programs_api()
......
......@@ -849,9 +849,6 @@ INSTALLED_APPS = (
# Microsite configuration application
'microsite_configuration',
# Credentials support
'openedx.core.djangoapps.credentials',
# edx-milestones service
'milestones',
......@@ -1069,6 +1066,7 @@ DEPRECATED_BLOCK_TYPES = [
'peergrading',
'combinedopenended',
'graphical_slider_tool',
'randomize',
]
# Adding components in this list will disable the creation of new problems for
......@@ -1129,8 +1127,3 @@ USERNAME_PATTERN = r'(?P<username>[\w.@+-]+)'
# Partner support link for CMS footer
PARTNER_SUPPORT_EMAIL = ''
################################ Settings for Credentials Service ################################
CREDENTIALS_SERVICE_USERNAME = 'credentials_service_user'
......@@ -97,11 +97,11 @@ function($, _, Backbone, gettext,
return {
id: this.model.get('id'),
uniqueId: _.uniqueId(),
name: this.model.escape('name'),
description: this.model.escape('description'),
course_title: this.model.escape('course_title'),
org_logo_path: this.model.escape('org_logo_path'),
is_active: this.model.escape('is_active'),
name: this.model.get('name'),
description: this.model.get('description'),
course_title: this.model.get('course_title'),
org_logo_path: this.model.get('org_logo_path'),
is_active: this.model.get('is_active'),
isNew: this.model.isNew()
};
},
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -12,8 +12,8 @@ define(["js/views/baseview", "underscore", "underscore.string", "jquery", "gette
},
render: function() {
this.$el.html(this.template({
name: this.model.escape('name'),
asset_path: this.model.escape('asset_path'),
name: this.model.get('name'),
asset_path: this.model.get('asset_path'),
order: this.model.get('order'),
error: this.model.validationError
}));
......@@ -52,8 +52,10 @@ define(["js/views/baseview", "underscore", "underscore.string", "jquery", "gette
asset_path: this.$("input.chapter-asset-path").val()
});
var msg = new FileUploadModel({
title: _.template(gettext("Upload a new PDF to “<%= name %>”"),
{name: course.escape('name')}),
title: _.template(
gettext("Upload a new PDF to “<%= name %>”"),
{name: window.course.escape('name')}
),
message: gettext("Please select a PDF file to upload."),
mimeTypes: ['application/pdf']
});
......
......@@ -13,7 +13,7 @@ define(["js/views/baseview", "underscore", "jquery", "js/views/edit_chapter", "c
className: "textbook",
render: function() {
this.$el.html(this.template({
name: this.model.escape('name'),
name: this.model.get('name'),
error: this.model.validationError
}));
this.addAll();
......
......@@ -29,8 +29,10 @@ define(["js/views/baseview", "underscore", "gettext", "common/js/components/view
if(e && e.preventDefault) { e.preventDefault(); }
var textbook = this.model, collection = this.model.collection;
var msg = new PromptView.Warning({
title: _.template(gettext("Delete “<%= name %>”?"),
{name: textbook.escape('name')}),
title: _.template(
gettext("Delete “<%= name %>”?"),
{name: textbook.get('name')}
),
message: gettext("Deleting a textbook cannot be undone and once deleted any reference to it in your courseware's navigation will also be removed."),
actions: {
primary: {
......
......@@ -43,7 +43,7 @@ define(["jquery", "underscore", "common/js/components/utils/view_utils", "js/vie
fragmentsRendered.always(function() {
xblockElement = self.$('.xblock').first();
try {
xblock = XBlock.initializeBlock(xblockElement);
xblock = XBlock.initializeBlock(xblockElement.get(0));
self.xblock = xblock;
self.xblockReady(xblock);
if (successCallback) {
......
......@@ -5,6 +5,7 @@
'use strict';
function VisibilityEditorView(runtime, element) {
var $element = $(element);
this.getGroupAccess = function() {
var groupAccess = {},
checkboxValues,
......@@ -15,12 +16,12 @@
// defined by VerificationPartitionScheme on the backend!
ALLOW_GROUP_ID = 1;
if (element.find('.visibility-level-all').prop('checked')) {
if ($element.find('.visibility-level-all').prop('checked')) {
return {};
}
// Cohort partitions (user is allowed to select more than one)
element.find('.field-visibility-content-group input:checked').each(function(index, input) {
$element.find('.field-visibility-content-group input:checked').each(function(index, input) {
checkboxValues = $(input).val().split("-");
partitionId = parseInt(checkboxValues[0], 10);
groupId = parseInt(checkboxValues[1], 10);
......@@ -33,7 +34,7 @@
});
// Verification partitions (user can select exactly one)
if (element.find('#verification-access-checkbox').prop('checked')) {
if ($element.find('#verification-access-checkbox').prop('checked')) {
partitionId = parseInt($('#verification-access-dropdown').val(), 10);
groupAccess[partitionId] = [ALLOW_GROUP_ID];
}
......@@ -42,19 +43,19 @@
};
// When selecting "all students and staff", uncheck the specific groups
element.find('.field-visibility-level input').change(function(event) {
$element.find('.field-visibility-level input').change(function(event) {
if ($(event.target).hasClass('visibility-level-all')) {
element.find('.field-visibility-content-group input, .field-visibility-verification input')
$element.find('.field-visibility-content-group input, .field-visibility-verification input')
.prop('checked', false);
}
});
// When selecting a specific group, deselect "all students and staff" and
// select "specific content groups" instead.`
element.find('.field-visibility-content-group input, .field-visibility-verification input')
$element.find('.field-visibility-content-group input, .field-visibility-verification input')
.change(function() {
element.find('.visibility-level-all').prop('checked', false);
element.find('.visibility-level-specific').prop('checked', true);
$element.find('.visibility-level-all').prop('checked', false);
$element.find('.visibility-level-specific').prop('checked', true);
});
}
......
......@@ -648,21 +648,22 @@ hr.divider {
// ui - skipnav
.nav-skip {
@extend %t-action3;
display: block;
display: inline-block;
position: absolute;
left: 0px;
top: -($baseline*30);
width: 1px;
height: 1px;
overflow: hidden;
background: $white;
border-bottom: 1px solid $gray-l4;
padding: ($baseline*0.75) ($baseline/2);
&:focus, &:active {
position: static;
&:focus,
&:active {
position: relative;
top: auto;
width: auto;
height: auto;
margin: 0;
}
}
......@@ -725,4 +726,3 @@ hr.divider {
color: $gray-l1;
}
}
......@@ -25,21 +25,22 @@ nav {
// skip navigation
.nav-skip {
@include font-size(13);
display: block;
display: inline-block;
position: absolute;
left: 0px;
top: -($baseline*30);
width: 1px;
height: 1px;
overflow: hidden;
background: $white;
border-bottom: 1px solid $gray-l4;
padding: ($baseline*0.75) ($baseline/2);
&:focus, &:active {
position: static;
&:focus,
&:active {
position: relative;
top: auto;
width: auto;
height: auto;
margin: 0;
}
}
......
## coding=utf-8
<%namespace name='static' file='static_content.html'/>
<%!
from django.utils.translation import ugettext as _
from openedx.core.djangolib.markup import ugettext as _
from openedx.core.djangolib.js_utils import (
dump_js_escaped_json, js_escaped_string
)
%>
<%page expression_filter="h"/>
<!doctype html>
<!--[if lte IE 9]><html class="ie9 lte9" lang="${LANGUAGE_CODE}"><![endif]-->
<!--[if !IE]><<!--><html lang="${LANGUAGE_CODE}"><!--<![endif]-->
......@@ -16,9 +17,9 @@ from openedx.core.djangolib.js_utils import (
<%block name="title"></%block> |
% if context_course:
<% ctx_loc = context_course.location %>
${context_course.display_name_with_default_escaped | h} |
${context_course.display_name_with_default} |
% elif context_library:
${context_library.display_name_with_default_escaped | h} |
${context_library.display_name_with_default} |
% endif
${settings.STUDIO_NAME}
</title>
......@@ -62,7 +63,7 @@ from openedx.core.djangolib.js_utils import (
<%block name="page_alert"></%block>
</div>
<div id="content">
<div id="content" tabindex="-1">
<%block name="content"></%block>
</div>
......@@ -82,24 +83,24 @@ from openedx.core.djangolib.js_utils import (
<script type="text/javascript">
require(['common/js/common_libraries'], function () {
require(['js/factories/base'], function () {
require(['js/models/course'], function(Course) {
% if context_course:
window.course = new Course({
id: "${context_course.id | n, js_escaped_string}",
name: "${context_course.display_name_with_default_escaped | h}",
url_name: "${context_course.location.name | h}",
org: "${context_course.location.org | h}",
num: "${context_course.location.course | h}",
display_course_number: "${context_course.display_coursenumber | n, js_escaped_string}",
revision: "${context_course.location.revision | h}",
self_paced: ${context_course.self_paced | n, dump_js_escaped_json}
});
% endif
% if user.is_authenticated():
require(['js/sock']);
% endif
<%block name='requirejs'></%block>
});
require(['js/models/course'], function(Course) {
% if context_course:
window.course = new Course({
id: "${context_course.id | n, js_escaped_string}",
name: "${context_course.display_name_with_default | n, js_escaped_string}",
url_name: "${context_course.location.name | n, js_escaped_string}",
org: "${context_course.location.org | n, js_escaped_string}",
num: "${context_course.location.course | n, js_escaped_string}",
display_course_number: "${context_course.display_coursenumber | n, js_escaped_string}",
revision: "${context_course.location.revision | n, js_escaped_string}",
self_paced: ${context_course.self_paced | n, dump_js_escaped_json}
});
% endif
% if user.is_authenticated():
require(['js/sock']);
% endif
<%block name='requirejs'></%block>
});
});
});
</script>
......
......@@ -21,7 +21,7 @@ from django.template.defaultfilters import escapejs
</%block>
<%block name="content">
<div id="content">
<div id="content" tabindex="-1">
<div class="wrapper-mast wrapper">
<header class="mast mast-wizard has-actions">
<h1 class="page-header">
......
......@@ -140,7 +140,7 @@
<aside class="content-supplementary" role="complementary">
<div class="bit">
<h3 class="title-3">${_("What are pages?")}</h3>
<p>${_("Pages are listed horizontally at the top of your course. Default pages (Courseware, Course info, Discussion, Wiki, and Progress) are followed by textbooks and custom pages that you create.")}</p>
<p>${_("Pages are listed horizontally at the top of your course. Default pages (Home, Course, Discussion, Wiki, and Progress) are followed by textbooks and custom pages that you create.")}</p>
</div>
<div class="bit">
<h3 class="title-3">${_("Custom pages")}</h3>
......@@ -159,7 +159,7 @@
<h3 class="title">${_("Pages in Your Course")}</h3>
<figure>
<img src="${static.url("images/preview-lms-staticpages.png")}" alt="${_('Preview of Pages in your course')}" />
<figcaption class="description">${_("Pages appear in your course's top navigation bar. The default pages (Courseware, Course Info, Discussion, Wiki, and Progress) are followed by textbooks and custom pages.")}</figcaption>
<figcaption class="description">${_("Pages appear in your course's top navigation bar. The default pages (Home, Course, Discussion, Wiki, and Progress) are followed by textbooks and custom pages.")}</figcaption>
</figure>
<a href="#" rel="view" class="action action-modal-close">
......
<div class="collection-details wrapper-certificate">
<header class="collection-header">
<h3 class="sr title">
<%= name %>
<%- name %>
</h3>
</header>
<ol class="collection-info certificate-info certificate-info-<% if(showDetails){ print('block'); } else { print('inline'); } %>">
<% if (!_.isUndefined(id)) { %>
<li class="sr certificate-id">
<span class="certificate-label"><%= gettext('ID') %>: </span>
<span class="certificate-value"><%= id %></span>
<span class="certificate-label"><%- gettext('ID') %>: </span>
<span class="certificate-value"><%- id %></span>
</li>
<% } %>
<% if (showDetails) { %>
<section class="certificate-settings course-details">
<header>
<h2 class="title title-2"><%= gettext("Certificate Details") %></h2>
<h2 class="title title-2"><%- gettext("Certificate Details") %></h2>
</header>
<div class='certificate-info-section'>
<div class='course-title-section pull-left'>
<p class="actual-course-title">
<span class="certificate-label"><%= gettext('Course Title') %>: </span>
<span class="certificate-value"><%= course.get('name') %></span>
<span class="certificate-label"><%- gettext('Course Title') %>: </span>
<span class="certificate-value"><%- course.get('name') %></span>
</p>
<% if (course_title) { %>
<p class="course-title-override">
<span class="certificate-label"><b><%= gettext('Course Title Override') %>: </b></span>
<span class="certificate-value"><%= course_title %></span>
<span class="certificate-label"><b><%- gettext('Course Title Override') %>: </b></span>
<span class="certificate-value"><%- course_title %></span>
</p>
<% } %>
</div>
<div class='course-number-section pull-left'>
<p class="actual-course-number">
<span class="certificate-label"><b><%= gettext('Course Number') %>: </b> </span>
<span class="certificate-value"><%= course.get('num') %></span>
<span class="certificate-label"><b><%- gettext('Course Number') %>: </b> </span>
<span class="certificate-value"><%- course.get('num') %></span>
</p>
<% if (course.get('display_course_number')) { %>
<p class="course-number-override">
<span class="certificate-label"><b><%= gettext('Course Number Override') %>: </b></span>
<span class="certificate-value"><%= course.get('display_course_number') %></span>
<span class="certificate-label"><b><%- gettext('Course Number Override') %>: </b></span>
<span class="certificate-value"><%- course.get('display_course_number') %></span>
</p>
<% } %>
</div>
......@@ -50,9 +50,9 @@
<section class="certificate-settings signatories">
<header>
<h2 class="title title-2"><%= gettext("Certificate Signatories") %></h2>
<h2 class="title title-2"><%- gettext("Certificate Signatories") %></h2>
</header>
<p class="instructions"><%= gettext("It is strongly recommended that you include four or fewer signatories. If you include additional signatories, preview the certificate in Print View to ensure the certificate will print correctly on one page.") %></p>
<p class="instructions"><%- gettext("It is strongly recommended that you include four or fewer signatories. If you include additional signatories, preview the certificate in Print View to ensure the certificate will print correctly on one page.") %></p>
<div class="signatory-details-list"></div>
</section>
<% } %>
......@@ -61,10 +61,10 @@
<ul class="actions certificate-actions">
<% if (CMS.User.isGlobalStaff || !is_active) { %>
<li class="action action-edit">
<button class="edit"><i class="icon fa fa-pencil" aria-hidden="true"></i> <%= gettext("Edit") %></button>
<button class="edit"><i class="icon fa fa-pencil" aria-hidden="true"></i> <%- gettext("Edit") %></button>
</li>
<li class="action action-delete wrapper-delete-button" data-tooltip="<%= gettext('Delete') %>">
<button class="delete action-icon"><i class="icon fa fa-trash-o" aria-hidden="true"></i><span><%= gettext("Delete") %></span></button>
<li class="action action-delete wrapper-delete-button" data-tooltip="<%- gettext('Delete') %>">
<button class="delete action-icon"><i class="icon fa fa-trash-o" aria-hidden="true"></i><span><%- gettext("Delete") %></span></button>
</li>
<% } %>
</ul>
......
......@@ -2,52 +2,52 @@
<div aria-live="polite">
<% if (error && error.message) { %>
<div class="certificate-edit-error message message-status message-status error is-shown" name="certificate-edit-error">
<%= gettext(error.message) %>
<%- gettext(error.message) %>
</div>
<% } %>
</div>
<div class="wrapper-form">
<fieldset class="collection-fields certificate-fields">
<legend class="sr"><%= gettext("Certificate Information") %></legend>
<legend class="sr"><%- gettext("Certificate Information") %></legend>
<div class="sr input-wrap field text required add-collection-name add-certificate-name <% if(error && error.attributes && error.attributes.name) { print('error'); } %>">
<label for="certificate-name-<%= uniqueId %>"><%= gettext("Certificate Name") %></label>
<input id="certificate-name-<%= uniqueId %>" class="collection-name-input input-text" name="certificate-name" type="text" placeholder="<%= gettext("Name of the certificate") %>" value="<%= name %>" aria-describedby="certificate-name-<%=uniqueId %>-tip" />
<span id="certificate-name-<%= uniqueId %>-tip" class="tip tip-stacked"><%= gettext("Name of the certificate") %></span>
<label for="certificate-name-<%- uniqueId %>"><%- gettext("Certificate Name") %></label>
<input id="certificate-name-<%- uniqueId %>" class="collection-name-input input-text" name="certificate-name" type="text" placeholder="<%- gettext("Name of the certificate") %>" value="<%- name %>" aria-describedby="certificate-name-<%-uniqueId %>-tip" />
<span id="certificate-name-<%- uniqueId %>-tip" class="tip tip-stacked"><%- gettext("Name of the certificate") %></span>
</div>
<div class="sr input-wrap field text add-certificate-description">
<label for="certificate-description-<%= uniqueId %>"><%= gettext("Description") %></label>
<textarea id="certificate-description-<%= uniqueId %>" class="certificate-description-input text input-text" name="certificate-description" placeholder="<%= gettext("Description of the certificate") %>" aria-describedby="certificate-description-<%=uniqueId %>-tip"><%= description %></textarea>
<span id="certificate-description-<%= uniqueId %>-tip" class="tip tip-stacked"><%= gettext("Description of the certificate") %></span>
<label for="certificate-description-<%- uniqueId %>"><%- gettext("Description") %></label>
<textarea id="certificate-description-<%- uniqueId %>" class="certificate-description-input text input-text" name="certificate-description" placeholder="<%- gettext("Description of the certificate") %>" aria-describedby="certificate-description-<%-uniqueId %>-tip"><%- description %></textarea>
<span id="certificate-description-<%- uniqueId %>-tip" class="tip tip-stacked"><%- gettext("Description of the certificate") %></span>
</div>
<header>
<h2 class="title title-2"><%= gettext("Certificate Details") %></h2>
<h2 class="title title-2"><%- gettext("Certificate Details") %></h2>
</header>
<div class="actual-course-title">
<span class="actual-course-title"><%= gettext("Course Title") %>: </span>
<span class="actual-title"><%= course.get('name') %></span>
<span class="actual-course-title"><%- gettext("Course Title") %>: </span>
<span class="actual-title"><%- course.get('name') %></span>
</div>
<div class="input-wrap field text add-certificate-course-title">
<label for="certificate-course-title-<%= uniqueId %>"><%= gettext("Course Title Override") %></label>
<input id="certificate-course-title-<%= uniqueId %>" class="certificate-course-title-input input-text" name="certificate-course-title" type="text" placeholder="<%= gettext("Course title") %>" value="<%= course_title %>" aria-describedby="certificate-course-title-<%=uniqueId %>-tip" />
<span id="certificate-course-title-<%= uniqueId %>-tip" class="tip tip-stacked"><%= gettext("Specify an alternative to the official course title to display on certificates. Leave blank to use the official course title.") %></span>
<label for="certificate-course-title-<%- uniqueId %>"><%- gettext("Course Title Override") %></label>
<input id="certificate-course-title-<%- uniqueId %>" class="certificate-course-title-input input-text" name="certificate-course-title" type="text" placeholder="<%- gettext("Course title") %>" value="<%- course_title %>" aria-describedby="certificate-course-title-<%-uniqueId %>-tip" />
<span id="certificate-course-title-<%- uniqueId %>-tip" class="tip tip-stacked"><%- gettext("Specify an alternative to the official course title to display on certificates. Leave blank to use the official course title.") %></span>
</div>
</fieldset>
<header>
<h2 class="title title-2"><%= gettext("Certificate Signatories") %></h2>
<h2 class="title title-2"><%- gettext("Certificate Signatories") %></h2>
</header>
<p class="instructions"><%= gettext("It is strongly recommended that you include four or fewer signatories. If you include additional signatories, preview the certificate in Print View to ensure the certificate will print correctly on one page.") %></p>
<p class="instructions"><%- gettext("It is strongly recommended that you include four or fewer signatories. If you include additional signatories, preview the certificate in Print View to ensure the certificate will print correctly on one page.") %></p>
<div class="signatory-edit-list"> </div>
<span>
<button class="action action-add-signatory" type="button"><%= gettext("Add Additional Signatory") %></button>
<span class="tip tip-stacked"><%= gettext("(Add signatories for a certificate)") %></span>
<button class="action action-add-signatory" type="button"><%- gettext("Add Additional Signatory") %></button>
<span class="tip tip-stacked"><%- gettext("(Add signatories for a certificate)") %></span>
</span>
</div>
<div class="actions">
<button class="action action-primary" type="submit"><% if (isNew) { print(gettext("Create")) } else { print(gettext("Save")) } %></button>
<button class="action action-secondary action-cancel"><%= gettext("Cancel") %></button>
<button class="action action-secondary action-cancel"><%- gettext("Cancel") %></button>
<% if (!isNew && (CMS.User.isGlobalStaff || !is_active)) { %>
<span class="wrapper-delete-button">
<a class="button action-delete delete" href="#"><%= gettext("Delete") %></a>
<a class="button action-delete delete" href="#"><%- gettext("Delete") %></a>
</span>
<% } %>
</div>
......
<div class="input-wrap field text required field-add-chapter-name chapter<%= order %>-name
<div class="input-wrap field text required field-add-chapter-name chapter<%- order %>-name
<% if (error && error.attributes && error.attributes.name) { print('error'); } %>">
<label for="chapter<%= order %>-name"><%= gettext("Chapter Name") %></label>
<input id="chapter<%= order %>-name" name="chapter<%= order %>-name" class="chapter-name short" placeholder="<%= _.str.sprintf(gettext("Chapter %s"), order) %>" value="<%= name %>" type="text">
<span class="tip tip-stacked"><%= gettext("provide the title/name of the chapter that will be used in navigating") %></span>
<label for="chapter<%- order %>-name"><%- gettext("Chapter Name") %></label>
<input id="chapter<%- order %>-name" name="chapter<%- order %>-name" class="chapter-name short" placeholder="<%- _.str.sprintf(gettext("Chapter %s"), order) %>" value="<%- name %>" type="text">
<span class="tip tip-stacked"><%- gettext("provide the title/name of the chapter that will be used in navigating") %></span>
</div>
<div class="input-wrap field text required field-add-chapter-asset chapter<%= order %>-asset
<div class="input-wrap field text required field-add-chapter-asset chapter<%- order %>-asset
<% if (error && error.attributes && error.attributes.asset_path) { print('error'); } %>">
<label for="chapter<%= order %>-asset-path"><%= gettext("Chapter Asset") %></label>
<input id="chapter<%= order %>-asset-path" name="chapter<%= order %>-asset-path" class="chapter-asset-path" placeholder="<%= _.str.sprintf(gettext("path/to/introductionToCookieBaking-CH%d.pdf"), order) %>" value="<%= asset_path %>" type="text" dir="ltr">
<span class="tip tip-stacked"><%= gettext("upload a PDF file or provide the path to a Studio asset file") %></span>
<button class="action action-upload"><%= gettext("Upload PDF") %></button>
<label for="chapter<%- order %>-asset-path"><%- gettext("Chapter Asset") %></label>
<input id="chapter<%- order %>-asset-path" name="chapter<%- order %>-asset-path" class="chapter-asset-path" placeholder="<%- _.str.sprintf(gettext("path/to/introductionToCookieBaking-CH%d.pdf"), order) %>" value="<%- asset_path %>" type="text" dir="ltr">
<span class="tip tip-stacked"><%- gettext("upload a PDF file or provide the path to a Studio asset file") %></span>
<button class="action action-upload"><%- gettext("Upload PDF") %></button>
</div>
<a href="" class="action action-close"><i class="icon fa fa-times-circle"></i> <span class="sr"><%= gettext("delete chapter") %></span></a>
<a href="" class="action action-close"><i class="icon fa fa-times-circle"></i> <span class="sr"><%- gettext("delete chapter") %></span></a>
......@@ -2,27 +2,27 @@
<div class="wrapper-form">
<% if (error && error.message) { %>
<div id="edit_textbook_error" class="message message-status message-status error is-shown" name="edit_textbook_error">
<%= gettext(error.message) %>
<%- gettext(error.message) %>
</div>
<% } %>
<fieldset class="textbook-fields">
<legend class="sr"><%= gettext("Textbook information") %></legend>
<legend class="sr"><%- gettext("Textbook information") %></legend>
<div class="input-wrap field text required add-textbook-name <% if(error && error.attributes && error.attributes.name) { print('error'); } %>">
<label for="textbook-name-input"><%= gettext("Textbook Name") %></label>
<input id="textbook-name-input" name="textbook-name" type="text" placeholder="<%= gettext("Introduction to Cookie Baking") %>" value="<%= name %>">
<span class="tip tip-stacked"><%= gettext("provide the title/name of the text book as you would like your students to see it") %></span>
<label for="textbook-name-input"><%- gettext("Textbook Name") %></label>
<input id="textbook-name-input" name="textbook-name" type="text" placeholder="<%- gettext("Introduction to Cookie Baking") %>" value="<%- name %>">
<span class="tip tip-stacked"><%- gettext("provide the title/name of the text book as you would like your students to see it") %></span>
</div>
</fieldset>
<fieldset class="chapters-fields">
<legend class="sr"><%= gettext("Chapter information") %></legend>
<legend class="sr"><%- gettext("Chapter information") %></legend>
<ol class="chapters list-input enum"></ol>
<button class="action action-add-chapter"><i class="icon fa fa-plus"></i> <%= gettext("Add a Chapter") %></button>
<button class="action action-add-chapter"><i class="icon fa fa-plus"></i> <%- gettext("Add a Chapter") %></button>
</fieldset>
</div>
<div class="actions">
<button class="action action-primary" type="submit"><%= gettext("Save") %></button>
<button class="action action-secondary action-cancel"><%= gettext("Cancel") %></button>
<button class="action action-primary" type="submit"><%- gettext("Save") %></button>
<button class="action action-secondary action-cancel"><%- gettext("Cancel") %></button>
</div>
</form>
......@@ -19,43 +19,43 @@ if (visibilityState === 'live') {
var visibleToStaffOnly = visibilityState === 'staff_only';
%>
<div class="bit-publishing <%= visibilityClass %> <% if (releaseDate) { %>is-scheduled<% } %>">
<h3 class="bar-mod-title pub-status"><span class="sr"><%= gettext("Publishing Status") %></span>
<%= title %>
<div class="bit-publishing <%- visibilityClass %> <% if (releaseDate) { %>is-scheduled<% } %>">
<h3 class="bar-mod-title pub-status"><span class="sr"><%- gettext("Publishing Status") %></span>
<%- title %>
</h3>
<div class="wrapper-last-draft bar-mod-content">
<p class="copy meta">
<% if (hasChanges && editedOn && editedBy) {
var message = gettext("Draft saved on %(last_saved_date)s by %(edit_username)s") %>
<%= interpolate(message, {
last_saved_date: '<span class="date">' + editedOn + '</span>',
edit_username: '<span class="user">' + editedBy + '</span>' }, true) %>
<%= interpolate(_.escape(message), {
last_saved_date: '<span class="date">' + _.escape(editedOn) + '</span>',
edit_username: '<span class="user">' + _.escape(editedBy) + '</span>' }, true) %>
<% } else if (publishedOn && publishedBy) {
var message = gettext("Last published %(last_published_date)s by %(publish_username)s"); %>
<%= interpolate(message, {
last_published_date: '<span class="date">' + publishedOn + '</span>',
publish_username: '<span class="user">' + publishedBy + '</span>' }, true) %>
<%= interpolate(_.escape(message), {
last_published_date: '<span class="date">' + _.escape(publishedOn) + '</span>',
publish_username: '<span class="user">' + _.escape(publishedBy) + '</span>' }, true) %>
<% } else { %>
<%= gettext("Previously published") %>
<%- gettext("Previously published") %>
<% } %>
</p>
</div>
<% if (!course.get('self_paced')) { %>
<div class="wrapper-release bar-mod-content">
<h5 class="title"><%= releaseLabel %></h5>
<h5 class="title"><%- releaseLabel %></h5>
<p class="copy">
<% if (releaseDate) { %>
<span class="release-date"><%= releaseDate %></span>
<span class="release-date"><%- releaseDate %></span>
<span class="release-with">
<%= interpolate(
<%- interpolate(
gettext('with %(release_date_from)s'), { release_date_from: releaseDateFrom }, true
) %>
</span>
<% } else { %>
<%= gettext("Unscheduled") %>
<%- gettext("Unscheduled") %>
<% } %>
</p>
</div>
......@@ -64,40 +64,40 @@ var visibleToStaffOnly = visibilityState === 'staff_only';
<div class="wrapper-visibility bar-mod-content">
<h5 class="title">
<% if (released && published && !hasChanges) { %>
<%= gettext("Is Visible To:") %>
<%- gettext("Is Visible To:") %>
<% } else { %>
<%= gettext("Will Be Visible To:") %>
<%- gettext("Will Be Visible To:") %>
<% } %>
</h5>
<% if (visibleToStaffOnly) { %>
<p class="visbility-copy copy">
<%= gettext("Staff Only") %>
<%- gettext("Staff Only") %>
<% if (!hasExplicitStaffLock) { %>
<span class="inherited-from">
<%= interpolate(
<%- interpolate(
gettext("with %(section_or_subsection)s"),{ section_or_subsection: staffLockFrom }, true
) %>
</span>
<% } %>
</p>
<% } else { %>
<p class="visbility-copy copy"><%= gettext("Staff and Students") %></p>
<p class="visbility-copy copy"><%- gettext("Staff and Students") %></p>
<% } %>
<% if (hasContentGroupComponents) { %>
<p class="note-visibility">
<i class="icon fa fa-eye" aria-hidden="true"></i>
<span class="note-copy"><%= gettext("Some content in this unit is visible only to particular content groups") %></span>
<span class="note-copy"><%- gettext("Some content in this unit is visible only to particular content groups") %></span>
</p>
<% } %>
<ul class="actions-inline">
<li class="action-inline">
<a href="" class="action-staff-lock" role="button" aria-pressed="<%= hasExplicitStaffLock %>">
<a href="" class="action-staff-lock" role="button" aria-pressed="<%- hasExplicitStaffLock %>">
<% if (hasExplicitStaffLock) { %>
<i class="icon fa fa-check-square-o" aria-hidden="true"></i>
<% } else { %>
<i class="icon fa fa-square-o" aria-hidden="true"></i>
<% } %>
<%= gettext('Hide from students') %>
<%- gettext('Hide from students') %>
</a>
</li>
</ul>
......@@ -107,12 +107,12 @@ var visibleToStaffOnly = visibilityState === 'staff_only';
<ul class="action-list">
<li class="action-item">
<a class="action-publish action-primary <% if (published && !hasChanges) { %>is-disabled<% } %>"
href="" aria-disabled="<% if (published && !hasChanges) { %>true<% } else { %>false<% } %>" ><%= gettext("Publish") %>
href="" aria-disabled="<% if (published && !hasChanges) { %>true<% } else { %>false<% } %>" ><%- gettext("Publish") %>
</a>
</li>
<li class="action-item">
<a class="action-discard action-secondary <% if (!published || !hasChanges) { %>is-disabled<% } %>"
href="" aria-disabled="<% if (!published || !hasChanges) { %>true<% } else { %>false<% } %>"><%= gettext("Discard Changes") %>
href="" aria-disabled="<% if (!published || !hasChanges) { %>true<% } else { %>false<% } %>"><%- gettext("Discard Changes") %>
</a>
</li>
</ul>
......
<div class="view-textbook">
<div class="view-textbook">
<div class="wrap-textbook">
<header>
<h3 class="textbook-title"><%= name %></h3>
<h3 class="textbook-title"><%- name %></h3>
</header>
<% if(chapters.length > 1) {%>
<p><a href="#" class="chapter-toggle
<% if(showChapters){ print('hide'); } else { print('show'); } %>-chapters">
<i class="ui-toggle-expansion icon fa fa-caret-<% if(showChapters){ print('down'); } else { print('right'); } %>"></i>
<%= chapters.length %> PDF Chapters
<%- chapters.length %> PDF Chapters
</a></p>
<% } else if(chapters.length === 1) { %>
<p dir="ltr">
<%= chapters.at(0).get("asset_path") %>
<%- chapters.at(0).get("asset_path") %>
</p>
<% } %>
......@@ -22,8 +22,8 @@
<ol class="chapters">
<% chapters.each(function(chapter) { %>
<li class="chapter">
<span class="chapter-name"><%= chapter.get('name') %></span>
<span class="chapter-asset-path"><%= chapter.get('asset_path') %></span>
<span class="chapter-name"><%- chapter.get('name') %></span>
<span class="chapter-asset-path"><%- chapter.get('asset_path') %></span>
</li>
<% }) %>
</ol>
......@@ -34,13 +34,13 @@
<ul class="actions textbook-actions">
<li class="action action-view">
<a href="//<%= CMS.URL.LMS_BASE %>/courses/<%= course.id %>/pdfbook/<%= bookindex %>/" class="view"><%= gettext("View Live") %></a>
<a href="//<%- CMS.URL.LMS_BASE %>/courses/<%- course.id %>/pdfbook/<%- bookindex %>/" class="view"><%- gettext("View Live") %></a>
</li>
<li class="action action-edit">
<button class="edit"><%= gettext("Edit") %></button>
<button class="edit"><%- gettext("Edit") %></button>
</li>
<li class="action action-delete">
<button class="delete action-icon"><i class="icon fa fa-trash-o"></i><span><%= gettext("Delete") %></span></button>
<button class="delete action-icon"><i class="icon fa fa-trash-o"></i><span><%- gettext("Delete") %></span></button>
</li>
</ul>
......
......@@ -2,30 +2,30 @@
<% if (CMS.User.isGlobalStaff || !certificate.get('is_active')) { %>
<div class="actions certificate-actions signatory-panel-edit">
<span class="action action-edit-signatory">
<a href="javascript:void(0);" class="edit-signatory"><i class="icon fa fa-pencil" aria-hidden="true"></i> <%= gettext("Edit") %></a>
<a href="javascript:void(0);" class="edit-signatory"><i class="icon fa fa-pencil" aria-hidden="true"></i> <%- gettext("Edit") %></a>
</span>
</div>
<% } %>
<div class="signatory-panel-header"><%= gettext("Signatory") %> <%= signatory_number %>&nbsp;</div>
<div class="signatory-panel-header"><%- gettext("Signatory") %> <%- signatory_number %>&nbsp;</div>
<div class="signatory-panel-body">
<div>
<div>
<span class="signatory-name-label"><b><%= gettext("Name") %>:</b>&nbsp;</span>
<span class="signatory-name-value"><%= name %></span>
<span class="signatory-name-label"><b><%- gettext("Name") %>:</b>&nbsp;</span>
<span class="signatory-name-value"><%- name %></span>
</div>
<div>
<span class="signatory-title-label"><b><%= gettext("Title") %>:</b>&nbsp;</span>
<span class="signatory-title-value"><%= title.replace(new RegExp('\r?\n','g'), '<br />') %></span>
<span class="signatory-title-label"><b><%- gettext("Title") %>:</b>&nbsp;</span>
<span class="signatory-title-value"><%= _.escape(title).replace(new RegExp('\r?\n','g'), '<br />') %></span>
</div>
<div>
<span class="signatory-organization-label"><b><%= gettext("Organization") %>:</b>&nbsp;</span>
<span class="signatory-organization-value"><%= organization %></span>
<span class="signatory-organization-label"><b><%- gettext("Organization") %>:</b>&nbsp;</span>
<span class="signatory-organization-value"><%- organization %></span>
</div>
</div>
<div class="signatory-image">
<% if (signature_image_path != "") { %>
<div class="wrapper-signature-image">
<img class="signature-image" src="<%= signature_image_path %>" alt="<%= gettext('Signature Image') %>">
<img class="signature-image" src="<%- signature_image_path %>" alt="<%- gettext('Signature Image') %>">
</div>
<% } %>
</div>
......
......@@ -2,48 +2,48 @@
<% if (is_editing_all_collections && signatories_count > 1 && (total_saved_signatories > 1 || isNew) ) { %>
<a class="signatory-panel-delete" href="#" data-tooltip="Delete">
<i class="icon fa fa-trash-o" aria-hidden="true"></i>
<span class="sr action-button-text"><%= gettext("Delete") %></span>
<span class="sr action-button-text"><%- gettext("Delete") %></span>
</a>
<% } %>
<div class="signatory-panel-header"><%= gettext("Signatory") %> <%= signatory_number %></div>
<div class="signatory-panel-header"><%- gettext("Signatory") %> <%- signatory_number %></div>
<div class="signatory-panel-body">
<fieldset class="collection-fields signatory-fields">
<legend class="sr"><%= gettext("Certificate Signatory Configuration") %></legend>
<legend class="sr"><%- gettext("Certificate Signatory Configuration") %></legend>
<div class="input-wrap field text add-signatory-name <% if(error && error.name) { print('error'); } %>">
<label for="signatory-name-<%= signatory_number %>"><%= gettext("Name ") %></label>
<input id="signatory-name-<%= signatory_number %>" class="collection-name-input input-text signatory-name-input" name="signatory-name" type="text" placeholder="<%= gettext("Name of the signatory") %>" value="<%= name %>" aria-describedby="signatory-name-<%= signatory_number %>-tip" />
<span id="signatory-name-<%= signatory_number %>-tip" class="tip tip-stacked"><%= gettext("The name of this signatory as it should appear on certificates.") %></span>
<label for="signatory-name-<%- signatory_number %>"><%- gettext("Name ") %></label>
<input id="signatory-name-<%- signatory_number %>" class="collection-name-input input-text signatory-name-input" name="signatory-name" type="text" placeholder="<%- gettext("Name of the signatory") %>" value="<%- name %>" aria-describedby="signatory-name-<%- signatory_number %>-tip" />
<span id="signatory-name-<%- signatory_number %>-tip" class="tip tip-stacked"><%- gettext("The name of this signatory as it should appear on certificates.") %></span>
<% if(error && error.name) { %>
<span class="message-error"><%= error.name %></span>
<span class="message-error"><%- error.name %></span>
<% } %>
</div>
<div class="input-wrap field text add-signatory-title <% if(error && error.title) { print('error'); } %>">
<label for="signatory-title-<%= signatory_number %>"><%= gettext("Title ") %></label>
<textarea id="signatory-title-<%= signatory_number %>" class="collection-name-input text input-text signatory-title-input" name="signatory-title" placeholder="<%= gettext("Title of the signatory") %>" aria-describedby="signatory-title-<%= signatory_number %>-tip" ><%= title %></textarea>
<span id="signatory-title-<%= signatory_number %>-tip" class="tip tip-stacked"><%= gettext("Titles more than 100 characters may prevent students from printing their certificate on a single page.") %></span>
<label for="signatory-title-<%- signatory_number %>"><%- gettext("Title ") %></label>
<textarea id="signatory-title-<%- signatory_number %>" class="collection-name-input text input-text signatory-title-input" name="signatory-title" placeholder="<%- gettext("Title of the signatory") %>" aria-describedby="signatory-title-<%- signatory_number %>-tip" ><%- title %></textarea>
<span id="signatory-title-<%- signatory_number %>-tip" class="tip tip-stacked"><%- gettext("Titles more than 100 characters may prevent students from printing their certificate on a single page.") %></span>
<% if(error && error.title) { %>
<span class="message-error"><%= error.title %></span>
<span class="message-error"><%- error.title %></span>
<% } %>
</div>
<div class="input-wrap field text add-signatory-organization <% if(error && error.organization) { print('error'); } %>">
<label for="signatory-organization-<%= signatory_number %>"><%= gettext("Organization ") %></label>
<input id="signatory-organization-<%= signatory_number %>" class="collection-name-input input-text signatory-organization-input" name="signatory-organization" type="text" placeholder="<%= gettext("Organization of the signatory") %>" value="<%= organization %>" aria-describedby="signatory-organization-<%= signatory_number %>-tip" />
<span id="signatory-organization-<%= signatory_number %>-tip" class="tip tip-stacked"><%= gettext("The organization that this signatory belongs to, as it should appear on certificates.") %></span>
<label for="signatory-organization-<%- signatory_number %>"><%- gettext("Organization ") %></label>
<input id="signatory-organization-<%- signatory_number %>" class="collection-name-input input-text signatory-organization-input" name="signatory-organization" type="text" placeholder="<%- gettext("Organization of the signatory") %>" value="<%- organization %>" aria-describedby="signatory-organization-<%- signatory_number %>-tip" />
<span id="signatory-organization-<%- signatory_number %>-tip" class="tip tip-stacked"><%- gettext("The organization that this signatory belongs to, as it should appear on certificates.") %></span>
<% if(error && error.organization) { %>
<span class="message-error"><%= error.organization %></span>
<span class="message-error"><%- error.organization %></span>
<% } %>
</div>
<div class="input-wrap field text add-signatory-signature">
<label for="signatory-signature-<%= signatory_number %>"><%= gettext("Signature Image") %></label>
<label for="signatory-signature-<%- signatory_number %>"><%- gettext("Signature Image") %></label>
<% if (signature_image_path != "") { %>
<div class="current-signature-image"><span class="wrapper-signature-image"><img class="signature-image" src="<%= signature_image_path %>" alt="Signature Image"></span></div>
<div class="current-signature-image"><span class="wrapper-signature-image"><img class="signature-image" src="<%- signature_image_path %>" alt="Signature Image"></span></div>
<% } %>
<div class="signature-upload-wrapper">
<div class="signature-upload-input-wrapper">
<input id="signatory-signature-<%= signatory_number %>" class="collection-name-input input-text signatory-signature-input" name="signatory-signature-url" type="text" placeholder="<%= gettext("Path to Signature Image") %>" value="<%= signature_image_path %>" aria-describedby="signatory-signature-<%= signatory_number %>-tip" readonly />
<span id="signatory-signature-<%= signatory_number %>-tip" class="tip tip-stacked"><%= gettext("Image must be in PNG format") %></span>
<input id="signatory-signature-<%- signatory_number %>" class="collection-name-input input-text signatory-signature-input" name="signatory-signature-url" type="text" placeholder="<%- gettext("Path to Signature Image") %>" value="<%- signature_image_path %>" aria-describedby="signatory-signature-<%- signatory_number %>-tip" readonly />
<span id="signatory-signature-<%- signatory_number %>-tip" class="tip tip-stacked"><%- gettext("Image must be in PNG format") %></span>
</div>
<button type="button" class="action action-upload-signature"><%= gettext("Upload Signature Image") %></button>
<button type="button" class="action action-upload-signature"><%- gettext("Upload Signature Image") %></button>
</div>
</div>
</fieldset>
......
......@@ -19,6 +19,7 @@ class MakoLoader(object):
This is a Django loader object which will load the template as a
Mako template if the first line is "## mako". It is based off BaseLoader
in django.template.loader.
We need this in order to be able to include mako templates inside main_django.html.
"""
is_usable = False
......
......@@ -303,7 +303,7 @@ class BaseMicrositeTemplateBackend(object):
configuration of microsite on filesystem.
"""
def get_template_path(self, relative_path, **kwargs):
def get_template_path(self, template_path, **kwargs):
"""
Returns a path (string) to a Mako template, which can either be in
an override or will just return what is passed in which is expected to be a string
......@@ -312,7 +312,6 @@ class BaseMicrositeTemplateBackend(object):
from microsite_configuration.microsite import get_value as microsite_get_value
microsite_template_path = microsite_get_value('template_dir', None)
if not microsite_template_path:
microsite_template_path = '/'.join([
settings.MICROSITE_ROOT_DIR,
......@@ -320,6 +319,7 @@ class BaseMicrositeTemplateBackend(object):
'templates',
])
relative_path = template_path[1:] if template_path.startswith('/') else template_path
search_path = os.path.join(microsite_template_path, relative_path)
if os.path.isfile(search_path):
path = '/{0}/templates/{1}'.format(
......@@ -328,7 +328,7 @@ class BaseMicrositeTemplateBackend(object):
)
return path
else:
return relative_path
return template_path
def get_template(self, uri):
"""
......
"""
Test Microsite filebased backends.
"""
import unittest
from mock import patch
from django.test import TestCase
from django.conf import settings
from django.core.urlresolvers import reverse
from microsite_configuration.backends.base import (
BaseMicrositeBackend,
BaseMicrositeTemplateBackend,
)
from microsite_configuration import microsite
from student.tests.factories import CourseEnrollmentFactory, UserFactory
from xmodule.modulestore.tests.factories import CourseFactory
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
@patch(
......@@ -114,3 +121,40 @@ class FilebasedMicrositeBackendTests(TestCase):
# if microsite config does not exist default config should be used
microsite.set_by_domain('unknown')
self.assertEqual(microsite.get_value('university'), 'default_university')
@patch(
'microsite_configuration.microsite.TEMPLATES_BACKEND',
microsite.get_backend(
'microsite_configuration.backends.filebased.FilebasedMicrositeTemplateBackend', BaseMicrositeTemplateBackend
)
)
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
class FilebasedMicrositeTemplateBackendTests(ModuleStoreTestCase):
"""
Go through and test the FilebasedMicrositeTemplateBackend class
"""
def setUp(self):
super(FilebasedMicrositeTemplateBackendTests, self).setUp()
self.microsite_subdomain = 'testmicrosite'
self.course = CourseFactory.create()
self.user = UserFactory.create(username="Bob", email="bob@example.com", password="edx")
self.client.login(username=self.user.username, password="edx")
def test_get_template_path(self):
"""
Tests get template path works for both relative and absolute paths.
"""
microsite.set_by_domain(self.microsite_subdomain)
CourseEnrollmentFactory(
course_id=self.course.id,
user=self.user
)
response = self.client.get(
reverse('syllabus', args=[unicode(self.course.id)]),
HTTP_HOST=settings.MICROSITE_TEST_HOSTNAME,
)
self.assertContains(response, "Microsite relative path template contents")
self.assertContains(response, "Microsite absolute path template contents")
......@@ -151,6 +151,7 @@ class UserProfileAdmin(admin.ModelAdmin):
""" Admin interface for UserProfile model. """
list_display = ('user', 'name',)
raw_id_fields = ('user',)
show_full_result_count = False
search_fields = ('user__username', 'user__first_name', 'user__last_name', 'user__email', 'name',)
def get_readonly_fields(self, request, obj=None):
......
......@@ -3,19 +3,23 @@ Tests student admin.py
"""
from django.core.urlresolvers import reverse
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
from student.tests.factories import UserFactory
class AdminCourseRolesPageTest(ModuleStoreTestCase):
class AdminCourseRolesPageTest(SharedModuleStoreTestCase):
"""Test the django admin course roles form saving data in db.
"""
@classmethod
def setUpClass(cls):
super(AdminCourseRolesPageTest, cls).setUpClass()
cls.course = CourseFactory.create(org='edx')
def setUp(self):
super(AdminCourseRolesPageTest, self).setUp()
self.user = UserFactory.create(is_staff=True, is_superuser=True)
self.user.save()
self.course = CourseFactory.create(org='edx')
def test_save_valid_data(self):
......
......@@ -12,7 +12,7 @@ from mock import patch
from opaque_keys.edx.locations import SlashSeparatedCourseKey
from student.tests.factories import UserFactory, CourseEnrollmentFactory
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
from xmodule.modulestore.tests.django_utils import TEST_DATA_MIXED_TOY_MODULESTORE
from xmodule.modulestore.tests.factories import CourseFactory
......@@ -22,16 +22,18 @@ from bulk_email.models import CourseAuthorization # pylint: disable=import-erro
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
class TestStudentDashboardEmailView(ModuleStoreTestCase):
class TestStudentDashboardEmailView(SharedModuleStoreTestCase):
"""
Check for email view displayed with flag
"""
@classmethod
def setUpClass(cls):
super(TestStudentDashboardEmailView, cls).setUpClass()
cls.course = CourseFactory.create()
def setUp(self):
super(TestStudentDashboardEmailView, self).setUp()
self.course = CourseFactory.create()
# Create student account
student = UserFactory.create()
CourseEnrollmentFactory.create(user=student, course_id=self.course.id)
......@@ -84,7 +86,7 @@ class TestStudentDashboardEmailView(ModuleStoreTestCase):
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
class TestStudentDashboardEmailViewXMLBacked(ModuleStoreTestCase):
class TestStudentDashboardEmailViewXMLBacked(SharedModuleStoreTestCase):
"""
Check for email view on student dashboard, with XML backed course.
"""
......
......@@ -8,8 +8,9 @@ from django.conf import settings
from django.core.urlresolvers import reverse
from mock import patch
from django.test.utils import override_settings
from xmodule.modulestore import ModuleStoreEnum
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
from student.tests.factories import UserFactory, CourseEnrollmentFactory
from certificates.tests.factories import GeneratedCertificateFactory # pylint: disable=import-error
......@@ -30,23 +31,28 @@ def _fake_is_request_in_microsite():
@ddt.ddt
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
class CertificateDisplayTest(ModuleStoreTestCase):
class CertificateDisplayTest(SharedModuleStoreTestCase):
"""Tests display of certificates on the student dashboard. """
USERNAME = "test_user"
PASSWORD = "password"
DOWNLOAD_URL = "http://www.example.com/certificate.pdf"
@classmethod
def setUpClass(cls):
super(CertificateDisplayTest, cls).setUpClass()
cls.course = CourseFactory()
cls.course.certificates_display_behavior = "early_with_info"
with cls.store.branch_setting(ModuleStoreEnum.Branch.draft_preferred, cls.course.id):
cls.store.update_item(cls.course, cls.USERNAME)
def setUp(self):
super(CertificateDisplayTest, self).setUp()
self.user = UserFactory.create(username=self.USERNAME, password=self.PASSWORD)
result = self.client.login(username=self.USERNAME, password=self.PASSWORD)
self.assertTrue(result, msg="Could not log in")
self.course = CourseFactory()
self.course.certificates_display_behavior = "early_with_info"
self.update_course(self.course, self.user.username)
@ddt.data('verified', 'professional')
@patch.dict('django.conf.settings.FEATURES', {'CERTIFICATES_HTML_VIEW': False})
def test_display_verified_certificate(self, enrollment_mode):
......
......@@ -8,7 +8,7 @@ from mock import patch
from django.conf import settings
from django.core.urlresolvers import reverse
from course_modes.models import CourseMode
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
from util.testing import UrlResetMixin
from embargo.test_utils import restrict_course
......@@ -18,7 +18,7 @@ from student.models import CourseEnrollment
@ddt.ddt
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
class EnrollmentTest(UrlResetMixin, ModuleStoreTestCase):
class EnrollmentTest(UrlResetMixin, SharedModuleStoreTestCase):
"""
Test student enrollment, especially with different course modes.
"""
......@@ -27,11 +27,15 @@ class EnrollmentTest(UrlResetMixin, ModuleStoreTestCase):
EMAIL = "bob@example.com"
PASSWORD = "edx"
@classmethod
def setUpClass(cls):
super(EnrollmentTest, cls).setUpClass()
cls.course = CourseFactory.create()
@patch.dict(settings.FEATURES, {'EMBARGO': True})
def setUp(self):
""" Create a course and user, then log in. """
super(EnrollmentTest, self).setUp('embargo')
self.course = CourseFactory.create()
self.user = UserFactory.create(username=self.USERNAME, email=self.EMAIL, password=self.PASSWORD)
self.client.login(username=self.USERNAME, password=self.PASSWORD)
......
......@@ -10,8 +10,7 @@ from django.core.urlresolvers import reverse
from util.testing import UrlResetMixin
from xmodule.modulestore.tests.factories import CourseFactory
from third_party_auth.tests.testutil import ThirdPartyAuthTestMixin
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
# This relies on third party auth being enabled in the test
# settings with the feature flag `ENABLE_THIRD_PARTY_AUTH`
......@@ -38,14 +37,19 @@ def _finish_auth_url(params):
@ddt.ddt
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
class LoginFormTest(ThirdPartyAuthTestMixin, UrlResetMixin, ModuleStoreTestCase):
class LoginFormTest(ThirdPartyAuthTestMixin, UrlResetMixin, SharedModuleStoreTestCase):
"""Test rendering of the login form. """
@classmethod
def setUpClass(cls):
super(LoginFormTest, cls).setUpClass()
cls.course = CourseFactory.create()
@patch.dict(settings.FEATURES, {"ENABLE_COMBINED_LOGIN_REGISTRATION": False})
def setUp(self):
super(LoginFormTest, self).setUp('lms.urls')
self.url = reverse("signin_user")
self.course = CourseFactory.create()
self.course_id = unicode(self.course.id)
self.courseware_url = reverse("courseware", args=[self.course_id])
self.configure_google_provider(enabled=True)
......@@ -148,14 +152,19 @@ class LoginFormTest(ThirdPartyAuthTestMixin, UrlResetMixin, ModuleStoreTestCase)
@ddt.ddt
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
class RegisterFormTest(ThirdPartyAuthTestMixin, UrlResetMixin, ModuleStoreTestCase):
class RegisterFormTest(ThirdPartyAuthTestMixin, UrlResetMixin, SharedModuleStoreTestCase):
"""Test rendering of the registration form. """
@classmethod
def setUpClass(cls):
super(RegisterFormTest, cls).setUpClass()
cls.course = CourseFactory.create()
@patch.dict(settings.FEATURES, {"ENABLE_COMBINED_LOGIN_REGISTRATION": False})
def setUp(self):
super(RegisterFormTest, self).setUp('lms.urls')
self.url = reverse("register_user")
self.course = CourseFactory.create()
self.course_id = unicode(self.course.id)
self.configure_google_provider(enabled=True)
self.configure_facebook_provider(enabled=True)
......
......@@ -41,10 +41,14 @@ class RefundableTest(SharedModuleStoreTestCase):
Tests for dashboard utility functions
"""
@classmethod
def setUpClass(cls):
super(RefundableTest, cls).setUpClass()
cls.course = CourseFactory.create()
def setUp(self):
""" Setup components used by each refund test."""
super(RefundableTest, self).setUp()
self.course = CourseFactory.create()
self.user = UserFactory.create(username="jack", email="jack@fake.edx.org", password='test')
self.verified_mode = CourseModeFactory.create(
course_id=self.course.id,
......
......@@ -12,13 +12,13 @@ from django.conf import settings
from student.tests.factories import UserFactory, CourseEnrollmentFactory
from student.models import CourseEnrollment
from student.helpers import DISABLE_UNENROLL_CERT_STATES
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
@ddt.ddt
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
class TestStudentDashboardUnenrollments(ModuleStoreTestCase):
class TestStudentDashboardUnenrollments(SharedModuleStoreTestCase):
"""
Test to ensure that the student dashboard does not show the unenroll button for users with certificates.
"""
......@@ -27,10 +27,14 @@ class TestStudentDashboardUnenrollments(ModuleStoreTestCase):
PASSWORD = "edx"
UNENROLL_ELEMENT_ID = "#actions-item-unenroll-0"
@classmethod
def setUpClass(cls):
super(TestStudentDashboardUnenrollments, cls).setUpClass()
cls.course = CourseFactory.create()
def setUp(self):
""" Create a course and user, then log in. """
super(TestStudentDashboardUnenrollments, self).setUp()
self.course = CourseFactory.create()
self.user = UserFactory.create(username=self.USERNAME, email=self.EMAIL, password=self.PASSWORD)
CourseEnrollmentFactory(course_id=self.course.id, user=self.user)
self.cert_status = None
......
......@@ -1358,6 +1358,7 @@ def logout_user(request):
"""
# We do not log here, because we have a handler registered
# to perform logging on successful logouts.
request.is_from_logout = True
logout(request)
if settings.FEATURES.get('AUTH_USE_CAS'):
target = reverse('cas-logout')
......
......@@ -177,7 +177,10 @@ class GoogleAnalyticsProcessor(object):
context = event.get('context', {})
course_id = context.get('course_id')
copied_event = event.copy()
if course_id is not None:
event['label'] = course_id
copied_event['label'] = course_id
event['nonInteraction'] = 1
copied_event['nonInteraction'] = 1
return copied_event
......@@ -149,3 +149,70 @@ class GoogleAnalyticsProcessorTestCase(EventTrackingTestCase):
'timestamp': FROZEN_TIME,
}
assert_events_equal(expected_event, emitted_event)
@override_settings(
EVENT_TRACKING_BACKENDS={
'0': {
'ENGINE': 'eventtracking.backends.routing.RoutingBackend',
'OPTIONS': {
'backends': {
'first': {'ENGINE': 'track.tests.InMemoryBackend'}
},
'processors': [
{
'ENGINE': 'track.shim.GoogleAnalyticsProcessor'
}
]
}
},
'1': {
'ENGINE': 'eventtracking.backends.routing.RoutingBackend',
'OPTIONS': {
'backends': {
'second': {
'ENGINE': 'track.tests.InMemoryBackend'
}
}
}
}
}
)
class MultipleShimGoogleAnalyticsProcessorTestCase(EventTrackingTestCase):
"""Ensure changes don't impact other backends"""
def test_multiple_backends(self):
data = {
sentinel.key: sentinel.value,
}
context = {
'path': sentinel.path,
'user_id': sentinel.user_id,
'course_id': sentinel.course_id,
'org_id': sentinel.org_id,
'client_id': sentinel.client_id,
}
with self.tracker.context('test', context):
self.tracker.emit(sentinel.name, data)
segment_emitted_event = self.tracker.backends['0'].backends['first'].events[0]
log_emitted_event = self.tracker.backends['1'].backends['second'].events[0]
expected_event = {
'context': context,
'data': data,
'label': sentinel.course_id,
'name': sentinel.name,
'nonInteraction': 1,
'timestamp': FROZEN_TIME,
}
assert_events_equal(expected_event, segment_emitted_event)
expected_event = {
'context': context,
'data': data,
'name': sentinel.name,
'timestamp': FROZEN_TIME,
}
assert_events_equal(expected_event, log_emitted_event)
......@@ -7,8 +7,8 @@ from microsite_configuration import microsite
from opaque_keys import InvalidKeyError
from opaque_keys.edx.locations import SlashSeparatedCourseKey
COURSE_REGEX = re.compile(r'^.*?/courses/{}'.format(settings.COURSE_ID_PATTERN))
# accommodates course api urls, excluding any course api routes that do not fall under v*/courses, such as v1/blocks.
COURSE_REGEX = re.compile(r'^(.*?/courses/)(?!v[0-9]+/[^/]+){}'.format(settings.COURSE_ID_PATTERN))
def safe_get_host(request):
......
......@@ -5,7 +5,6 @@ import unittest
from django.conf import settings
from django.core.exceptions import SuspiciousOperation
from django.test.client import RequestFactory
from util.request import course_id_from_url, safe_get_host
......@@ -50,8 +49,24 @@ class ResponseTestCase(unittest.TestCase):
self.assertIsNone(course_id_from_url('/login'))
self.assertIsNone(course_id_from_url('/course/edX/maths/2020'))
self.assertIsNone(course_id_from_url('/courses/edX/maths/'))
self.assertIsNone(course_id_from_url('/api/courses/v1/blocks/edX/maths/2020'))
self.assertIsNone(course_id_from_url('/api/courses/v1/blocks/course-v1:incidental+courseid+formatting'))
self.assertIsNone(course_id_from_url('/api/courses/v41/notcourses/course-v1:incidental+courseid+formatting'))
course_id = course_id_from_url('/courses/course-v1:edX+maths+2020')
self.assertCourseIdFieldsMatch(course_id=course_id, org="edX", course='maths', run='2020')
course_id = course_id_from_url('/courses/edX/maths/2020')
self.assertEqual(course_id.org, 'edX')
self.assertEqual(course_id.course, 'maths')
self.assertEqual(course_id.run, '2020')
self.assertCourseIdFieldsMatch(course_id=course_id, org='edX', course='maths', run='2020')
course_id = course_id_from_url('/api/courses/v1/courses/course-v1:edX+maths+2020')
self.assertCourseIdFieldsMatch(course_id=course_id, org='edX', course='maths', run='2020')
course_id = course_id_from_url('/api/courses/v1/courses/edX/maths/2020')
self.assertCourseIdFieldsMatch(course_id=course_id, org='edX', course='maths', run='2020')
def assertCourseIdFieldsMatch(self, course_id, org, course, run):
""" Asserts that the passed-in course id matches the specified fields"""
self.assertEqual(course_id.org, org)
self.assertEqual(course_id.course, course)
self.assertEqual(course_id.run, run)
......@@ -26,7 +26,7 @@ class @HTMLEditingDescriptor
CUSTOM_FONTS + STANDARD_FONTS
constructor: (element) ->
@element = element
@element = $(element)
@base_asset_url = @element.find("#editor-tab").data('base-asset-url')
@editor_choice = @element.find("#editor-tab").data('editor')
if @base_asset_url == undefined
......
......@@ -13,7 +13,7 @@ class @MarkdownEditingDescriptor extends XModule.Descriptor
@explanationTemplate: "[explanation]\n#{gettext 'Short explanation'}\n[explanation]\n"
constructor: (element) ->
@element = element
@element = $(element)
if $(".markdown-box", @element).length != 0
@markdown_editor = CodeMirror.fromTextArea($(".markdown-box", element)[0], {
......
......@@ -2,7 +2,7 @@ class @TabsEditingDescriptor
@isInactiveClass : "is-inactive"
constructor: (element) ->
@element = element;
@element = $(element)
###
Not tested on syncing of multiple editors of same type in tabs
(Like many CodeMirrors).
......
../../../../node_modules/underscore/underscore-min.js
\ No newline at end of file
......@@ -69,7 +69,7 @@
} else {
block = {};
}
block.element = element;
block.element = $element;
block.name = $element.data('name');
block.type = $element.data('block-type');
$element.trigger('xblock-initialized');
......
"""Pages relevant for OAuth2 confirmation."""
from common.test.acceptance.pages.lms import BASE_URL
from bok_choy.page_object import PageObject
class OAuth2Confirmation(PageObject):
"""Page for OAuth2 confirmation view."""
def __init__(self, browser, client_id="test-id", scopes=("email",)):
super(OAuth2Confirmation, self).__init__(browser)
self.client_id = client_id
self.scopes = scopes
@property
def url(self):
return "{}/oauth2/authorize?client_id={}&response_type=code&scope={}".format(
BASE_URL, self.client_id, ' '.join(self.scopes))
def is_browser_on_page(self):
return self.q(css="body.oauth2").visible
def cancel(self):
"""
Cancel the request.
This redirects to an invalid URI, because we don't want actual network
connections being made.
"""
self.q(css="input[name=cancel]").click()
def confirm(self):
"""
Confirm OAuth access
This redirects to an invalid URI, because we don't want actual network
connections being made.
"""
self.q(css="input[name=authorize]").click()
@property
def has_error(self):
"""Boolean for if the page has an error or not."""
return self.q(css=".error").present
@property
def error_message(self):
"""Text of the page's error message."""
return self.q(css='.error').text[0]
......@@ -462,7 +462,6 @@ class AccountSettingsA11yTest(AccountSettingsTestMixin, WebAppTest):
self.account_settings_page.a11y_audit.config.set_rules({
'ignore': [
'link-href', # TODO: AC-233, AC-238
'skip-link', # TODO: AC-179
],
})
self.account_settings_page.a11y_audit.check_for_accessibility_errors()
......@@ -795,7 +795,6 @@ class LearnerProfileA11yTest(LearnerProfileTestMixin, WebAppTest):
profile_page.a11y_audit.config.set_rules({
"ignore": [
'skip-link', # TODO: AC-179
'link-href', # TODO: AC-231
],
})
......
......@@ -235,8 +235,7 @@ class LmsDashboardA11yTest(BaseLmsDashboardTest):
self.dashboard_page.a11y_audit.config.set_rules({
"ignore": [
'skip-link', # TODO: AC-179
'link-href', # TODO: AC-238, AC-179
'link-href', # TODO: AC-238
],
})
......
# -*- coding: utf-8 -*-
"""Tests for OAuth2 permission delegation."""
from common.test.acceptance.pages.lms.oauth2_confirmation import OAuth2Confirmation
from common.test.acceptance.pages.lms.auto_auth import AutoAuthPage
from bok_choy.web_app_test import WebAppTest
from urlparse import urlparse, parse_qsl
class OAuth2PermissionDelegationTests(WebAppTest):
"""
Tests for acceptance/denial of permission delegation requests.
"""
def setUp(self):
super(OAuth2PermissionDelegationTests, self).setUp()
self.oauth_page = OAuth2Confirmation(self.browser)
def _auth(self):
"""Authenticate the user."""
AutoAuthPage(self.browser).visit()
def _qs(self, url):
"""Parse url's querystring into a dict."""
return dict(parse_qsl(urlparse(url).query))
def test_error_for_invalid_scopes(self):
"""Requests for invalid scopes throw errors."""
self._auth()
self.oauth_page.scopes = ('email', 'does-not-exist')
assert self.oauth_page.visit()
self.assertTrue(self.oauth_page.has_error)
self.assertIn('not a valid scope', self.oauth_page.error_message)
def test_cancelling_redirects(self):
"""
If you cancel the request, you're redirected to the redirect_url with a
denied query param.
"""
self._auth()
assert self.oauth_page.visit()
self.oauth_page.cancel()
# This redirects to an invalid URI.
query = self._qs(self.browser.current_url)
self.assertEqual('access_denied', query['error'])
def test_accepting_redirects(self):
"""
If you accept the request, you're redirected to the redirect_url with
the correct query params.
"""
self._auth()
assert self.oauth_page.visit()
self.oauth_page.confirm()
# This redirects to an invalid URI.
query = self._qs(self.browser.current_url)
self.assertIn('code', query)
......@@ -7,23 +7,22 @@ import time
from dateutil.parser import parse
import ddt
from flaky import flaky
from nose.plugins.attrib import attr
from selenium.common.exceptions import TimeoutException
from uuid import uuid4
from ..helpers import get_modal_alert, EventsTestMixin, UniqueCourseTest
from ...fixtures import LMS_BASE_URL
from ...fixtures.course import CourseFixture
from ...fixtures.discussion import (
from common.test.acceptance.tests.helpers import get_modal_alert, EventsTestMixin, UniqueCourseTest
from common.test.acceptance.fixtures import LMS_BASE_URL
from common.test.acceptance.fixtures.course import CourseFixture
from common.test.acceptance.fixtures.discussion import (
Thread,
MultipleThreadFixture
)
from ...pages.lms.auto_auth import AutoAuthPage
from ...pages.lms.course_info import CourseInfoPage
from ...pages.lms.learner_profile import LearnerProfilePage
from ...pages.lms.tab_nav import TabNavPage
from ...pages.lms.teams import (
from common.test.acceptance.pages.lms.auto_auth import AutoAuthPage
from common.test.acceptance.pages.lms.course_info import CourseInfoPage
from common.test.acceptance.pages.lms.learner_profile import LearnerProfilePage
from common.test.acceptance.pages.lms.tab_nav import TabNavPage
from common.test.acceptance.pages.lms.teams import (
TeamsPage,
MyTeamsPage,
BrowseTopicsPage,
......@@ -32,7 +31,7 @@ from ...pages.lms.teams import (
EditMembershipPage,
TeamPage
)
from ...pages.common.utils import confirm_prompt
from common.test.acceptance.pages.common.utils import confirm_prompt
TOPICS_PER_PAGE = 12
......
......@@ -659,7 +659,6 @@ class StudioLibraryA11yTest(StudioLibraryTest):
'color-contrast', # TODO: AC-225
'link-href', # TODO: AC-226
'nav-aria-label', # TODO: AC-227
'skip-link', # TODO: AC-228
'icon-aria-hidden', # TODO: AC-229
],
})
......
[
{
"fields": {
"client_id": "test-id",
"client_secret": "test-secret",
"client_type": 0,
"name": "Test OAuth2 Client",
"redirect_uri": "http://does-not-exist/",
"url": "http://does-not-exist/",
"user": null
},
"model": "oauth2.client",
"pk": 3
}
]
## mako
<%namespace name='static' file='/static_content.html'/>
<%include file="${static.get_template_path('courseware/test_relative_path.html')}" />
<%include file="${static.get_template_path('/courseware/test_absolute_path.html')}" />
## mako
<%namespace name='static' file='/static_content.html'/>
<%!
from django.utils.translation import ugettext as _
from django.core.urlresolvers import reverse
%>
<%page args="tab_list, active_page, default_tab, tab_image" />
<%
def url_class(is_active):
if is_active:
return "active"
return ""
%>
% for tab in tab_list:
<%
tab_is_active = tab.tab_id in (active_page, default_tab)
tab_class = url_class(tab_is_active)
%>
<li>
<a href="${tab.link_func(course, reverse) | h}" class="${tab_class}">
Test Microsite Tab: ${_(tab.name) | h}
% if tab_is_active:
<span class="sr">, current location</span>
%endif
% if tab_image:
## Translators: 'needs attention' is an alternative string for the
## notification image that indicates the tab "needs attention".
<img src="${tab_image}" alt="${_('needs attention')}" />
%endif
</a>
</li>
% endfor
## mako
<%namespace name='static' file='/static_content.html'/>
<div>Microsite absolute path template contents</div>
\ No newline at end of file
## mako
<%namespace name='static' file='/static_content.html'/>
<div>Microsite relative path template contents</div>
\ No newline at end of file
......@@ -37,8 +37,8 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.1a\n"
"Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n"
"POT-Creation-Date: 2016-02-11 12:22+0000\n"
"PO-Revision-Date: 2016-02-11 12:22:01.484359\n"
"POT-Creation-Date: 2016-02-17 20:08+0000\n"
"PO-Revision-Date: 2016-02-17 20:08:29.277697\n"
"Last-Translator: \n"
"Language-Team: openedx-translation <openedx-translation@googlegroups.com>\n"
"MIME-Version: 1.0\n"
......@@ -5628,6 +5628,18 @@ msgstr ""
msgid "Course {course_id} does not exist."
msgstr "Çöürsé {course_id} döés nöt éxïst. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕ#"
#: lms/djangoapps/commerce/models.py
msgid "Use the checkout page hosted by the E-Commerce service."
msgstr ""
"Ûsé thé çhéçköüt pägé höstéd ßý thé É-Çömmérçé sérvïçé. Ⱡ'σяєм ιρѕυм ∂σłσя "
"ѕιт αмєт, ¢σηѕє¢тєтυя α#"
#: lms/djangoapps/commerce/models.py
msgid "Path to single course checkout page hosted by the E-Commerce service."
msgstr ""
"Päth tö sïnglé çöürsé çhéçköüt pägé höstéd ßý thé É-Çömmérçé sérvïçé. Ⱡ'σяєм"
" ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє¢тєтυя #"
#: lms/djangoapps/commerce/signals.py
msgid ""
"A refund request has been initiated for {username} ({email}). To process "
......@@ -10540,6 +10552,18 @@ msgstr ""
"Énäßlé Prögräm Çértïfïçäté Générätïön Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, "
"¢σηѕє¢тєтυ#"
#: openedx/core/djangoapps/programs/models.py
msgid "Maximum Certification Retries"
msgstr "Mäxïmüm Çértïfïçätïön Rétrïés Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє¢#"
#: openedx/core/djangoapps/programs/models.py
msgid ""
"When making requests to award certificates, make at most this many attempts "
"to retry a failing request."
msgstr ""
"Whén mäkïng réqüésts tö äwärd çértïfïçätés, mäké ät möst thïs mäný ättémpts "
"tö rétrý ä fäïlïng réqüést. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αм#"
#: openedx/core/djangoapps/self_paced/models.py
msgid "Enable course home page improvements."
msgstr ""
......@@ -20399,17 +20423,17 @@ msgstr "Whät äré pägés? Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт α#"
#: cms/templates/edit-tabs.html
msgid ""
"Pages are listed horizontally at the top of your course. Default pages "
"(Courseware, Course info, Discussion, Wiki, and Progress) are followed by "
"textbooks and custom pages that you create."
"(Home, Course, Discussion, Wiki, and Progress) are followed by textbooks and"
" custom pages that you create."
msgstr ""
"Pägés äré lïstéd hörïzöntällý ät thé töp öf ýöür çöürsé. Défäült pägés "
"(Çöürséwäré, Çöürsé ïnfö, Dïsçüssïön, Wïkï, änd Prögréss) äré föllöwéd ßý "
"téxtßööks änd çüstöm pägés thät ýöü çréäté. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, "
"¢σηѕє¢тєтυя α∂ιριѕι¢ιηg єłιт, ѕє∂ ∂σ єιυѕмσ∂ тємρσя ιη¢ι∂ι∂υηт υт łαвσяє єт "
"∂σłσяє мαgηα αłιqυα. υт єηιм α∂ мιηιм νєηιαм, qυιѕ ησѕтяυ∂ єχєя¢ιтαтιση "
"υłłαм¢σ łαвσяιѕ ηιѕι υт αłιqυιρ єχ єα ¢σммσ∂σ ¢σηѕєqυαт. ∂υιѕ αυтє ιяυяє "
"∂σłσя ιη яєρяєнєη∂єяιт ιη νσłυρтαтє νєłιт єѕѕє ¢ιłłυм ∂σłσяє єυ ƒυgιαт ηυłłα"
" ραяιαтυя. єχ¢єρтєυя ѕιηт σ¢¢αє¢αт ¢υρι∂αтαт ηση ρяσ#"
"(Hömé, Çöürsé, Dïsçüssïön, Wïkï, änd Prögréss) äré föllöwéd ßý téxtßööks änd"
" çüstöm pägés thät ýöü çréäté. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє¢тєтυя "
"α∂ιριѕι¢ιηg єłιт, ѕє∂ ∂σ єιυѕмσ∂ тємρσя ιη¢ι∂ι∂υηт υт łαвσяє єт ∂σłσяє мαgηα"
" αłιqυα. υт єηιм α∂ мιηιм νєηιαм, qυιѕ ησѕтяυ∂ єχєя¢ιтαтιση υłłαм¢σ łαвσяιѕ "
"ηιѕι υт αłιqυιρ єχ єα ¢σммσ∂σ ¢σηѕєqυαт. ∂υιѕ αυтє ιяυяє ∂σłσя ιη "
"яєρяєнєη∂єяιт ιη νσłυρтαтє νєłιт єѕѕє ¢ιłłυм ∂σłσяє єυ ƒυgιαт ηυłłα "
"ραяιαтυя. єχ¢єρтєυя ѕιηт σ¢¢αє¢αт ¢υρι∂αтαт ηση ρяσι∂єηт, ѕυηт ιη ¢#"
#: cms/templates/edit-tabs.html
msgid "Custom pages"
......@@ -20458,19 +20482,18 @@ msgstr "Prévïéw öf Pägés ïn ýöür çöürsé Ⱡ'σяєм ιρѕυм
#: cms/templates/edit-tabs.html
msgid ""
"Pages appear in your course's top navigation bar. The default pages "
"(Courseware, Course Info, Discussion, Wiki, and Progress) are followed by "
"textbooks and custom pages."
"Pages appear in your course's top navigation bar. The default pages (Home, "
"Course, Discussion, Wiki, and Progress) are followed by textbooks and custom"
" pages."
msgstr ""
"Pägés äppéär ïn ýöür çöürsé's töp nävïgätïön ßär. Thé défäült pägés "
"(Çöürséwäré, Çöürsé Ìnfö, Dïsçüssïön, Wïkï, änd Prögréss) äré föllöwéd ßý "
"téxtßööks änd çüstöm pägés. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє¢тєтυя "
"α∂ιριѕι¢ιηg єłιт, ѕє∂ ∂σ єιυѕмσ∂ тємρσя ιη¢ι∂ι∂υηт υт łαвσяє єт ∂σłσяє мαgηα"
" αłιqυα. υт єηιм α∂ мιηιм νєηιαм, qυιѕ ησѕтяυ∂ єχєя¢ιтαтιση υłłαм¢σ łαвσяιѕ "
"ηιѕι υт αłιqυιρ єχ єα ¢σммσ∂σ ¢σηѕєqυαт. ∂υιѕ αυтє ιяυяє ∂σłσя ιη "
"яєρяєнєη∂єяιт ιη νσłυρтαтє νєłιт єѕѕє ¢ιłłυм ∂σłσяє єυ ƒυgιαт ηυłłα "
"ραяιαтυя. єχ¢єρтєυя ѕιηт σ¢¢αє¢αт ¢υρι∂αтαт ηση ρяσι∂єηт, ѕυηт ιη ¢υłρα qυι "
"σƒ#"
"Pägés äppéär ïn ýöür çöürsé's töp nävïgätïön ßär. Thé défäült pägés (Hömé, "
"Çöürsé, Dïsçüssïön, Wïkï, änd Prögréss) äré föllöwéd ßý téxtßööks änd çüstöm"
" pägés. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє¢тєтυя α∂ιριѕι¢ιηg єłιт, ѕє∂ ∂σ "
"єιυѕмσ∂ тємρσя ιη¢ι∂ι∂υηт υт łαвσяє єт ∂σłσяє мαgηα αłιqυα. υт єηιм α∂ мιηιм"
" νєηιαм, qυιѕ ησѕтяυ∂ єχєя¢ιтαтιση υłłαм¢σ łαвσяιѕ ηιѕι υт αłιqυιρ єχ єα "
"¢σммσ∂σ ¢σηѕєqυαт. ∂υιѕ αυтє ιяυяє ∂σłσя ιη яєρяєнєη∂єяιт ιη νσłυρтαтє νєłιт"
" єѕѕє ¢ιłłυм ∂σłσяє єυ ƒυgιαт ηυłłα ραяιαтυя. єχ¢єρтєυя ѕιηт σ¢¢αє¢αт "
"¢υρι∂αтαт ηση ρяσι∂єηт, ѕυηт ιη ¢υłρα qυι σƒƒι¢ια ∂єѕєяυηт#"
#: cms/templates/edit-tabs.html cms/templates/howitworks.html
msgid "close modal"
......@@ -26,8 +26,8 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.1a\n"
"Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n"
"POT-Creation-Date: 2016-02-11 12:21+0000\n"
"PO-Revision-Date: 2016-02-11 12:22:01.889310\n"
"POT-Creation-Date: 2016-02-17 20:07+0000\n"
"PO-Revision-Date: 2016-02-17 20:08:29.637881\n"
"Last-Translator: \n"
"Language-Team: openedx-translation <openedx-translation@googlegroups.com>\n"
"MIME-Version: 1.0\n"
......@@ -9344,7 +9344,6 @@ msgstr ""
"∂σłσя ѕιт αмєт, ¢σηѕ#"
#: cms/templates/js/publish-history.underscore
#: cms/templates/js/publish-xblock.underscore
#: cms/templates/js/staff-lock-editor.underscore
msgid "message"
msgstr "méssägé Ⱡ'σяєм ιρѕυм #"
......
......@@ -86,7 +86,7 @@
# Assyass Mahmoud <mahmoud.assyass@edu.uiz.ac.ma>, 2015
# bekairi tahar <tahar48omar@gmail.com>, 2014
# natg94 <bgenson@voo.be>, 2015
# Eric Fortin, 2014
# Eric Fortin, 2014,2016
# Françoise Docq, 2014
# Gérard Vidal <Gerard.Vidal@ens-lyon.fr>, 2014-2015
# Hélène Tindon <helene.tindon@gmail.com>, 2015
......
......@@ -22,6 +22,7 @@
# Edgar Aparecido Pereira de Melo <edgarapmelo@gmail.com>, 2014
# Emili Costa <m.lee.costa@gmail.com>, 2015
# Erik Henrique <contato@erikhenrique.com.br>, 2015
# Fabio Eis <fabio@fabioeis.com>, 2016
# Francisco Cantarutti <francisco.cantarutti@sisqualis.com.br>, 2014
# Gislene Kucker Arantes <gislene.trad@gmail.com>, 2015
# Gustavo Henrique de Almeida Gonçalves <gustavo@faprender.org>, 2015
......@@ -78,6 +79,7 @@
# Diego Rabatone Oliveira <diraol@diraol.eng.br>, 2015
# Edgar Aparecido Pereira de Melo <edgarapmelo@gmail.com>, 2014
# aivuk <e@vaz.io>, 2014
# Fabio Eis <fabio@fabioeis.com>, 2016
# Fernando Nunes <fernubr@gmail.com>, 2015
# Francisco Cantarutti <francisco.cantarutti@sisqualis.com.br>, 2014
# Guilherme Batista Ferreira <guilherme@uft.edu.br>, 2015
......@@ -115,6 +117,7 @@
# Bruno Sette <brunosette@gmail.com>, 2015
# Cleomir Waiczyk <w.cleomir@gmail.com>, 2015
# Edgar Aparecido Pereira de Melo <edgarapmelo@gmail.com>, 2014
# Fabio Eis <fabio@fabioeis.com>, 2016
# Felipe Lube de Bragança <inactive+felubra@transifex.com>, 2015
# Fernando Nunes <fernubr@gmail.com>, 2015
# Francisco Cantarutti <francisco.cantarutti@sisqualis.com.br>, 2014
......@@ -147,6 +150,7 @@
# Cleomir Waiczyk <w.cleomir@gmail.com>, 2015
# Edgar Aparecido Pereira de Melo <edgarapmelo@gmail.com>, 2014
# aivuk <e@vaz.io>, 2014
# Fabio Eis <fabio@fabioeis.com>, 2016
# Fernando Nunes <fernubr@gmail.com>, 2015
# Jefferson Floyd Conz, 2014
# Leonardo Lehnemann Agostinho Martins <lehneman@gmail.com>, 2014
......@@ -167,8 +171,8 @@ msgstr ""
"Project-Id-Version: edx-platform\n"
"Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n"
"POT-Creation-Date: 2016-02-11 11:53+0000\n"
"PO-Revision-Date: 2016-02-04 20:03+0000\n"
"Last-Translator: Ned Batchelder <ned@edx.org>\n"
"PO-Revision-Date: 2016-02-13 17:13+0000\n"
"Last-Translator: Fabio Eis <fabio@fabioeis.com>\n"
"Language-Team: Portuguese (Brazil) (http://www.transifex.com/open-edx/edx-platform/language/pt_BR/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
......
......@@ -37,8 +37,8 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.1a\n"
"Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n"
"POT-Creation-Date: 2016-02-11 12:22+0000\n"
"PO-Revision-Date: 2016-02-11 12:22:01.484359\n"
"POT-Creation-Date: 2016-02-17 20:08+0000\n"
"PO-Revision-Date: 2016-02-17 20:08:29.277697\n"
"Last-Translator: \n"
"Language-Team: openedx-translation <openedx-translation@googlegroups.com>\n"
"MIME-Version: 1.0\n"
......@@ -4826,6 +4826,14 @@ msgstr "{course_id} هس رخف ش دشمهي ذخعقسث نثغ."
msgid "Course {course_id} does not exist."
msgstr "ذخعقسث {course_id} يخثس رخف ثطهسف."
#: lms/djangoapps/commerce/models.py
msgid "Use the checkout page hosted by the E-Commerce service."
msgstr "عسث فاث ذاثذنخعف حشلث اخسفثي زغ فاث ث-ذخووثقذث سثقدهذث."
#: lms/djangoapps/commerce/models.py
msgid "Path to single course checkout page hosted by the E-Commerce service."
msgstr "حشفا فخ سهرلمث ذخعقسث ذاثذنخعف حشلث اخسفثي زغ فاث ث-ذخووثقذث سثقدهذث."
#: lms/djangoapps/commerce/signals.py
msgid ""
"A refund request has been initiated for {username} ({email}). To process "
......@@ -9123,6 +9131,18 @@ msgstr "ثرشزمث سفعيهخ شعفاخقهرل هرفثقبشذث"
msgid "Enable Program Certificate Generation"
msgstr "ثرشزمث حقخلقشو ذثقفهبهذشفث لثرثقشفهخر"
#: openedx/core/djangoapps/programs/models.py
msgid "Maximum Certification Retries"
msgstr "وشطهوعو ذثقفهبهذشفهخر قثفقهثس"
#: openedx/core/djangoapps/programs/models.py
msgid ""
"When making requests to award certificates, make at most this many attempts "
"to retry a failing request."
msgstr ""
"صاثر وشنهرل قثضعثسفس فخ شصشقي ذثقفهبهذشفثس, وشنث شف وخسف فاهس وشرغ شففثوحفس "
"فخ قثفقغ ش بشهمهرل قثضعثسف."
#: openedx/core/djangoapps/self_paced/models.py
msgid "Enable course home page improvements."
msgstr "ثرشزمث ذخعقسث اخوث حشلث هوحقخدثوثرفس."
......@@ -17733,12 +17753,12 @@ msgstr "صاشف شقث حشلثس?"
#: cms/templates/edit-tabs.html
msgid ""
"Pages are listed horizontally at the top of your course. Default pages "
"(Courseware, Course info, Discussion, Wiki, and Progress) are followed by "
"textbooks and custom pages that you create."
"(Home, Course, Discussion, Wiki, and Progress) are followed by textbooks and"
" custom pages that you create."
msgstr ""
"حشلثس شقث مهسفثي اخقهظخرفشممغ شف فاث فخح خب غخعق ذخعقسث. يثبشعمف حشلثس "
"(ذخعقسثصشقث, ذخعقسث هربخ, يهسذعسسهخر, صهنه, شري حقخلقثسس) شقث بخممخصثي زغ "
"فثطفزخخنس شري ذعسفخو حشلثس فاشف غخع ذقثشفث."
"(اخوث, ذخعقسث, يهسذعسسهخر, صهنه, شري حقخلقثسس) شقث بخممخصثي زغ فثطفزخخنس شري"
" ذعسفخو حشلثس فاشف غخع ذقثشفث."
#: cms/templates/edit-tabs.html
msgid "Custom pages"
......@@ -17780,13 +17800,13 @@ msgstr "حقثدهثص خب حشلثس هر غخعق ذخعقسث"
#: cms/templates/edit-tabs.html
msgid ""
"Pages appear in your course's top navigation bar. The default pages "
"(Courseware, Course Info, Discussion, Wiki, and Progress) are followed by "
"textbooks and custom pages."
"Pages appear in your course's top navigation bar. The default pages (Home, "
"Course, Discussion, Wiki, and Progress) are followed by textbooks and custom"
" pages."
msgstr ""
"حشلثس شححثشق هر غخعق ذخعقسث'س فخح رشدهلشفهخر زشق. فاث يثبشعمف حشلثس "
"(ذخعقسثصشقث, ذخعقسث هربخ, يهسذعسسهخر, صهنه, شري حقخلقثسس) شقث بخممخصثي زغ "
"فثطفزخخنس شري ذعسفخو حشلثس."
"حشلثس شححثشق هر غخعق ذخعقسث'س فخح رشدهلشفهخر زشق. فاث يثبشعمف حشلثس (اخوث, "
"ذخعقسث, يهسذعسسهخر, صهنه, شري حقخلقثسس) شقث بخممخصثي زغ فثطفزخخنس شري ذعسفخو"
" حشلثس."
#: cms/templates/edit-tabs.html cms/templates/howitworks.html
msgid "close modal"
......
......@@ -26,8 +26,8 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.1a\n"
"Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n"
"POT-Creation-Date: 2016-02-11 12:21+0000\n"
"PO-Revision-Date: 2016-02-11 12:22:01.889310\n"
"POT-Creation-Date: 2016-02-17 20:07+0000\n"
"PO-Revision-Date: 2016-02-17 20:08:29.637881\n"
"Last-Translator: \n"
"Language-Team: openedx-translation <openedx-translation@googlegroups.com>\n"
"MIME-Version: 1.0\n"
......@@ -8392,7 +8392,6 @@ msgid "Last published %(last_published_date)s by %(publish_username)s"
msgstr "مشسف حعزمهساثي %(last_published_date)s زغ %(publish_username)s"
#: cms/templates/js/publish-history.underscore
#: cms/templates/js/publish-xblock.underscore
#: cms/templates/js/staff-lock-editor.underscore
msgid "message"
msgstr "وثسسشلث"
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -270,6 +270,14 @@ will drop you into pdb on error. This lets you go up and down the stack
and see what the values of the variables are. Check out `the pdb
documentation <http://docs.python.org/library/pdb.html>`__
Use this command to put a temporary debugging breakpoint in a test.
If you check this in, your tests will hang on jenkins.
::
from nose.tools import set_trace; set_trace()
Note: More on the ``--failed`` functionality
* In order to use this, you must run the tests first. If you haven't already
......@@ -407,7 +415,8 @@ test case method.
During acceptance test execution, log files and also screenshots of
failed tests are captured in test\_root/log.
Use this command to put a debugging breakpoint in a test.
Use this command to put a temporary debugging breakpoint in a test.
If you check this in, your tests will hang on jenkins.
::
......
......@@ -54,6 +54,7 @@ class GeneratedCertificateAdmin(admin.ModelAdmin):
Django admin customizations for GeneratedCertificate model
"""
raw_id_fields = ('user',)
show_full_result_count = False
search_fields = ('course_id', 'user__username')
list_display = ('id', 'course_id', 'mode', 'user')
......
......@@ -289,8 +289,8 @@ def get_course_info_section(request, user, course, section_key):
except Exception: # pylint: disable=broad-except
html = render_to_string('courseware/error-message.html', None)
log.exception(
u"Error rendering course=%s, section_key=%s",
course, section_key
u"Error rendering course_id=%s, section_key=%s",
unicode(course.id), section_key
)
return html
......
......@@ -41,29 +41,34 @@ SHIB_ERROR_STR = "The currently logged-in user account does not have permission
@attr('shard_1')
class AboutTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase, EventTrackingTestCase, MilestonesTestCaseMixin):
class AboutTestCase(LoginEnrollmentTestCase, SharedModuleStoreTestCase, EventTrackingTestCase, MilestonesTestCaseMixin):
"""
Tests about xblock.
"""
def setUp(self):
super(AboutTestCase, self).setUp()
self.course = CourseFactory.create()
self.about = ItemFactory.create(
category="about", parent_location=self.course.location,
@classmethod
def setUpClass(cls):
super(AboutTestCase, cls).setUpClass()
cls.course = CourseFactory.create()
cls.course_without_about = CourseFactory.create(catalog_visibility=CATALOG_VISIBILITY_NONE)
cls.course_with_about = CourseFactory.create(catalog_visibility=CATALOG_VISIBILITY_ABOUT)
cls.purchase_course = CourseFactory.create(org='MITx', number='buyme', display_name='Course To Buy')
cls.about = ItemFactory.create(
category="about", parent_location=cls.course.location,
data="OOGIE BLOOGIE", display_name="overview"
)
self.course_without_about = CourseFactory.create(catalog_visibility=CATALOG_VISIBILITY_NONE)
self.about = ItemFactory.create(
category="about", parent_location=self.course_without_about.location,
cls.about = ItemFactory.create(
category="about", parent_location=cls.course_without_about.location,
data="WITHOUT ABOUT", display_name="overview"
)
self.course_with_about = CourseFactory.create(catalog_visibility=CATALOG_VISIBILITY_ABOUT)
self.about = ItemFactory.create(
category="about", parent_location=self.course_with_about.location,
cls.about = ItemFactory.create(
category="about", parent_location=cls.course_with_about.location,
data="WITH ABOUT", display_name="overview"
)
self.purchase_course = CourseFactory.create(org='MITx', number='buyme', display_name='Course To Buy')
def setUp(self):
super(AboutTestCase, self).setUp()
self.course_mode = CourseMode(
course_id=self.purchase_course.id,
mode_slug=CourseMode.DEFAULT_MODE_SLUG,
......@@ -152,7 +157,7 @@ class AboutTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase, EventTrackingT
def test_about_page_unfulfilled_prereqs(self):
pre_requisite_course = CourseFactory.create(
org='edX',
course='900',
course='901',
display_name='pre requisite course',
)
......@@ -222,21 +227,24 @@ class AboutTestCaseXML(LoginEnrollmentTestCase, ModuleStoreTestCase):
@attr('shard_1')
class AboutWithCappedEnrollmentsTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase):
class AboutWithCappedEnrollmentsTestCase(LoginEnrollmentTestCase, SharedModuleStoreTestCase):
"""
This test case will check the About page when a course has a capped enrollment
"""
@classmethod
def setUpClass(cls):
super(AboutWithCappedEnrollmentsTestCase, cls).setUpClass()
cls.course = CourseFactory.create(metadata={"max_student_enrollments_allowed": 1})
cls.about = ItemFactory.create(
category="about", parent_location=cls.course.location,
data="OOGIE BLOOGIE", display_name="overview"
)
def setUp(self):
"""
Set up the tests
"""
super(AboutWithCappedEnrollmentsTestCase, self).setUp()
self.course = CourseFactory.create(metadata={"max_student_enrollments_allowed": 1})
self.about = ItemFactory.create(
category="about", parent_location=self.course.location,
data="OOGIE BLOOGIE", display_name="overview"
)
def test_enrollment_cap(self):
"""
......@@ -272,20 +280,22 @@ class AboutWithCappedEnrollmentsTestCase(LoginEnrollmentTestCase, ModuleStoreTes
@attr('shard_1')
class AboutWithInvitationOnly(ModuleStoreTestCase):
class AboutWithInvitationOnly(SharedModuleStoreTestCase):
"""
This test case will check the About page when a course is invitation only.
"""
def setUp(self):
super(AboutWithInvitationOnly, self).setUp()
self.course = CourseFactory.create(metadata={"invitation_only": True})
self.about = ItemFactory.create(
category="about", parent_location=self.course.location,
@classmethod
def setUpClass(cls):
super(AboutWithInvitationOnly, cls).setUpClass()
cls.course = CourseFactory.create(metadata={"invitation_only": True})
cls.about = ItemFactory.create(
category="about", parent_location=cls.course.location,
display_name="overview"
)
def setUp(self):
super(AboutWithInvitationOnly, self).setUp()
def test_invitation_only(self):
"""
Test for user not logged in, invitation only course.
......@@ -320,19 +330,22 @@ class AboutWithInvitationOnly(ModuleStoreTestCase):
@attr('shard_1')
@patch.dict(settings.FEATURES, {'RESTRICT_ENROLL_BY_REG_METHOD': True})
class AboutTestCaseShibCourse(LoginEnrollmentTestCase, ModuleStoreTestCase):
class AboutTestCaseShibCourse(LoginEnrollmentTestCase, SharedModuleStoreTestCase):
"""
Test cases covering about page behavior for courses that use shib enrollment domain ("shib courses")
"""
def setUp(self):
super(AboutTestCaseShibCourse, self).setUp()
self.course = CourseFactory.create(enrollment_domain="shib:https://idp.stanford.edu/")
self.about = ItemFactory.create(
category="about", parent_location=self.course.location,
@classmethod
def setUpClass(cls):
super(AboutTestCaseShibCourse, cls).setUpClass()
cls.course = CourseFactory.create(enrollment_domain="shib:https://idp.stanford.edu/")
cls.about = ItemFactory.create(
category="about", parent_location=cls.course.location,
data="OOGIE BLOOGIE", display_name="overview"
)
def setUp(self):
super(AboutTestCaseShibCourse, self).setUp()
def test_logged_in_shib_course(self):
"""
For shib courses, logged in users will see the enroll button, but get rejected once they click there
......@@ -366,8 +379,8 @@ class AboutWithClosedEnrollment(ModuleStoreTestCase):
set but it is currently outside of that period.
"""
def setUp(self):
super(AboutWithClosedEnrollment, self).setUp()
self.course = CourseFactory.create(metadata={"invitation_only": False})
# Setup enrollment period to be in future
......@@ -385,7 +398,6 @@ class AboutWithClosedEnrollment(ModuleStoreTestCase):
)
def test_closed_enrollmement(self):
url = reverse('about_course', args=[self.course.id.to_deprecated_string()])
resp = self.client.get(url)
self.assertEqual(resp.status_code, 200)
......@@ -406,15 +418,32 @@ class AboutWithClosedEnrollment(ModuleStoreTestCase):
@attr('shard_1')
@patch.dict(settings.FEATURES, {'ENABLE_SHOPPING_CART': True})
@patch.dict(settings.FEATURES, {'ENABLE_PAID_COURSE_REGISTRATION': True})
class AboutPurchaseCourseTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase):
class AboutPurchaseCourseTestCase(LoginEnrollmentTestCase, SharedModuleStoreTestCase):
"""
This test class runs through a suite of verifications regarding
purchaseable courses
"""
@classmethod
def setUpClass(cls):
super(AboutPurchaseCourseTestCase, cls).setUpClass()
cls.course = CourseFactory.create(org='MITx', number='buyme', display_name='Course To Buy')
now = datetime.datetime.now(pytz.UTC)
tomorrow = now + datetime.timedelta(days=1)
nextday = tomorrow + datetime.timedelta(days=1)
cls.closed_course = CourseFactory.create(
org='MITx',
number='closed',
display_name='Closed Course To Buy',
enrollment_start=tomorrow,
enrollment_end=nextday
)
def setUp(self):
super(AboutPurchaseCourseTestCase, self).setUp()
self.course = CourseFactory.create(org='MITx', number='buyme', display_name='Course To Buy')
self._set_ecomm(self.course)
self._set_ecomm(self.closed_course)
def _set_ecomm(self, course):
"""
......@@ -487,19 +516,12 @@ class AboutPurchaseCourseTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase):
window
"""
self.setup_user()
now = datetime.datetime.now(pytz.UTC)
tomorrow = now + datetime.timedelta(days=1)
nextday = tomorrow + datetime.timedelta(days=1)
self.course.enrollment_start = tomorrow
self.course.enrollment_end = nextday
self.course = self.update_course(self.course, self.user.id)
url = reverse('about_course', args=[self.course.id.to_deprecated_string()])
url = reverse('about_course', args=[self.closed_course.id.to_deprecated_string()])
resp = self.client.get(url)
self.assertEqual(resp.status_code, 200)
self.assertIn("Enrollment is Closed", resp.content)
self.assertNotIn("Add buyme to Cart <span>($10 USD)</span>", resp.content)
self.assertNotIn("Add closed to Cart <span>($10 USD)</span>", resp.content)
# course price is visible ihe course_about page when the course
# mode is set to honor and it's price is set
......
......@@ -71,3 +71,18 @@ class TestComprehensiveTheming(TestCase):
def test_overridden_logo_image(self):
result = staticfiles.finders.find('images/logo.png')
self.assertEqual(result, settings.REPO_ROOT / 'themes/red-theme/lms/static/images/logo.png')
def test_default_favicon(self):
"""
Test default favicon is served if no theme is applied
"""
result = staticfiles.finders.find('images/favicon.ico')
self.assertEqual(result, settings.REPO_ROOT / 'lms/static/images/favicon.ico')
@with_comprehensive_theme(settings.REPO_ROOT / 'themes/red-theme')
def test_overridden_favicon(self):
"""
Test comprehensive theme override on favicon image.
"""
result = staticfiles.finders.find('images/favicon.ico')
self.assertEqual(result, settings.REPO_ROOT / 'themes/red-theme/lms/static/images/favicon.ico')
......@@ -30,18 +30,22 @@ from lms.djangoapps.ccx.tests.factories import CcxFactory
@attr('shard_1')
class CourseInfoTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase):
class CourseInfoTestCase(LoginEnrollmentTestCase, SharedModuleStoreTestCase):
"""
Tests for the Course Info page
"""
def setUp(self):
super(CourseInfoTestCase, self).setUp()
self.course = CourseFactory.create()
self.page = ItemFactory.create(
category="course_info", parent_location=self.course.location,
@classmethod
def setUpClass(cls):
super(CourseInfoTestCase, cls).setUpClass()
cls.course = CourseFactory.create()
cls.page = ItemFactory.create(
category="course_info", parent_location=cls.course.location,
data="OOGIE BLOOGIE", display_name="updates"
)
def setUp(self):
super(CourseInfoTestCase, self).setUp()
def test_logged_in_unenrolled(self):
self.setup_user()
url = reverse('info', args=[self.course.id.to_deprecated_string()])
......@@ -242,11 +246,15 @@ class SelfPacedCourseInfoTestCase(LoginEnrollmentTestCase, SharedModuleStoreTest
Tests for the info page of self-paced courses.
"""
@classmethod
def setUpClass(cls):
super(SelfPacedCourseInfoTestCase, cls).setUpClass()
cls.instructor_paced_course = CourseFactory.create(self_paced=False)
cls.self_paced_course = CourseFactory.create(self_paced=True)
def setUp(self):
SelfPacedConfiguration(enabled=True).save()
super(SelfPacedCourseInfoTestCase, self).setUp()
self.instructor_paced_course = CourseFactory.create(self_paced=False)
self.self_paced_course = CourseFactory.create(self_paced=True)
self.setup_user()
def fetch_course_info_with_queries(self, course, sql_queries, mongo_queries):
......
......@@ -13,27 +13,42 @@ from survey.models import SurveyForm, SurveyAnswer
from common.test.utils import XssTestMixin
from xmodule.modulestore.tests.factories import CourseFactory
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
from courseware.tests.helpers import LoginEnrollmentTestCase
@attr('shard_1')
class SurveyViewsTests(LoginEnrollmentTestCase, ModuleStoreTestCase, XssTestMixin):
class SurveyViewsTests(LoginEnrollmentTestCase, SharedModuleStoreTestCase, XssTestMixin):
"""
All tests for the views.py file
"""
STUDENT_INFO = [('view@test.com', 'foo')]
@classmethod
def setUpClass(cls):
super(SurveyViewsTests, cls).setUpClass()
cls.test_survey_name = 'TestSurvey'
cls.course = CourseFactory.create(
display_name='<script>alert("XSS")</script>',
course_survey_required=True,
course_survey_name=cls.test_survey_name
)
cls.course_with_bogus_survey = CourseFactory.create(
course_survey_required=True,
course_survey_name="DoesNotExist"
)
cls.course_without_survey = CourseFactory.create()
def setUp(self):
"""
Set up the test data used in the specific tests
"""
super(SurveyViewsTests, self).setUp()
self.test_survey_name = 'TestSurvey'
self.test_form = '<input name="field1"></input>'
self.survey = SurveyForm.create(self.test_survey_name, self.test_form)
self.student_answers = OrderedDict({
......@@ -41,19 +56,6 @@ class SurveyViewsTests(LoginEnrollmentTestCase, ModuleStoreTestCase, XssTestMixi
u'field2': u'value2',
})
self.course = CourseFactory.create(
display_name='<script>alert("XSS")</script>',
course_survey_required=True,
course_survey_name=self.test_survey_name
)
self.course_with_bogus_survey = CourseFactory.create(
course_survey_required=True,
course_survey_name="DoesNotExist"
)
self.course_without_survey = CourseFactory.create()
# Create student accounts and activate them.
for i in range(len(self.STUDENT_INFO)):
email, password = self.STUDENT_INFO[i]
......
......@@ -11,7 +11,7 @@ from pytz import UTC
from django.conf import settings
from django.core.urlresolvers import reverse
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
from student.tests.factories import UserFactory, CourseEnrollmentFactory
from util.date_utils import get_time_display, DEFAULT_SHORT_DATE_FORMAT
......@@ -23,7 +23,7 @@ from openedx.core.djangoapps.credit.models import CreditCourse
@patch.dict(settings.FEATURES, {"ENABLE_CREDIT_ELIGIBILITY": True})
@ddt.ddt
class ProgressPageCreditRequirementsTest(ModuleStoreTestCase):
class ProgressPageCreditRequirementsTest(SharedModuleStoreTestCase):
"""
Tests for credit requirement display on the progress page.
"""
......@@ -35,11 +35,15 @@ class ProgressPageCreditRequirementsTest(ModuleStoreTestCase):
MIN_GRADE_REQ_DISPLAY = "Final Grade Credit Requirement"
VERIFICATION_REQ_DISPLAY = "Midterm Exam Credit Requirement"
@classmethod
def setUpClass(cls):
super(ProgressPageCreditRequirementsTest, cls).setUpClass()
cls.course = CourseFactory.create()
def setUp(self):
super(ProgressPageCreditRequirementsTest, self).setUp()
# Create a course and configure it as a credit course
self.course = CourseFactory.create()
# Configure course as a credit course
CreditCourse.objects.create(course_key=self.course.id, enabled=True)
# Configure credit requirements (passing grade and in-course reverification)
......
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment