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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
4 changes: 0 additions & 4 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,6 @@ Studio: Support for viewing built-in tabs on the Pages page. STUD-1193
Blades: Fixed bug when image mapped input's Show Answer multiplies rectangles on
many inputtypes. BLD-810.

Studio and LMS: Upgrade version of TinyMCE to 4.0.16. Switch from tabbed Visual/HTML
Editor for HTML modules to showing the code editor as a plugin within TinyMCE (triggered
from toolbar). STUD-1422

LMS: Enabled screen reader feedback of problem responses.
LMS-2158

Expand Down
18 changes: 7 additions & 11 deletions cms/djangoapps/contentstore/features/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -318,23 +318,19 @@ def i_am_shown_a_notification(step):
assert world.is_css_present('.wrapper-prompt')


def type_in_codemirror(index, text, find_prefix="$"):
def type_in_codemirror(index, text):
script = """
var cm = {find_prefix}('div.CodeMirror:eq({index})').get(0).CodeMirror;
var cm = $('div.CodeMirror:eq({})').get(0).CodeMirror;
cm.getInputField().focus();
cm.setValue(arguments[0]);
cm.getInputField().blur();""".format(index=index, find_prefix=find_prefix)
cm.getInputField().blur();""".format(index)
world.browser.driver.execute_script(script, str(text))
world.wait_for_ajax_complete()


def get_codemirror_value(index=0, find_prefix="$"):
return world.browser.driver.execute_script(
"""
return {find_prefix}('div.CodeMirror:eq({index})').get(0).CodeMirror.getValue();
""".format(index=index, find_prefix=find_prefix)
)

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)
Expand Down
50 changes: 3 additions & 47 deletions cms/djangoapps/contentstore/features/html-editor.feature
Original file line number Diff line number Diff line change
Expand Up @@ -22,50 +22,6 @@ Feature: CMS.HTML Editor

Scenario: TinyMCE image plugin sets urls correctly
Given I have created a Blank HTML Page
When I edit the page
And I add an image with static link "/static/image.jpg" via the Image Plugin Icon
Then the src link is rewritten to "c4x/MITx/999/asset/image.jpg"
And the link is shown as "/static/image.jpg" in the Image Plugin

Scenario: TinyMCE link plugin sets urls correctly
Given I have created a Blank HTML Page
When I edit the page
And I add a link with static link "/static/image.jpg" via the Link Plugin Icon
Then the href link is rewritten to "c4x/MITx/999/asset/image.jpg"
And the link is shown as "/static/image.jpg" in the Link Plugin

Scenario: TinyMCE and CodeMirror preserve style tags
Given I have created a Blank HTML Page
When I edit the page
And type "<p class='title'>pages</p><style><!-- .title { color: red; } --></style>" in the code editor and press OK
And I save the page
Then the page text contains:
"""
<p class="title">pages</p>
<style><!--
.title { color: red; }
--></style>
"""

Scenario: TinyMCE toolbar buttons are as expected
Given I have created a Blank HTML Page
When I edit the page
Then the expected toolbar buttons are displayed

Scenario: Static links are converted when switching between code editor and WYSIWYG views
Given I have created a Blank HTML Page
When I edit the page
And type "<img src="/static/image.jpg">" in the code editor and press OK
Then the src link is rewritten to "c4x/MITx/999/asset/image.jpg"
And the code editor displays "<p><img src="/static/image.jpg" alt="" /></p>"

Scenario: Code format toolbar button wraps text with code tags
Given I have created a Blank HTML Page
When I edit the page
And I set the text to "display as code" and I select the text
And I select the code toolbar button
And I save the page
Then the page text contains:
"""
<p><code>display as code</code></p>
"""
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
180 changes: 25 additions & 155 deletions cms/djangoapps/contentstore/features/html-editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@
# pylint: disable=C0111

from lettuce import world, step
from nose.tools import assert_in, assert_equal # pylint: disable=no-name-in-module
from common import type_in_codemirror, get_codemirror_value

CODEMIRROR_SELECTOR_PREFIX = "$('iframe').contents().find"
from nose.tools import assert_in # pylint: disable=no-name-in-module


@step('I have created a Blank HTML Page$')
Expand Down Expand Up @@ -34,168 +31,41 @@ def i_created_etext_in_latex(step):
)


@step('I edit the page$')
@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 static link "(.*)" via the Image Plugin Icon$')
def i_click_on_image_plugin_icon(step, path):
use_plugin(
'.mce-i-image',
lambda: world.css_fill('.mce-textbox', path, 0)
)


@step('the link is shown as "(.*)" in the Image Plugin$')
def check_link_in_image_plugin(step, path):
use_plugin(
'.mce-i-image',
lambda: assert_equal(path, world.css_find('.mce-textbox')[0].value)
)


@step('I add a link with static link "(.*)" via the Link Plugin Icon$')
def i_click_on_link_plugin_icon(step, path):
def fill_in_link_fields():
world.css_fill('.mce-textbox', path, 0)
world.css_fill('.mce-textbox', 'picture', 1)

use_plugin('.mce-i-link', fill_in_link_fields)
@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:

@step('the link is shown as "(.*)" in the Link Plugin$')
def check_link_in_link_plugin(step, path):
# Ensure caret position is within the link just created.
script = """
var editor = tinyMCE.activeEditor;
editor.selection.select(editor.dom.select('a')[0]);"""
world.browser.driver.execute_script(script)
world.wait_for_ajax_complete()

use_plugin(
'.mce-i-link',
lambda: assert_equal(path, world.css_find('.mce-textbox')[0].value)
)


@step('type "(.*)" in the code editor and press OK$')
def type_in_codemirror_plugin(step, text):
use_code_editor(
lambda: type_in_codemirror(0, text, CODEMIRROR_SELECTOR_PREFIX)
)


@step('and the code editor displays "(.*)"$')
def verify_code_editor_text(step, text):
use_code_editor(
lambda: assert_equal(text, get_codemirror_value(0, CODEMIRROR_SELECTOR_PREFIX))
)
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()

def use_plugin(button_class, action):
# Click on plugin button
world.css_click(button_class)
perform_action_in_plugin(action)
world.browser.switch_to_window(parent_window) # Switch back to the main window


def use_code_editor(action):
# Click on plugin button
buttons = world.css_find('div.mce-widget>button')

code_editor = [button for button in buttons if button.text == 'HTML']
assert_equal(1, len(code_editor))
code_editor[0].click()

perform_action_in_plugin(action)


def perform_action_in_plugin(action):
# Wait for the plugin window to open.
world.wait_for_visible('.mce-window')

# Trigger the action
action()

# Click OK
world.css_click('.mce-primary')


@step('I save the page$')
def i_click_on_save(step):
world.save_component(step)


@step('the page text contains:')
def check_page_text(step):
assert_in(step.multiline, world.css_find('.xmodule_HtmlModule').html)


@step('the src link is rewritten to "(.*)"$')
def image_static_link_is_rewritten(step, path):
@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
assert_in(path, image['src'])


@step('the href link is rewritten to "(.*)"$')
def link_static_link_is_rewritten(step, path):
# Find the TinyMCE iframe within the main window
with world.browser.get_iframe('mce_0_ifr') as tinymce:
link = tinymce.find_by_tag('a').first
assert_in(path, link['href'])


@step('the expected toolbar buttons are displayed$')
def check_toolbar_buttons(step):
dropdowns = world.css_find('.mce-listbox')
assert_equal(2, len(dropdowns))

# Format dropdown
assert_equal('Paragraph', dropdowns[0].text)
# Font dropdown
assert_equal('Font Family', dropdowns[1].text)

buttons = world.css_find('.mce-ico')

# Note that the code editor icon is not present because we are now showing text instead of an icon.
# However, other test points user the code editor, so we have already verified its presence.
expected_buttons = [
'bold',
'italic',
'underline',
'forecolor',
# This is our custom "code style" button, which uses an image instead of a class.
'none',
'bullist',
'numlist',
'outdent',
'indent',
'blockquote',
'link',
'unlink',
'image'
]

assert_equal(len(expected_buttons), len(buttons))

for index, button in enumerate(expected_buttons):
class_names = buttons[index]._element.get_attribute('class')
assert_equal("mce-ico mce-i-" + button, class_names)


@step('I set the text to "(.*)" and I select the text$')
def set_text_and_select(step, text):
script = """
var editor = tinyMCE.activeEditor;
editor.setContent(arguments[0]);
editor.selection.select(editor.dom.select('p')[0]);"""
world.browser.driver.execute_script(script, str(text))
world.wait_for_ajax_complete()


@step('I select the code toolbar button$')
def select_code_button(step):
# This is our custom "code style" button. It uses an image instead of a class.
world.css_click(".mce-i-none")
# Test onExecCommandHandler set the url to absolute.
assert_in('c4x/MITx/999/asset/image.jpg', image['src'])
1 change: 0 additions & 1 deletion cms/static/sass/views/_unit.scss
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,6 @@ body.course.unit,.view-unit {

.row {
margin-bottom: 0px;
overflow: hidden;
}

// Module Actions, also used for Pages
Expand Down
4 changes: 2 additions & 2 deletions cms/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@
"backbone": "js/vendor/backbone-min",
"backbone.associations": "js/vendor/backbone-associations-min",
"backbone.paginator": "js/vendor/backbone.paginator.min",
"tinymce": "js/vendor/tiny_mce/tinymce.min",
"jquery.tinymce": "js/vendor/tiny_mce/jquery.tinymce.min",
"tinymce": "js/vendor/tiny_mce/tiny_mce",
"jquery.tinymce": "js/vendor/tiny_mce/jquery.tinymce",
"xmodule": "/xmodule/xmodule",
"xblock": "coffee/src/xblock",
"utility": "js/src/utility",
Expand Down
6 changes: 6 additions & 0 deletions cms/templates/widgets/html-edit.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,14 @@

<div class="wrapper-comp-editor" id="editor-tab" data-base-asset-url="${base_asset_url}">
<section class="html-editor editor">
<ul class="editor-tabs">
<li><a href="#" class="visual-tab tab current" data-tab="visual">${_("Visual")}</a></li>
<li><a href="#" class="html-tab tab" data-tab="advanced">${_("HTML")}</a></li>
</ul>

<div class="row">
<textarea class="tiny-mce">${data | h}</textarea>
<textarea name="" class="edit-box">${data | h}</textarea>
</div>
</section>
</div>
Expand Down
2 changes: 1 addition & 1 deletion common/lib/xmodule/xmodule/css/html/edit.scss
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
}

.editor-tabs {
top: 0 !important;
top: 11px !important;
right: 10px;
z-index: 99;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<section class="html-edit">
<textarea class="tiny-mce">dummy</textarea>
<!--
The text passed in is the escaped version of
&lt;problem>
&lt;p>&lt;/p>
&lt;multiplechoiceresponse>
<pre>&lt;problem>
&lt;p>&lt;/p></pre>
<div><foo>bar</foo></div>
-->
<textarea name="" class="edit-box">&amp;lt;problem&gt;
&amp;lt;p&gt;&amp;lt;/p&gt;
&amp;lt;multiplechoiceresponse&gt;
&lt;pre&gt;&amp;lt;problem&gt;
&amp;lt;p&gt;&amp;lt;/p&gt;</pre>
<div><foo>bar</foo></div></textarea>
</section>
10 changes: 10 additions & 0 deletions common/lib/xmodule/xmodule/js/fixtures/html-edit-with-links.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<section class="html-edit">
<ul class="editor-tabs">
<li><a href="#" class="visual-tab tab current" data-tab="visual">Visual</a></li>
<li><a href="#" class="html-tab tab" data-tab="advanced">HTML</a></li>
</ul>
<div class="row">
<textarea class="tiny-mce">dummy text</textarea>
<textarea name="" class="edit-box">Advanced Editor Text with link /static/dummy.jpg</textarea>
</div>
</section>
11 changes: 9 additions & 2 deletions common/lib/xmodule/xmodule/js/fixtures/html-edit.html
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
<section class="html-edit">
<textarea class="tiny-mce">dummy text</textarea>
</section>
<ul class="editor-tabs">
<li><a href="#" class="visual-tab tab current" data-tab="visual">Visual</a></li>
<li><a href="#" class="html-tab tab" data-tab="advanced">HTML</a></li>
</ul>
<div class="row">
<textarea class="tiny-mce">dummy text</textarea>
<textarea name="" class="edit-box">Advanced Editor Text</textarea>
</div>
</section>
Loading