Merge pull request #11955 from edx/rc/2016-03-29
Release Candidate rc/2016-03-29
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -73,6 +73,7 @@ bin/
|
||||
lms/static/css/
|
||||
lms/static/certificates/css/
|
||||
cms/static/css/
|
||||
common/static/common/js/vendor/
|
||||
|
||||
### Styling generated from templates
|
||||
lms/static/sass/*.css
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
@shard_1
|
||||
Feature: Course export
|
||||
I want to export my course to a tar.gz file to share with others or check into source control
|
||||
|
||||
# Disabling due to failure on master. 05/21/2014 TODO: fix
|
||||
# Scenario: User is directed to problem with & in it when export fails
|
||||
# Given I am in Studio editing a new unit
|
||||
# When I add a "Blank Advanced Problem" "Advanced Problem" component
|
||||
# And I edit and enter an ampersand
|
||||
# And I export the course
|
||||
# Then I get an error dialog
|
||||
# And I can click to go to the unit with the error
|
||||
@@ -1,71 +0,0 @@
|
||||
# pylint: disable=missing-docstring
|
||||
# pylint: disable=redefined-outer-name
|
||||
# pylint: disable=unused-argument
|
||||
|
||||
from lettuce import world, step
|
||||
from component_settings_editor_helpers import enter_xml_in_advanced_problem
|
||||
from nose.tools import assert_true, assert_equal
|
||||
from contentstore.utils import reverse_usage_url
|
||||
|
||||
|
||||
@step('I go to the export page$')
|
||||
def i_go_to_the_export_page(step):
|
||||
world.click_tools()
|
||||
link_css = 'li.nav-course-tools-export a'
|
||||
world.css_click(link_css)
|
||||
|
||||
|
||||
@step('I export the course$')
|
||||
def i_export_the_course(step):
|
||||
step.given('I go to the export page')
|
||||
world.css_click('a.action-export')
|
||||
|
||||
|
||||
@step('I edit and enter bad XML$')
|
||||
def i_enter_bad_xml(step):
|
||||
enter_xml_in_advanced_problem(
|
||||
step,
|
||||
"""<problem><h1>Smallest Canvas</h1>
|
||||
<p>You want to make the smallest canvas you can.</p>
|
||||
<multiplechoiceresponse>
|
||||
<choicegroup type="MultipleChoice">
|
||||
<choice correct="false"><verbatim><canvas id="myCanvas" width = 10 height = 100> </canvas></verbatim></choice>
|
||||
<choice correct="true"><code><canvas id="myCanvas" width = 10 height = 10> </canvas></code></choice>
|
||||
</choicegroup>
|
||||
</multiplechoiceresponse>
|
||||
</problem>"""
|
||||
)
|
||||
|
||||
|
||||
@step('I edit and enter an ampersand$')
|
||||
def i_enter_an_ampersand(step):
|
||||
enter_xml_in_advanced_problem(step, "<problem>&</problem>")
|
||||
|
||||
|
||||
@step('I get an error dialog$')
|
||||
def get_an_error_dialog(step):
|
||||
assert_true(world.is_css_present("div.prompt.error"))
|
||||
|
||||
|
||||
@step('I can click to go to the unit with the error$')
|
||||
def i_click_on_error_dialog(step):
|
||||
world.css_click("button.action-primary")
|
||||
|
||||
problem_string = unicode(world.scenario_dict['COURSE'].id.make_usage_key("problem", 'ignore'))
|
||||
problem_string = u"Problem {}".format(problem_string[:problem_string.rfind('ignore')])
|
||||
css_selector = "span.inline-error"
|
||||
world.wait_for_visible(css_selector)
|
||||
assert_true(
|
||||
world.css_html(css_selector).startswith(problem_string),
|
||||
u"{} does not start with {}".format(
|
||||
world.css_html(css_selector), problem_string
|
||||
))
|
||||
# we don't know the actual ID of the vertical. So just check that we did go to a
|
||||
# vertical page in the course (there should only be one).
|
||||
vertical_usage_key = world.scenario_dict['COURSE'].id.make_usage_key("vertical", "test")
|
||||
vertical_url = reverse_usage_url('container_handler', vertical_usage_key)
|
||||
# Remove the trailing "/None" from the URL - we don't know the course ID, so we just want to
|
||||
# check that we visited a vertical URL.
|
||||
if vertical_url.endswith("/test") or vertical_url.endswith("@test"):
|
||||
vertical_url = vertical_url[:-5]
|
||||
assert_equal(1, world.browser.url.count(vertical_url))
|
||||
@@ -101,27 +101,3 @@ Feature: CMS.Problem Editor
|
||||
Then I can see Reply to Annotation link
|
||||
And I see that page has scrolled "down" when I click on "annotatable-reply" link
|
||||
And I see that page has scrolled "up" when I click on "annotation-return" link
|
||||
|
||||
# Disabled 11/13/2013 after failing in master
|
||||
# The screenshot showed that the LaTeX editor had the text "hi",
|
||||
# but Selenium timed out waiting for the text to appear.
|
||||
# It also caused later tests to fail with "UnexpectedAlertPresent"
|
||||
#
|
||||
# This feature will work in Firefox only when Firefox is the active window
|
||||
# IE will not interact with the high level source in sauce labs
|
||||
#@skip_internetexplorer
|
||||
#Scenario: High Level source is persisted for LaTeX problem (bug STUD-280)
|
||||
# Given I have created a LaTeX Problem
|
||||
# When I edit and compile the High Level Source
|
||||
# Then my change to the High Level Source is persisted
|
||||
# And when I view the High Level Source I see my changes
|
||||
|
||||
# Disabled 10/28/13 due to flakiness observed in master
|
||||
# Scenario: Exceptions don't cause problem to be uneditable (bug STUD-786)
|
||||
#Given I have an empty course
|
||||
#And I go to the import page
|
||||
#And I import the file "get_html_exception_test.tar.gz"
|
||||
#When I go to the unit "Probability and BMI"
|
||||
#And I click on "edit a draft"
|
||||
#Then I see a message that says "We're having trouble rendering your component"
|
||||
#And I can edit the problem
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
@shard_2 @requires_stub_youtube
|
||||
Feature: CMS Video Component
|
||||
As a course author, I want to be able to view my created videos in Studio
|
||||
|
||||
# 2
|
||||
# Disabled 2/19/14 after intermittent failures in master
|
||||
#Scenario: Check that position is stored on page refresh, position within start-end range
|
||||
# Given I have created a Video component with subtitles
|
||||
# And Make sure captions are closed
|
||||
# And I edit the component
|
||||
# And I open tab "Advanced"
|
||||
# And I set value "00:00:12" to the field "Video Start Time"
|
||||
# And I set value "00:00:24" to the field "Video Stop Time"
|
||||
# And I save changes
|
||||
# And I click video button "play"
|
||||
# Then I see a range on slider
|
||||
# Then I seek video to "16" seconds
|
||||
# And I click video button "pause"
|
||||
# And I reload the page
|
||||
# And I click video button "play"
|
||||
# Then I see video starts playing from "0:16" position
|
||||
|
||||
# 3
|
||||
# Disabled 2/18/14 after intermittent failures in master
|
||||
# Scenario: Check that position is stored on page refresh, position before start-end range
|
||||
# Given I have created a Video component with subtitles
|
||||
# And Make sure captions are closed
|
||||
# And I edit the component
|
||||
# And I open tab "Advanced"
|
||||
# And I set value "00:00:12" to the field "Video Start Time"
|
||||
# And I set value "00:00:24" to the field "Video Stop Time"
|
||||
# And I save changes
|
||||
# And I click video button "play"
|
||||
# Then I see a range on slider
|
||||
# Then I seek video to "5" seconds
|
||||
# And I click video button "pause"
|
||||
# And I reload the page
|
||||
# And I click video button "play"
|
||||
# Then I see video starts playing from "0:12" position
|
||||
|
||||
# 4
|
||||
# Disabled 2/18/14 after intermittent failures in master
|
||||
# Scenario: Check that position is stored on page refresh, position after start-end range
|
||||
# Given I have created a Video component with subtitles
|
||||
# And Make sure captions are closed
|
||||
# And I edit the component
|
||||
# And I open tab "Advanced"
|
||||
# And I set value "00:00:12" to the field "Video Start Time"
|
||||
# And I set value "00:00:24" to the field "Video Stop Time"
|
||||
# And I save changes
|
||||
# And I click video button "play"
|
||||
# Then I see a range on slider
|
||||
# Then I seek video to "30" seconds
|
||||
# And I click video button "pause"
|
||||
# And I reload the page
|
||||
# And I click video button "play"
|
||||
# Then I see video starts playing from "0:12" position
|
||||
@@ -1,321 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# disable missing docstring
|
||||
# pylint: disable=missing-docstring
|
||||
|
||||
import requests
|
||||
from lettuce import world, step
|
||||
from nose.tools import assert_true, assert_equal, assert_in, assert_not_equal
|
||||
from terrain.steps import reload_the_page
|
||||
from django.conf import settings
|
||||
from common import upload_file, attach_file
|
||||
|
||||
TEST_ROOT = settings.COMMON_TEST_DATA_ROOT
|
||||
|
||||
DISPLAY_NAME = "Component Display Name"
|
||||
NATIVE_LANGUAGES = {lang: label for lang, label in settings.LANGUAGES if len(lang) == 2}
|
||||
LANGUAGES = {
|
||||
lang: NATIVE_LANGUAGES.get(lang, display)
|
||||
for lang, display in settings.ALL_LANGUAGES
|
||||
}
|
||||
|
||||
LANGUAGES.update({
|
||||
'table': 'Table of Contents'
|
||||
})
|
||||
|
||||
TRANSLATION_BUTTONS = {
|
||||
'add': '.metadata-video-translations .create-action',
|
||||
'upload': '.metadata-video-translations .upload-action',
|
||||
'download': '.metadata-video-translations .download-action',
|
||||
'remove': '.metadata-video-translations .remove-action',
|
||||
'clear': '.metadata-video-translations .setting-clear',
|
||||
}
|
||||
|
||||
VIDEO_MENUS = {
|
||||
'language': '.lang .menu',
|
||||
}
|
||||
|
||||
|
||||
class RequestHandlerWithSessionId(object):
|
||||
def get(self, url):
|
||||
"""
|
||||
Sends a request.
|
||||
"""
|
||||
kwargs = dict()
|
||||
|
||||
session_id = [{i['name']:i['value']} for i in world.browser.cookies.all() if i['name'] == u'sessionid']
|
||||
if session_id:
|
||||
kwargs.update({
|
||||
'cookies': session_id[0]
|
||||
})
|
||||
|
||||
response = requests.get(url, **kwargs)
|
||||
self.response = response
|
||||
self.status_code = response.status_code
|
||||
self.headers = response.headers
|
||||
self.content = response.content
|
||||
|
||||
return self
|
||||
|
||||
def is_success(self):
|
||||
"""
|
||||
Returns `True` if the response was succeed, otherwise, returns `False`.
|
||||
"""
|
||||
if self.status_code < 400:
|
||||
return True
|
||||
return False
|
||||
|
||||
def check_header(self, name, value):
|
||||
"""
|
||||
Returns `True` if the response header exist and has appropriate value,
|
||||
otherwise, returns `False`.
|
||||
"""
|
||||
if value in self.headers.get(name, ''):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def success_upload_file(filename):
|
||||
upload_file(filename, sub_path="uploads/")
|
||||
world.css_has_text('#upload_confirm', 'Success!')
|
||||
world.is_css_not_present('.wrapper-modal-window-assetupload', wait_time=30)
|
||||
|
||||
|
||||
def get_translations_container():
|
||||
return world.browser.find_by_xpath('//label[text()="Transcript Languages"]/following-sibling::div')
|
||||
|
||||
|
||||
def get_setting_container(lang_code):
|
||||
try:
|
||||
get_xpath = lambda value: './/descendant::a[@data-lang="{}" and contains(@class,"remove-setting")]/parent::*'.format(value)
|
||||
return get_translations_container().find_by_xpath(get_xpath(lang_code)).first
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def get_last_dropdown():
|
||||
return get_translations_container().find_by_xpath('.//descendant::select[last()]').last
|
||||
|
||||
|
||||
def choose_option(dropdown, value):
|
||||
dropdown.find_by_value(value)[0].click()
|
||||
|
||||
|
||||
def choose_new_lang(lang_code):
|
||||
world.css_click(TRANSLATION_BUTTONS['add'])
|
||||
choose_option(get_last_dropdown(), lang_code)
|
||||
assert_equal(get_last_dropdown().value, lang_code, "Option with provided value is not available or was not selected")
|
||||
|
||||
|
||||
def open_menu(menu):
|
||||
world.browser.execute_script("$('{selector}').parent().addClass('is-opened')".format(
|
||||
selector=VIDEO_MENUS[menu]
|
||||
))
|
||||
|
||||
|
||||
@step('I have set "transcript display" to (.*)$')
|
||||
def set_show_captions(step, setting):
|
||||
# Prevent cookies from overriding course settings
|
||||
world.browser.cookies.delete('hide_captions')
|
||||
|
||||
world.edit_component()
|
||||
world.select_editor_tab('Advanced')
|
||||
world.browser.select('Show Transcript', setting)
|
||||
world.save_component()
|
||||
|
||||
|
||||
@step('when I view the video it (.*) show the captions$')
|
||||
def shows_captions(_step, show_captions):
|
||||
world.wait_for_js_variable_truthy("Video")
|
||||
world.wait(0.5)
|
||||
if show_captions == 'does not':
|
||||
assert_true(world.is_css_present('div.video.closed'))
|
||||
else:
|
||||
assert_true(world.is_css_not_present('div.video.closed'))
|
||||
|
||||
# Prevent cookies from overriding course settings
|
||||
world.browser.cookies.delete('hide_captions')
|
||||
world.browser.cookies.delete('current_player_mode')
|
||||
|
||||
|
||||
@step('I see the correct video settings and default values$')
|
||||
def correct_video_settings(_step):
|
||||
expected_entries = [
|
||||
# basic
|
||||
[DISPLAY_NAME, 'Video', False],
|
||||
['Default Video URL', 'http://youtu.be/3_yD_cEKoCk, , ', False],
|
||||
|
||||
# advanced
|
||||
[DISPLAY_NAME, 'Video', False],
|
||||
['Default Timed Transcript', '', False],
|
||||
['Download Transcript Allowed', 'False', False],
|
||||
['Downloadable Transcript URL', '', False],
|
||||
['Show Transcript', 'True', False],
|
||||
['Transcript Languages', '', False],
|
||||
['Upload Handout', '', False],
|
||||
['Video Available on Web Only', 'False', False],
|
||||
['Video Download Allowed', 'False', False],
|
||||
['Video File URLs', '', False],
|
||||
['Video Start Time', '00:00:00', False],
|
||||
['Video Stop Time', '00:00:00', False],
|
||||
['YouTube ID', '3_yD_cEKoCk', False],
|
||||
['YouTube ID for .75x speed', '', False],
|
||||
['YouTube ID for 1.25x speed', '', False],
|
||||
['YouTube ID for 1.5x speed', '', False]
|
||||
]
|
||||
world.verify_all_setting_entries(expected_entries)
|
||||
|
||||
|
||||
@step('my video display name change is persisted on save$')
|
||||
def video_name_persisted(step):
|
||||
world.save_component()
|
||||
reload_the_page(step)
|
||||
world.wait_for_xmodule()
|
||||
world.edit_component()
|
||||
|
||||
world.verify_setting_entry(
|
||||
world.get_setting_entry(DISPLAY_NAME),
|
||||
DISPLAY_NAME, '3.4', True
|
||||
)
|
||||
|
||||
|
||||
@step('I can modify video display name')
|
||||
def i_can_modify_video_display_name(_step):
|
||||
index = world.get_setting_entry_index(DISPLAY_NAME)
|
||||
world.set_field_value(index, '3.4')
|
||||
world.verify_setting_entry(world.get_setting_entry(DISPLAY_NAME), DISPLAY_NAME, '3.4', True)
|
||||
|
||||
|
||||
@step('I upload transcript file(?:s)?:$')
|
||||
def upload_transcript(step):
|
||||
input_hidden = '.metadata-video-translations .input'
|
||||
# Number of previously added translations
|
||||
initial_index = len(world.css_find(TRANSLATION_BUTTONS['download']))
|
||||
|
||||
if step.hashes:
|
||||
for i, item in enumerate(step.hashes):
|
||||
lang_code = item['lang_code']
|
||||
filename = item['filename']
|
||||
index = initial_index + i
|
||||
|
||||
choose_new_lang(lang_code)
|
||||
|
||||
expected_text = world.css_text(TRANSLATION_BUTTONS['upload'], index=index)
|
||||
assert_equal(expected_text, "Upload")
|
||||
assert_equal(world.css_find(input_hidden).last.value, "")
|
||||
|
||||
world.css_click(TRANSLATION_BUTTONS['upload'], index=index)
|
||||
success_upload_file(filename)
|
||||
|
||||
world.wait_for_visible(TRANSLATION_BUTTONS['download'], index=index)
|
||||
assert_equal(world.css_find(TRANSLATION_BUTTONS['upload']).last.text, "Replace")
|
||||
assert_equal(world.css_find(input_hidden).last.value, filename)
|
||||
|
||||
|
||||
@step('I try to upload transcript file "([^"]*)"$')
|
||||
def try_to_upload_transcript(step, filename):
|
||||
world.css_click(TRANSLATION_BUTTONS['upload'])
|
||||
attach_file(filename, 'uploads/')
|
||||
|
||||
|
||||
@step('I upload transcript file "([^"]*)" for "([^"]*)" language code$')
|
||||
def upload_transcript_for_lang(step, filename, lang_code):
|
||||
get_xpath = lambda value: './/div/a[contains(@class, "upload-action")]'.format(value)
|
||||
container = get_setting_container(lang_code)
|
||||
|
||||
# If translation isn't uploaded, prepare drop-down and try to find container again
|
||||
choose_new_lang(lang_code)
|
||||
container = get_setting_container(lang_code)
|
||||
|
||||
button = container.find_by_xpath(get_xpath(lang_code)).first
|
||||
button.click()
|
||||
success_upload_file(filename)
|
||||
|
||||
|
||||
@step('I replace transcript file for "([^"]*)" language code by "([^"]*)"$')
|
||||
def replace_transcript_for_lang(step, lang_code, filename):
|
||||
get_xpath = lambda value: './/div/a[contains(@class, "upload-action")]'.format(value)
|
||||
container = get_setting_container(lang_code)
|
||||
|
||||
button = container.find_by_xpath(get_xpath(lang_code)).first
|
||||
button.click()
|
||||
success_upload_file(filename)
|
||||
|
||||
|
||||
@step('I see validation error "([^"]*)"$')
|
||||
def verify_validation_error_message(step, error_message):
|
||||
assert_equal(world.css_text('#upload_error'), error_message)
|
||||
|
||||
|
||||
@step('I can download transcript for "([^"]*)" language code, that contains text "([^"]*)"$')
|
||||
def i_can_download_transcript(_step, lang_code, text):
|
||||
MIME_TYPE = 'application/x-subrip'
|
||||
get_xpath = lambda value: './/div/a[contains(text(), "Download")]'.format(value)
|
||||
container = get_setting_container(lang_code)
|
||||
assert container
|
||||
button = container.find_by_xpath(get_xpath(lang_code)).first
|
||||
url = button['href']
|
||||
request = RequestHandlerWithSessionId()
|
||||
assert_true(request.get(url).is_success())
|
||||
assert_true(request.check_header('content-type', MIME_TYPE))
|
||||
assert_in(text.encode('utf-8'), request.content)
|
||||
|
||||
|
||||
@step('I remove translation for "([^"]*)" language code$')
|
||||
def i_can_remove_transcript(_step, lang_code):
|
||||
get_xpath = lambda value: './/descendant::a[@data-lang="{}" and contains(@class,"remove-setting")]'.format(value)
|
||||
container = get_setting_container(lang_code)
|
||||
assert container
|
||||
button = container.find_by_xpath(get_xpath(lang_code)).first
|
||||
button.click()
|
||||
|
||||
|
||||
@step('I see translations for "([^"]*)"$')
|
||||
def verify_translations(_step, lang_codes_string):
|
||||
expected = [l.strip() for l in lang_codes_string.split(',')]
|
||||
actual = [l['data-lang'] for l in world.css_find('.metadata-video-translations .remove-setting')]
|
||||
assert_equal(set(expected), set(actual))
|
||||
|
||||
|
||||
@step('I do not see translations$')
|
||||
def no_translations(_step):
|
||||
assert_true(world.is_css_not_present('.metadata-video-translations .remove-setting'))
|
||||
|
||||
|
||||
@step('I confirm prompt$')
|
||||
def confirm_prompt(_step):
|
||||
world.confirm_studio_prompt()
|
||||
|
||||
|
||||
@step('I (cannot )?choose "([^"]*)" language code$')
|
||||
def i_choose_lang_code(_step, cannot, lang_code):
|
||||
choose_option(get_last_dropdown(), lang_code)
|
||||
if cannot:
|
||||
assert_not_equal(get_last_dropdown().value, lang_code, "Option with provided value was selected, but shouldn't")
|
||||
else:
|
||||
assert_equal(get_last_dropdown().value, lang_code, "Option with provided value is not available or was not selected")
|
||||
|
||||
|
||||
@step('I click button "([^"]*)"$')
|
||||
def click_button(_step, button):
|
||||
world.css_click(TRANSLATION_BUTTONS[button.lower()])
|
||||
|
||||
|
||||
@step('video language menu has "([^"]*)" translations$')
|
||||
def i_see_correct_langs(_step, langs):
|
||||
menu_name = 'language'
|
||||
open_menu(menu_name)
|
||||
items = world.css_find(VIDEO_MENUS[menu_name] + ' li')
|
||||
translations = {t.strip(): LANGUAGES[t.strip()] for t in langs.split(',')}
|
||||
|
||||
assert_equal(len(translations), len(items))
|
||||
for lang_code, label in translations.items():
|
||||
assert_true(any([i.text == label for i in items]))
|
||||
assert_true(any([i['data-lang-code'] == lang_code for i in items]))
|
||||
|
||||
|
||||
@step('video language with code "([^"]*)" at position "(\d+)"$')
|
||||
def i_see_lang_at_position(_step, code, position):
|
||||
menu_name = 'language'
|
||||
open_menu(menu_name)
|
||||
item = world.css_find(VIDEO_MENUS[menu_name] + ' li')[int(position)]
|
||||
assert_equal(item['data-lang-code'], code)
|
||||
@@ -1,40 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# disable missing docstring
|
||||
# pylint: disable=missing-docstring
|
||||
|
||||
from lettuce import world, step
|
||||
from nose.tools import assert_true
|
||||
from video_editor import RequestHandlerWithSessionId, success_upload_file
|
||||
|
||||
|
||||
@step('I (?:upload|replace) handout file(?: by)? "([^"]*)"$')
|
||||
def upload_handout(step, filename):
|
||||
world.css_click('.wrapper-comp-setting.file-uploader .upload-action')
|
||||
success_upload_file(filename)
|
||||
|
||||
|
||||
@step('I can download handout file( in editor)? with mime type "([^"]*)"$')
|
||||
def i_can_download_handout_with_mime_type(_step, is_editor, mime_type):
|
||||
if is_editor:
|
||||
selector = '.wrapper-comp-setting.file-uploader .download-action'
|
||||
else:
|
||||
selector = '.video-handout.video-download-button a'
|
||||
|
||||
button = world.css_find(selector).first
|
||||
url = button['href']
|
||||
request = RequestHandlerWithSessionId()
|
||||
assert_true(request.get(url).is_success())
|
||||
assert_true(request.check_header('content-type', mime_type))
|
||||
|
||||
|
||||
@step('I clear handout$')
|
||||
def clear_handout(_step):
|
||||
world.css_click('.wrapper-comp-setting.file-uploader .setting-clear')
|
||||
|
||||
|
||||
@step('I have created a Video component with handout file "([^"]*)"')
|
||||
def create_video_with_handout(_step, filename):
|
||||
_step.given('I have created a Video component')
|
||||
_step.given('I edit the component')
|
||||
_step.given('I open tab "Advanced"')
|
||||
_step.given('I upload handout file "{0}"'.format(filename))
|
||||
@@ -90,6 +90,7 @@ from util.organizations_helpers import (
|
||||
organizations_enabled,
|
||||
)
|
||||
from util.string_utils import _has_non_ascii_characters
|
||||
from util.course_key_utils import from_string_or_404
|
||||
from xmodule.contentstore.content import StaticContent
|
||||
from xmodule.course_module import CourseFields
|
||||
from xmodule.course_module import DEFAULT_START_DATE
|
||||
@@ -868,10 +869,7 @@ def course_info_handler(request, course_key_string):
|
||||
GET
|
||||
html: return html for editing the course info handouts and updates.
|
||||
"""
|
||||
try:
|
||||
course_key = CourseKey.from_string(course_key_string)
|
||||
except InvalidKeyError:
|
||||
raise Http404
|
||||
course_key = from_string_or_404(course_key_string)
|
||||
|
||||
with modulestore().bulk_operations(course_key):
|
||||
course_module = get_course_and_check_access(course_key, request.user)
|
||||
|
||||
@@ -283,6 +283,17 @@ else:
|
||||
DATABASES = AUTH_TOKENS['DATABASES']
|
||||
|
||||
MODULESTORE = convert_module_store_setting_if_needed(AUTH_TOKENS.get('MODULESTORE', MODULESTORE))
|
||||
|
||||
MODULESTORE_FIELD_OVERRIDE_PROVIDERS = ENV_TOKENS.get(
|
||||
'MODULESTORE_FIELD_OVERRIDE_PROVIDERS',
|
||||
MODULESTORE_FIELD_OVERRIDE_PROVIDERS
|
||||
)
|
||||
|
||||
XBLOCK_FIELD_DATA_WRAPPERS = ENV_TOKENS.get(
|
||||
'XBLOCK_FIELD_DATA_WRAPPERS',
|
||||
XBLOCK_FIELD_DATA_WRAPPERS
|
||||
)
|
||||
|
||||
CONTENTSTORE = AUTH_TOKENS['CONTENTSTORE']
|
||||
DOC_STORE_CONFIG = AUTH_TOKENS['DOC_STORE_CONFIG']
|
||||
# Datadog for events!
|
||||
|
||||
@@ -383,6 +383,9 @@ XBLOCK_MIXINS = (
|
||||
|
||||
XBLOCK_SELECT_FUNCTION = prefer_xmodules
|
||||
|
||||
# Paths to wrapper methods which should be applied to every XBlock's FieldData.
|
||||
XBLOCK_FIELD_DATA_WRAPPERS = ()
|
||||
|
||||
############################ Modulestore Configuration ################################
|
||||
MODULESTORE_BRANCH = 'draft-preferred'
|
||||
|
||||
@@ -417,6 +420,10 @@ MODULESTORE = {
|
||||
}
|
||||
}
|
||||
|
||||
# Modulestore-level field override providers. These field override providers don't
|
||||
# require student context.
|
||||
MODULESTORE_FIELD_OVERRIDE_PROVIDERS = ()
|
||||
|
||||
#################### Python sandbox ############################################
|
||||
|
||||
CODE_JAIL = {
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
"moment": "js/vendor/moment.min",
|
||||
"moment-with-locales": "js/vendor/moment-with-locales.min",
|
||||
"text": 'js/vendor/requirejs/text',
|
||||
"underscore": "js/vendor/underscore-min",
|
||||
"underscore": "common/js/vendor/underscore",
|
||||
"underscore.string": "js/vendor/underscore.string.min",
|
||||
"backbone": "js/vendor/backbone-min",
|
||||
"backbone-relational" : "js/vendor/backbone-relational.min",
|
||||
|
||||
@@ -26,7 +26,7 @@ requirejs.config({
|
||||
"moment": "xmodule_js/common_static/js/vendor/moment.min",
|
||||
"moment-with-locales": "xmodule_js/common_static/js/vendor/moment-with-locales.min",
|
||||
"text": "xmodule_js/common_static/js/vendor/requirejs/text",
|
||||
"underscore": "xmodule_js/common_static/js/vendor/underscore-min",
|
||||
"underscore": "xmodule_js/common_static/common/js/vendor/underscore",
|
||||
"underscore.string": "xmodule_js/common_static/js/vendor/underscore.string.min",
|
||||
"backbone": "xmodule_js/common_static/js/vendor/backbone-min",
|
||||
"backbone.associations": "xmodule_js/common_static/js/vendor/backbone-associations-min",
|
||||
|
||||
@@ -22,7 +22,7 @@ requirejs.config({
|
||||
"datepair": "xmodule_js/common_static/js/vendor/timepicker/datepair",
|
||||
"date": "xmodule_js/common_static/js/vendor/date",
|
||||
"text": "xmodule_js/common_static/js/vendor/requirejs/text",
|
||||
"underscore": "xmodule_js/common_static/js/vendor/underscore-min",
|
||||
"underscore": "xmodule_js/common_static/common/js/vendor/underscore",
|
||||
"underscore.string": "xmodule_js/common_static/js/vendor/underscore.string.min",
|
||||
"backbone": "xmodule_js/common_static/js/vendor/backbone-min",
|
||||
"backbone.associations": "xmodule_js/common_static/js/vendor/backbone-associations-min",
|
||||
|
||||
1
cms/static/edx-pattern-library
Symbolic link
1
cms/static/edx-pattern-library
Symbolic link
@@ -0,0 +1 @@
|
||||
../../common/static/edx-pattern-library
|
||||
1
cms/static/edx-ui-toolkit
Symbolic link
1
cms/static/edx-ui-toolkit
Symbolic link
@@ -0,0 +1 @@
|
||||
../../common/static/edx-ui-toolkit
|
||||
@@ -129,9 +129,9 @@ function ($, _, Backbone, gettext,
|
||||
if (event && event.preventDefault) { event.preventDefault(); }
|
||||
var model = this.model;
|
||||
var self = this;
|
||||
var titleText = gettext('Delete "<%= signatoryName %>" from the list of signatories?');
|
||||
var titleTextTemplate = _.template(gettext('Delete "<%= signatoryName %>" from the list of signatories?'));
|
||||
var confirm = new PromptView.Warning({
|
||||
title: _.template(titleText, {signatoryName: model.get('name')}),
|
||||
title: titleTextTemplate({signatoryName: model.get('name')}),
|
||||
message: gettext('This action cannot be undone.'),
|
||||
actions: {
|
||||
primary: {
|
||||
|
||||
@@ -1605,6 +1605,7 @@
|
||||
"Update post": "\u062a\u062d\u062f\u064a\u062b \u0627\u0644\u0645\u0646\u0634\u0648\u0631 ",
|
||||
"Update response": "\u062a\u062d\u062f\u064a\u062b \u0627\u0644\u0631\u062f ",
|
||||
"Update team.": "\u0639\u062f\u0651\u0644 \u0641\u0631\u064a\u0642.",
|
||||
"Updating Tags": "\u062a\u062d\u062f\u064a\u062b \u0627\u0644\u0634\u064e\u0627\u0631\u0627\u062a",
|
||||
"Updating with latest library content": "\u062c\u0627\u0631\u064a \u0627\u0644\u062a\u062d\u062f\u064a\u062b \u0645\u0639 \u0645\u0633\u062a\u062c\u062f\u0651\u0627\u062a \u0645\u062d\u062a\u0648\u0649 \u0627\u0644\u0645\u0643\u062a\u0628\u0629",
|
||||
"Upgrade Deadline": "\u0627\u0644\u0645\u0648\u0639\u062f \u0627\u0644\u0646\u0647\u0627\u0626\u064a \u0644\u0644\u062a\u062d\u062f\u064a\u062b ",
|
||||
"Upgrade to a Verified Certificate for %(courseName)s": "\u0642\u0645 \u0628\u0627\u0644\u062a\u0631\u0642\u064a\u0629 \u0644\u062a\u062d\u0635\u0644 \u0639\u0644\u0649 \u0634\u0647\u0627\u062f\u0629 \u0645\u0648\u062b\u0651\u0642\u0629 \u0644\u0644\u0645\u0633\u0627\u0642 %(courseName)s",
|
||||
|
||||
@@ -301,7 +301,7 @@
|
||||
"Cancel team updating.": "\u00c7\u00e4n\u00e7\u00e9l t\u00e9\u00e4m \u00fcpd\u00e4t\u00efng. \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455\u03b9\u0442 \u03b1\u043c\u0454\u0442, #",
|
||||
"Cannot delete when in use by a unit": "\u00c7\u00e4nn\u00f6t d\u00e9l\u00e9t\u00e9 wh\u00e9n \u00efn \u00fcs\u00e9 \u00df\u00fd \u00e4 \u00fcn\u00eft \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455\u03b9\u0442 \u03b1\u043c\u0454\u0442, \u00a2\u03c3\u03b7\u0455\u0454\u00a2\u0442\u0454\u0442#",
|
||||
"Cannot delete when in use by an experiment": "\u00c7\u00e4nn\u00f6t d\u00e9l\u00e9t\u00e9 wh\u00e9n \u00efn \u00fcs\u00e9 \u00df\u00fd \u00e4n \u00e9xp\u00e9r\u00efm\u00e9nt \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455\u03b9\u0442 \u03b1\u043c\u0454\u0442, \u00a2\u03c3\u03b7\u0455\u0454\u00a2\u0442\u0454\u0442\u03c5\u044f #",
|
||||
"Cannot drop more <% attrs.types %> than will assigned.": "\u00c7\u00e4nn\u00f6t dr\u00f6p m\u00f6r\u00e9 <% attrs.types %> th\u00e4n w\u00efll \u00e4ss\u00efgn\u00e9d. \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455\u03b9\u0442 \u03b1\u043c\u0454\u0442, \u00a2\u03c3\u03b7\u0455\u0454\u00a2\u0442\u0454\u0442\u03c5\u044f#",
|
||||
"Cannot drop more <%= types %> assignments than are assigned.": "\u00c7\u00e4nn\u00f6t dr\u00f6p m\u00f6r\u00e9 <%= types %> \u00e4ss\u00efgnm\u00e9nts th\u00e4n \u00e4r\u00e9 \u00e4ss\u00efgn\u00e9d. \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455\u03b9\u0442 \u03b1\u043c\u0454\u0442, \u00a2\u03c3\u03b7\u0455\u0454\u00a2\u0442\u0454\u0442\u03c5\u044f \u03b1#",
|
||||
"Caption": "\u00c7\u00e4pt\u00ef\u00f6n \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c #",
|
||||
"Caution: The last published version of this unit is live. By publishing changes you will change the student experience.": "\u00c7\u00e4\u00fct\u00ef\u00f6n: Th\u00e9 l\u00e4st p\u00fc\u00dfl\u00efsh\u00e9d v\u00e9rs\u00ef\u00f6n \u00f6f th\u00efs \u00fcn\u00eft \u00efs l\u00efv\u00e9. B\u00fd p\u00fc\u00dfl\u00efsh\u00efng \u00e7h\u00e4ng\u00e9s \u00fd\u00f6\u00fc w\u00efll \u00e7h\u00e4ng\u00e9 th\u00e9 st\u00fcd\u00e9nt \u00e9xp\u00e9r\u00ef\u00e9n\u00e7\u00e9. \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c#",
|
||||
"Cell": "\u00c7\u00e9ll \u2c60'\u03c3\u044f\u0454\u043c \u03b9#",
|
||||
@@ -385,7 +385,6 @@
|
||||
"Cohorts Disabled": "\u00c7\u00f6h\u00f6rts D\u00efs\u00e4\u00dfl\u00e9d \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455\u03b9\u0442 \u03b1\u043c#",
|
||||
"Cohorts Enabled": "\u00c7\u00f6h\u00f6rts \u00c9n\u00e4\u00dfl\u00e9d \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455\u03b9\u0442 \u03b1#",
|
||||
"Collapse All": "\u00c7\u00f6ll\u00e4ps\u00e9 \u00c0ll \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455#",
|
||||
"Collapse All Sections": "\u00c7\u00f6ll\u00e4ps\u00e9 \u00c0ll S\u00e9\u00e7t\u00ef\u00f6ns \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455\u03b9\u0442 \u03b1\u043c\u0454\u0442, #",
|
||||
"Collapse Instructions": "\u00c7\u00f6ll\u00e4ps\u00e9 \u00ccnstr\u00fc\u00e7t\u00ef\u00f6ns \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455\u03b9\u0442 \u03b1\u043c\u0454\u0442, #",
|
||||
"Collapse discussion": "\u00c7\u00f6ll\u00e4ps\u00e9 d\u00efs\u00e7\u00fcss\u00ef\u00f6n \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455\u03b9\u0442 \u03b1\u043c\u0454\u0442,#",
|
||||
"Collapse/Expand this %(xblock_type)s": "\u00c7\u00f6ll\u00e4ps\u00e9/\u00c9xp\u00e4nd th\u00efs %(xblock_type)s \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455\u03b9\u0442 \u03b1\u043c\u0454\u0442, \u00a2\u03c3\u03b7#",
|
||||
@@ -550,7 +549,6 @@
|
||||
"Edit Team": "\u00c9d\u00eft T\u00e9\u00e4m \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142#",
|
||||
"Edit Your Name": "\u00c9d\u00eft \u00dd\u00f6\u00fcr N\u00e4m\u00e9 \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455\u03b9\u0442#",
|
||||
"Edit post title": "\u00c9d\u00eft p\u00f6st t\u00eftl\u00e9 \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455\u03b9\u0442 \u03b1#",
|
||||
"Edit section release date": "\u00c9d\u00eft s\u00e9\u00e7t\u00ef\u00f6n r\u00e9l\u00e9\u00e4s\u00e9 d\u00e4t\u00e9 \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455\u03b9\u0442 \u03b1\u043c\u0454\u0442, \u00a2\u03c3\u03b7\u0455#",
|
||||
"Edit the name": "\u00c9d\u00eft th\u00e9 n\u00e4m\u00e9 \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455\u03b9#",
|
||||
"Edit this certificate?": "\u00c9d\u00eft th\u00efs \u00e7\u00e9rt\u00eff\u00ef\u00e7\u00e4t\u00e9? \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455\u03b9\u0442 \u03b1\u043c\u0454\u0442, \u00a2#",
|
||||
"Editable": "\u00c9d\u00eft\u00e4\u00dfl\u00e9 \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202#",
|
||||
@@ -639,7 +637,6 @@
|
||||
"Exception Granted": "\u00c9x\u00e7\u00e9pt\u00ef\u00f6n Gr\u00e4nt\u00e9d \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455\u03b9\u0442 \u03b1\u043c\u0454#",
|
||||
"Exit full browser": "\u00c9x\u00eft f\u00fcll \u00dfr\u00f6ws\u00e9r \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455\u03b9\u0442 \u03b1\u043c\u0454#",
|
||||
"Expand All": "\u00c9xp\u00e4nd \u00c0ll \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3#",
|
||||
"Expand All Sections": "\u00c9xp\u00e4nd \u00c0ll S\u00e9\u00e7t\u00ef\u00f6ns \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455\u03b9\u0442 \u03b1\u043c\u0454\u0442,#",
|
||||
"Expand Instructions": "\u00c9xp\u00e4nd \u00ccnstr\u00fc\u00e7t\u00ef\u00f6ns \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455\u03b9\u0442 \u03b1\u043c\u0454\u0442,#",
|
||||
"Expand discussion": "\u00c9xp\u00e4nd d\u00efs\u00e7\u00fcss\u00ef\u00f6n \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455\u03b9\u0442 \u03b1\u043c\u0454#",
|
||||
"Explain if other.": "\u00c9xpl\u00e4\u00efn \u00eff \u00f6th\u00e9r. \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455\u03b9\u0442 \u03b1\u043c\u0454#",
|
||||
@@ -1085,7 +1082,6 @@
|
||||
"Release Date:": "R\u00e9l\u00e9\u00e4s\u00e9 D\u00e4t\u00e9: \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455\u03b9#",
|
||||
"Release Status:": "R\u00e9l\u00e9\u00e4s\u00e9 St\u00e4t\u00fcs: \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455\u03b9\u0442 \u03b1#",
|
||||
"Release Time in UTC:": "R\u00e9l\u00e9\u00e4s\u00e9 T\u00efm\u00e9 \u00efn \u00dbT\u00c7: \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455\u03b9\u0442 \u03b1\u043c\u0454\u0442, #",
|
||||
"Release date:": "R\u00e9l\u00e9\u00e4s\u00e9 d\u00e4t\u00e9: \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455\u03b9#",
|
||||
"Release:": "R\u00e9l\u00e9\u00e4s\u00e9: \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202#",
|
||||
"Released:": "R\u00e9l\u00e9\u00e4s\u00e9d: \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142#",
|
||||
"Removal is in progress. To avoid errors, stay on this page until the process is complete.": "R\u00e9m\u00f6v\u00e4l \u00efs \u00efn pr\u00f6gr\u00e9ss. T\u00f6 \u00e4v\u00f6\u00efd \u00e9rr\u00f6rs, st\u00e4\u00fd \u00f6n th\u00efs p\u00e4g\u00e9 \u00fcnt\u00efl th\u00e9 pr\u00f6\u00e7\u00e9ss \u00efs \u00e7\u00f6mpl\u00e9t\u00e9. \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455\u03b9\u0442 \u03b1\u043c\u0454\u0442, \u00a2\u03c3\u03b7\u0455#",
|
||||
@@ -1291,7 +1287,7 @@
|
||||
"Task Type": "T\u00e4sk T\u00fdp\u00e9 \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142#",
|
||||
"Task inputs": "T\u00e4sk \u00efnp\u00fcts \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f #",
|
||||
"Teaching Assistant": "T\u00e9\u00e4\u00e7h\u00efng \u00c0ss\u00efst\u00e4nt \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455\u03b9\u0442 \u03b1\u043c\u0454\u0442#",
|
||||
"Team \"%(team)s\" successfully deleted.": "T\u00e9\u00e4m \"%(team)s\" s\u00fc\u00e7\u00e7\u00e9ssf\u00fcll\u00fd d\u00e9l\u00e9t\u00e9d. \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455\u03b9\u0442 \u03b1\u043c\u0454\u0442, \u00a2\u03c3\u03b7\u0455\u0454\u00a2\u0442\u0454#",
|
||||
"Team \"{team}\" successfully deleted.": "T\u00e9\u00e4m \"{team}\" s\u00fc\u00e7\u00e7\u00e9ssf\u00fcll\u00fd d\u00e9l\u00e9t\u00e9d. \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455\u03b9\u0442 \u03b1\u043c\u0454\u0442, \u00a2\u03c3\u03b7\u0455\u0454\u00a2\u0442\u0454#",
|
||||
"Team Description (Required) *": "T\u00e9\u00e4m D\u00e9s\u00e7r\u00efpt\u00ef\u00f6n (R\u00e9q\u00fc\u00efr\u00e9d) * \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455\u03b9\u0442 \u03b1\u043c\u0454\u0442, \u00a2\u03c3\u03b7\u0455\u0454\u00a2#",
|
||||
"Team Details": "T\u00e9\u00e4m D\u00e9t\u00e4\u00efls \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455#",
|
||||
"Team Name (Required) *": "T\u00e9\u00e4m N\u00e4m\u00e9 (R\u00e9q\u00fc\u00efr\u00e9d) * \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455\u03b9\u0442 \u03b1\u043c\u0454\u0442, \u00a2#",
|
||||
@@ -1818,7 +1814,6 @@
|
||||
"with %(section_or_subsection)s": "w\u00efth %(section_or_subsection)s \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202#",
|
||||
"{browse_span_start}Browse teams in other topics{span_end} or {search_span_start}search teams{span_end} in this topic. If you still can't find a team to join, {create_span_start}create a new team in this topic{span_end}.": "{browse_span_start}Br\u00f6ws\u00e9 t\u00e9\u00e4ms \u00efn \u00f6th\u00e9r t\u00f6p\u00ef\u00e7s{span_end} \u00f6r {search_span_start}s\u00e9\u00e4r\u00e7h t\u00e9\u00e4ms{span_end} \u00efn th\u00efs t\u00f6p\u00ef\u00e7. \u00ccf \u00fd\u00f6\u00fc st\u00efll \u00e7\u00e4n't f\u00efnd \u00e4 t\u00e9\u00e4m t\u00f6 j\u00f6\u00efn, {create_span_start}\u00e7r\u00e9\u00e4t\u00e9 \u00e4 n\u00e9w t\u00e9\u00e4m \u00efn th\u00efs t\u00f6p\u00ef\u00e7{span_end}. \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455\u03b9\u0442 \u03b1\u043c\u0454\u0442, \u00a2\u03c3\u03b7\u0455\u0454\u00a2\u0442\u0454\u0442\u03c5\u044f \u03b1\u2202\u03b9\u03c1\u03b9\u0455\u03b9\u00a2\u03b9\u03b7g \u0454\u0142\u03b9\u0442, \u0455\u0454\u2202 \u2202\u03c3 \u0454\u03b9\u03c5\u0455\u043c\u03c3\u2202 \u0442\u0454\u043c\u03c1\u03c3\u044f \u03b9\u03b7\u00a2\u03b9\u2202\u03b9\u2202\u03c5\u03b7\u0442 \u03c5\u0442 \u0142\u03b1\u0432\u03c3\u044f\u0454 \u0454\u0442 \u2202\u03c3\u0142\u03c3\u044f\u0454 \u043c\u03b1g\u03b7\u03b1 \u03b1\u0142\u03b9q\u03c5\u03b1. \u03c5\u0442 \u0454\u03b7\u03b9\u043c \u03b1\u2202 \u043c\u03b9\u03b7\u03b9\u043c \u03bd\u0454\u03b7\u03b9\u03b1\u043c, q\u03c5\u03b9\u0455 \u03b7\u03c3\u0455\u0442\u044f\u03c5\u2202 \u0454\u03c7\u0454\u044f\u00a2\u03b9\u0442\u03b1\u0442\u03b9\u03c3\u03b7 \u03c5\u0142\u0142\u03b1\u043c\u00a2\u03c3 \u0142\u03b1\u0432\u03c3\u044f\u03b9\u0455 \u03b7\u03b9\u0455\u03b9 \u03c5\u0442 \u03b1\u0142\u03b9q\u03c5\u03b9\u03c1 \u0454\u03c7 \u0454\u03b1 \u00a2\u03c3\u043c\u043c\u03c3\u2202\u03c3 \u00a2\u03c3\u03b7\u0455\u0454q\u03c5\u03b1\u0442. \u2202\u03c5\u03b9\u0455 \u03b1\u03c5\u0442\u0454 \u03b9\u044f\u03c5\u044f\u0454 \u2202\u03c3\u0142\u03c3\u044f \u03b9\u03b7 \u044f\u0454\u03c1\u044f\u0454\u043d\u0454\u03b7\u2202\u0454\u044f\u03b9\u0442 \u03b9\u03b7 \u03bd\u03c3\u0142\u03c5\u03c1\u0442\u03b1\u0442\u0454 \u03bd\u0454\u0142\u03b9\u0442 \u0454\u0455\u0455\u0454 \u00a2\u03b9\u0142\u0142\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f\u0454 \u0454\u03c5 \u0192\u03c5g\u03b9\u03b1\u0442 \u03b7\u03c5\u0142\u0142\u03b1 \u03c1\u03b1\u044f\u03b9\u03b1\u0442\u03c5\u044f. \u0454\u03c7\u00a2\u0454\u03c1\u0442\u0454\u03c5\u044f \u0455\u03b9\u03b7\u0442 \u03c3\u00a2\u00a2\u03b1\u0454\u00a2\u03b1\u0442 \u00a2\u03c5\u03c1\u03b9\u2202\u03b1\u0442\u03b1\u0442 \u03b7\u03c3\u03b7 \u03c1\u044f\u03c3\u03b9\u2202\u0454\u03b7\u0442, \u0455\u03c5\u03b7\u0442 \u03b9\u03b7 \u00a2\u03c5\u0142\u03c1\u03b1 q\u03c5\u03b9 \u03c3\u0192\u0192\u03b9\u00a2\u03b9\u03b1 \u2202\u0454\u0455\u0454\u044f\u03c5\u03b7\u0442 \u043c\u03c3\u0142\u0142\u03b9\u0442 \u03b1\u03b7#",
|
||||
"{email} is already on the {container} team. Recheck the email address if you want to add a new member.": "{email} \u00efs \u00e4lr\u00e9\u00e4d\u00fd \u00f6n th\u00e9 {container} t\u00e9\u00e4m. R\u00e9\u00e7h\u00e9\u00e7k th\u00e9 \u00e9m\u00e4\u00efl \u00e4ddr\u00e9ss \u00eff \u00fd\u00f6\u00fc w\u00e4nt t\u00f6 \u00e4dd \u00e4 n\u00e9w m\u00e9m\u00df\u00e9r. \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455\u03b9\u0442 \u03b1\u043c\u0454\u0442, \u00a2\u03c3\u03b7\u0455#",
|
||||
"{month}/{day}/{year} at {hour}:{minute} UTC": "{month}/{day}/{year} \u00e4t {hour}:{minute} \u00dbT\u00c7 \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455\u03b9\u0442 \u03b1\u043c\u0454\u0442, \u00a2\u03c3\u03b7\u0455#",
|
||||
"{numMoved} student was removed from {oldCohort}": [
|
||||
"{numMoved} st\u00fcd\u00e9nt w\u00e4s r\u00e9m\u00f6v\u00e9d fr\u00f6m {oldCohort} \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455\u03b9\u0442 \u03b1\u043c\u0454\u0442, \u00a2\u03c3\u03b7\u0455\u0454\u00a2\u0442\u0454#",
|
||||
"{numMoved} st\u00fcd\u00e9nts w\u00e9r\u00e9 r\u00e9m\u00f6v\u00e9d fr\u00f6m {oldCohort} \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455\u03b9\u0442 \u03b1\u043c\u0454\u0442, \u00a2\u03c3\u03b7\u0455\u0454\u00a2\u0442\u0454\u0442#"
|
||||
|
||||
@@ -301,7 +301,7 @@
|
||||
"Cancel team updating.": "\u023b\u0250n\u0254\u01ddl \u0287\u01dd\u0250\u026f ndd\u0250\u0287\u1d09n\u0183.",
|
||||
"Cannot delete when in use by a unit": "\u023b\u0250nn\u00f8\u0287 d\u01ddl\u01dd\u0287\u01dd \u028d\u0265\u01ddn \u1d09n ns\u01dd b\u028e \u0250 nn\u1d09\u0287",
|
||||
"Cannot delete when in use by an experiment": "\u023b\u0250nn\u00f8\u0287 d\u01ddl\u01dd\u0287\u01dd \u028d\u0265\u01ddn \u1d09n ns\u01dd b\u028e \u0250n \u01ddxd\u01dd\u0279\u1d09\u026f\u01ddn\u0287",
|
||||
"Cannot drop more <% attrs.types %> than will assigned.": "\u023b\u0250nn\u00f8\u0287 d\u0279\u00f8d \u026f\u00f8\u0279\u01dd <% attrs.types %> \u0287\u0265\u0250n \u028d\u1d09ll \u0250ss\u1d09\u0183n\u01ddd.",
|
||||
"Cannot drop more <%= types %> assignments than are assigned.": "\u023b\u0250nn\u00f8\u0287 d\u0279\u00f8d \u026f\u00f8\u0279\u01dd <%= types %> \u0250ss\u1d09\u0183n\u026f\u01ddn\u0287s \u0287\u0265\u0250n \u0250\u0279\u01dd \u0250ss\u1d09\u0183n\u01ddd.",
|
||||
"Caption": "\u023b\u0250d\u0287\u1d09\u00f8n",
|
||||
"Caution: The last published version of this unit is live. By publishing changes you will change the student experience.": "\u023b\u0250n\u0287\u1d09\u00f8n: \u0166\u0265\u01dd l\u0250s\u0287 dnbl\u1d09s\u0265\u01ddd \u028c\u01dd\u0279s\u1d09\u00f8n \u00f8\u025f \u0287\u0265\u1d09s nn\u1d09\u0287 \u1d09s l\u1d09\u028c\u01dd. \u0243\u028e dnbl\u1d09s\u0265\u1d09n\u0183 \u0254\u0265\u0250n\u0183\u01dds \u028e\u00f8n \u028d\u1d09ll \u0254\u0265\u0250n\u0183\u01dd \u0287\u0265\u01dd s\u0287nd\u01ddn\u0287 \u01ddxd\u01dd\u0279\u1d09\u01ddn\u0254\u01dd.",
|
||||
"Cell": "\u023b\u01ddll",
|
||||
@@ -385,7 +385,6 @@
|
||||
"Cohorts Disabled": "\u023b\u00f8\u0265\u00f8\u0279\u0287s \u0110\u1d09s\u0250bl\u01ddd",
|
||||
"Cohorts Enabled": "\u023b\u00f8\u0265\u00f8\u0279\u0287s \u0246n\u0250bl\u01ddd",
|
||||
"Collapse All": "\u023b\u00f8ll\u0250ds\u01dd \u023all",
|
||||
"Collapse All Sections": "\u023b\u00f8ll\u0250ds\u01dd \u023all S\u01dd\u0254\u0287\u1d09\u00f8ns",
|
||||
"Collapse Instructions": "\u023b\u00f8ll\u0250ds\u01dd \u0197ns\u0287\u0279n\u0254\u0287\u1d09\u00f8ns",
|
||||
"Collapse discussion": "\u023b\u00f8ll\u0250ds\u01dd d\u1d09s\u0254nss\u1d09\u00f8n",
|
||||
"Collapse/Expand this %(xblock_type)s": "\u023b\u00f8ll\u0250ds\u01dd/\u0246xd\u0250nd \u0287\u0265\u1d09s %(xblock_type)s",
|
||||
@@ -550,7 +549,6 @@
|
||||
"Edit Team": "\u0246d\u1d09\u0287 \u0166\u01dd\u0250\u026f",
|
||||
"Edit Your Name": "\u0246d\u1d09\u0287 \u024e\u00f8n\u0279 N\u0250\u026f\u01dd",
|
||||
"Edit post title": "\u0246d\u1d09\u0287 d\u00f8s\u0287 \u0287\u1d09\u0287l\u01dd",
|
||||
"Edit section release date": "\u0246d\u1d09\u0287 s\u01dd\u0254\u0287\u1d09\u00f8n \u0279\u01ddl\u01dd\u0250s\u01dd d\u0250\u0287\u01dd",
|
||||
"Edit the name": "\u0246d\u1d09\u0287 \u0287\u0265\u01dd n\u0250\u026f\u01dd",
|
||||
"Edit this certificate?": "\u0246d\u1d09\u0287 \u0287\u0265\u1d09s \u0254\u01dd\u0279\u0287\u1d09\u025f\u1d09\u0254\u0250\u0287\u01dd?",
|
||||
"Editable": "\u0246d\u1d09\u0287\u0250bl\u01dd",
|
||||
@@ -639,7 +637,6 @@
|
||||
"Exception Granted": "\u0246x\u0254\u01ddd\u0287\u1d09\u00f8n \u01e4\u0279\u0250n\u0287\u01ddd",
|
||||
"Exit full browser": "\u0246x\u1d09\u0287 \u025fnll b\u0279\u00f8\u028ds\u01dd\u0279",
|
||||
"Expand All": "\u0246xd\u0250nd \u023all",
|
||||
"Expand All Sections": "\u0246xd\u0250nd \u023all S\u01dd\u0254\u0287\u1d09\u00f8ns",
|
||||
"Expand Instructions": "\u0246xd\u0250nd \u0197ns\u0287\u0279n\u0254\u0287\u1d09\u00f8ns",
|
||||
"Expand discussion": "\u0246xd\u0250nd d\u1d09s\u0254nss\u1d09\u00f8n",
|
||||
"Explain if other.": "\u0246xdl\u0250\u1d09n \u1d09\u025f \u00f8\u0287\u0265\u01dd\u0279.",
|
||||
@@ -1085,7 +1082,6 @@
|
||||
"Release Date:": "\u024c\u01ddl\u01dd\u0250s\u01dd \u0110\u0250\u0287\u01dd:",
|
||||
"Release Status:": "\u024c\u01ddl\u01dd\u0250s\u01dd S\u0287\u0250\u0287ns:",
|
||||
"Release Time in UTC:": "\u024c\u01ddl\u01dd\u0250s\u01dd \u0166\u1d09\u026f\u01dd \u1d09n \u0244\u0166\u023b:",
|
||||
"Release date:": "\u024c\u01ddl\u01dd\u0250s\u01dd d\u0250\u0287\u01dd:",
|
||||
"Release:": "\u024c\u01ddl\u01dd\u0250s\u01dd:",
|
||||
"Released:": "\u024c\u01ddl\u01dd\u0250s\u01ddd:",
|
||||
"Removal is in progress. To avoid errors, stay on this page until the process is complete.": "\u024c\u01dd\u026f\u00f8\u028c\u0250l \u1d09s \u1d09n d\u0279\u00f8\u0183\u0279\u01ddss. \u0166\u00f8 \u0250\u028c\u00f8\u1d09d \u01dd\u0279\u0279\u00f8\u0279s, s\u0287\u0250\u028e \u00f8n \u0287\u0265\u1d09s d\u0250\u0183\u01dd nn\u0287\u1d09l \u0287\u0265\u01dd d\u0279\u00f8\u0254\u01ddss \u1d09s \u0254\u00f8\u026fdl\u01dd\u0287\u01dd.",
|
||||
@@ -1291,7 +1287,7 @@
|
||||
"Task Type": "\u0166\u0250s\u029e \u0166\u028ed\u01dd",
|
||||
"Task inputs": "\u0166\u0250s\u029e \u1d09ndn\u0287s",
|
||||
"Teaching Assistant": "\u0166\u01dd\u0250\u0254\u0265\u1d09n\u0183 \u023ass\u1d09s\u0287\u0250n\u0287",
|
||||
"Team \"%(team)s\" successfully deleted.": "\u0166\u01dd\u0250\u026f \"%(team)s\" sn\u0254\u0254\u01ddss\u025fnll\u028e d\u01ddl\u01dd\u0287\u01ddd.",
|
||||
"Team \"{team}\" successfully deleted.": "\u0166\u01dd\u0250\u026f \"{team}\" sn\u0254\u0254\u01ddss\u025fnll\u028e d\u01ddl\u01dd\u0287\u01ddd.",
|
||||
"Team Description (Required) *": "\u0166\u01dd\u0250\u026f \u0110\u01dds\u0254\u0279\u1d09d\u0287\u1d09\u00f8n (\u024c\u01ddbn\u1d09\u0279\u01ddd) *",
|
||||
"Team Details": "\u0166\u01dd\u0250\u026f \u0110\u01dd\u0287\u0250\u1d09ls",
|
||||
"Team Name (Required) *": "\u0166\u01dd\u0250\u026f N\u0250\u026f\u01dd (\u024c\u01ddbn\u1d09\u0279\u01ddd) *",
|
||||
@@ -1818,7 +1814,6 @@
|
||||
"with %(section_or_subsection)s": "\u028d\u1d09\u0287\u0265 %(section_or_subsection)s",
|
||||
"{browse_span_start}Browse teams in other topics{span_end} or {search_span_start}search teams{span_end} in this topic. If you still can't find a team to join, {create_span_start}create a new team in this topic{span_end}.": "{browse_span_start}\u0243\u0279\u00f8\u028ds\u01dd \u0287\u01dd\u0250\u026fs \u1d09n \u00f8\u0287\u0265\u01dd\u0279 \u0287\u00f8d\u1d09\u0254s{span_end} \u00f8\u0279 {search_span_start}s\u01dd\u0250\u0279\u0254\u0265 \u0287\u01dd\u0250\u026fs{span_end} \u1d09n \u0287\u0265\u1d09s \u0287\u00f8d\u1d09\u0254. \u0197\u025f \u028e\u00f8n s\u0287\u1d09ll \u0254\u0250n'\u0287 \u025f\u1d09nd \u0250 \u0287\u01dd\u0250\u026f \u0287\u00f8 \u027e\u00f8\u1d09n, {create_span_start}\u0254\u0279\u01dd\u0250\u0287\u01dd \u0250 n\u01dd\u028d \u0287\u01dd\u0250\u026f \u1d09n \u0287\u0265\u1d09s \u0287\u00f8d\u1d09\u0254{span_end}.",
|
||||
"{email} is already on the {container} team. Recheck the email address if you want to add a new member.": "{email} \u1d09s \u0250l\u0279\u01dd\u0250d\u028e \u00f8n \u0287\u0265\u01dd {container} \u0287\u01dd\u0250\u026f. \u024c\u01dd\u0254\u0265\u01dd\u0254\u029e \u0287\u0265\u01dd \u01dd\u026f\u0250\u1d09l \u0250dd\u0279\u01ddss \u1d09\u025f \u028e\u00f8n \u028d\u0250n\u0287 \u0287\u00f8 \u0250dd \u0250 n\u01dd\u028d \u026f\u01dd\u026fb\u01dd\u0279.",
|
||||
"{month}/{day}/{year} at {hour}:{minute} UTC": "{month}/{day}/{year} \u0250\u0287 {hour}:{minute} \u0244\u0166\u023b",
|
||||
"{numMoved} student was removed from {oldCohort}": [
|
||||
"{numMoved} s\u0287nd\u01ddn\u0287 \u028d\u0250s \u0279\u01dd\u026f\u00f8\u028c\u01ddd \u025f\u0279\u00f8\u026f {oldCohort}",
|
||||
"{numMoved} s\u0287nd\u01ddn\u0287s \u028d\u01dd\u0279\u01dd \u0279\u01dd\u026f\u00f8\u028c\u01ddd \u025f\u0279\u00f8\u026f {oldCohort}"
|
||||
|
||||
@@ -301,7 +301,7 @@
|
||||
"Cancel team updating.": "\u0630\u0634\u0631\u0630\u062b\u0645 \u0641\u062b\u0634\u0648 \u0639\u062d\u064a\u0634\u0641\u0647\u0631\u0644.",
|
||||
"Cannot delete when in use by a unit": "\u0630\u0634\u0631\u0631\u062e\u0641 \u064a\u062b\u0645\u062b\u0641\u062b \u0635\u0627\u062b\u0631 \u0647\u0631 \u0639\u0633\u062b \u0632\u063a \u0634 \u0639\u0631\u0647\u0641",
|
||||
"Cannot delete when in use by an experiment": "\u0630\u0634\u0631\u0631\u062e\u0641 \u064a\u062b\u0645\u062b\u0641\u062b \u0635\u0627\u062b\u0631 \u0647\u0631 \u0639\u0633\u062b \u0632\u063a \u0634\u0631 \u062b\u0637\u062d\u062b\u0642\u0647\u0648\u062b\u0631\u0641",
|
||||
"Cannot drop more <% attrs.types %> than will assigned.": "\u0630\u0634\u0631\u0631\u062e\u0641 \u064a\u0642\u062e\u062d \u0648\u062e\u0642\u062b <% attrs.types %> \u0641\u0627\u0634\u0631 \u0635\u0647\u0645\u0645 \u0634\u0633\u0633\u0647\u0644\u0631\u062b\u064a.",
|
||||
"Cannot drop more <%= types %> assignments than are assigned.": "\u0630\u0634\u0631\u0631\u062e\u0641 \u064a\u0642\u062e\u062d \u0648\u062e\u0642\u062b <%= types %> \u0634\u0633\u0633\u0647\u0644\u0631\u0648\u062b\u0631\u0641\u0633 \u0641\u0627\u0634\u0631 \u0634\u0642\u062b \u0634\u0633\u0633\u0647\u0644\u0631\u062b\u064a.",
|
||||
"Caption": "\u0630\u0634\u062d\u0641\u0647\u062e\u0631",
|
||||
"Caution: The last published version of this unit is live. By publishing changes you will change the student experience.": "\u0630\u0634\u0639\u0641\u0647\u062e\u0631: \u0641\u0627\u062b \u0645\u0634\u0633\u0641 \u062d\u0639\u0632\u0645\u0647\u0633\u0627\u062b\u064a \u062f\u062b\u0642\u0633\u0647\u062e\u0631 \u062e\u0628 \u0641\u0627\u0647\u0633 \u0639\u0631\u0647\u0641 \u0647\u0633 \u0645\u0647\u062f\u062b. \u0632\u063a \u062d\u0639\u0632\u0645\u0647\u0633\u0627\u0647\u0631\u0644 \u0630\u0627\u0634\u0631\u0644\u062b\u0633 \u063a\u062e\u0639 \u0635\u0647\u0645\u0645 \u0630\u0627\u0634\u0631\u0644\u062b \u0641\u0627\u062b \u0633\u0641\u0639\u064a\u062b\u0631\u0641 \u062b\u0637\u062d\u062b\u0642\u0647\u062b\u0631\u0630\u062b.",
|
||||
"Cell": "\u0630\u062b\u0645\u0645",
|
||||
@@ -385,7 +385,6 @@
|
||||
"Cohorts Disabled": "\u0630\u062e\u0627\u062e\u0642\u0641\u0633 \u064a\u0647\u0633\u0634\u0632\u0645\u062b\u064a",
|
||||
"Cohorts Enabled": "\u0630\u062e\u0627\u062e\u0642\u0641\u0633 \u062b\u0631\u0634\u0632\u0645\u062b\u064a",
|
||||
"Collapse All": "\u0630\u062e\u0645\u0645\u0634\u062d\u0633\u062b \u0634\u0645\u0645",
|
||||
"Collapse All Sections": "\u0630\u062e\u0645\u0645\u0634\u062d\u0633\u062b \u0634\u0645\u0645 \u0633\u062b\u0630\u0641\u0647\u062e\u0631\u0633",
|
||||
"Collapse Instructions": "\u0630\u062e\u0645\u0645\u0634\u062d\u0633\u062b \u0647\u0631\u0633\u0641\u0642\u0639\u0630\u0641\u0647\u062e\u0631\u0633",
|
||||
"Collapse discussion": "\u0630\u062e\u0645\u0645\u0634\u062d\u0633\u062b \u064a\u0647\u0633\u0630\u0639\u0633\u0633\u0647\u062e\u0631",
|
||||
"Collapse/Expand this %(xblock_type)s": "\u0630\u062e\u0645\u0645\u0634\u062d\u0633\u062b/\u062b\u0637\u062d\u0634\u0631\u064a \u0641\u0627\u0647\u0633 %(xblock_type)s",
|
||||
@@ -550,7 +549,6 @@
|
||||
"Edit Team": "\u062b\u064a\u0647\u0641 \u0641\u062b\u0634\u0648",
|
||||
"Edit Your Name": "\u062b\u064a\u0647\u0641 \u063a\u062e\u0639\u0642 \u0631\u0634\u0648\u062b",
|
||||
"Edit post title": "\u062b\u064a\u0647\u0641 \u062d\u062e\u0633\u0641 \u0641\u0647\u0641\u0645\u062b",
|
||||
"Edit section release date": "\u062b\u064a\u0647\u0641 \u0633\u062b\u0630\u0641\u0647\u062e\u0631 \u0642\u062b\u0645\u062b\u0634\u0633\u062b \u064a\u0634\u0641\u062b",
|
||||
"Edit the name": "\u062b\u064a\u0647\u0641 \u0641\u0627\u062b \u0631\u0634\u0648\u062b",
|
||||
"Edit this certificate?": "\u062b\u064a\u0647\u0641 \u0641\u0627\u0647\u0633 \u0630\u062b\u0642\u0641\u0647\u0628\u0647\u0630\u0634\u0641\u062b?",
|
||||
"Editable": "\u062b\u064a\u0647\u0641\u0634\u0632\u0645\u062b",
|
||||
@@ -639,7 +637,6 @@
|
||||
"Exception Granted": "\u062b\u0637\u0630\u062b\u062d\u0641\u0647\u062e\u0631 \u0644\u0642\u0634\u0631\u0641\u062b\u064a",
|
||||
"Exit full browser": "\u062b\u0637\u0647\u0641 \u0628\u0639\u0645\u0645 \u0632\u0642\u062e\u0635\u0633\u062b\u0642",
|
||||
"Expand All": "\u062b\u0637\u062d\u0634\u0631\u064a \u0634\u0645\u0645",
|
||||
"Expand All Sections": "\u062b\u0637\u062d\u0634\u0631\u064a \u0634\u0645\u0645 \u0633\u062b\u0630\u0641\u0647\u062e\u0631\u0633",
|
||||
"Expand Instructions": "\u062b\u0637\u062d\u0634\u0631\u064a \u0647\u0631\u0633\u0641\u0642\u0639\u0630\u0641\u0647\u062e\u0631\u0633",
|
||||
"Expand discussion": "\u062b\u0637\u062d\u0634\u0631\u064a \u064a\u0647\u0633\u0630\u0639\u0633\u0633\u0647\u062e\u0631",
|
||||
"Explain if other.": "\u062b\u0637\u062d\u0645\u0634\u0647\u0631 \u0647\u0628 \u062e\u0641\u0627\u062b\u0642.",
|
||||
@@ -1085,7 +1082,6 @@
|
||||
"Release Date:": "\u0642\u062b\u0645\u062b\u0634\u0633\u062b \u064a\u0634\u0641\u062b:",
|
||||
"Release Status:": "\u0642\u062b\u0645\u062b\u0634\u0633\u062b \u0633\u0641\u0634\u0641\u0639\u0633:",
|
||||
"Release Time in UTC:": "\u0642\u062b\u0645\u062b\u0634\u0633\u062b \u0641\u0647\u0648\u062b \u0647\u0631 \u0639\u0641\u0630:",
|
||||
"Release date:": "\u0642\u062b\u0645\u062b\u0634\u0633\u062b \u064a\u0634\u0641\u062b:",
|
||||
"Release:": "\u0642\u062b\u0645\u062b\u0634\u0633\u062b:",
|
||||
"Released:": "\u0642\u062b\u0645\u062b\u0634\u0633\u062b\u064a:",
|
||||
"Removal is in progress. To avoid errors, stay on this page until the process is complete.": "\u0642\u062b\u0648\u062e\u062f\u0634\u0645 \u0647\u0633 \u0647\u0631 \u062d\u0642\u062e\u0644\u0642\u062b\u0633\u0633. \u0641\u062e \u0634\u062f\u062e\u0647\u064a \u062b\u0642\u0642\u062e\u0642\u0633, \u0633\u0641\u0634\u063a \u062e\u0631 \u0641\u0627\u0647\u0633 \u062d\u0634\u0644\u062b \u0639\u0631\u0641\u0647\u0645 \u0641\u0627\u062b \u062d\u0642\u062e\u0630\u062b\u0633\u0633 \u0647\u0633 \u0630\u062e\u0648\u062d\u0645\u062b\u0641\u062b.",
|
||||
@@ -1291,7 +1287,7 @@
|
||||
"Task Type": "\u0641\u0634\u0633\u0646 \u0641\u063a\u062d\u062b",
|
||||
"Task inputs": "\u0641\u0634\u0633\u0646 \u0647\u0631\u062d\u0639\u0641\u0633",
|
||||
"Teaching Assistant": "\u0641\u062b\u0634\u0630\u0627\u0647\u0631\u0644 \u0634\u0633\u0633\u0647\u0633\u0641\u0634\u0631\u0641",
|
||||
"Team \"%(team)s\" successfully deleted.": "\u0641\u062b\u0634\u0648 \"%(team)s\" \u0633\u0639\u0630\u0630\u062b\u0633\u0633\u0628\u0639\u0645\u0645\u063a \u064a\u062b\u0645\u062b\u0641\u062b\u064a.",
|
||||
"Team \"{team}\" successfully deleted.": "\u0641\u062b\u0634\u0648 \"{team}\" \u0633\u0639\u0630\u0630\u062b\u0633\u0633\u0628\u0639\u0645\u0645\u063a \u064a\u062b\u0645\u062b\u0641\u062b\u064a.",
|
||||
"Team Description (Required) *": "\u0641\u062b\u0634\u0648 \u064a\u062b\u0633\u0630\u0642\u0647\u062d\u0641\u0647\u062e\u0631 (\u0642\u062b\u0636\u0639\u0647\u0642\u062b\u064a) *",
|
||||
"Team Details": "\u0641\u062b\u0634\u0648 \u064a\u062b\u0641\u0634\u0647\u0645\u0633",
|
||||
"Team Name (Required) *": "\u0641\u062b\u0634\u0648 \u0631\u0634\u0648\u062b (\u0642\u062b\u0636\u0639\u0647\u0642\u062b\u064a) *",
|
||||
@@ -1818,7 +1814,6 @@
|
||||
"with %(section_or_subsection)s": "\u0635\u0647\u0641\u0627 %(section_or_subsection)s",
|
||||
"{browse_span_start}Browse teams in other topics{span_end} or {search_span_start}search teams{span_end} in this topic. If you still can't find a team to join, {create_span_start}create a new team in this topic{span_end}.": "{browse_span_start}\u0632\u0642\u062e\u0635\u0633\u062b \u0641\u062b\u0634\u0648\u0633 \u0647\u0631 \u062e\u0641\u0627\u062b\u0642 \u0641\u062e\u062d\u0647\u0630\u0633{span_end} \u062e\u0642 {search_span_start}\u0633\u062b\u0634\u0642\u0630\u0627 \u0641\u062b\u0634\u0648\u0633{span_end} \u0647\u0631 \u0641\u0627\u0647\u0633 \u0641\u062e\u062d\u0647\u0630. \u0647\u0628 \u063a\u062e\u0639 \u0633\u0641\u0647\u0645\u0645 \u0630\u0634\u0631'\u0641 \u0628\u0647\u0631\u064a \u0634 \u0641\u062b\u0634\u0648 \u0641\u062e \u062a\u062e\u0647\u0631, {create_span_start}\u0630\u0642\u062b\u0634\u0641\u062b \u0634 \u0631\u062b\u0635 \u0641\u062b\u0634\u0648 \u0647\u0631 \u0641\u0627\u0647\u0633 \u0641\u062e\u062d\u0647\u0630{span_end}.",
|
||||
"{email} is already on the {container} team. Recheck the email address if you want to add a new member.": "{email} \u0647\u0633 \u0634\u0645\u0642\u062b\u0634\u064a\u063a \u062e\u0631 \u0641\u0627\u062b {container} \u0641\u062b\u0634\u0648. \u0642\u062b\u0630\u0627\u062b\u0630\u0646 \u0641\u0627\u062b \u062b\u0648\u0634\u0647\u0645 \u0634\u064a\u064a\u0642\u062b\u0633\u0633 \u0647\u0628 \u063a\u062e\u0639 \u0635\u0634\u0631\u0641 \u0641\u062e \u0634\u064a\u064a \u0634 \u0631\u062b\u0635 \u0648\u062b\u0648\u0632\u062b\u0642.",
|
||||
"{month}/{day}/{year} at {hour}:{minute} UTC": "{month}/{day}/{year} \u0634\u0641 {hour}:{minute} \u0639\u0641\u0630",
|
||||
"{numMoved} student was removed from {oldCohort}": [
|
||||
"{numMoved} \u0633\u0641\u0639\u064a\u062b\u0631\u0641 \u0635\u0634\u0633 \u0642\u062b\u0648\u062e\u062f\u062b\u064a \u0628\u0642\u062e\u0648 {oldCohort}",
|
||||
"{numMoved} \u0633\u0641\u0639\u064a\u062b\u0631\u0641\u0633 \u0635\u062b\u0642\u062b \u0642\u062b\u0648\u062e\u062f\u062b\u064a \u0628\u0642\u062e\u0648 {oldCohort}"
|
||||
|
||||
@@ -649,6 +649,7 @@
|
||||
"Error deleting entrance exam state for student '{student_id}'. Make sure student identifier is correct.": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u0441\u0442\u0430\u0442\u0443\u0441\u0430 \u0432\u0441\u0442\u0443\u043f\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u0438\u0441\u043f\u044b\u0442\u0430\u043d\u0438\u044f \u0434\u043b\u044f '{student_id}'. \u041f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f.",
|
||||
"Error deleting student '<%= student_id %>'s state on problem '<%= problem_id %>'. Make sure that the problem and student identifiers are complete and correct.": "\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0438 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0437\u0430\u0434\u0430\u043d\u0438\u044f '<%= problem_id %>' \u0434\u043b\u044f '<%= student_id %>'. \u0423\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u044b \u0437\u0430\u0434\u0430\u043d\u0438\u044f \u0438 \u0441\u043b\u0443\u0448\u0430\u0442\u0435\u043b\u044f \u0432\u0432\u0435\u0434\u0435\u043d\u044b \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e \u0438 \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e.",
|
||||
"Error enrolling/unenrolling users.": "\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438/\u043e\u0442\u043c\u0435\u043d\u0435 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439:",
|
||||
"Error generating ORA data report. Please try again.": "\u041f\u0440\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 \u043e\u0442\u0447\u0435\u0442\u0430 \u043f\u043e \u0437\u0430\u0434\u0430\u043d\u0438\u044e \u0441 \u043e\u0442\u0432\u0435\u0442\u0430\u043c\u0438 \u0432 \u0441\u0432\u043e\u0431\u043e\u0434\u043d\u043e\u0439 \u0444\u043e\u0440\u043c\u0435 \u0432\u043e\u0437\u043d\u0438\u043a\u043b\u0430 \u043e\u0448\u0438\u0431\u043a\u0430. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0435\u0449\u0435 \u0440\u0430\u0437.",
|
||||
"Error generating grades. Please try again.": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0440\u0430\u0441\u0447\u0451\u0442\u0430 \u043e\u0446\u0435\u043d\u043e\u043a. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0435\u0449\u0451 \u0440\u0430\u0437.",
|
||||
"Error generating list of students who may enroll. Please try again.": "\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 \u0441\u043f\u0438\u0441\u043a\u0430 \u0441\u043b\u0443\u0448\u0430\u0442\u0435\u043b\u0435\u0439, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u0433\u0443\u0442 \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043d\u0430 \u043a\u0443\u0440\u0441. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0435\u0449\u0451 \u0440\u0430\u0437.",
|
||||
"Error generating problem grade report. Please try again.": "\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e\u0433\u043e \u043e\u0446\u0435\u043d\u043e\u0447\u043d\u043e\u0433\u043e \u043b\u0438\u0441\u0442\u0430. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0435\u0449\u0451 \u0440\u0430\u0437.",
|
||||
|
||||
@@ -66,9 +66,10 @@ var CourseGrader = Backbone.Model.extend({
|
||||
else attrs.drop_count = intDropCount;
|
||||
}
|
||||
if (_.has(attrs, 'min_count') && _.has(attrs, 'drop_count') && !_.has(errors, 'min_count') && !_.has(errors, 'drop_count') && attrs.drop_count > attrs.min_count) {
|
||||
errors.drop_count = _.template(
|
||||
gettext("Cannot drop more <% attrs.types %> than will assigned."),
|
||||
attrs, {variable: 'attrs'});
|
||||
var template = _.template(
|
||||
gettext("Cannot drop more <%= types %> assignments than are assigned.")
|
||||
);
|
||||
errors.drop_count = template({types: attrs.type});
|
||||
}
|
||||
if (!_.isEmpty(errors)) return errors;
|
||||
}
|
||||
|
||||
@@ -15,8 +15,7 @@ var FileUpload = Backbone.Model.extend({
|
||||
validate: function(attrs, options) {
|
||||
if(attrs.selectedFile && !this.checkTypeValidity(attrs.selectedFile)) {
|
||||
return {
|
||||
message: _.template(
|
||||
gettext("Only <%= fileTypes %> files can be uploaded. Please select a file ending in <%= fileExtensions %> to upload."),
|
||||
message: _.template(gettext("Only <%= fileTypes %> files can be uploaded. Please select a file ending in <%= fileExtensions %> to upload."))( // jshint ignore:line
|
||||
this.formatValidTypes()
|
||||
),
|
||||
attributes: {selectedFile: true}
|
||||
@@ -64,7 +63,7 @@ var FileUpload = Backbone.Model.extend({
|
||||
}
|
||||
var or = gettext('or');
|
||||
var formatTypes = function(types) {
|
||||
return _.template('<%= initial %> <%= or %> <%= last %>', {
|
||||
return _.template('<%= initial %> <%= or %> <%= last %>')({
|
||||
initial: _.initial(types).join(', '),
|
||||
or: or,
|
||||
last: _.last(types)
|
||||
|
||||
@@ -359,12 +359,12 @@ define(["jquery", "jquery.ui", "underscore", "gettext", "draggabilly",
|
||||
makeDraggable: function (element, options) {
|
||||
var draggable;
|
||||
options = _.defaults({
|
||||
type: null,
|
||||
handleClass: null,
|
||||
droppableClass: null,
|
||||
parentLocationSelector: null,
|
||||
refresh: null,
|
||||
ensureChildrenRendered: null
|
||||
type: undefined,
|
||||
handleClass: undefined,
|
||||
droppableClass: undefined,
|
||||
parentLocationSelector: undefined,
|
||||
refresh: undefined,
|
||||
ensureChildrenRendered: undefined
|
||||
}, options);
|
||||
|
||||
if ($(element).data('droppable-class') !== options.droppableClass) {
|
||||
|
||||
@@ -67,7 +67,7 @@ define(["jquery", "underscore", "gettext", "js/views/baseview", "js/models/asset
|
||||
ViewUtils.hideLoadingIndicator();
|
||||
|
||||
// Create the table
|
||||
this.$el.html(_.template(asset_library_template, {typeData: this.typeData}));
|
||||
this.$el.html(_.template(asset_library_template)({typeData: this.typeData}));
|
||||
tableBody = this.$('#asset-table-body');
|
||||
this.tableBody = tableBody;
|
||||
this.pagingHeader = new PagingHeader({view: this, el: $('#asset-paging-header')});
|
||||
|
||||
@@ -75,18 +75,18 @@ define([
|
||||
},
|
||||
|
||||
getOutlineAnchorMessage: function () {
|
||||
var message = gettext(
|
||||
var message = _.escape(gettext(
|
||||
/*
|
||||
Translators: 'outlineAnchor' is an anchor pointing to
|
||||
the course outline page.
|
||||
*/
|
||||
'This content group is not in use. Add a content group to any unit from the %(outlineAnchor)s.'
|
||||
),
|
||||
)),
|
||||
anchor = str.sprintf(
|
||||
'<a href="%(url)s" title="%(text)s">%(text)s</a>',
|
||||
{
|
||||
url: this.model.collection.parents[0].outlineUrl,
|
||||
text: gettext('Course Outline')
|
||||
text: _.escape(gettext('Course Outline'))
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/*global course */
|
||||
|
||||
define(["js/views/baseview", "underscore", "underscore.string", "jquery", "gettext", "js/models/uploads", "js/views/uploads"],
|
||||
function(BaseView, _, str, $, gettext, FileUploadModel, UploadDialogView) {
|
||||
_.str = str; // used in template
|
||||
@@ -52,10 +54,8 @@ 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: window.course.escape('name')}
|
||||
),
|
||||
title: _.template(gettext("Upload a new PDF to “<%= name %>”"))(
|
||||
{name: course.escape('name')}),
|
||||
message: gettext("Please select a PDF file to upload."),
|
||||
mimeTypes: ['application/pdf']
|
||||
});
|
||||
|
||||
@@ -54,8 +54,8 @@ define(['jquery', 'underscore', 'gettext', "js/views/baseview",
|
||||
title: messages.alreadyMember.title,
|
||||
message: _.template(
|
||||
messages.alreadyMember.messageTpl,
|
||||
{email: email, container: containerName},
|
||||
{interpolate: /\{(.+?)}/g}
|
||||
{interpolate: /\{(.+?)}/g})(
|
||||
{email: email, container: containerName}
|
||||
),
|
||||
actions: {
|
||||
primary: {
|
||||
@@ -140,7 +140,9 @@ define(['jquery', 'underscore', 'gettext', "js/views/baseview",
|
||||
roles = _.object(_.pluck(this.roles, 'key'), _.pluck(this.roles, "name")),
|
||||
adminRoleCount = this.getAdminRoleCount(),
|
||||
viewHelpers = {
|
||||
format: function (template, data) { return _.template(template, data, {interpolate: /\{(.+?)}/g}); }
|
||||
format: function (template, data) {
|
||||
return _.template(template, {interpolate: /\{(.+?)}/g})(data);
|
||||
}
|
||||
};
|
||||
for (var i = 0; i < this.users.length; i++) {
|
||||
var user = this.users[i],
|
||||
@@ -284,8 +286,8 @@ define(['jquery', 'underscore', 'gettext', "js/views/baseview",
|
||||
title: self.messages.deleteUser.title,
|
||||
message: _.template(
|
||||
self.messages.deleteUser.messageTpl,
|
||||
{email: email, container: self.containerName},
|
||||
{interpolate: /\{(.+?)}/g}
|
||||
{interpolate: /\{(.+?)}/g})(
|
||||
{email: email, container: self.containerName}
|
||||
),
|
||||
actions: {
|
||||
primary: {
|
||||
|
||||
@@ -1,242 +0,0 @@
|
||||
define(["domReady", "jquery", "jquery.ui", "underscore", "gettext", "common/js/components/views/feedback_notification",
|
||||
"js/utils/cancel_on_escape", "js/utils/date_utils", "js/utils/module", "common/js/components/utils/view_utils"],
|
||||
function (domReady, $, ui, _, gettext, NotificationView, CancelOnEscape,
|
||||
DateUtils, ModuleUtils, ViewUtils) {
|
||||
|
||||
var modalSelector = '.edit-section-publish-settings';
|
||||
|
||||
var toggleSections = function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var $section = $('.courseware-section');
|
||||
var $button = $(this);
|
||||
var $labelCollapsed = $('<i class="icon fa fa-arrow-up"></i> <span class="label">' +
|
||||
gettext('Collapse All Sections') + '</span>');
|
||||
var $labelExpanded = $('<i class="icon fa fa-arrow-down"></i> <span class="label">' +
|
||||
gettext('Expand All Sections') + '</span>');
|
||||
|
||||
var buttonLabel = $button.hasClass('is-activated') ? $labelCollapsed : $labelExpanded;
|
||||
$button.toggleClass('is-activated').html(buttonLabel);
|
||||
|
||||
if ($button.hasClass('is-activated')) {
|
||||
$section.addClass('collapsed');
|
||||
// first child in order to avoid the icons on the subsection lists which are not in the first child
|
||||
$section.find('header .expand-collapse').removeClass('collapse').addClass('expand');
|
||||
} else {
|
||||
$section.removeClass('collapsed');
|
||||
// first child in order to avoid the icons on the subsection lists which are not in the first child
|
||||
$section.find('header .expand-collapse').removeClass('expand').addClass('collapse');
|
||||
}
|
||||
};
|
||||
|
||||
var toggleSubmodules = function(e) {
|
||||
e.preventDefault();
|
||||
$(this).toggleClass('expand collapse');
|
||||
$(this).closest('.is-collapsible, .window').toggleClass('collapsed');
|
||||
};
|
||||
|
||||
|
||||
var closeModalNew = function (e) {
|
||||
if (e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
$('body').removeClass('modal-window-is-shown');
|
||||
$('.edit-section-publish-settings').removeClass('is-shown');
|
||||
};
|
||||
|
||||
var editSectionPublishDate = function (e) {
|
||||
e.preventDefault();
|
||||
var $modal = $(modalSelector);
|
||||
$modal.attr('data-locator', $(this).attr('data-locator'));
|
||||
$modal.find('.start-date').val($(this).attr('data-date'));
|
||||
$modal.find('.start-time').val($(this).attr('data-time'));
|
||||
if ($modal.find('.start-date').val() == '' && $modal.find('.start-time').val() == '') {
|
||||
$modal.find('.save-button').hide();
|
||||
}
|
||||
$modal.find('.section-name').html('"' + $(this).closest('.courseware-section').find('.section-name-span').text() + '"');
|
||||
$('body').addClass('modal-window-is-shown');
|
||||
$('.edit-section-publish-settings').addClass('is-shown');
|
||||
};
|
||||
|
||||
var saveSetSectionScheduleDate = function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
var datetime = DateUtils.getDate(
|
||||
$('.edit-section-publish-settings .start-date'),
|
||||
$('.edit-section-publish-settings .start-time')
|
||||
);
|
||||
|
||||
var locator = $(modalSelector).attr('data-locator');
|
||||
|
||||
analytics.track('Edited Section Release Date', {
|
||||
'course': course_location_analytics,
|
||||
'id': locator,
|
||||
'start': datetime
|
||||
});
|
||||
|
||||
var saving = new NotificationView.Mini({
|
||||
title: gettext("Saving")
|
||||
});
|
||||
saving.show();
|
||||
// call into server to commit the new order
|
||||
$.ajax({
|
||||
url: ModuleUtils.getUpdateUrl(locator),
|
||||
type: "PUT",
|
||||
dataType: "json",
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify({
|
||||
'metadata': {
|
||||
'start': datetime
|
||||
}
|
||||
})
|
||||
}).success(function() {
|
||||
var pad2 = function(number) {
|
||||
// pad a number to two places: useful for formatting months, days, hours, etc
|
||||
// when displaying a date/time
|
||||
return (number < 10 ? '0' : '') + number;
|
||||
};
|
||||
|
||||
var $thisSection = $('.courseware-section[data-locator="' + locator + '"]');
|
||||
var html = _.template(
|
||||
'<span class="published-status">' +
|
||||
'<strong>' + gettext("Release date:") + ' </strong>' +
|
||||
gettext("{month}/{day}/{year} at {hour}:{minute} UTC") +
|
||||
'</span>' +
|
||||
'<a href="#" class="edit-release-date action" data-date="{month}/{day}/{year}" data-time="{hour}:{minute}" data-locator="{locator}"><i class="icon fa fa-time"></i> <span class="sr">' +
|
||||
gettext("Edit section release date") +
|
||||
'</span></a>',
|
||||
{year: datetime.getUTCFullYear(), month: pad2(datetime.getUTCMonth() + 1), day: pad2(datetime.getUTCDate()),
|
||||
hour: pad2(datetime.getUTCHours()), minute: pad2(datetime.getUTCMinutes()),
|
||||
locator: locator},
|
||||
{interpolate: /\{(.+?)\}/g});
|
||||
$thisSection.find('.section-published-date').html(html);
|
||||
saving.hide();
|
||||
closeModalNew();
|
||||
});
|
||||
};
|
||||
|
||||
var addNewSection = function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
$(e.target).addClass('disabled');
|
||||
|
||||
var $newSection = $($('#new-section-template').html());
|
||||
var $cancelButton = $newSection.find('.new-section-name-cancel');
|
||||
$('.courseware-overview').prepend($newSection);
|
||||
$newSection.find('.new-section-name').focus().select();
|
||||
$newSection.find('.section-name-form').bind('submit', saveNewSection);
|
||||
$cancelButton.bind('click', cancelNewSection);
|
||||
CancelOnEscape($cancelButton);
|
||||
};
|
||||
|
||||
var saveNewSection = function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
var $saveButton = $(this).find('.new-section-name-save');
|
||||
var parent = $saveButton.data('parent');
|
||||
var category = $saveButton.data('category');
|
||||
var display_name = $(this).find('.new-section-name').val();
|
||||
|
||||
analytics.track('Created a Section', {
|
||||
'course': course_location_analytics,
|
||||
'display_name': display_name
|
||||
});
|
||||
|
||||
$.postJSON(ModuleUtils.getUpdateUrl(), {
|
||||
'parent_locator': parent,
|
||||
'category': category,
|
||||
'display_name': display_name
|
||||
},
|
||||
|
||||
function(data) {
|
||||
if (data.locator != undefined) location.reload();
|
||||
});
|
||||
};
|
||||
|
||||
var cancelNewSection = function (e) {
|
||||
e.preventDefault();
|
||||
$('.new-courseware-section-button').removeClass('disabled');
|
||||
$(this).parents('section.new-section').remove();
|
||||
};
|
||||
|
||||
var addNewSubsection = function (e) {
|
||||
e.preventDefault();
|
||||
var $section = $(this).closest('.courseware-section');
|
||||
var $newSubsection = $($('#new-subsection-template').html());
|
||||
$section.find('.subsection-list > ol').append($newSubsection);
|
||||
$section.find('.new-subsection-name-input').focus().select();
|
||||
|
||||
var $saveButton = $newSubsection.find('.new-subsection-name-save');
|
||||
var $cancelButton = $newSubsection.find('.new-subsection-name-cancel');
|
||||
|
||||
var parent = $(this).parents("section.courseware-section").data("locator");
|
||||
|
||||
$saveButton.data('parent', parent);
|
||||
$saveButton.data('category', $(this).data('category'));
|
||||
|
||||
$newSubsection.find('.new-subsection-form').bind('submit', saveNewSubsection);
|
||||
$cancelButton.bind('click', cancelNewSubsection);
|
||||
CancelOnEscape($cancelButton);
|
||||
};
|
||||
|
||||
var saveNewSubsection = function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
var parent = $(this).find('.new-subsection-name-save').data('parent');
|
||||
var category = $(this).find('.new-subsection-name-save').data('category');
|
||||
var display_name = $(this).find('.new-subsection-name-input').val();
|
||||
|
||||
analytics.track('Created a Subsection', {
|
||||
'course': course_location_analytics,
|
||||
'display_name': display_name
|
||||
});
|
||||
|
||||
|
||||
$.postJSON(ModuleUtils.getUpdateUrl(), {
|
||||
'parent_locator': parent,
|
||||
'category': category,
|
||||
'display_name': display_name
|
||||
},
|
||||
|
||||
function(data) {
|
||||
if (data.locator != undefined) {
|
||||
location.reload();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var cancelNewSubsection = function (e) {
|
||||
e.preventDefault();
|
||||
$(this).parents('li.courseware-subsection').remove();
|
||||
};
|
||||
|
||||
|
||||
|
||||
domReady(function() {
|
||||
// toggling overview section details
|
||||
$(function() {
|
||||
if ($('.courseware-section').length > 0) {
|
||||
$('.toggle-button-sections').addClass('is-shown');
|
||||
}
|
||||
});
|
||||
$('.toggle-button-sections').bind('click', toggleSections);
|
||||
$('.expand-collapse').bind('click', toggleSubmodules);
|
||||
|
||||
$('.dismiss-button').bind('click', ViewUtils.deleteNotificationHandler(function () {
|
||||
$('.wrapper-alert-announcement').remove();
|
||||
}));
|
||||
|
||||
var $body = $('body');
|
||||
$body.on('click', '.section-published-date .edit-release-date', editSectionPublishDate);
|
||||
$body.on('click', '.edit-section-publish-settings .action-save', saveSetSectionScheduleDate);
|
||||
$body.on('click', '.edit-section-publish-settings .action-cancel', closeModalNew);
|
||||
|
||||
$('.new-courseware-section-button').bind('click', addNewSection);
|
||||
$('.new-subsection-item').bind('click', addNewSubsection);
|
||||
|
||||
});
|
||||
|
||||
return {
|
||||
saveSetSectionScheduleDate: saveSetSectionScheduleDate
|
||||
};
|
||||
});
|
||||
@@ -22,9 +22,7 @@ define(["underscore", "backbone", "gettext", "text!templates/paging-header.under
|
||||
currentPage = collection.currentPage,
|
||||
lastPage = collection.totalPages - 1,
|
||||
messageHtml = this.messageHtml();
|
||||
this.$el.html(_.template(paging_header_template, {
|
||||
messageHtml: messageHtml
|
||||
}));
|
||||
this.$el.html(_.template(paging_header_template)({ messageHtml: messageHtml}));
|
||||
this.$(".previous-page-link").toggleClass("is-disabled", currentPage === 0).attr('aria-disabled', currentPage === 0);
|
||||
this.$(".next-page-link").toggleClass("is-disabled", currentPage === lastPage).attr('aria-disabled', currentPage === lastPage);
|
||||
return this;
|
||||
|
||||
@@ -27,10 +27,9 @@ define(["js/views/baseview", "underscore", "gettext", "common/js/components/view
|
||||
},
|
||||
confirmDelete: function(e) {
|
||||
if(e && e.preventDefault) { e.preventDefault(); }
|
||||
var textbook = this.model, collection = this.model.collection;
|
||||
var msg = new PromptView.Warning({
|
||||
title: _.template(
|
||||
gettext("Delete “<%= name %>”?"),
|
||||
var textbook = this.model;
|
||||
new PromptView.Warning({
|
||||
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."),
|
||||
|
||||
@@ -18,7 +18,9 @@ function($, Backbone, _, Utils) {
|
||||
uploadTpl: '#file-upload',
|
||||
|
||||
initialize: function () {
|
||||
_.bindAll(this);
|
||||
_.bindAll(this,
|
||||
'changeHandler', 'clickHandler', 'xhrResetProgressBar', 'xhrProgressHandler', 'xhrCompleteHandler'
|
||||
);
|
||||
|
||||
this.file = false;
|
||||
this.render();
|
||||
|
||||
@@ -29,7 +29,9 @@ function($, Backbone, _, Utils, FileUploader, gettext) {
|
||||
},
|
||||
|
||||
initialize: function () {
|
||||
_.bindAll(this);
|
||||
_.bindAll(this,
|
||||
'importHandler', 'replaceHandler', 'chooseHandler', 'useExistingHandler', 'showError', 'hideError'
|
||||
);
|
||||
|
||||
this.component_locator = this.$el.closest('[data-locator]').data('locator');
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ lib_paths:
|
||||
- xmodule_js/common_static/js/vendor/jquery-ui.min.js
|
||||
- xmodule_js/common_static/js/vendor/jquery.cookie.js
|
||||
- xmodule_js/common_static/js/vendor/jquery.simulate.js
|
||||
- xmodule_js/common_static/js/vendor/underscore-min.js
|
||||
- xmodule_js/common_static/common/js/vendor/underscore.js
|
||||
- xmodule_js/common_static/js/vendor/underscore.string.min.js
|
||||
- xmodule_js/common_static/js/vendor/backbone-min.js
|
||||
- xmodule_js/common_static/js/vendor/backbone-associations-min.js
|
||||
@@ -78,6 +78,8 @@ src_paths:
|
||||
- js/certificates
|
||||
- js/factories
|
||||
- common/js
|
||||
- edx-pattern-library/js
|
||||
- edx-ui-toolkit/js
|
||||
|
||||
# Paths to spec (test) JavaScript files
|
||||
# We should define the custom path mapping in /coffee/spec/main.coffee as well e.g. certificates etc.
|
||||
|
||||
@@ -34,7 +34,7 @@ lib_paths:
|
||||
- xmodule_js/common_static/js/vendor/jquery.min.js
|
||||
- xmodule_js/common_static/js/vendor/jquery-ui.min.js
|
||||
- xmodule_js/common_static/js/vendor/jquery.cookie.js
|
||||
- xmodule_js/common_static/js/vendor/underscore-min.js
|
||||
- xmodule_js/common_static/common/js/vendor/underscore.js
|
||||
- xmodule_js/common_static/js/vendor/underscore.string.min.js
|
||||
- xmodule_js/common_static/js/vendor/backbone-min.js
|
||||
- xmodule_js/common_static/js/vendor/backbone-associations-min.js
|
||||
@@ -73,6 +73,8 @@ src_paths:
|
||||
- js/utils
|
||||
- js/views
|
||||
- common/js
|
||||
- edx-pattern-library/js
|
||||
- edx-ui-toolkit/js
|
||||
|
||||
# Paths to spec (test) JavaScript files
|
||||
spec_paths:
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
<%! from django.utils.translation import ugettext as _ %>
|
||||
<%page expression_filter="h"/>
|
||||
<%!
|
||||
from django.utils.translation import ugettext as _
|
||||
from openedx.core.djangolib.markup import Text, HTML
|
||||
%>
|
||||
<%inherit file="base.html" />
|
||||
<%block name="title">${_("Page Not Found")}</%block>
|
||||
<%block name="bodyclass">view-util util-404</%block>
|
||||
@@ -11,12 +15,14 @@
|
||||
<h1 class="title title-1">${_("Page not found")}</h1>
|
||||
</header>
|
||||
<article class="content-primary" role="main">
|
||||
<p>${_('The page that you were looking for was not found.')}
|
||||
${_('Go back to the {homepage} or let us know about any pages that may have been moved at {email}.').format(
|
||||
homepage='<a href="/">homepage</a>',
|
||||
email=u'<a href="mailto:{address}">{address}</a>'.format(
|
||||
address=settings.TECH_SUPPORT_EMAIL,
|
||||
))}
|
||||
<p>
|
||||
${_('The page that you were looking for was not found.')}
|
||||
${Text(_('Go back to the {homepage} or let us know about any pages that may have been moved at {email}.')).format(
|
||||
homepage=HTML('<a href="/">homepage</a>'),
|
||||
email=HTML('<a href="mailto:{address}">{address}</a>'.format(
|
||||
address=Text(settings.TECH_SUPPORT_EMAIL)
|
||||
))
|
||||
)}
|
||||
</p>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
<%! from django.utils.translation import ugettext as _ %>
|
||||
<%page expression_filter="h"/>
|
||||
<%!
|
||||
from openedx.core.djangolib.markup import Text, HTML
|
||||
from django.utils.translation import ugettext as _
|
||||
%>
|
||||
<%inherit file="base.html" />
|
||||
<%block name="title">${_("{studio_name} Server Error").format(studio_name=settings.STUDIO_SHORT_NAME)}</%block>
|
||||
<%block name="title">
|
||||
${Text(_("{studio_name} Server Error")).format(
|
||||
studio_name=Text(settings.STUDIO_SHORT_NAME)
|
||||
)}
|
||||
</%block>
|
||||
<%block name="bodyclass">view-util util-500</%block>
|
||||
|
||||
<%block name="content">
|
||||
@@ -8,20 +16,24 @@
|
||||
<section class="content">
|
||||
<header>
|
||||
<h1 class="title title-1">
|
||||
${_("The {studio_name} servers encountered an error").format(
|
||||
studio_name=u"<em>{studio_name}</em>".format(studio_name=settings.STUDIO_SHORT_NAME)
|
||||
${Text(_(u"The {em_start}{studio_name}{em_end} servers encountered an error")).format(
|
||||
em_start=HTML('<em>'),
|
||||
em_end=HTML('</em>'),
|
||||
studio_name=Text(settings.STUDIO_SHORT_NAME),
|
||||
)}
|
||||
</h1>
|
||||
</header>
|
||||
<article class="content-primary" role="main">
|
||||
<p>
|
||||
${_("An error occurred in {studio_name} and the page could not be loaded. Please try again in a few moments.").format(studio_name=settings.STUDIO_SHORT_NAME)}
|
||||
${Text(_("An error occurred in {studio_name} and the page could not be loaded. Please try again in a few moments.")).format(
|
||||
studio_name=Text(settings.STUDIO_SHORT_NAME),
|
||||
)}
|
||||
${_("We've logged the error and our staff is currently working to resolve this error as soon as possible.")}
|
||||
${_('If the problem persists, please email us at {email_link}.').format(
|
||||
email_link=u'<a href="mailto:{email_address}">{email_address}</a>'.format(
|
||||
email_address=settings.TECH_SUPPORT_EMAIL,
|
||||
)
|
||||
)}
|
||||
${Text(_(u'If the problem persists, please email us at {email_link}.')).format(
|
||||
email_link=HTML(u'<a href="mailto:{email_address}">{email_address}</a>'.format(
|
||||
email_address=Text(settings.TECH_SUPPORT_EMAIL),
|
||||
))
|
||||
)}
|
||||
</p>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
<%! from django.utils.translation import ugettext as _ %>
|
||||
<%!
|
||||
from openedx.core.djangolib.markup import Text
|
||||
from django.utils.translation import ugettext as _
|
||||
%>
|
||||
<%page expression_filter="h"/>
|
||||
<%inherit file="base.html" />
|
||||
|
||||
<%block name="content">
|
||||
<div class="wrapper-mast wrapper sr">
|
||||
<header class="mast">
|
||||
<h1 class="page-header">${_("{studio_name} Account Activation").format(studio_name=settings.STUDIO_SHORT_NAME)}</h1>
|
||||
<h1 class="page-header">
|
||||
${Text(_("{studio_name} Account Activation")).format(
|
||||
studio_name=Text(settings.STUDIO_SHORT_NAME),
|
||||
)}
|
||||
</h1>
|
||||
</header>
|
||||
</div>
|
||||
|
||||
@@ -17,14 +25,20 @@
|
||||
<div class="msg">
|
||||
<h1 class="title">${_("Your account activation is complete!")}</h1>
|
||||
<div class="copy">
|
||||
<p>${_("Thank you for activating your account. You may now sign in and start using {studio_name} to author courses.").format(studio_name=settings.STUDIO_NAME)}</p>
|
||||
<p>
|
||||
${Text(_("Thank you for activating your account. You may now sign in and start using {studio_name} to author courses.")).format(
|
||||
studio_name=Text(settings.STUDIO_NAME)
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="list-actions">
|
||||
<li class="action-item">
|
||||
<a href="/signin" class="action-primary action-signin">
|
||||
${_("Sign into {studio_name}").format(studio_name=settings.STUDIO_SHORT_NAME)}
|
||||
${Text(_("Sign into {studio_name}")).format(
|
||||
studio_name=Text(settings.STUDIO_SHORT_NAME)
|
||||
)}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
<%! from django.utils.translation import ugettext as _ %>
|
||||
<%!
|
||||
from openedx.core.djangolib.markup import HTML, Text
|
||||
from django.utils.translation import ugettext as _
|
||||
%>
|
||||
<%page expression_filter="h"/>
|
||||
<%inherit file="base.html" />
|
||||
|
||||
<%block name="content">
|
||||
<div class="wrapper-mast wrapper sr">
|
||||
<header class="mast">
|
||||
<h1 class="page-header">${_("{studio_name} Account Activation").format(studio_name=settings.STUDIO_SHORT_NAME)}</h1>
|
||||
<h1 class="page-header">
|
||||
${Text(_("{studio_name} Account Activation")).format(
|
||||
studio_name=Text(settings.STUDIO_SHORT_NAME)
|
||||
)}
|
||||
</h1>
|
||||
</header>
|
||||
</div>
|
||||
|
||||
@@ -18,10 +26,14 @@
|
||||
<h1 class="title">${_("Your account activation is invalid")}</h1>
|
||||
<div class="copy">
|
||||
<p>${_("We're sorry. Something went wrong with your activation. Check to make sure the URL you went to was correct, as e-mail programs will sometimes split it into two lines.")}</p>
|
||||
<p>${_("If you still have issues, contact {platform_name} Support. In the meantime, you can also return to {link_start}the {studio_name} homepage.{link_end}").format(
|
||||
platform_name=settings.PLATFORM_NAME, studio_name=settings.STUDIO_NAME,
|
||||
link_start='<a href="/">', link_end="</a>"
|
||||
)}</p>
|
||||
<p>
|
||||
${Text(_("If you still have issues, contact {platform_name} Support. In the meantime, you can also return to {link_start}the {studio_name} homepage.{link_end}")).format(
|
||||
platform_name=Text(settings.PLATFORM_NAME),
|
||||
studio_name=Text(settings.STUDIO_NAME),
|
||||
link_start=HTML('<a href="/">'),
|
||||
link_end=HTML('</a>')
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<%page expression_filter="h"/>
|
||||
<%inherit file="../base.html" />
|
||||
<%block name="content">
|
||||
You're in dev mode!
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<h3 class="title">
|
||||
<a href="#" class="toggle group-toggle <% if (showContentGroupUsages){ print('hide'); } else { print('show'); } %>-groups">
|
||||
<i class="ui-toggle-expansion icon fa fa-caret-<% if (showContentGroupUsages){ print('down'); } else { print('right'); } %>"></i>
|
||||
<%= name %>
|
||||
<%- name %>
|
||||
</a>
|
||||
</h3>
|
||||
</header>
|
||||
@@ -11,28 +11,28 @@
|
||||
<ol class="collection-info group-configuration-info group-configuration-info-<% if(showContentGroupUsages){ print('block'); } else { print('inline'); } %>">
|
||||
<% if (!_.isUndefined(id)) { %>
|
||||
<li class="group-configuration-id"
|
||||
><span class="group-configuration-label"><%= gettext('ID') %>: </span
|
||||
><span class="group-configuration-value"><%= id %></span
|
||||
><span class="group-configuration-label"><%- gettext('ID') %>: </span
|
||||
><span class="group-configuration-value"><%- id %></span
|
||||
></li>
|
||||
<% } %>
|
||||
<% if (!showContentGroupUsages) { %>
|
||||
<li class="group-configuration-usage-count">
|
||||
<%= usageCountMessage %>
|
||||
<%- usageCountMessage %>
|
||||
</li>
|
||||
<% } %>
|
||||
</ol>
|
||||
|
||||
<ul class="actions group-configuration-actions">
|
||||
<li class="action action-edit">
|
||||
<button class="edit"><i class="icon fa fa-pencil"></i> <%= gettext("Edit") %></button>
|
||||
<button class="edit"><i class="icon fa fa-pencil"></i> <%- gettext("Edit") %></button>
|
||||
</li>
|
||||
<% if (_.isEmpty(usage)) { %>
|
||||
<li class="action action-delete wrapper-delete-button" data-tooltip="<%= gettext('Delete') %>">
|
||||
<button class="delete action-icon"><i class="icon fa fa-trash-o"></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"></i><span><%- gettext("Delete") %></span></button>
|
||||
</li>
|
||||
<% } else { %>
|
||||
<li class="action action-delete wrapper-delete-button" data-tooltip="<%= gettext('Cannot delete when in use by a unit') %>">
|
||||
<button class="delete action-icon is-disabled" aria-disabled="true" disabled="disabled"><i class="icon fa fa-trash-o"></i><span><%= gettext("Delete") %></span></button>
|
||||
<li class="action action-delete wrapper-delete-button" data-tooltip="<%- gettext('Cannot delete when in use by a unit') %>">
|
||||
<button class="delete action-icon is-disabled" aria-disabled="true" disabled="disabled"><i class="icon fa fa-trash-o"></i><span><%- gettext("Delete") %></span></button>
|
||||
</li>
|
||||
<% } %>
|
||||
</ul>
|
||||
@@ -41,17 +41,18 @@
|
||||
<% if (showContentGroupUsages) { %>
|
||||
<div class="collection-references wrapper-group-configuration-usages">
|
||||
<% if (!_.isEmpty(usage)) { %>
|
||||
<h4 class="intro group-configuration-usage-text"><%= gettext('This content group is used in:') %></h4>
|
||||
<h4 class="intro group-configuration-usage-text"><%- gettext('This content group is used in:') %></h4>
|
||||
<ol class="usage group-configuration-usage">
|
||||
<% _.each(usage, function(unit) { %>
|
||||
<li class="usage-unit group-configuration-usage-unit">
|
||||
<p><a href=<%= unit.url %> ><%= unit.label %></a></p>
|
||||
<p><a href=<%- unit.url %> ><%- unit.label %></a></p>
|
||||
</li>
|
||||
<% }) %>
|
||||
</ol>
|
||||
<% } else { %>
|
||||
<p class="group-configuration-usage-text">
|
||||
<%= outlineAnchorMessage %>
|
||||
<!-- This contains an anchor link and therefore can't be escaped. -->
|
||||
<%= outlineAnchorMessage %>
|
||||
</p>
|
||||
<% } %>
|
||||
</div>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<h3 class="title group-configuration-title">
|
||||
<a href="#" class="toggle group-toggle <% if(showGroups){ print('hide'); } else { print('show'); } %>-groups">
|
||||
<i class="ui-toggle-expansion icon fa fa-caret-<% if(showGroups){ print('down'); } else { print('right'); } %>"></i>
|
||||
<%= name %>
|
||||
<%- name %>
|
||||
</a>
|
||||
</h3>
|
||||
</header>
|
||||
@@ -11,20 +11,20 @@
|
||||
<ol class="collection-info group-configuration-info group-configuration-info-<% if(showGroups){ print('block'); } else { print('inline'); } %>">
|
||||
<% if (!_.isUndefined(id)) { %>
|
||||
<li class="group-configuration-id"
|
||||
><span class="group-configuration-label"><%= gettext('ID') %>: </span
|
||||
><span class="group-configuration-value"><%= id %></span
|
||||
><span class="group-configuration-label"><%- gettext('ID') %>: </span
|
||||
><span class="group-configuration-value"><%- id %></span
|
||||
></li>
|
||||
<% } %>
|
||||
<% if (showGroups) { %>
|
||||
<li class="collection-description group-configuration-description">
|
||||
<%= description %>
|
||||
<%- description %>
|
||||
</li>
|
||||
<% } else { %>
|
||||
<li class="group-configuration-groups-count">
|
||||
<%= groupsCountMessage %>
|
||||
<%- groupsCountMessage %>
|
||||
</li>
|
||||
<li class="group-configuration-usage-count">
|
||||
<%= usageCountMessage %>
|
||||
<%- usageCountMessage %>
|
||||
</li>
|
||||
<% } %>
|
||||
</ol>
|
||||
@@ -34,23 +34,23 @@
|
||||
<ol class="collection-items groups groups-<%= index %>">
|
||||
<% groups.each(function(group, groupIndex) { %>
|
||||
<li class="item group group-<%= groupIndex %>">
|
||||
<span class="name group-name"><%= group.get('name') %></span>
|
||||
<span class="meta group-allocation"><%= allocation %>%</span>
|
||||
<span class="name group-name"><%- group.get('name') %></span>
|
||||
<span class="meta group-allocation"><%- allocation %>%</span>
|
||||
</li>
|
||||
<% }) %>
|
||||
</ol>
|
||||
<% } %>
|
||||
<ul class="actions group-configuration-actions">
|
||||
<li class="action action-edit">
|
||||
<button class="edit"><i class="icon fa fa-pencil"></i> <%= gettext("Edit") %></button>
|
||||
<button class="edit"><i class="icon fa fa-pencil"></i> <%- gettext("Edit") %></button>
|
||||
</li>
|
||||
<% if (_.isEmpty(usage)) { %>
|
||||
<li class="action action-delete wrapper-delete-button">
|
||||
<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>
|
||||
<% } else { %>
|
||||
<li class="action action-delete wrapper-delete-button" data-tooltip="<%= gettext('Cannot delete when in use by an experiment') %>">
|
||||
<button class="delete action-icon is-disabled" aria-disabled="true"><i class="icon fa fa-trash-o"></i><span><%= gettext("Delete") %></span></button>
|
||||
<li class="action action-delete wrapper-delete-button" data-tooltip="<%- gettext('Cannot delete when in use by an experiment') %>">
|
||||
<button class="delete action-icon is-disabled" aria-disabled="true"><i class="icon fa fa-trash-o"></i><span><%- gettext("Delete") %></span></button>
|
||||
</li>
|
||||
<% } %>
|
||||
</ul>
|
||||
@@ -58,11 +58,11 @@
|
||||
<% if(showGroups) { %>
|
||||
<div class="collection-references wrapper-group-configuration-usages">
|
||||
<% if (!_.isEmpty(usage)) { %>
|
||||
<h4 class="intro group-configuration-usage-text"><%= gettext('This Group Configuration is used in:') %></h4>
|
||||
<h4 class="intro group-configuration-usage-text"><%- gettext('This Group Configuration is used in:') %></h4>
|
||||
<ol class="usage group-configuration-usage">
|
||||
<% _.each(usage, function(unit) { %>
|
||||
<li class="usage-unit group-configuration-usage-unit">
|
||||
<p><a href=<%= unit.url %> ><%= unit.label %></a></p>
|
||||
<p><a href=<%- unit.url %> ><%- unit.label %></a></p>
|
||||
<% if (unit.validation) { %>
|
||||
<p>
|
||||
<% if (unit.validation.type === 'warning') { %>
|
||||
@@ -71,7 +71,7 @@
|
||||
<i class="icon fa fa-exclamation-circle"></i>
|
||||
<% } %>
|
||||
<span class="usage-validation-message group-configuration-validation-message">
|
||||
<%= unit.validation.text %>
|
||||
<%- unit.validation.text %>
|
||||
</span>
|
||||
</p>
|
||||
<% } %>
|
||||
@@ -80,6 +80,7 @@
|
||||
</ol>
|
||||
<% } else { %>
|
||||
<p class="group-configuration-usage-text">
|
||||
<!-- This contains an anchor link and therefore can't be escaped. -->
|
||||
<%= outlineAnchorMessage %>
|
||||
</p>
|
||||
<% } %>
|
||||
|
||||
@@ -2,17 +2,17 @@
|
||||
<div class "error-header">
|
||||
<p>
|
||||
<%= _.template(
|
||||
ngettext(
|
||||
"There was {strong_start}{num_errors} validation error{strong_end} while trying to save the course settings in the database.",
|
||||
"There were {strong_start}{num_errors} validation errors{strong_end} while trying to save the course settings in the database.",
|
||||
num_errors
|
||||
),
|
||||
{
|
||||
strong_start:'<strong>',
|
||||
num_errors: num_errors,
|
||||
strong_end: '</strong>'
|
||||
},
|
||||
{interpolate: /\{(.+?)\}/g})%>
|
||||
ngettext(
|
||||
"There was {strong_start}{num_errors} validation error{strong_end} while trying to save the course settings in the database.",
|
||||
"There were {strong_start}{num_errors} validation errors{strong_end} while trying to save the course settings in the database.",
|
||||
num_errors
|
||||
),
|
||||
{interpolate: /\{(.+?)\}/g})(
|
||||
{
|
||||
strong_start:'<strong>',
|
||||
num_errors: num_errors,
|
||||
strong_end: '</strong>'
|
||||
})%>
|
||||
<%= gettext("Please check the following validation feedbacks and reflect them in your course settings:")%></p>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<%page expression_filter="h"/>
|
||||
<%inherit file="../../base.html" />
|
||||
|
||||
<%block name="view_notes">
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<%page expression_filter="h"/>
|
||||
<div class="wrapper wrapper-modal-window wrapper-modal-window-edit-xblock" aria-labelledby="modal-window-title" role="dialog">
|
||||
<div class="modal-window-overlay"></div>
|
||||
<div class="modal-window confirm modal-med modal-type-html modal-editor" style="top: 50px; left: 400px;" tabindex="-1" aria-labelledby="modal-window-title">
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<%page expression_filter="h"/>
|
||||
<%! from django.utils.translation import ugettext as _ %>
|
||||
|
||||
<div class="wrapper wrapper-modal-window wrapper-modal-window-bulkpublish-section" aria-labelledby="modal-window-title" role="dialog">
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<%page expression_filter="h"/>
|
||||
<%! from django.utils.translation import ugettext as _ %>
|
||||
|
||||
<div class="wrapper wrapper-modal-window wrapper-modal-window-bulkpublish-subsection" aria-labelledby="modal-window-title" role="dialog">
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<%page expression_filter="h"/>
|
||||
<%! from django.utils.translation import ugettext as _ %>
|
||||
|
||||
<div class="wrapper wrapper-modal-window wrapper-modal-window-bulkpublish-unit" aria-labelledby="modal-window-title" role="dialog">
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<%page expression_filter="h"/>
|
||||
<div class="add-section add-item">
|
||||
<a href="#" class="button button-new" data-category="" data-parent="" data-default-name="New Section">
|
||||
<i class="icon fa fa-plus"></i> New Section
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<%page expression_filter="h"/>
|
||||
<div class="add-subsection add-item">
|
||||
<a href="#" class="button button-new" data-category="" data-parent="" data-default-name="New Subsection">
|
||||
<i class="icon fa fa-plus"></i> New Subsection
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<%page expression_filter="h"/>
|
||||
<div class="add-unit add-item">
|
||||
<a href="#" class="button button-new" data-category="" data-parent="" data-default-name="New Unit">
|
||||
<i class="icon fa fa-plus"></i> New Unit
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<%page expression_filter="h"/>
|
||||
<div class="section-header">
|
||||
<h3 class="section-header-details ui-toggle-expansion" title="Collapse/Expand this Section">
|
||||
<i class="icon fa fa-caret-down icon"></i>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<%page expression_filter="h"/>
|
||||
<div class="section-header">
|
||||
<h3 class="section-header-details ui-toggle-expansion" title="Collapse/Expand this Section">
|
||||
<i class="icon fa fa-caret-down icon"></i>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<%page expression_filter="h"/>
|
||||
<div class="status-grading">
|
||||
<p>
|
||||
<span class="sr status-grading-label">Graded as:</span>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<%page expression_filter="h"/>
|
||||
<div class="status-message">
|
||||
<i class="icon fa fa-warning"></i>
|
||||
<p class="status-message-copy">Critical error</p>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<%page expression_filter="h"/>
|
||||
<div class="status-message">
|
||||
<i class="icon fa fa-lock"></i>
|
||||
<p class="status-message-copy">Contains Staff only content</p>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<%page expression_filter="h"/>
|
||||
<div class="status-message">
|
||||
<i class="icon fa fa-file-o"></i>
|
||||
<p class="status-message-copy">Unpublished change(s) to live content</p>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<%page expression_filter="h"/>
|
||||
<div class="status-message">
|
||||
<i class="icon fa fa-file-o"></i>
|
||||
<p class="status-message-copy">Unpublished unit(s) will not be released</p>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<%page expression_filter="h"/>
|
||||
<div class="status-release">
|
||||
<p>
|
||||
<span class="sr status-release-label">Release Status:</span>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<%page expression_filter="h"/>
|
||||
<div class="status-release">
|
||||
<p>
|
||||
<span class="sr status-release-label">Release Status:</span>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<%page expression_filter="h"/>
|
||||
<div class="status-release">
|
||||
<p>
|
||||
<span class="sr status-release-label">Release Status:</span>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<%page expression_filter="h"/>
|
||||
<div class="status-release">
|
||||
<p>
|
||||
<span class="sr status-release-label">Release Status:</span>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<%page expression_filter="h"/>
|
||||
<div class="status-release">
|
||||
<p>
|
||||
<span class="sr status-release-label">Release Status:</span>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<%page expression_filter="h"/>
|
||||
<div class="status-release">
|
||||
<p>
|
||||
<span class="sr status-release-label">Release Status:</span>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<%page expression_filter="h"/>
|
||||
<div class="subsection-header">
|
||||
<h3 class="subsection-header-details ui-toggle-expansion" title="Collapse/Expand this Subsection">
|
||||
<i class="icon fa fa-caret-down icon"></i>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<%page expression_filter="h"/>
|
||||
<div class="subsection-header">
|
||||
<h3 class="subsection-header-details ui-toggle-expansion" title="Collapse/Expand this Subsection">
|
||||
<i class="icon fa fa-caret-down icon"></i>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<%page expression_filter="h"/>
|
||||
<div class="unit-header">
|
||||
<h3 class="unit-header-details">
|
||||
<span class="unit-title item-title"><a class="unit-url" href="">Unit Name</a></span>
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
<%page expression_filter="h"/>
|
||||
<span class="draggable-drop-indicator draggable-drop-indicator-after"><i class="icon fa fa-caret-right"></i></span>
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
<%page expression_filter="h"/>
|
||||
<span class="draggable-drop-indicator draggable-drop-indicator-before"><i class="icon fa fa-caret-right"></i></span>
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
<%page expression_filter="h"/>
|
||||
<span class="draggable-drop-indicator draggable-drop-indicator-initial"><i class="icon fa fa-caret-right"></i></span>
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
<%page expression_filter="h"/>
|
||||
<%include file="metadata-edit.html" />
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
<%page expression_filter="h"/>
|
||||
<%! from openedx.core.djangolib.js_utils import js_escaped_string %>
|
||||
|
||||
% if settings.CMS_SEGMENT_KEY:
|
||||
<!-- begin segment footer -->
|
||||
<script type="text/javascript">
|
||||
@@ -6,10 +9,10 @@
|
||||
// screws up RequireJS' JQuery initialization.
|
||||
var onLoadCallback = function() {
|
||||
analytics.identify(
|
||||
"${user.id}",
|
||||
"${ user.id | n, js_escaped_string }",
|
||||
{
|
||||
email: "${user.email}",
|
||||
username: "${user.username}"
|
||||
email: "${ user.email | n, js_escaped_string }",
|
||||
username: "${ user.username | n, js_escaped_string }"
|
||||
},
|
||||
{
|
||||
integrations: {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<%! from django.template.defaultfilters import escapejs %>
|
||||
<%page expression_filter="h"/>
|
||||
<%! from openedx.core.djangolib.js_utils import js_escaped_string %>
|
||||
|
||||
% if context_course:
|
||||
<%
|
||||
@@ -11,12 +12,12 @@
|
||||
<script type="text/javascript">
|
||||
// if inside course, inject the course location into the JS namespace
|
||||
%if context_course:
|
||||
var course_location_analytics = "${locator | escapejs}";
|
||||
var course_location_analytics = "${ locator | n, js_escaped_string }";
|
||||
%endif
|
||||
|
||||
// Asynchronously load Segment's analytics.js library
|
||||
!function(){var analytics=window.analytics=window.analytics||[];if(!analytics.initialize)if(analytics.invoked)window.console&&console.error&&console.error("Segment snippet included twice.");else{analytics.invoked=!0;analytics.methods=["trackSubmit","trackClick","trackLink","trackForm","pageview","identify","reset","group","track","ready","alias","page","once","off","on"];analytics.factory=function(t){return function(){var e=Array.prototype.slice.call(arguments);e.unshift(t);analytics.push(e);return analytics}};for(var t=0;t<analytics.methods.length;t++){var e=analytics.methods[t];analytics[e]=analytics.factory(e)}analytics.load=function(t){var e=document.createElement("script");e.type="text/javascript";e.async=!0;e.src=("https:"===document.location.protocol?"https://":"http://")+"cdn.segment.com/analytics.js/v1/"+t+"/analytics.min.js";var n=document.getElementsByTagName("script")[0];n.parentNode.insertBefore(e,n)};analytics.SNIPPET_VERSION="3.1.0";
|
||||
analytics.load("${ settings.CMS_SEGMENT_KEY }");
|
||||
analytics.load("${ settings.CMS_SEGMENT_KEY | n, js_escaped_string }");
|
||||
analytics.page();
|
||||
}}();
|
||||
// Note: user tracking moved to segment-io-footer.html
|
||||
@@ -26,7 +27,7 @@
|
||||
<!-- dummy Segment -->
|
||||
<script type="text/javascript">
|
||||
%if context_course:
|
||||
var course_location_analytics = "${locator | escapejs}";
|
||||
var course_location_analytics = "${ locator | n, js_escaped_string }";
|
||||
%endif
|
||||
var analytics = {
|
||||
"track": function() {}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<%page expression_filter="h"/>
|
||||
<section class="sequence-edit">
|
||||
<%include file="metadata-edit.html" />
|
||||
</section>
|
||||
|
||||
@@ -9,6 +9,7 @@ from datetime import datetime, timedelta
|
||||
from urlparse import urljoin
|
||||
|
||||
import pytz
|
||||
from markupsafe import escape
|
||||
from mock import Mock, patch
|
||||
from opaque_keys.edx.locations import SlashSeparatedCourseKey
|
||||
from pyquery import PyQuery as pq
|
||||
@@ -404,7 +405,7 @@ class DashboardTest(ModuleStoreTestCase):
|
||||
self.assertNotIn('Add Certificate to LinkedIn', response.content)
|
||||
|
||||
response_url = 'http://www.linkedin.com/profile/add?_ed='
|
||||
self.assertNotContains(response, response_url)
|
||||
self.assertNotContains(response, escape(response_url))
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@patch.dict('django.conf.settings.FEATURES', {'CERTIFICATES_HTML_VIEW': False})
|
||||
@@ -452,7 +453,7 @@ class DashboardTest(ModuleStoreTestCase):
|
||||
'pfCertificationUrl=www.edx.org&'
|
||||
'source=o'
|
||||
)
|
||||
self.assertContains(response, expected_url)
|
||||
self.assertContains(response, escape(expected_url))
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
@ddt.data(ModuleStoreEnum.Type.mongo, ModuleStoreEnum.Type.split)
|
||||
|
||||
30
common/djangoapps/util/course_key_utils.py
Normal file
30
common/djangoapps/util/course_key_utils.py
Normal file
@@ -0,0 +1,30 @@
|
||||
"""
|
||||
Convenience methods for working with course objects
|
||||
"""
|
||||
from django.http import Http404
|
||||
from opaque_keys import InvalidKeyError
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
|
||||
|
||||
def from_string_or_404(course_key_string):
|
||||
"""
|
||||
Gets CourseKey from the string passed as parameter.
|
||||
|
||||
Parses course key from string(containing course key) or raises 404 if the string's format is invalid.
|
||||
|
||||
Arguments:
|
||||
course_key_string(str): It is string containing the course key
|
||||
|
||||
Returns:
|
||||
CourseKey: A key that uniquely identifies a course
|
||||
|
||||
Raises:
|
||||
HTTP404: A 404 not found exception will be thrown if course_key_string's format is invalid
|
||||
|
||||
"""
|
||||
try:
|
||||
course_key = CourseKey.from_string(course_key_string)
|
||||
except InvalidKeyError:
|
||||
raise Http404
|
||||
|
||||
return course_key
|
||||
32
common/djangoapps/util/tests/test_course_key_utils.py
Normal file
32
common/djangoapps/util/tests/test_course_key_utils.py
Normal file
@@ -0,0 +1,32 @@
|
||||
"""
|
||||
Tests for util.course_key_utils
|
||||
"""
|
||||
from nose.tools import assert_equals, assert_raises # pylint: disable=no-name-in-module
|
||||
from util.course_key_utils import from_string_or_404
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
from django.http import Http404
|
||||
|
||||
|
||||
def test_from_string_or_404():
|
||||
|
||||
#testing with split style course keys
|
||||
assert_raises(
|
||||
Http404,
|
||||
from_string_or_404,
|
||||
"/some.invalid.key/course-v1:TTT+CS01+2015_T0"
|
||||
)
|
||||
assert_equals(
|
||||
CourseKey.from_string("course-v1:TTT+CS01+2015_T0"),
|
||||
from_string_or_404("course-v1:TTT+CS01+2015_T0")
|
||||
)
|
||||
|
||||
#testing with mongo style course keys
|
||||
assert_raises(
|
||||
Http404,
|
||||
from_string_or_404,
|
||||
"/some.invalid.key/TTT/CS01/2015_T0"
|
||||
)
|
||||
assert_equals(
|
||||
CourseKey.from_string("TTT/CS01/2015_T0"),
|
||||
from_string_or_404("TTT/CS01/2015_T0")
|
||||
)
|
||||
@@ -45,7 +45,7 @@ lib_paths:
|
||||
- common_static/js/vendor/jquery.ui.draggable.js
|
||||
- common_static/js/vendor/jquery.cookie.js
|
||||
- common_static/js/vendor/json2.js
|
||||
- common_static/js/vendor/underscore-min.js
|
||||
- common_static/common/js/vendor/underscore.js
|
||||
- common_static/js/vendor/backbone-min.js
|
||||
- common_static/js/vendor/jquery.leanModal.js
|
||||
- common_static/js/vendor/CodeMirror/codemirror.js
|
||||
|
||||
@@ -361,9 +361,14 @@ function (VideoPlayer) {
|
||||
|
||||
describe('onSeek', function () {
|
||||
beforeEach(function () {
|
||||
// jasmine.Clock can't be used to fake out debounce with newer versions of underscore
|
||||
spyOn(_, 'debounce').andCallFake(function (func) {
|
||||
return function () {
|
||||
func.apply(this, arguments);
|
||||
};
|
||||
});
|
||||
state = jasmine.initializePlayer();
|
||||
state.videoEl = $('video, iframe');
|
||||
jasmine.Clock.useMock();
|
||||
spyOn(state.videoPlayer, 'duration').andReturn(120);
|
||||
});
|
||||
|
||||
@@ -384,9 +389,6 @@ function (VideoPlayer) {
|
||||
spyOn(state.videoPlayer, 'stopTimer');
|
||||
spyOn(state.videoPlayer, 'runTimer');
|
||||
state.videoPlayer.seekTo(10);
|
||||
// Video player uses _.debounce (with a wait time in 300 ms) for seeking.
|
||||
// That's why we have to do this tick(300).
|
||||
jasmine.Clock.tick(300);
|
||||
expect(state.videoPlayer.currentTime).toBe(10);
|
||||
expect(state.videoPlayer.stopTimer).toHaveBeenCalled();
|
||||
expect(state.videoPlayer.runTimer).toHaveBeenCalled();
|
||||
@@ -399,9 +401,6 @@ function (VideoPlayer) {
|
||||
state.videoProgressSlider.onSlide(
|
||||
jQuery.Event('slide'), { value: 30 }
|
||||
);
|
||||
// Video player uses _.debounce (with a wait time in 300 ms) for seeking.
|
||||
// That's why we have to do this tick(300).
|
||||
jasmine.Clock.tick(300);
|
||||
expect(state.videoPlayer.currentTime).toBe(30);
|
||||
expect(state.videoPlayer.player.seekTo).toHaveBeenCalledWith(30, true);
|
||||
});
|
||||
@@ -413,9 +412,6 @@ function (VideoPlayer) {
|
||||
state.videoProgressSlider.onSlide(
|
||||
jQuery.Event('slide'), { value: 30 }
|
||||
);
|
||||
// Video player uses _.debounce (with a wait time in 300 ms) for seeking.
|
||||
// That's why we have to do this tick(300).
|
||||
jasmine.Clock.tick(300);
|
||||
expect(state.videoPlayer.currentTime).toBe(30);
|
||||
expect(state.videoPlayer.updatePlayTime).toHaveBeenCalledWith(30, true);
|
||||
});
|
||||
@@ -426,17 +422,11 @@ function (VideoPlayer) {
|
||||
state.videoProgressSlider.onSlide(
|
||||
jQuery.Event('slide'), { value: 20 }
|
||||
);
|
||||
// Video player uses _.debounce (with a wait time in 300 ms) for seeking.
|
||||
// That's why we have to do this tick(300).
|
||||
jasmine.Clock.tick(300);
|
||||
state.videoPlayer.pause();
|
||||
expect(state.videoPlayer.currentTime).toBe(20);
|
||||
state.videoProgressSlider.onSlide(
|
||||
jQuery.Event('slide'), { value: 10 }
|
||||
);
|
||||
// Video player uses _.debounce (with a wait time in 300 ms) for seeking.
|
||||
// That's why we have to do this tick(300).
|
||||
jasmine.Clock.tick(300);
|
||||
expect(state.videoPlayer.currentTime).toBe(10);
|
||||
});
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ var setupFullScreenModal = function() {
|
||||
"largeALT": smallImageObject.attr('alt'),
|
||||
"largeSRC": largeImageSRC
|
||||
};
|
||||
var html = _.template($("#image-modal-tpl").text(), data);
|
||||
var html = _.template($("#image-modal-tpl").text())(data);
|
||||
$(this).replaceWith(html);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1160,7 +1160,7 @@ class ModuleStoreReadBase(BulkOperationsMixin, ModuleStoreRead):
|
||||
contentstore=None,
|
||||
doc_store_config=None, # ignore if passed up
|
||||
metadata_inheritance_cache_subsystem=None, request_cache=None,
|
||||
xblock_mixins=(), xblock_select=None, disabled_xblock_types=(), # pylint: disable=bad-continuation
|
||||
xblock_mixins=(), xblock_select=None, xblock_field_data_wrappers=(), disabled_xblock_types=(), # pylint: disable=bad-continuation
|
||||
# temporary parms to enable backward compatibility. remove once all envs migrated
|
||||
db=None, collection=None, host=None, port=None, tz_aware=True, user=None, password=None,
|
||||
# allow lower level init args to pass harmlessly
|
||||
@@ -1177,6 +1177,7 @@ class ModuleStoreReadBase(BulkOperationsMixin, ModuleStoreRead):
|
||||
self.request_cache = request_cache
|
||||
self.xblock_mixins = xblock_mixins
|
||||
self.xblock_select = xblock_select
|
||||
self.xblock_field_data_wrappers = xblock_field_data_wrappers
|
||||
self.disabled_xblock_types = disabled_xblock_types
|
||||
self.contentstore = contentstore
|
||||
|
||||
|
||||
@@ -120,11 +120,25 @@ def load_function(path):
|
||||
"""
|
||||
Load a function by name.
|
||||
|
||||
path is a string of the form "path.to.module.function"
|
||||
returns the imported python object `function` from `path.to.module`
|
||||
Arguments:
|
||||
path: String of the form 'path.to.module.function'. Strings of the form
|
||||
'path.to.module:Class.function' are also valid.
|
||||
|
||||
Returns:
|
||||
The imported object 'function'.
|
||||
"""
|
||||
module_path, _, name = path.rpartition('.')
|
||||
return getattr(import_module(module_path), name)
|
||||
if ':' in path:
|
||||
module_path, _, method_path = path.rpartition(':')
|
||||
module = import_module(module_path)
|
||||
|
||||
class_name, method_name = method_path.split('.')
|
||||
_class = getattr(module, class_name)
|
||||
function = getattr(_class, method_name)
|
||||
else:
|
||||
module_path, _, name = path.rpartition('.')
|
||||
function = getattr(import_module(module_path), name)
|
||||
|
||||
return function
|
||||
|
||||
|
||||
def create_modulestore_instance(
|
||||
@@ -179,12 +193,15 @@ def create_modulestore_instance(
|
||||
else:
|
||||
disabled_xblock_types = ()
|
||||
|
||||
xblock_field_data_wrappers = [load_function(path) for path in settings.XBLOCK_FIELD_DATA_WRAPPERS]
|
||||
|
||||
return class_(
|
||||
contentstore=content_store,
|
||||
metadata_inheritance_cache_subsystem=metadata_inheritance_cache,
|
||||
request_cache=request_cache,
|
||||
xblock_mixins=getattr(settings, 'XBLOCK_MIXINS', ()),
|
||||
xblock_select=getattr(settings, 'XBLOCK_SELECT_FUNCTION', None),
|
||||
xblock_field_data_wrappers=xblock_field_data_wrappers,
|
||||
disabled_xblock_types=disabled_xblock_types,
|
||||
doc_store_config=doc_store_config,
|
||||
i18n_service=i18n_service or ModuleI18nService(),
|
||||
|
||||
@@ -218,6 +218,17 @@ class InheritanceMixin(XBlockMixin):
|
||||
default=False
|
||||
)
|
||||
|
||||
self_paced = Boolean(
|
||||
display_name=_('Self Paced'),
|
||||
help=_(
|
||||
'Set this to "true" to mark this course as self-paced. Self-paced courses do not have '
|
||||
'due dates for assignments, and students can progress through the course at any rate before '
|
||||
'the course ends.'
|
||||
),
|
||||
default=False,
|
||||
scope=Scope.settings
|
||||
)
|
||||
|
||||
|
||||
def compute_inherited_metadata(descriptor):
|
||||
"""Given a descriptor, traverse all of its descendants and do metadata
|
||||
|
||||
@@ -12,27 +12,24 @@ structure:
|
||||
}
|
||||
"""
|
||||
|
||||
import pymongo
|
||||
import sys
|
||||
import logging
|
||||
import copy
|
||||
from datetime import datetime
|
||||
from importlib import import_module
|
||||
import logging
|
||||
import pymongo
|
||||
import re
|
||||
import sys
|
||||
from uuid import uuid4
|
||||
|
||||
from bson.son import SON
|
||||
from datetime import datetime
|
||||
from contracts import contract, new_contract
|
||||
from fs.osfs import OSFS
|
||||
from mongodb_proxy import autoretry_read
|
||||
from opaque_keys.edx.keys import UsageKey, CourseKey, AssetKey
|
||||
from opaque_keys.edx.locations import Location, BlockUsageLocator, SlashSeparatedCourseKey
|
||||
from opaque_keys.edx.locator import CourseLocator, LibraryLocator
|
||||
from path import Path as path
|
||||
from pytz import UTC
|
||||
from contracts import contract, new_contract
|
||||
|
||||
from importlib import import_module
|
||||
from opaque_keys.edx.keys import UsageKey, CourseKey, AssetKey
|
||||
from opaque_keys.edx.locations import Location, BlockUsageLocator
|
||||
from opaque_keys.edx.locations import SlashSeparatedCourseKey
|
||||
from opaque_keys.edx.locator import CourseLocator, LibraryLocator
|
||||
|
||||
from xblock.core import XBlock
|
||||
from xblock.exceptions import InvalidScopeError
|
||||
from xblock.fields import Scope, ScopeIds, Reference, ReferenceList, ReferenceValueDict
|
||||
@@ -54,6 +51,7 @@ from xmodule.modulestore.xml import CourseLocationManager
|
||||
from xmodule.modulestore.store_utilities import DETACHED_XBLOCK_TYPES
|
||||
from xmodule.services import SettingsService
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
new_contract('CourseKey', CourseKey)
|
||||
@@ -318,6 +316,9 @@ class CachingDescriptorSystem(MakoDescriptorSystem, EditInfoRuntimeMixin):
|
||||
).replace(tzinfo=UTC)
|
||||
module._edit_info['published_by'] = raw_metadata.get('published_by')
|
||||
|
||||
for wrapper in self.modulestore.xblock_field_data_wrappers:
|
||||
module._field_data = wrapper(module, module._field_data) # pylint: disable=protected-access
|
||||
|
||||
# decache any computed pending field settings
|
||||
module.save()
|
||||
return module
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import sys
|
||||
import logging
|
||||
|
||||
from contracts import contract, new_contract
|
||||
from fs.osfs import OSFS
|
||||
from lazy import lazy
|
||||
@@ -7,6 +8,7 @@ from xblock.runtime import KvsFieldData, KeyValueStore
|
||||
from xblock.fields import ScopeIds
|
||||
from xblock.core import XBlock
|
||||
from opaque_keys.edx.locator import BlockUsageLocator, LocalId, CourseLocator, LibraryLocator, DefinitionLocator
|
||||
|
||||
from xmodule.library_tools import LibraryToolsService
|
||||
from xmodule.mako_module import MakoDescriptorSystem
|
||||
from xmodule.error_module import ErrorDescriptor
|
||||
@@ -263,6 +265,10 @@ class CachingDescriptorSystem(MakoDescriptorSystem, EditInfoRuntimeMixin):
|
||||
module.update_version = edit_info.update_version
|
||||
module.source_version = edit_info.source_version
|
||||
module.definition_locator = DefinitionLocator(block_key.type, definition_id)
|
||||
|
||||
for wrapper in self.modulestore.xblock_field_data_wrappers:
|
||||
module._field_data = wrapper(module, module._field_data) # pylint: disable=protected-access
|
||||
|
||||
# decache any pending field settings
|
||||
module.save()
|
||||
|
||||
|
||||
@@ -108,6 +108,8 @@ class VideoModule(VideoFields, VideoTranscriptsMixin, VideoStudentViewHandlers,
|
||||
# To make sure that js files are called in proper order we use numerical
|
||||
# index. We do that to avoid issues that occurs in tests.
|
||||
module = __name__.replace('.video_module', '', 2)
|
||||
|
||||
#TODO: For each of the following, ensure that any generated html is properly escaped.
|
||||
js = {
|
||||
'js': [
|
||||
resource_string(module, 'js/src/video/00_component.js'),
|
||||
|
||||
@@ -23,6 +23,14 @@ class @DiscussionViewSpecHelper
|
||||
}
|
||||
$.extend(thread, props)
|
||||
|
||||
@checkVoteClasses = (view) ->
|
||||
view.render()
|
||||
display_button = view.$el.find(".display-vote")
|
||||
expect(display_button.hasClass("is-hidden")).toBe(true)
|
||||
action_button = view.$el.find(".action-vote")
|
||||
# Check that inline css is not applied to the ".action-vote"
|
||||
expect(action_button).not.toHaveAttr('style','display: inline; ');
|
||||
|
||||
@expectVoteRendered = (view, model, user) ->
|
||||
button = view.$el.find(".action-vote")
|
||||
expect(button.hasClass("is-checked")).toBe(user.voted(model))
|
||||
|
||||
@@ -30,6 +30,9 @@ describe "ThreadResponseShowView", ->
|
||||
it "renders the vote state correctly", ->
|
||||
DiscussionViewSpecHelper.checkRenderVote(@view, @comment)
|
||||
|
||||
it "check the vote classes after renders", ->
|
||||
DiscussionViewSpecHelper.checkVoteClasses(@view)
|
||||
|
||||
it "votes correctly via click", ->
|
||||
DiscussionViewSpecHelper.checkUpvote(@view, @comment, @user, $.Event("click"))
|
||||
|
||||
@@ -115,7 +118,7 @@ describe "ThreadResponseShowView", ->
|
||||
expect(@view.$(".posted-details").text()).not.toMatch("marked as answer")
|
||||
|
||||
it "allows a moderator to mark an answer in a question thread", ->
|
||||
DiscussionUtil.loadRoles({"Moderator": parseInt(window.user.id)})
|
||||
DiscussionUtil.loadRoles({"Moderator": [parseInt(window.user.id)]})
|
||||
@thread.set({
|
||||
"thread_type": "question",
|
||||
"user_id": (parseInt(window.user.id) + 1).toString()
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
class @DiscussionFilter
|
||||
|
||||
# TODO: this helper class duplicates functionality in DiscussionThreadListView.filterTopics
|
||||
# for use with a very similar category dropdown in the New Post form. The two menus' implementations
|
||||
# should be merged into a single reusable view.
|
||||
|
||||
@filterDrop: (e) ->
|
||||
$drop = $(e.target).parents('.topic-menu-wrapper')
|
||||
query = $(e.target).val()
|
||||
$items = $drop.find('.topic-menu-item')
|
||||
|
||||
if(query.length == 0)
|
||||
$items.removeClass('hidden')
|
||||
return;
|
||||
|
||||
$items.addClass('hidden')
|
||||
$items.each (i) ->
|
||||
|
||||
path = $(this).parents(".topic-menu-item").andSelf()
|
||||
pathTitles = path.children(".topic-title").map((i, elem) -> $(elem).text()).get()
|
||||
pathText = pathTitles.join(" / ").toLowerCase()
|
||||
|
||||
if query.split(" ").every((term) -> pathText.search(term.toLowerCase()) != -1)
|
||||
$(this).removeClass('hidden')
|
||||
# show children
|
||||
$(this).find('.topic-menu-item').removeClass('hidden');
|
||||
# show parents
|
||||
$(this).parents('.topic-menu-item').removeClass('hidden');
|
||||
@@ -138,7 +138,6 @@ if Backbone?
|
||||
closed: (closed) ->
|
||||
@updateButtonState(".action-close", closed)
|
||||
@$(".post-label-closed").toggleClass("is-hidden", not closed)
|
||||
@$(".action-vote").toggle(not closed)
|
||||
@$(".display-vote").toggle(closed)
|
||||
})
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
this.threadType = this.model.get('thread_type');
|
||||
this.topicId = this.model.get('commentable_id');
|
||||
this.context = options.context || 'course';
|
||||
_.bindAll(this);
|
||||
_.bindAll(this, 'updateHandler', 'cancelHandler');
|
||||
return this;
|
||||
},
|
||||
|
||||
|
||||
@@ -39,9 +39,9 @@ if Backbone?
|
||||
|
||||
@searchAlertCollection.on "add", (searchAlert) =>
|
||||
content = _.template(
|
||||
$("#search-alert-template").html(),
|
||||
$("#search-alert-template").html())(
|
||||
{'message': searchAlert.attributes.message, 'cid': searchAlert.cid}
|
||||
)
|
||||
)
|
||||
@$(".search-alerts").append(content)
|
||||
@$("#search-alert-" + searchAlert.cid + " a.dismiss").bind "click", searchAlert, (event) =>
|
||||
@removeSearchAlert(event.data.cid)
|
||||
@@ -491,7 +491,7 @@ if Backbone?
|
||||
message = interpolate(
|
||||
_.escape(gettext('Show posts by %(username)s.')),
|
||||
{"username":
|
||||
_.template('<a class="link-jump" href="<%= url %>"><%- username %></a>', {
|
||||
_.template('<a class="link-jump" href="<%= url %>"><%- username %></a>')({
|
||||
url: DiscussionUtil.urlFor("user_profile", response.users[0].id),
|
||||
username: response.users[0].username
|
||||
})
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
'click .post-topic-button': 'toggleTopicDropdown',
|
||||
'click .topic-menu-wrapper': 'handleTopicEvent',
|
||||
'click .topic-filter-label': 'ignoreClick',
|
||||
'keyup .topic-filter-input': this.DiscussionFilter.filterDrop
|
||||
'keyup .topic-filter-input': 'filterDrop'
|
||||
},
|
||||
|
||||
attributes: {
|
||||
@@ -17,7 +17,9 @@
|
||||
this.course_settings = options.course_settings;
|
||||
this.currentTopicId = options.topicId;
|
||||
this.maxNameWidth = 100;
|
||||
_.bindAll(this);
|
||||
_.bindAll(this,
|
||||
'toggleTopicDropdown', 'handleTopicEvent', 'hideTopicDropdown', 'ignoreClick'
|
||||
);
|
||||
return this;
|
||||
},
|
||||
|
||||
@@ -34,7 +36,7 @@
|
||||
render: function() {
|
||||
var context = _.clone(this.course_settings.attributes);
|
||||
context.topics_html = this.renderCategoryMap(this.course_settings.get('category_map'));
|
||||
this.$el.html(_.template($('#topic-template').html(), context));
|
||||
this.$el.html(_.template($('#topic-template').html())(context));
|
||||
this.dropdownButton = this.$('.post-topic-button');
|
||||
this.topicMenu = this.$('.topic-menu-wrapper');
|
||||
this.selectedTopic = this.$('.js-selected-topic');
|
||||
@@ -187,6 +189,38 @@
|
||||
}
|
||||
}
|
||||
return name;
|
||||
},
|
||||
|
||||
// TODO: this helper class duplicates functionality in DiscussionThreadListView.filterTopics
|
||||
// for use with a very similar category dropdown in the New Post form. The two menus' implementations
|
||||
// should be merged into a single reusable view.
|
||||
filterDrop: function (e) {
|
||||
var $drop, $items, query;
|
||||
$drop = $(e.target).parents('.topic-menu-wrapper');
|
||||
query = $(e.target).val();
|
||||
$items = $drop.find('.topic-menu-item');
|
||||
|
||||
if (query.length === 0) {
|
||||
$items.removeClass('hidden');
|
||||
return;
|
||||
}
|
||||
|
||||
$items.addClass('hidden');
|
||||
$items.each(function (_index, item) {
|
||||
var path, pathText, pathTitles;
|
||||
path = $(item).parents(".topic-menu-item").andSelf();
|
||||
pathTitles = path.children(".topic-title").map(function (_, elem) {
|
||||
return $(elem).text();
|
||||
}).get();
|
||||
pathText = pathTitles.join(" / ").toLowerCase();
|
||||
if (query.split(" ").every(function (term) {
|
||||
return pathText.search(term.toLowerCase()) !== -1;
|
||||
})) {
|
||||
$(item).removeClass('hidden');
|
||||
$(item).find('.topic-menu-item').removeClass('hidden');
|
||||
$(item).parents('.topic-menu-item').removeClass('hidden');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ if Backbone?
|
||||
mode: @mode,
|
||||
form_id: @mode + (if @topicId then "-" + @topicId else "")
|
||||
})
|
||||
@$el.html(_.template($("#new-post-template").html(), context))
|
||||
@$el.html(_.template($("#new-post-template").html())(context))
|
||||
threadTypeTemplate = _.template($("#thread-type-template").html());
|
||||
if $('.js-group-select').is(':disabled')
|
||||
$('.group-selector-wrapper').addClass('disabled')
|
||||
|
||||
@@ -79,6 +79,9 @@
|
||||
* underlying server API.
|
||||
*/
|
||||
getPage: function () {
|
||||
// TODO: this.currentPage is currently returning a function sometimes when it is called.
|
||||
// It is possible it always did this, but we either need to investigate more, or just wait until
|
||||
// we replace this code with the pattern library.
|
||||
return this.currentPage + (this.isZeroIndexed ? 1 : 0);
|
||||
},
|
||||
|
||||
|
||||
@@ -244,7 +244,7 @@
|
||||
if (!validateTotalKeyLength(key_field_selectors)) {
|
||||
$(selectors.errorWrapper).addClass(classes.shown).removeClass(classes.hiding);
|
||||
$(selectors.errorMessage).html(
|
||||
'<p>' + _.template(message_tpl, {limit: MAX_SUM_KEY_LENGTH}) + '</p>'
|
||||
'<p>' + _.template(message_tpl)({limit: MAX_SUM_KEY_LENGTH}) + '</p>'
|
||||
);
|
||||
$(selectors.save).addClass(classes.disabled);
|
||||
} else {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
'use strict';
|
||||
define(["jquery", "underscore", "underscore.string", "common/js/components/views/feedback"],
|
||||
function($, _, str, SystemFeedbackView) {
|
||||
str = str || _.str;
|
||||
|
||||
var Alert = SystemFeedbackView.extend({
|
||||
options: $.extend({}, SystemFeedbackView.prototype.options, {
|
||||
type: "alert"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
'use strict';
|
||||
define(["jquery", "underscore", "underscore.string", "common/js/components/views/feedback"],
|
||||
function($, _, str, SystemFeedbackView) {
|
||||
str = str || _.str;
|
||||
|
||||
var Notification = SystemFeedbackView.extend({
|
||||
options: $.extend({}, SystemFeedbackView.prototype.options, {
|
||||
type: "notification",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
'use strict';
|
||||
define(["jquery", "underscore", "underscore.string", "common/js/components/views/feedback"],
|
||||
function($, _, str, SystemFeedbackView) {
|
||||
str = str || _.str;
|
||||
|
||||
var Prompt = SystemFeedbackView.extend({
|
||||
options: $.extend({}, SystemFeedbackView.prototype.options, {
|
||||
type: "prompt",
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.$el.html(_.template(paginatedViewTemplate, {type: this.type}));
|
||||
this.$el.html(_.template(paginatedViewTemplate)({type: this.type}));
|
||||
this.assign(this.listView, '.' + this.type + '-list');
|
||||
if (this.headerView) {
|
||||
this.assign(this.headerView, '.' + this.type + '-paging-header');
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user