Commit ae886f1b by cahrens

Add support for raw HTML editor.

STUD-1562
parent 56bf7d86
......@@ -5,6 +5,9 @@ These are notable changes in edx-platform. This is a rolling list of changes,
in roughly chronological order, most recent first. Add your entries at or near
the top. Include a label indicating the component affected.
Studio: Add "raw HTML" editor so that authors can write HTML that will not be
changed in any way. STUD-1562
Blades: Show the HD button only if there is an HD version available. BLD-937.
Studio: Add edit button to leaf xblocks on the container page. STUD-1306.
......
......@@ -5,7 +5,7 @@ Feature: CMS.HTML Editor
Scenario: User can view metadata
Given I have created a Blank HTML Page
And I edit and select Settings
Then I see only the HTML display name setting
Then I see the HTML component settings
# Safari doesn't save the name properly
@skip_safari
......
......@@ -18,9 +18,14 @@ def i_created_blank_html_page(step):
)
@step('I see only the HTML display name setting$')
@step('I see the HTML component settings$')
def i_see_only_the_html_display_name(step):
world.verify_all_setting_entries([['Display Name', "Text", False]])
world.verify_all_setting_entries(
[
['Display Name', "Text", False],
['Editor', "Visual", False]
]
)
@step('I have created an E-text Written in LaTeX$')
......
<%! from django.utils.translation import ugettext as _ %>
<div class="wrapper-comp-editor" id="editor-tab" data-base-asset-url="${base_asset_url}">
<div class="wrapper-comp-editor" id="editor-tab" data-base-asset-url="${base_asset_url}" data-editor="${editor}">
<section class="html-editor editor">
<div class="row">
<textarea class="tiny-mce">${data | h}</textarea>
% if editor == 'visual':
<textarea class="tiny-mce">${data | h}</textarea>
% endif
<textarea name="" class="edit-box">${data | h}</textarea>
</div>
</section>
</div>
......
......@@ -36,6 +36,16 @@ class HtmlFields(object):
default=False,
scope=Scope.settings
)
editor = String(
help="Supports switching between the Visual Editor and the Raw HTML Editor. The change does not take effect until Save is pressed.",
display_name="Editor",
default="visual",
values=[
{"display_name": "Visual", "value": "visual"},
{"display_name": "Raw", "value": "raw"}
],
scope=Scope.settings
)
class HtmlModule(HtmlFields, XModule):
......@@ -113,6 +123,7 @@ class HtmlDescriptor(HtmlFields, XmlDescriptor, EditingDescriptor):
_context.update({
'base_asset_url': StaticContent.get_base_url_path_for_course_assets(self.location) + '/',
'enable_latex_compiler': self.use_latex_compiler,
'editor': self.editor
})
return _context
......
<div class="test-component">
<div class="wrapper-comp-editor" id="editor-tab" data-base-asset-url="/c4x/foo/bar/asset/" data-editor="visual">
<section class="html-editor editor">
<div class="row">
<textarea class="tiny-mce"><p>original visual text</p></textarea>
<textarea name="" class="edit-box">raw text</textarea>
</div>
</section>
</div>
</div>
<section class="html-edit">
<textarea class="tiny-mce">dummy text</textarea>
</section>
<div class="test-component">
<div class="wrapper-comp-editor" id="editor-tab" data-editor="raw">
<section class="html-editor editor">
<div class="row">
<textarea name="" class="edit-box">raw text</textarea>
</div>
</section>
</div>
</div>
......@@ -3,39 +3,32 @@ describe 'HTMLEditingDescriptor', ->
window.baseUrl = "/static/deadbeef"
afterEach ->
delete window.baseUrl
describe 'HTML Editor', ->
describe 'Visual HTML Editor', ->
beforeEach ->
loadFixtures 'html-edit.html'
@descriptor = new HTMLEditingDescriptor($('.html-edit'))
it 'Returns data from Visual Editor if Visual Editor is dirty', ->
loadFixtures 'html-edit-visual.html'
@descriptor = new HTMLEditingDescriptor($('.test-component'))
it 'Returns data from Visual Editor if text has changed', ->
visualEditorStub =
isDirty: () -> true
getContent: () -> 'from visual editor'
spyOn(@descriptor, 'getVisualEditor').andCallFake () ->
visualEditorStub
data = @descriptor.save().data
expect(data).toEqual('from visual editor')
it 'Returns data from Visual Editor even if Visual Editor is not dirty', ->
it 'Returns data from Raw Editor if text has not changed', ->
visualEditorStub =
isDirty: () -> false
getContent: () -> 'from visual editor'
getContent: () -> '<p>original visual text</p>'
spyOn(@descriptor, 'getVisualEditor').andCallFake () ->
visualEditorStub
data = @descriptor.save().data
expect(data).toEqual('from visual editor')
expect(data).toEqual('raw text')
it 'Performs link rewriting for static assets when saving', ->
visualEditorStub =
isDirty: () -> true
getContent: () -> 'from visual editor with /c4x/foo/bar/asset/image.jpg'
spyOn(@descriptor, 'getVisualEditor').andCallFake () ->
visualEditorStub
@descriptor.base_asset_url = '/c4x/foo/bar/asset/'
data = @descriptor.save().data
expect(data).toEqual('from visual editor with /static/image.jpg')
it 'When showing visual editor links are rewritten to c4x format', ->
@descriptor = new HTMLEditingDescriptor($('.html-edit'))
@descriptor.base_asset_url = '/c4x/foo/bar/asset/'
visualEditorStub =
content: 'text /static/image.jpg'
startContent: 'text /static/image.jpg'
......@@ -45,3 +38,10 @@ describe 'HTMLEditingDescriptor', ->
@descriptor.initInstanceCallback(visualEditorStub)
expect(visualEditorStub.getContent()).toEqual('text /c4x/foo/bar/asset/image.jpg')
describe 'Raw HTML Editor', ->
beforeEach ->
loadFixtures 'html-editor-raw.html'
@descriptor = new HTMLEditingDescriptor($('.test-component'))
it 'Returns data from raw editor', ->
data = @descriptor.save().data
expect(data).toEqual('raw text')
class @HTMLEditingDescriptor
constructor: (element) ->
@element = element;
@element = element
@base_asset_url = @element.find("#editor-tab").data('base-asset-url')
@editor_choice = @element.find("#editor-tab").data('editor')
if @base_asset_url == undefined
@base_asset_url = null
# Create an array of all content CSS links to use in and pass to Tiny MCE.
# We create this dynamically in order to support hashed files from our Django pipeline.
# CSS files that are to be used by Tiny MCE should contain the string "tinymce" so
# they can be found by the search below.
# We filter for only those files that are "content" files (as opposed to "skin" files).
tiny_mce_css_links = []
$("link[rel=stylesheet][href*='tinymce']").filter("[href*='content']").each ->
tiny_mce_css_links.push $(this).attr("href")
return
# This is a workaround for the fact that tinyMCE's baseURL property is not getting correctly set on AWS
# instances (like sandbox). It is not necessary to explicitly set baseURL when running locally.
tinyMCE.baseURL = "#{baseUrl}/js/vendor/tinymce/js/tinymce"
# This is necessary for the LMS bulk e-mail acceptance test. In that particular scenario,
# tinyMCE incorrectly decides that the suffix should be "", which means it fails to load files.
tinyMCE.suffix = ".min"
@tiny_mce_textarea = $(".tiny-mce", @element).tinymce({
script_url : "#{baseUrl}/js/vendor/tinymce/js/tinymce/tinymce.full.min.js",
theme : "modern",
skin: 'studio-tmce4',
schema: "html5",
# Necessary to preserve relative URLs to our images.
convert_urls : false,
content_css : tiny_mce_css_links.join(", "),
formats : {
# tinyMCE does block level for code by default
code: {inline: 'code'}
},
# Disable visual aid on borderless table.
visual: false,
plugins: "textcolor, link, image, codemirror",
codemirror: {
path: "#{baseUrl}/js/vendor"
},
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",
block_formats: "Paragraph=p;Preformatted=pre;Heading 1=h1;Heading 2=h2;Heading 3=h3",
width: '100%',
height: '400px',
menubar: false,
statusbar: false,
# Necessary to avoid stripping of style tags.
valid_children : "+body[style]",
# Allow any elements to be used, e.g. link, script, math
valid_elements: "*[*]",
extended_valid_elements: "*[*]",
invalid_elements: "",
setup: @setupTinyMCE,
# Cannot get access to tinyMCE Editor instance (for focusing) until after it is rendered.
# The tinyMCE callback passes in the editor as a parameter.
init_instance_callback: @initInstanceCallback
# We always create the "raw editor" so we can get the text out of it if necessary on save.
@advanced_editor = CodeMirror.fromTextArea($(".edit-box", @element)[0], {
mode: "text/html"
lineNumbers: true
lineWrapping: true
})
if @editor_choice == 'visual'
@$advancedEditorWrapper = $(@advanced_editor.getWrapperElement())
@$advancedEditorWrapper.addClass('is-inactive')
# Create an array of all content CSS links to use in and pass to Tiny MCE.
# We create this dynamically in order to support hashed files from our Django pipeline.
# CSS files that are to be used by Tiny MCE should contain the string "tinymce" so
# they can be found by the search below.
# We filter for only those files that are "content" files (as opposed to "skin" files).
tiny_mce_css_links = []
$("link[rel=stylesheet][href*='tinymce']").filter("[href*='content']").each ->
tiny_mce_css_links.push $(this).attr("href")
return
# This is a workaround for the fact that tinyMCE's baseURL property is not getting correctly set on AWS
# instances (like sandbox). It is not necessary to explicitly set baseURL when running locally.
tinyMCE.baseURL = "#{baseUrl}/js/vendor/tinymce/js/tinymce"
# This is necessary for the LMS bulk e-mail acceptance test. In that particular scenario,
# tinyMCE incorrectly decides that the suffix should be "", which means it fails to load files.
tinyMCE.suffix = ".min"
@tiny_mce_textarea = $(".tiny-mce", @element).tinymce({
script_url : "#{baseUrl}/js/vendor/tinymce/js/tinymce/tinymce.full.min.js",
theme : "modern",
skin: 'studio-tmce4',
schema: "html5",
# Necessary to preserve relative URLs to our images.
convert_urls : false,
content_css : tiny_mce_css_links.join(", "),
formats : {
# tinyMCE does block level for code by default
code: {inline: 'code'}
},
# Disable visual aid on borderless table.
visual: false,
plugins: "textcolor, link, image, codemirror",
codemirror: {
path: "#{baseUrl}/js/vendor"
},
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",
block_formats: "Paragraph=p;Preformatted=pre;Heading 1=h1;Heading 2=h2;Heading 3=h3",
width: '100%',
height: '400px',
menubar: false,
statusbar: false,
# Necessary to avoid stripping of style tags.
valid_children : "+body[style]",
# Allow any elements to be used, e.g. link, script, math
valid_elements: "*[*]",
extended_valid_elements: "*[*]",
invalid_elements: "",
setup: @setupTinyMCE,
# Cannot get access to tinyMCE Editor instance (for focusing) until after it is rendered.
# The tinyMCE callback passes in the editor as a parameter.
init_instance_callback: @initInstanceCallback
})
setupTinyMCE: (ed) =>
ed.addButton('wrapAsCode', {
title : 'Code block',
......@@ -116,6 +127,9 @@ class @HTMLEditingDescriptor
initInstanceCallback: (visualEditor) =>
visualEditor.setContent(rewriteStaticLinks(visualEditor.getContent({no_events: 1}), '/static/', @base_asset_url))
# Unfortunately, just setting visualEditor.isNortDirty = true is not enough to convince TinyMCE we
# haven't dirtied the Editor. Store the raw content so we can compare it later.
@starting_content = visualEditor.getContent({format:"raw", no_events: 1})
visualEditor.focus()
getVisualEditor: () ->
......@@ -127,6 +141,14 @@ class @HTMLEditingDescriptor
return @visualEditor
save: ->
visualEditor = @getVisualEditor()
text = rewriteStaticLinks(visualEditor.getContent({no_events: 1}), @base_asset_url, '/static/')
text = undefined
if @editor_choice == 'visual'
visualEditor = @getVisualEditor()
content = visualEditor.getContent({format:"raw", no_events: 1})
if @starting_content != content
text = rewriteStaticLinks(content, @base_asset_url, '/static/')
if text == undefined
text = @advanced_editor.getValue()
data: text
---
metadata:
display_name: Raw HTML
editor: raw
data: |
<p>For use with complex HTML, to allow complete control over the final product.</p>
<%! from django.utils.translation import ugettext as _ %>
<section class="html-editor editor">
<div class="row">
<textarea class="tiny-mce">${data | h}</textarea>
</div>
</section>
<div class="wrapper-comp-editor" id="editor-tab" data-editor="${editor}">
<section class="html-editor editor">
<div class="row">
% if editor == 'visual':
<textarea class="tiny-mce">${data | h}</textarea>
% endif
<textarea name="" class="edit-box">${data | h}</textarea>
</div>
</section>
</div>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment