Commit ad3d8c78 by cahrens Committed by David Baumgold

Add support for raw HTML editor.

STUD-1562
parent 01fdc223
...@@ -10,6 +10,9 @@ Blades: Set initial video quality to large instead of default to avoid automatic ...@@ -10,6 +10,9 @@ Blades: Set initial video quality to large instead of default to avoid automatic
Blades: Add an upload button for authors to provide students with an option to Blades: Add an upload button for authors to provide students with an option to
download a handout associated with a video (of arbitrary file format). BLD-1000. download a handout associated with a video (of arbitrary file format). BLD-1000.
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. 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. Studio: Add edit button to leaf xblocks on the container page. STUD-1306.
......
...@@ -5,7 +5,7 @@ Feature: CMS.HTML Editor ...@@ -5,7 +5,7 @@ Feature: CMS.HTML Editor
Scenario: User can view metadata Scenario: User can view metadata
Given I have created a Blank HTML Page Given I have created a Blank HTML Page
And I edit and select Settings 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 # Safari doesn't save the name properly
@skip_safari @skip_safari
......
...@@ -18,9 +18,14 @@ def i_created_blank_html_page(step): ...@@ -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): 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$') @step('I have created an E-text Written in LaTeX$')
......
<%! from django.utils.translation import ugettext as _ %> <%! 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"> <section class="html-editor editor">
<div class="row"> <div class="row">
% if editor == 'visual':
<textarea class="tiny-mce">${data | h}</textarea> <textarea class="tiny-mce">${data | h}</textarea>
% endif
<textarea name="" class="edit-box">${data | h}</textarea>
</div> </div>
</section> </section>
</div> </div>
......
...@@ -36,6 +36,16 @@ class HtmlFields(object): ...@@ -36,6 +36,16 @@ class HtmlFields(object):
default=False, default=False,
scope=Scope.settings 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): class HtmlModule(HtmlFields, XModule):
...@@ -113,6 +123,7 @@ class HtmlDescriptor(HtmlFields, XmlDescriptor, EditingDescriptor): ...@@ -113,6 +123,7 @@ class HtmlDescriptor(HtmlFields, XmlDescriptor, EditingDescriptor):
_context.update({ _context.update({
'base_asset_url': StaticContent.get_base_url_path_for_course_assets(self.location) + '/', 'base_asset_url': StaticContent.get_base_url_path_for_course_assets(self.location) + '/',
'enable_latex_compiler': self.use_latex_compiler, 'enable_latex_compiler': self.use_latex_compiler,
'editor': self.editor
}) })
return _context 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', -> ...@@ -3,39 +3,32 @@ describe 'HTMLEditingDescriptor', ->
window.baseUrl = "/static/deadbeef" window.baseUrl = "/static/deadbeef"
afterEach -> afterEach ->
delete window.baseUrl delete window.baseUrl
describe 'HTML Editor', -> describe 'Visual HTML Editor', ->
beforeEach -> beforeEach ->
loadFixtures 'html-edit.html' loadFixtures 'html-edit-visual.html'
@descriptor = new HTMLEditingDescriptor($('.html-edit')) @descriptor = new HTMLEditingDescriptor($('.test-component'))
it 'Returns data from Visual Editor if Visual Editor is dirty', -> it 'Returns data from Visual Editor if text has changed', ->
visualEditorStub = visualEditorStub =
isDirty: () -> true
getContent: () -> 'from visual editor' getContent: () -> 'from visual editor'
spyOn(@descriptor, 'getVisualEditor').andCallFake () -> spyOn(@descriptor, 'getVisualEditor').andCallFake () ->
visualEditorStub visualEditorStub
data = @descriptor.save().data data = @descriptor.save().data
expect(data).toEqual('from visual editor') 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 = visualEditorStub =
isDirty: () -> false getContent: () -> '<p>original visual text</p>'
getContent: () -> 'from visual editor'
spyOn(@descriptor, 'getVisualEditor').andCallFake () -> spyOn(@descriptor, 'getVisualEditor').andCallFake () ->
visualEditorStub visualEditorStub
data = @descriptor.save().data data = @descriptor.save().data
expect(data).toEqual('from visual editor') expect(data).toEqual('raw text')
it 'Performs link rewriting for static assets when saving', -> it 'Performs link rewriting for static assets when saving', ->
visualEditorStub = visualEditorStub =
isDirty: () -> true
getContent: () -> 'from visual editor with /c4x/foo/bar/asset/image.jpg' getContent: () -> 'from visual editor with /c4x/foo/bar/asset/image.jpg'
spyOn(@descriptor, 'getVisualEditor').andCallFake () -> spyOn(@descriptor, 'getVisualEditor').andCallFake () ->
visualEditorStub visualEditorStub
@descriptor.base_asset_url = '/c4x/foo/bar/asset/'
data = @descriptor.save().data data = @descriptor.save().data
expect(data).toEqual('from visual editor with /static/image.jpg') expect(data).toEqual('from visual editor with /static/image.jpg')
it 'When showing visual editor links are rewritten to c4x format', -> 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 = visualEditorStub =
content: 'text /static/image.jpg' content: 'text /static/image.jpg'
startContent: 'text /static/image.jpg' startContent: 'text /static/image.jpg'
...@@ -45,3 +38,10 @@ describe 'HTMLEditingDescriptor', -> ...@@ -45,3 +38,10 @@ describe 'HTMLEditingDescriptor', ->
@descriptor.initInstanceCallback(visualEditorStub) @descriptor.initInstanceCallback(visualEditorStub)
expect(visualEditorStub.getContent()).toEqual('text /c4x/foo/bar/asset/image.jpg') 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 class @HTMLEditingDescriptor
constructor: (element) -> constructor: (element) ->
@element = element; @element = element
@base_asset_url = @element.find("#editor-tab").data('base-asset-url') @base_asset_url = @element.find("#editor-tab").data('base-asset-url')
@editor_choice = @element.find("#editor-tab").data('editor')
if @base_asset_url == undefined if @base_asset_url == undefined
@base_asset_url = null @base_asset_url = null
# 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. # 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. # 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 # CSS files that are to be used by Tiny MCE should contain the string "tinymce" so
...@@ -16,11 +27,11 @@ class @HTMLEditingDescriptor ...@@ -16,11 +27,11 @@ class @HTMLEditingDescriptor
tiny_mce_css_links.push $(this).attr("href") tiny_mce_css_links.push $(this).attr("href")
return return
# This is a workaround for the fact that tinyMCE's baseURL property is not getting correctly set on AWS # 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. # instances (like sandbox). It is not necessary to explicitly set baseURL when running locally.
tinyMCE.baseURL = "#{baseUrl}/js/vendor/tinymce/js/tinymce" tinyMCE.baseURL = "#{baseUrl}/js/vendor/tinymce/js/tinymce"
# This is necessary for the LMS bulk e-mail acceptance test. In that particular scenario, # 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 incorrectly decides that the suffix should be "", which means it fails to load files.
tinyMCE.suffix = ".min" tinyMCE.suffix = ".min"
@tiny_mce_textarea = $(".tiny-mce", @element).tinymce({ @tiny_mce_textarea = $(".tiny-mce", @element).tinymce({
script_url : "#{baseUrl}/js/vendor/tinymce/js/tinymce/tinymce.full.min.js", script_url : "#{baseUrl}/js/vendor/tinymce/js/tinymce/tinymce.full.min.js",
...@@ -116,6 +127,9 @@ class @HTMLEditingDescriptor ...@@ -116,6 +127,9 @@ class @HTMLEditingDescriptor
initInstanceCallback: (visualEditor) => initInstanceCallback: (visualEditor) =>
visualEditor.setContent(rewriteStaticLinks(visualEditor.getContent({no_events: 1}), '/static/', @base_asset_url)) 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() visualEditor.focus()
getVisualEditor: () -> getVisualEditor: () ->
...@@ -127,6 +141,14 @@ class @HTMLEditingDescriptor ...@@ -127,6 +141,14 @@ class @HTMLEditingDescriptor
return @visualEditor return @visualEditor
save: -> save: ->
text = undefined
if @editor_choice == 'visual'
visualEditor = @getVisualEditor() visualEditor = @getVisualEditor()
text = rewriteStaticLinks(visualEditor.getContent({no_events: 1}), @base_asset_url, '/static/') 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 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 _ %> <%! from django.utils.translation import ugettext as _ %>
<section class="html-editor editor"> <div class="wrapper-comp-editor" id="editor-tab" data-editor="${editor}">
<section class="html-editor editor">
<div class="row"> <div class="row">
% if editor == 'visual':
<textarea class="tiny-mce">${data | h}</textarea> <textarea class="tiny-mce">${data | h}</textarea>
% endif
<textarea name="" class="edit-box">${data | h}</textarea>
</div> </div>
</section> </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