Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cms/djangoapps/contentstore/config/waffle.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
# Switches
ENABLE_ACCESSIBILITY_POLICY_PAGE = u'enable_policy_page'
ENABLE_ASSETS_SEARCH = u'enable_assets_search'
ENABLE_IN_CONTEXT_IMAGE_SELECTION = u'enable_in_context_image_selection'


def waffle():
Expand Down
1 change: 1 addition & 0 deletions cms/djangoapps/contentstore/views/component.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ def container_handler(request, usage_key_string):
index += 1

return render_to_response('container.html', {
'language_code': request.LANGUAGE_CODE,
'context_course': course, # Needed only for display of menus at top of page.
'action': action,
'xblock': xblock,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ def test_container_page_with_valid_and_invalid_usage_key_string(self):
"""
request = RequestFactory().get('foo')
request.user = self.user
request.LANGUAGE_CODE = 'en'

# Check for invalid 'usage_key_strings'
self.assertRaises(
Expand Down
20 changes: 20 additions & 0 deletions cms/templates/container.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<%!
from django.utils.translation import ugettext as _

from cms.djangoapps.contentstore.config.waffle import waffle, ENABLE_IN_CONTEXT_IMAGE_SELECTION
from contentstore.views.helpers import xblock_studio_url, xblock_type_display_name
from openedx.core.djangolib.js_utils import (
dump_js_escaped_json, js_escaped_string
Expand All @@ -33,6 +34,10 @@
<%static:include path="common/templates/image-modal.underscore" />
</script>
<link rel="stylesheet" type="text/css" href="${static.url('js/vendor/timepicker/jquery.timepicker.css')}" />
% if waffle().is_enabled(ENABLE_IN_CONTEXT_IMAGE_SELECTION):
<link rel="stylesheet" type="text/css" href="${static.url('common/css/vendor/common.min.css')}" />
<link rel="stylesheet" type="text/css" href="${static.url('common/css/vendor/editImageModal.min.css')}" />
% endif
</%block>

<%block name="requirejs">
Expand All @@ -52,6 +57,12 @@

<%block name="content">

% if waffle().is_enabled(ENABLE_IN_CONTEXT_IMAGE_SELECTION):
<script type="text/javascript">
window.STUDIO_FRONTEND_IN_CONTEXT_IMAGE_SELECTION = true;
</script>
% endif


<div class="wrapper-mast wrapper">
<header class="mast has-actions has-navigation has-subtitle">
Expand Down Expand Up @@ -159,6 +170,15 @@ <h5 class="title">${_("Location in Course Outline")}</h5>
% endif
</aside>
</section>
% if waffle().is_enabled(ENABLE_IN_CONTEXT_IMAGE_SELECTION):
<div id="edit-image-modal">
<%static:studiofrontend entry="editImageModal">
{
"lang": "${language_code | n, js_escaped_string}"
}
</%static:studiofrontend>
</div>
% endif
</div>
</div>
</%block>
48 changes: 46 additions & 2 deletions common/lib/xmodule/xmodule/js/src/html/edit.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class @HTMLEditingDescriptor
@element = element
@base_asset_url = @element.find("#editor-tab").data('base-asset-url')
@editor_choice = @element.find("#editor-tab").data('editor')
@new_image_modal = window.STUDIO_FRONTEND_IN_CONTEXT_IMAGE_SELECTION
if @base_asset_url == undefined
@base_asset_url = null

Expand Down Expand Up @@ -81,7 +82,9 @@ class @HTMLEditingDescriptor
},
image_advtab: true,
# We may want to add "styleselect" when we collect all styles used throughout the LMS
toolbar: "formatselect | fontselect | bold italic underline forecolor wrapAsCode | bullist numlist outdent indent blockquote | link unlink image | code",
toolbar: "formatselect | fontselect | bold italic underline forecolor wrapAsCode | " +
"bullist numlist outdent indent blockquote | link unlink " +
"#{if @new_image_modal then 'insertImage' else 'image'} | code",
block_formats: interpolate("%(paragraph)s=p;%(preformatted)s=pre;%(heading3)s=h3;%(heading4)s=h4;%(heading5)s=h5;%(heading6)s=h6", {
paragraph: gettext("Paragraph"),
preformatted: gettext("Preformatted"),
Expand Down Expand Up @@ -931,13 +934,26 @@ class @HTMLEditingDescriptor

setupTinyMCE: (ed) =>
ed.addButton('wrapAsCode', {
title : 'Code block',
###
Translators: this is a toolbar button tooltip from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
title : gettext('Code block'),
image : "#{baseUrl}/images/ico-tinymce-code.png",
onclick : () ->
ed.formatter.toggle('code')
})

ed.addButton('insertImage', {
###
Translators: this is a toolbar button tooltip from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
title : gettext('Insert/Edit Image'),
icon: 'image',
onclick : @openImageModal
})

@visualEditor = ed
@imageModal = $('#edit-image-modal .modal')

# These events were added to the plugin code as the TinyMCE PluginManager
# does not fire any events when plugins are opened or closed.
Expand All @@ -948,6 +964,8 @@ class @HTMLEditingDescriptor
ed.on('ShowCodeEditor', @showCodeEditor)
ed.on('SaveCodeEditor', @saveCodeEditor)

@imageModal.on('submitForm', @editImageSubmit)

editImage: (data) =>
# Called when the image plugin will be shown. Input arg is the JSON version of the image data.
if data['src']
Expand All @@ -958,6 +976,32 @@ class @HTMLEditingDescriptor
if data['src']
data['src'] = rewriteStaticLinks(data['src'], '/static/', @base_asset_url)

openImageModal: () =>
img = $(@visualEditor.selection.getNode())
imgAttrs =
baseAssetUrl: @base_asset_url
if img && img.is('img')
imgAttrs['src'] = rewriteStaticLinks(img.attr('src'), @base_asset_url, '/static/')
imgAttrs['alt'] = img.attr('alt')
imgAttrs['width'] = parseInt(img.attr('width'), 10) || img[0].naturalWidth
imgAttrs['height'] = parseInt(img.attr('height'), 10) || img[0].naturalHeight
imgAttrs['style'] = img.attr('style')
@imageModal[0].dispatchEvent(new CustomEvent('openModal', {bubbles: true, detail: imgAttrs}))

closeImageModal: () =>
@imageModal[0].dispatchEvent(new CustomEvent('closeModal', {bubbles: true}))

saveImageFromModal: (data) =>
# Insert img node from studio-frontend modal form data passed as a javascript object
if data['src']
data['src'] = rewriteStaticLinks(data['src'], '/static/', @base_asset_url)

@visualEditor.insertContent(@visualEditor.dom.createHTML('img', data))

editImageSubmit: (event) =>
if event.detail
@saveImageFromModal(event.detail)

editLink: (data) =>
# Called when the link plugin will be shown. Input arg is the JSON version of the link data.
if data['href']
Expand Down
Loading