Merge remote-tracking branch 'origin/master' into release, conflicts resolved

Conflicts:
	cms/envs/common.py
	common/lib/xmodule/xmodule/seq_module.py
	lms/envs/common.py
	requirements/edx/edx-private.txt
This commit is contained in:
Han Su Kim
2014-03-06 16:56:16 -05:00
225 changed files with 17128 additions and 4270 deletions

View File

@@ -3,10 +3,9 @@
from lettuce import world, step
from nose.tools import assert_false, assert_equal, assert_regexp_matches # pylint: disable=E0611
from common import type_in_codemirror, press_the_notification_button
from common import type_in_codemirror, press_the_notification_button, get_codemirror_value
KEY_CSS = '.key input.policy-key'
VALUE_CSS = 'textarea.json'
DISPLAY_NAME_KEY = "display_name"
DISPLAY_NAME_VALUE = '"Robot Super Course"'
@@ -101,7 +100,7 @@ def assert_policy_entries(expected_keys, expected_values):
for key, value in zip(expected_keys, expected_values):
index = get_index_of(key)
assert_false(index == -1, "Could not find key: {key}".format(key=key))
found_value = world.css_find(VALUE_CSS)[index].value
found_value = get_codemirror_value(index)
assert_equal(
value, found_value,
"Expected {} to have value {} but found {}".format(key, value, found_value)
@@ -120,15 +119,13 @@ def get_index_of(expected_key):
def get_display_name_value():
index = get_index_of(DISPLAY_NAME_KEY)
return world.css_value(VALUE_CSS, index=index)
return get_codemirror_value(index)
def change_display_name_value(step, new_value):
change_value(step, DISPLAY_NAME_KEY, new_value)
def change_value(step, key, new_value):
type_in_codemirror(get_index_of(key), new_value)
world.wait(0.5)
index = get_index_of(key)
type_in_codemirror(index, new_value)
press_the_notification_button(step, "Save")
world.wait_for_ajax_complete()

View File

@@ -319,20 +319,18 @@ def i_am_shown_a_notification(step):
def type_in_codemirror(index, text):
world.wait(1) # For now, slow this down so that it works. TODO: fix it.
world.css_click("div.CodeMirror-lines", index=index)
world.browser.execute_script("$('div.CodeMirror.CodeMirror-focused > div').css('overflow', '')")
g = world.css_find("div.CodeMirror.CodeMirror-focused > div > textarea")
if world.is_mac():
g._element.send_keys(Keys.COMMAND + 'a')
else:
g._element.send_keys(Keys.CONTROL + 'a')
g._element.send_keys(Keys.DELETE)
g._element.send_keys(text)
if world.is_firefox():
world.trigger_event('div.CodeMirror', index=index, event='blur')
script = """
var cm = $('div.CodeMirror:eq({})').get(0).CodeMirror;
cm.getInputField().focus();
cm.setValue(arguments[0]);
cm.getInputField().blur();""".format(index)
world.browser.driver.execute_script(script, str(text))
world.wait_for_ajax_complete()
def get_codemirror_value(index=0):
return world.browser.driver.execute_script("""
return $('div.CodeMirror:eq({})').get(0).CodeMirror.getValue();
""".format(index))
def upload_file(filename):
path = os.path.join(TEST_ROOT, filename)

View File

@@ -3,7 +3,7 @@
from lettuce import world, step
from selenium.webdriver.common.keys import Keys
from common import type_in_codemirror
from common import type_in_codemirror, get_codemirror_value
from nose.tools import assert_in # pylint: disable=E0611
@@ -74,7 +74,7 @@ def change_date(_step, new_date):
@step(u'I should see the date "([^"]*)"$')
def check_date(_step, date):
date_css = 'span.date-display'
assert date == world.css_html(date_css)
assert_in(date, world.css_html(date_css))
@step(u'I modify the handout to "([^"]*)"$')
@@ -87,7 +87,7 @@ def edit_handouts(_step, text):
@step(u'I see the handout "([^"]*)"$')
def check_handout(_step, handout):
handout_css = 'div.handouts-content'
assert handout in world.css_html(handout_css)
assert_in(handout, world.css_html(handout_css))
@step(u'I see the handout error text')
@@ -127,6 +127,6 @@ def change_text(text):
def verify_text_in_editor_and_update(button_css, before, after):
world.css_click(button_css)
text = world.css_find(".cm-string").html
assert before in text
text = get_codemirror_value()
assert_in(before, text)
change_text(after)

View File

@@ -19,3 +19,9 @@ Feature: CMS.HTML Editor
Given I have created an E-text Written in LaTeX
When I edit and select Settings
Then Edit High Level Source is visible
Scenario: TinyMCE image plugin sets urls correctly
Given I have created a Blank HTML Page
When I edit the page and select the Visual Editor
And I add an image with a static link via the Image Plugin Icon
Then the image static link is rewritten to translate the path

View File

@@ -2,6 +2,7 @@
#pylint: disable=C0111
from lettuce import world, step
from nose.tools import assert_in # pylint: disable=no-name-in-module
@step('I have created a Blank HTML Page$')
@@ -28,3 +29,43 @@ def i_created_etext_in_latex(step):
category='html',
component_type='E-text Written in LaTeX'
)
@step('I edit the page and select the Visual Editor')
def i_click_on_edit_icon(step):
world.edit_component()
world.wait_for(lambda _driver: world.css_visible('a.visual-tab'))
world.css_click('a.visual-tab')
@step('I add an image with a static link via the Image Plugin Icon')
def i_click_on_image_plugin_icon(step):
# Click on image plugin button
world.wait_for(lambda _driver: world.css_visible('a.mce_image'))
world.css_click('a.mce_image')
# Change to the non-modal TinyMCE Image window
# keeping parent window so we can go back to it.
parent_window = world.browser.current_window
for window in world.browser.windows:
world.browser.switch_to_window(window) # Switch to a different window
if world.browser.title == 'Insert/Edit Image':
# This is the Image window so find the url text box,
# enter text in it then hit Insert button.
url_elem = world.browser.find_by_id("src")
url_elem.fill('/static/image.jpg')
world.browser.find_by_id('insert').click()
world.browser.switch_to_window(parent_window) # Switch back to the main window
@step('the image static link is rewritten to translate the path')
def image_static_link_is_rewritten(step):
# Find the TinyMCE iframe within the main window
with world.browser.get_iframe('mce_0_ifr') as tinymce:
image = tinymce.find_by_tag('img').first
# Test onExecCommandHandler set the url to absolute.
assert_in('c4x/MITx/999/asset/image.jpg', image['src'])

View File

@@ -173,7 +173,7 @@ def cancel_does_not_save_changes(step):
def enable_latex_compiler(step):
url = world.browser.url
step.given("I select the Advanced Settings")
change_value(step, 'use_latex_compiler', True)
change_value(step, 'use_latex_compiler', 'true')
world.visit(url)
world.wait_for_xmodule()

View File

@@ -1003,7 +1003,7 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
# We had a bug where orphaned draft nodes caused export to fail. This is here to cover that case.
vertical.location = mongo.draft.as_draft(vertical.location.replace(name='no_references'))
draft_store.save_xmodule(vertical)
draft_store.update_item(vertical, allow_not_found=True)
orphan_vertical = draft_store.get_item(vertical.location)
self.assertEqual(orphan_vertical.location.name, 'no_references')
@@ -1020,13 +1020,14 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
# now create a new/different private (draft only) vertical
vertical.location = mongo.draft.as_draft(Location(['i4x', 'edX', 'toy', 'vertical', 'a_private_vertical', None]))
draft_store.save_xmodule(vertical)
draft_store.update_item(vertical, allow_not_found=True)
private_vertical = draft_store.get_item(vertical.location)
vertical = None # blank out b/c i destructively manipulated its location 2 lines above
# add the new private to list of children
sequential = module_store.get_item(Location(['i4x', 'edX', 'toy',
'sequential', 'vertical_sequential', None]))
sequential = module_store.get_item(
Location('i4x', 'edX', 'toy', 'sequential', 'vertical_sequential', None)
)
private_location_no_draft = private_vertical.location.replace(revision=None)
sequential.children.append(private_location_no_draft.url())
module_store.update_item(sequential, self.user.id)

View File

@@ -17,6 +17,7 @@ from .utils import CourseTestCase
import contentstore.git_export_utils as git_export_utils
from xmodule.contentstore.django import _CONTENTSTORE
from xmodule.modulestore.django import modulestore
from contentstore.utils import get_modulestore
TEST_DATA_CONTENTSTORE = copy.deepcopy(settings.CONTENTSTORE)
TEST_DATA_CONTENTSTORE['DOC_STORE_CONFIG']['db'] = 'test_xcontent_%s' % uuid4().hex
@@ -70,7 +71,7 @@ class TestExportGit(CourseTestCase):
Test failed course export response.
"""
self.course_module.giturl = 'foobar'
modulestore().save_xmodule(self.course_module)
get_modulestore(self.course_module.location).update_item(self.course_module)
response = self.client.get('{}?action=push'.format(self.test_url))
self.assertIn('Export Failed:', response.content)
@@ -93,7 +94,7 @@ class TestExportGit(CourseTestCase):
self.populateCourse()
self.course_module.giturl = 'file://{}'.format(bare_repo_dir)
modulestore().save_xmodule(self.course_module)
get_modulestore(self.course_module.location).update_item(self.course_module)
response = self.client.get('{}?action=push'.format(self.test_url))
self.assertIn('Export Succeeded', response.content)

View File

@@ -23,11 +23,10 @@ from xblock.exceptions import NoSuchHandlerError
from xblock.fields import Scope
from xblock.plugin import PluginMissingError
from xblock.runtime import Mixologist
from xmodule.x_module import prefer_xmodules
from lms.lib.xblock.runtime import unquote_slashes
from contentstore.utils import get_lms_link_for_item, compute_unit_state, UnitState
from contentstore.utils import get_lms_link_for_item, compute_unit_state, UnitState, get_modulestore
from contentstore.views.helpers import get_parent_xblock
from models.settings.course_grading import CourseGradingModel
@@ -310,13 +309,20 @@ def container_handler(request, tag=None, package_id=None, branch=None, version_g
old_location, course, xblock, __ = _get_item_in_course(request, locator)
except ItemNotFoundError:
return HttpResponseBadRequest()
parent_xblock = get_parent_xblock(xblock)
ancestor_xblocks = []
parent = get_parent_xblock(xblock)
while parent and parent.category != 'sequential':
ancestor_xblocks.append(parent)
parent = get_parent_xblock(parent)
ancestor_xblocks.reverse()
return render_to_response('container.html', {
'context_course': course,
'xblock': xblock,
'xblock_locator': locator,
'parent_xblock': parent_xblock,
'ancestor_xblocks': ancestor_xblocks,
})
else:
return HttpResponseBadRequest("Only supports html requests")
@@ -359,7 +365,7 @@ def component_handler(request, usage_id, handler, suffix=''):
location = unquote_slashes(usage_id)
descriptor = modulestore().get_item(location)
descriptor = get_modulestore(location).get_item(location)
# Let the module handle the AJAX
req = django_to_webob_request(request)
@@ -370,6 +376,8 @@ def component_handler(request, usage_id, handler, suffix=''):
log.info("XBlock %s attempted to access missing handler %r", descriptor, handler, exc_info=True)
raise Http404
modulestore().save_xmodule(descriptor)
# unintentional update to handle any side effects of handle call; so, request user didn't author
# the change
get_modulestore(location).update_item(descriptor, None)
return webob_to_django_response(resp)

View File

@@ -127,6 +127,12 @@ def xblock_handler(request, tag=None, package_id=None, branch=None, version_guid
return _delete_item_at_location(old_location, delete_children, delete_all_versions, request.user)
else: # Since we have a package_id, we are updating an existing xblock.
if block == 'handouts' and old_location is None:
# update handouts location in loc_mapper
course_location = loc_mapper().translate_locator_to_location(locator, get_course=True)
old_location = course_location.replace(category='course_info', name=block)
locator = loc_mapper().translate_location(course_location.course_id, old_location)
return _save_item(
request,
locator,
@@ -202,16 +208,16 @@ def xblock_view_handler(request, package_id, view_name, tag=None, branch=None, v
log.debug("unable to render studio_view for %r", component, exc_info=True)
fragment = Fragment(render_to_string('html_error.html', {'message': str(exc)}))
store.save_xmodule(component)
# change not authored by requestor but by xblocks.
store.update_item(component, None)
elif view_name == 'student_view' and component.has_children:
# For non-leaf xblocks on the unit page, show the special rendering
# which links to the new container page.
course_location = loc_mapper().translate_locator_to_location(locator, True)
course = store.get_item(course_location)
html = render_to_string('unit_container_xblock_component.html', {
'course': course,
html = render_to_string('container_xblock_component.html', {
'xblock': component,
'locator': locator
'locator': locator,
'reordering_enabled': True,
})
return JsonResponse({
'html': html,
@@ -521,8 +527,8 @@ def orphan_handler(request, tag=None, package_id=None, branch=None, version_guid
if request.method == 'DELETE':
if request.user.is_staff:
items = modulestore().get_orphans(old_location, 'draft')
for item in items:
modulestore('draft').delete_item(item, delete_all_versions=True)
for itemloc in items:
modulestore('draft').delete_item(itemloc, delete_all_versions=True)
return JsonResponse({'deleted': items})
else:
raise PermissionDenied()

View File

@@ -179,6 +179,8 @@ def _studio_wrap_xblock(xblock, view, frag, context, display_name_only=False):
}
if xblock.category == 'vertical':
template = 'studio_vertical_wrapper.html'
elif xblock.location != context.get('root_xblock').location and xblock.has_children:
template = 'container_xblock_component.html'
else:
template = 'studio_xblock_wrapper.html'
html = render_to_string(template, template_context)

View File

@@ -26,8 +26,44 @@ class ContainerViewTestCase(CourseTestCase):
category="video", display_name="My Video")
def test_container_html(self):
url = xblock_studio_url(self.child_vertical)
self._test_html_content(
self.child_vertical,
expected_section_tag='<section class="wrapper-xblock level-page" data-locator="MITx.999.Robot_Super_Course/branch/published/block/Child_Vertical"/>',
expected_breadcrumbs=(
r'<a href="/unit/MITx.999.Robot_Super_Course/branch/published/block/Unit"\s*'
r'class="navigation-link navigation-parent">Unit</a>\s*'
r'<a href="#" class="navigation-link navigation-current">Child Vertical</a>'),
)
def test_container_on_container_html(self):
"""
Create the scenario of an xblock with children (non-vertical) on the container page.
This should create a container page that is a child of another container page.
"""
xblock_with_child = ItemFactory.create(parent_location=self.child_vertical.location,
category="wrapper", display_name="Wrapper")
ItemFactory.create(parent_location=xblock_with_child.location,
category="html", display_name="Child HTML")
self._test_html_content(
xblock_with_child,
expected_section_tag='<section class="wrapper-xblock level-page" data-locator="MITx.999.Robot_Super_Course/branch/published/block/Wrapper"/>',
expected_breadcrumbs=(
r'<a href="/unit/MITx.999.Robot_Super_Course/branch/published/block/Unit"\s*'
r'class="navigation-link navigation-parent">Unit</a>\s*'
r'<a href="/container/MITx.999.Robot_Super_Course/branch/published/block/Child_Vertical"\s*'
r'class="navigation-link navigation-parent">Child Vertical</a>\s*'
r'<a href="#" class="navigation-link navigation-current">Wrapper</a>'),
)
def _test_html_content(self, xblock, expected_section_tag, expected_breadcrumbs):
"""
Get the HTML for a container page and verify the section tag is correct
and the breadcrumbs trail is correct.
"""
url = xblock_studio_url(xblock, self.course)
resp = self.client.get_html(url)
self.assertEqual(resp.status_code, 200)
html = resp.content
self.assertIn('<section class="wrapper-xblock level-page" data-locator="MITx.999.Robot_Super_Course/branch/published/block/Child_Vertical"/>', html)
self.assertIn(expected_section_tag, html)
# Verify the navigation link at the top of the page is correct.
self.assertRegexpMatches(html, expected_breadcrumbs)

View File

@@ -230,7 +230,8 @@ class CourseUpdateTest(CourseTestCase):
def test_post_course_update(self):
"""
Test that a user can successfully post on course updates of a course whose location in not in loc_mapper
Test that a user can successfully post on course updates and handouts of a course
whose location in not in loc_mapper
"""
# create a course via the view handler
course_location = Location(['i4x', 'Org_1', 'Course_1', 'course', 'Run_1'])
@@ -270,3 +271,19 @@ class CourseUpdateTest(CourseTestCase):
updates_locator = loc_mapper().translate_location(course_location.course_id, updates_location)
self.assertTrue(isinstance(updates_locator, BlockUsageLocator))
self.assertEqual(updates_locator.block_id, block)
# check posting on handouts
block = u'handouts'
handouts_locator = BlockUsageLocator(
package_id=updates_locator.package_id, branch=updates_locator.branch, version_guid=version, block_id=block
)
course_handouts_url = handouts_locator.url_reverse('xblock')
content = u"Sample handout"
payload = {"data": content}
resp = self.client.ajax_post(course_handouts_url, payload)
# check that response status is 200 not 500
self.assertEqual(resp.status_code, 200)
payload = json.loads(resp.content)
self.assertHTMLEqual(payload['data'], content)

View File

@@ -132,6 +132,31 @@ class GetItem(ItemTest):
# Verify that the Studio element wrapper has been added
self.assertIn('level-element', html)
def test_get_container_nested_container_fragment(self):
"""
Test the case of the container page containing a link to another container page.
"""
# Add a wrapper with child beneath a child vertical
root_locator = self._create_vertical()
resp = self.create_xblock(parent_locator=root_locator, category="wrapper")
self.assertEqual(resp.status_code, 200)
wrapper_locator = self.response_locator(resp)
resp = self.create_xblock(parent_locator=wrapper_locator, category='problem', boilerplate='multiplechoice.yaml')
self.assertEqual(resp.status_code, 200)
# Get the preview HTML and verify the View -> link is present.
html, __ = self._get_container_preview(root_locator)
self.assertIn('wrapper-xblock', html)
self.assertRegexpMatches(
html,
# The instance of the wrapper class will have an auto-generated ID (wrapperxxx). Allow anything
# for the 3 characters after wrapper.
(r'"/container/MITx.999.Robot_Super_Course/branch/published/block/wrapper.{3}" class="action-button">\s*'
'<span class="action-button-text">View</span>')
)
class DeleteItem(ItemTest):
"""Tests for '/xblock' DELETE url."""
@@ -636,11 +661,11 @@ class TestComponentHandler(TestCase):
def setUp(self):
self.request_factory = RequestFactory()
patcher = patch('contentstore.views.component.modulestore')
self.modulestore = patcher.start()
patcher = patch('contentstore.views.component.get_modulestore')
self.get_modulestore = patcher.start()
self.addCleanup(patcher.stop)
self.descriptor = self.modulestore.return_value.get_item.return_value
self.descriptor = self.get_modulestore.return_value.get_item.return_value
self.usage_id = 'dummy_usage_id'

View File

@@ -194,7 +194,7 @@ class CourseDetails(object):
result = None
if video_key:
result = '<iframe width="560" height="315" src="//www.youtube.com/embed/' + \
video_key + '?autoplay=1&rel=0" frameborder="0" allowfullscreen=""></iframe>'
video_key + '?rel=0" frameborder="0" allowfullscreen=""></iframe>'
return result

View File

@@ -16,7 +16,7 @@ os.environ['SERVICE_VARIANT'] = 'bok_choy'
os.environ['CONFIG_ROOT'] = path(__file__).abspath().dirname() #pylint: disable=E1120
from .aws import * # pylint: disable=W0401, W0614
from xmodule.x_module import prefer_xmodules
from xmodule.modulestore import prefer_xmodules
######################### Testing overrides ####################################

View File

@@ -296,7 +296,7 @@ PIPELINE_CSS = {
'css/vendor/normalize.css',
'css/vendor/font-awesome.css',
'css/vendor/html5-input-polyfills/number-polyfill.css',
'js/vendor/CodeMirror/codemirror.css',
'js/vendor/CodeMirror/codemirror-3.21.0.css',
'css/vendor/ui-lightness/jquery-ui-1.8.22.custom.css',
'css/vendor/jquery.qtip.min.css',
'js/vendor/markitup/skins/simple/style.css',

View File

@@ -16,6 +16,7 @@ from .common import *
import os
from path import path
from warnings import filterwarnings
from xmodule.modulestore import prefer_xmodules
# Nose Test Runner
TEST_RUNNER = 'django_nose.NoseTestSuiteRunner'
@@ -158,7 +159,6 @@ filterwarnings('ignore', message='No request passed to the backend, unable to ra
################################# XBLOCK ######################################
from xmodule.x_module import prefer_xmodules
XBLOCK_SELECT_FUNCTION = prefer_xmodules

View File

@@ -20,6 +20,10 @@ define [
super()
@savingNotification = new NotificationView.Mini
title: gettext('Saving&hellip;')
@alert = new NotificationView.Error
title: "OpenAssessment Save Error",
closeIcon: false,
shown: false
handlerUrl: (element, handlerName, suffix, query, thirdparty) ->
uri = URI("/xblock").segment($(element).data('usage-id'))
@@ -41,11 +45,15 @@ define [
# Starting to save, so show the "Saving..." notification
if data.state == 'start'
@_hideEditor()
@savingNotification.show()
# Finished saving, so hide the "Saving..." notification
else if data.state == 'end'
# Hide the editor *after* we finish saving in case there are validation
# errors that the user needs to correct.
@_hideEditor()
$('.component.editing').removeClass('editing')
@savingNotification.hide()
@@ -54,7 +62,8 @@ define [
else if name == 'error'
if 'msg' of data
@_showAlert(data.msg)
@alert.options.message = data.msg
@alert.show()
_hideEditor: () ->
# This will close all open component editors, which works
@@ -64,9 +73,6 @@ define [
el.find('.component-editor').slideUp(150)
ModalUtils.hideModalCover()
_showAlert: (msg) ->
new NotificationView.Error({
title: "OpenAssessment Save Error",
message: msg,
closeIcon: false
}).show()
# Hide any alerts that are being shown
if @alert.options.shown
@alert.hide()

View File

@@ -7,9 +7,10 @@ define(["codemirror", 'js/utils/handle_iframe_binding', "utility"],
mode: "text/html",
lineNumbers: true,
lineWrapping: true,
onChange: function () {
autoCloseTags: true
});
$codeMirror.on('change', function () {
$('.save-button').removeClass('is-disabled');
}
});
$codeMirror.setValue(content);
$codeMirror.clearHistory();

View File

@@ -47,9 +47,11 @@ var AdvancedView = ValidatingView.extend({
var self = this;
var oldValue = $(textarea).val();
CodeMirror.fromTextArea(textarea, {
mode: "application/json", lineNumbers: false, lineWrapping: false,
onChange: function(instance, changeobj) {
var cm = CodeMirror.fromTextArea(textarea, {
mode: "application/json",
lineNumbers: false,
lineWrapping: false});
cm.on('change', function(instance, changeobj) {
instance.save();
// this event's being called even when there's no change :-(
if (instance.getValue() !== oldValue) {
@@ -58,11 +60,11 @@ var AdvancedView = ValidatingView.extend({
_.bind(self.saveView, self),
_.bind(self.revertView, self));
}
},
onFocus : function(mirror) {
});
cm.on('focus', function(mirror) {
$(textarea).parent().children('label').addClass("is-focused");
},
onBlur: function (mirror) {
});
cm.on('blur', function (mirror) {
$(textarea).parent().children('label').removeClass("is-focused");
var key = $(mirror.getWrapperElement()).closest('.field-group').children('.key').attr('id');
var stringValue = $.trim(mirror.getValue());
@@ -91,8 +93,7 @@ var AdvancedView = ValidatingView.extend({
if (JSONValue !== undefined) {
self.model.set(key, JSONValue);
}
}
});
});
},
saveView : function() {
// TODO one last verification scan:

View File

@@ -206,15 +206,14 @@ var DetailsView = ValidatingView.extend({
var cachethis = this;
var field = this.selectorToField[thisTarget.id];
this.codeMirrors[thisTarget.id] = CodeMirror.fromTextArea(thisTarget, {
mode: "text/html", lineNumbers: true, lineWrapping: true,
onChange: function (mirror) {
mode: "text/html", lineNumbers: true, lineWrapping: true});
this.codeMirrors[thisTarget.id].on('change', function (mirror) {
mirror.save();
cachethis.clearValidationErrors();
var newVal = mirror.getValue();
if (cachethis.model.get(field) != newVal) {
cachethis.setAndValidate(field, newVal);
}
}
});
}
},

View File

@@ -334,11 +334,12 @@ p, ul, ol, dl {
.navigation-link {
@extend %cont-truncated;
display: inline-block;
max-width: 150px;
max-width: 250px;
&.navigation-current {
@extend %ui-disabled;
color: $gray;
max-width: 250px;
&:before {
color: $gray;

View File

@@ -55,7 +55,7 @@
}
// UI: xblock is collapsible
.wrapper-xblock.is-collapsible {
.wrapper-xblock.is-collapsible, .wrapper-xblock.xblock-type-container {
[class^="icon-"] {
font-style: normal;

View File

@@ -831,19 +831,15 @@
font-family: 'Open Sans', sans-serif;
color: $baseFontColor;
outline: 0;
height: auto;
min-height: ($baseline*2.25);
max-height: ($baseline*10);
&.CodeMirror-focused {
@include linear-gradient($paleYellow, tint($paleYellow, 90%));
outline: 0;
}
.CodeMirror-scroll {
overflow: hidden;
height: auto;
min-height: ($baseline*1.5);
max-height: ($baseline*10);
}
// editor color changes just for JSON
.CodeMirror-lines {

View File

@@ -16,7 +16,7 @@ from django.utils.translation import ugettext as _
<%
xblock_info = {
'id': str(xblock_locator),
'display-name': xblock.display_name,
'display-name': xblock.display_name_with_default,
'category': xblock.category,
};
%>
@@ -47,14 +47,16 @@ xblock_info = {
<header class="mast has-actions has-navigation">
<h1 class="page-header">
<small class="navigation navigation-parents">
<%
parent_url = xblock_studio_url(parent_xblock, context_course)
%>
% if parent_url:
<a href="${parent_url}"
class="navigation-link navigation-parent">${parent_xblock.display_name | h}</a>
% endif
<a href="#" class="navigation-link navigation-current">${xblock.display_name | h}</a>
% for ancestor in ancestor_xblocks:
<%
ancestor_url = xblock_studio_url(ancestor, context_course)
%>
% if ancestor_url:
<a href="${ancestor_url}"
class="navigation-link navigation-parent">${ancestor.display_name_with_default | h}</a>
% endif
% endfor
<a href="#" class="navigation-link navigation-current">${xblock.display_name_with_default | h}</a>
</small>
</h1>

View File

@@ -7,12 +7,12 @@ from contentstore.views.helpers import xblock_studio_url
<section class="wrapper-xblock xblock-type-container level-element" data-locator="${locator}">
<header class="xblock-header">
<div class="header-details">
${xblock.display_name}
${xblock.display_name_with_default}
</div>
<div class="header-actions">
<ul class="actions-list">
<li class="action-item action-view">
<a href="${xblock_studio_url(xblock, course)}" class="action-button">
<a href="${xblock_studio_url(xblock)}" class="action-button">
## Translators: this is a verb describing the action of viewing more details
<span class="action-button-text">${_('View')}</span>
<i class="icon-arrow-right"></i>
@@ -21,5 +21,8 @@ from contentstore.views.helpers import xblock_studio_url
</ul>
</div>
</header>
<span data-tooltip="${_("Drag to reorder")}" class="drag-handle"></span>
## We currently support reordering only on the unit page.
% if reordering_enabled:
<span data-tooltip="${_("Drag to reorder")}" class="drag-handle"></span>
% endif
</section>

View File

@@ -8,7 +8,7 @@
<i class="icon-caret-down ui-toggle-expansion"></i>
<span class="sr">${_('Expand or Collapse')}</span>
</a>
<span>${xblock.display_name | h}</span>
<span>${xblock.display_name_with_default | h}</span>
</div>
<div class="header-actions">
<ul class="actions-list">

View File

@@ -8,7 +8,7 @@
% endif
<header class="xblock-header">
<div class="header-details">
${xblock.display_name | h}
${xblock.display_name_with_default | h}
</div>
<div class="header-actions">
<ul class="actions-list">

View File

@@ -97,7 +97,7 @@ require(["jquery", "jquery.leanModal", "codemirror/stex"], function($) {
});
// resize the codemirror box
var h = el.height();
el.find('.CodeMirror-scroll').height(h - 100);
el.find('.CodeMirror').height(h - 160);
}
// compile & save button