Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
1b2d1858
Commit
1b2d1858
authored
Apr 14, 2014
by
Andy Armstrong
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fix duplicate save and cancel buttons in Studio
STUD-1531 Also add support for refreshing the modal on custom save
parent
f1eefc11
Hide whitespace changes
Inline
Side-by-side
Showing
21 changed files
with
277 additions
and
192 deletions
+277
-192
cms/djangoapps/contentstore/views/item.py
+0
-4
cms/djangoapps/contentstore/views/tests/test_item.py
+0
-31
cms/static/coffee/spec/views/module_edit_spec.coffee
+0
-1
cms/static/coffee/src/views/module_edit.coffee
+0
-1
cms/static/coffee/src/xblock/cms.runtime.v1.coffee
+59
-57
cms/static/js/spec/views/modals/edit_xblock_spec.js
+50
-8
cms/static/js/spec/views/pages/container_spec.js
+24
-1
cms/static/js/spec/views/xblock_editor_spec.js
+0
-14
cms/static/js/spec_helpers/edit_helpers.js
+6
-10
cms/static/js/spec_helpers/modal_helpers.js
+1
-1
cms/static/js/views/modals/base_modal.js
+19
-3
cms/static/js/views/modals/edit_xblock.js
+48
-10
cms/static/js/views/xblock_editor.js
+30
-17
cms/static/sass/elements/_modal-window.scss
+18
-1
cms/templates/component.html
+0
-26
cms/templates/container.html
+0
-3
cms/templates/edit-tabs.html
+0
-2
cms/templates/js/basic-modal.underscore
+1
-1
cms/templates/js/mock/mock-xblock-editor-with-custom-buttons.underscore
+20
-0
cms/templates/unit.html
+0
-1
common/static/coffee/src/xblock/core.coffee
+1
-0
No files found.
cms/djangoapps/contentstore/views/item.py
View file @
1b2d1858
...
...
@@ -246,10 +246,6 @@ def xblock_view_handler(request, package_id, view_name, tag=None, branch=None, v
fragment
.
content
=
render_to_string
(
'component.html'
,
{
'preview'
:
fragment
.
content
,
'label'
:
component
.
display_name
or
component
.
scope_ids
.
block_type
,
# Native XBlocks are responsible for persisting their own data,
# so they are also responsible for providing save/cancel buttons.
'show_save_cancel'
:
isinstance
(
component
,
xmodule
.
x_module
.
XModuleDescriptor
),
})
else
:
raise
Http404
...
...
cms/djangoapps/contentstore/views/tests/test_item.py
View file @
1b2d1858
...
...
@@ -765,34 +765,3 @@ class TestComponentHandler(TestCase):
self
.
descriptor
.
handle
=
create_response
self
.
assertEquals
(
component_handler
(
self
.
request
,
self
.
usage_id
,
'dummy_handler'
)
.
status_code
,
status_code
)
@ddt.ddt
class
TestNativeXBlock
(
ItemTest
):
"""
Test a "native" XBlock (not an XModule shim).
"""
@ddt.data
((
'problem'
,
True
),
(
'acid'
,
False
))
@ddt.unpack
def
test_save_cancel_buttons
(
self
,
category
,
include_buttons
):
"""
Native XBlocks handle their own persistence, so Studio
should not render Save/Cancel buttons for them.
"""
# Create the XBlock
resp
=
self
.
create_xblock
(
category
=
category
)
self
.
assertEqual
(
resp
.
status_code
,
200
)
native_loc
=
json
.
loads
(
resp
.
content
)[
'locator'
]
# Render the XBlock
view_url
=
'/xblock/{locator}/student_view'
.
format
(
locator
=
native_loc
)
resp
=
self
.
client
.
get
(
view_url
,
HTTP_ACCEPT
=
'application/json'
)
self
.
assertEqual
(
resp
.
status_code
,
200
)
# Check that the save and cancel buttons are hidden for native XBlocks,
# but shown for XModule shim XBlocks
resp_html
=
json
.
loads
(
resp
.
content
)[
'html'
]
assert_func
=
self
.
assertIn
if
include_buttons
else
self
.
assertNotIn
assert_func
(
'save-button'
,
resp_html
)
assert_func
(
'cancel-button'
,
resp_html
)
cms/static/coffee/spec/views/module_edit_spec.coffee
View file @
1b2d1858
...
...
@@ -25,7 +25,6 @@ define ["jquery", "js/spec_helpers/edit_helpers", "coffee/src/views/module_edit"
</section>
</li>
</ul>
<div class="edit-xblock-modal"/>
"""
edit_helpers
.
installEditTemplates
(
true
);
spyOn
(
$
,
'ajax'
).
andReturn
(
@
moduleData
)
...
...
cms/static/coffee/src/views/module_edit.coffee
View file @
1b2d1858
...
...
@@ -47,7 +47,6 @@ define ["jquery", "underscore", "gettext", "xblock/runtime.v1",
clickEditButton
:
(
event
)
->
event
.
preventDefault
()
modal
=
new
EditXBlockModal
({
el
:
$
(
'.edit-xblock-modal'
),
view
:
'student_view'
});
modal
.
edit
(
this
.
$el
,
self
.
model
,
{
refresh
:
_
.
bind
(
@
render
,
this
)
})
cms/static/coffee/src/xblock/cms.runtime.v1.coffee
View file @
1b2d1858
...
...
@@ -5,74 +5,76 @@ define [
@
PreviewRuntime
=
{}
class
PreviewRuntime
.
v1
extends
XBlock
.
Runtime
.
v1
handlerUrl
:
(
element
,
handlerName
,
suffix
,
query
,
thirdparty
)
->
uri
=
URI
(
"/preview/xblock"
).
segment
(
$
(
element
).
data
(
'usage-id'
))
.
segment
(
'handler'
)
.
segment
(
handlerName
)
if
suffix
?
then
uri
.
segment
(
suffix
)
if
query
?
then
uri
.
search
(
query
)
uri
.
toString
()
handlerUrl
:
(
element
,
handlerName
,
suffix
,
query
,
thirdparty
)
->
uri
=
URI
(
"/preview/xblock"
).
segment
(
$
(
element
).
data
(
'usage-id'
))
.
segment
(
'handler'
)
.
segment
(
handlerName
)
if
suffix
?
then
uri
.
segment
(
suffix
)
if
query
?
then
uri
.
search
(
query
)
uri
.
toString
()
@
StudioRuntime
=
{}
class
StudioRuntime
.
v1
extends
XBlock
.
Runtime
.
v1
constructor
:
()
->
super
()
@
savingNotification
=
new
NotificationView
.
Mini
title
:
gettext
(
'Saving…'
)
@
alert
=
new
NotificationView
.
Error
title
:
"OpenAssessment Save Error"
,
closeIcon
:
false
,
shown
:
false
constructor
:
()
->
super
()
@
savingNotification
=
new
NotificationView
.
Mini
title
:
gettext
(
'Saving…'
)
@
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'
))
.
segment
(
'handler'
)
.
segment
(
handlerName
)
if
suffix
?
then
uri
.
segment
(
suffix
)
if
query
?
then
uri
.
search
(
query
)
uri
.
toString
()
handlerUrl
:
(
element
,
handlerName
,
suffix
,
query
,
thirdparty
)
->
uri
=
URI
(
"/xblock"
).
segment
(
$
(
element
).
data
(
'usage-id'
))
.
segment
(
'handler'
)
.
segment
(
handlerName
)
if
suffix
?
then
uri
.
segment
(
suffix
)
if
query
?
then
uri
.
search
(
query
)
uri
.
toString
()
# Notify the Studio client-side runtime so it can update
# the UI in a consistent way. Currently, this is used
# for save / cancel when editing an XBlock.
# Although native XBlocks should handle their own persistence,
# Studio still needs to update the UI in a consistent way
# (showing the "Saving..." notification, closing the modal editing dialog, etc.)
notify
:
(
name
,
data
)
->
if
name
==
'save'
if
'state'
of
data
# Notify the Studio client-side runtime so it can update
# the UI in a consistent way. Currently, this is used
# for save / cancel when editing an XBlock.
# Although native XBlocks should handle their own persistence,
# Studio still needs to update the UI in a consistent way
# (showing the "Saving..." notification, closing the modal editing dialog, etc.)
notify
:
(
name
,
data
)
->
if
name
==
'save'
if
'state'
of
data
# Starting to save, so show the "Saving..." notification
if
data
.
state
==
'start'
@
savingNotification
.
show
()
# Starting to save, so show the "Saving..." notification
if
data
.
state
==
'start'
@
savingNotification
.
show
()
# Finished saving, so hide the "Saving..." notification
else
if
data
.
state
==
'end'
# Finished saving, so hide the "Saving..." notification
else
if
data
.
state
==
'end'
@
_hideAlerts
()
# Hide the editor *after* we finish saving in case there are validation
# errors that the user needs to correct.
@
_hideEditor
()
# Notify the modal that the save has completed so that it can hide itself
# and then refresh the xblock.
if
@
modal
@
modal
.
onSave
()
$
(
'.component.editing'
).
removeClass
(
'editing'
)
@
savingNotification
.
hide
()
@
savingNotification
.
hide
()
else
if
name
==
'cancel
'
@
_hideEditor
()
else
if
name
==
'edit-modal-shown
'
@
modal
=
data
else
if
name
==
'error'
if
'msg'
of
data
@
alert
.
options
.
message
=
data
.
msg
@
alert
.
show
()
else
if
name
==
'edit-modal-hidden'
@
modal
=
null
_hideEditor
:
()
->
# This will close all open component editors, which works
# if we assume that <= 1 are open at a time.
el
=
$
(
'.component.editing'
)
el
.
removeClass
(
'editing'
)
el
.
find
(
'.component-editor'
).
slideUp
(
150
)
ModalUtils
.
hideModalCover
()
else
if
name
==
'cancel'
@
_hideAlerts
()
if
@
modal
@
modal
.
cancel
()
# Hide any alerts that are being shown
if
@
alert
.
options
.
shown
@
alert
.
hide
()
else
if
name
==
'error'
if
'msg'
of
data
@
alert
.
options
.
message
=
data
.
msg
@
alert
.
show
()
_hideAlerts
:
()
->
# Hide any alerts that are being shown
if
@
alert
.
options
.
shown
@
alert
.
hide
()
cms/static/js/spec/views/modals/edit_xblock_spec.js
View file @
1b2d1858
...
...
@@ -5,9 +5,9 @@ define(["jquery", "underscore", "js/spec_helpers/create_sinon", "js/spec_helpers
describe
(
"EditXBlockModal"
,
function
()
{
var
model
,
modal
,
showModal
;
showModal
=
function
(
requests
,
mockHtml
)
{
showModal
=
function
(
requests
,
mockHtml
,
options
)
{
var
xblockElement
=
$
(
'.xblock'
);
return
edit_helpers
.
showEditModal
(
requests
,
xblockElement
,
model
,
mockHtml
);
return
edit_helpers
.
showEditModal
(
requests
,
xblockElement
,
model
,
mockHtml
,
options
);
};
beforeEach
(
function
()
{
...
...
@@ -45,6 +45,13 @@ define(["jquery", "underscore", "js/spec_helpers/create_sinon", "js/spec_helpers
expect
(
edit_helpers
.
isShowingModal
(
modal
)).
toBeFalsy
();
});
it
(
'does not show the "Save" button'
,
function
()
{
var
requests
=
create_sinon
.
requests
(
this
);
modal
=
showModal
(
requests
,
mockXBlockEditorHtml
);
expect
(
modal
.
$
(
'.action-save'
)).
not
.
toBeVisible
();
expect
(
modal
.
$
(
'.action-cancel'
).
text
()).
toBe
(
'OK'
);
});
it
(
'shows the correct title'
,
function
()
{
var
requests
=
create_sinon
.
requests
(
this
);
modal
=
showModal
(
requests
,
mockXBlockEditorHtml
);
...
...
@@ -56,6 +63,43 @@ define(["jquery", "underscore", "js/spec_helpers/create_sinon", "js/spec_helpers
modal
=
showModal
(
requests
,
mockXBlockEditorHtml
);
expect
(
modal
.
$
(
'.editor-modes a'
).
length
).
toBe
(
0
);
});
it
(
'hides itself and refreshes after save notification'
,
function
()
{
var
requests
=
create_sinon
.
requests
(
this
),
refreshed
=
false
,
refresh
=
function
()
{
refreshed
=
true
;
};
modal
=
showModal
(
requests
,
mockXBlockEditorHtml
,
{
refresh
:
refresh
});
modal
.
runtime
.
notify
(
'save'
,
{
state
:
'start'
});
modal
.
runtime
.
notify
(
'save'
,
{
state
:
'end'
});
expect
(
edit_helpers
.
isShowingModal
(
modal
)).
toBeFalsy
();
expect
(
refreshed
).
toBeTruthy
();
});
it
(
'hides itself and does not refresh after cancel notification'
,
function
()
{
var
requests
=
create_sinon
.
requests
(
this
),
refreshed
=
false
,
refresh
=
function
()
{
refreshed
=
true
;
};
modal
=
showModal
(
requests
,
mockXBlockEditorHtml
,
{
refresh
:
refresh
});
modal
.
runtime
.
notify
(
'cancel'
);
expect
(
edit_helpers
.
isShowingModal
(
modal
)).
toBeFalsy
();
expect
(
refreshed
).
toBeFalsy
();
});
describe
(
"Custom Buttons"
,
function
()
{
var
mockCustomButtonsHtml
;
mockCustomButtonsHtml
=
readFixtures
(
'mock/mock-xblock-editor-with-custom-buttons.underscore'
);
it
(
'hides the modal
\'
s button bar'
,
function
()
{
var
requests
=
create_sinon
.
requests
(
this
);
modal
=
showModal
(
requests
,
mockCustomButtonsHtml
);
expect
(
modal
.
$
(
'.modal-actions'
)).
toBeHidden
();
});
});
});
describe
(
"XModule Editor"
,
function
()
{
...
...
@@ -64,12 +108,11 @@ define(["jquery", "underscore", "js/spec_helpers/create_sinon", "js/spec_helpers
mockXModuleEditorHtml
=
readFixtures
(
'mock/mock-xmodule-editor.underscore'
);
beforeEach
(
function
()
{
// Mock the VerticalDescriptor so that the module can be rendered
window
.
VerticalDescriptor
=
XModule
.
Descriptor
;
edit_helpers
.
installMockXModule
();
});
afterEach
(
function
()
{
window
.
VerticalDescriptor
=
null
;
edit_helpers
.
uninstallMockXModule
()
;
});
it
(
'can render itself'
,
function
()
{
...
...
@@ -140,12 +183,11 @@ define(["jquery", "underscore", "js/spec_helpers/create_sinon", "js/spec_helpers
mockXModuleEditorHtml
=
readFixtures
(
'mock/mock-xmodule-settings-only-editor.underscore'
);
beforeEach
(
function
()
{
// Mock the VerticalDescriptor so that the module can be rendered
window
.
VerticalDescriptor
=
XModule
.
Descriptor
;
edit_helpers
.
installMockXModule
();
});
afterEach
(
function
()
{
window
.
VerticalDescriptor
=
null
;
edit_helpers
.
uninstallMockXModule
()
;
});
it
(
'can render itself'
,
function
()
{
...
...
cms/static/js/spec/views/pages/container_spec.js
View file @
1b2d1858
...
...
@@ -107,6 +107,29 @@ define(["jquery", "js/spec_helpers/create_sinon", "js/spec_helpers/edit_helpers"
});
expect
(
edit_helpers
.
isShowingModal
()).
toBeTruthy
();
});
});
describe
(
"Editing an xmodule"
,
function
()
{
var
mockContainerXBlockHtml
,
mockXModuleEditor
,
newDisplayName
=
'New Display Name'
;
beforeEach
(
function
()
{
edit_helpers
.
installMockXModule
({
data
:
"<p>Some HTML</p>"
,
metadata
:
{
display_name
:
newDisplayName
}
});
});
afterEach
(
function
()
{
edit_helpers
.
uninstallMockXModule
();
edit_helpers
.
cancelModalIfShowing
();
});
mockContainerXBlockHtml
=
readFixtures
(
'mock/mock-container-xblock.underscore'
);
mockXModuleEditor
=
readFixtures
(
'mock/mock-xmodule-editor.underscore'
);
it
(
'can save changes to settings'
,
function
()
{
var
editButtons
,
modal
,
mockUpdatedXBlockHtml
;
...
...
@@ -117,7 +140,7 @@ define(["jquery", "js/spec_helpers/create_sinon", "js/spec_helpers/edit_helpers"
expect
(
editButtons
.
length
).
toBe
(
6
);
editButtons
.
first
().
click
();
create_sinon
.
respondWithJson
(
requests
,
{
html
:
mockX
BlockEditorHtml
,
html
:
mockX
ModuleEditor
,
resources
:
[]
});
...
...
cms/static/js/spec/views/xblock_editor_spec.js
View file @
1b2d1858
...
...
@@ -49,20 +49,6 @@ define([ "jquery", "underscore", "js/spec_helpers/create_sinon", "js/spec_helper
expect
(
editor
.
$el
.
select
(
'.xblock-header'
)).
toBeTruthy
();
expect
(
editor
.
getMode
()).
toEqual
(
'settings'
);
});
it
(
'saves any custom metadata'
,
function
()
{
var
requests
=
create_sinon
.
requests
(
this
),
request
,
response
;
editor
.
render
();
create_sinon
.
respondWithJson
(
requests
,
{
html
:
mockXBlockEditorHtml
,
resources
:
[]
});
editor
.
save
();
request
=
requests
[
requests
.
length
-
1
];
response
=
JSON
.
parse
(
request
.
requestBody
);
expect
(
response
.
metadata
.
display_name
).
toBe
(
testDisplayName
);
expect
(
response
.
metadata
.
custom_field
).
toBe
(
'Custom Value'
);
});
});
describe
(
"Editing an xmodule"
,
function
()
{
...
...
cms/static/js/spec_helpers/edit_helpers.js
View file @
1b2d1858
/**
* Provides helper methods for invoking Studio editors in Jasmine tests.
*/
define
([
"jquery"
,
"
js/spec_helpers/create_sinon"
,
"js/spec_helpers/modal_helpers"
,
"js/views/modals/edit_xblock
"
,
"xmodule"
,
"coffee/src/main"
,
"xblock/cms.runtime.v1"
],
function
(
$
,
create_sinon
,
modal_helpers
,
EditXBlockModal
)
{
define
([
"jquery"
,
"
underscore"
,
"js/spec_helpers/create_sinon"
,
"js/spec_helpers/modal_helpers
"
,
"
js/views/modals/edit_xblock"
,
"
xmodule"
,
"coffee/src/main"
,
"xblock/cms.runtime.v1"
],
function
(
$
,
_
,
create_sinon
,
modal_helpers
,
EditXBlockModal
)
{
var
editorTemplate
=
readFixtures
(
'metadata-editor.underscore'
),
numberEntryTemplate
=
readFixtures
(
'metadata-number-entry.underscore'
),
...
...
@@ -12,19 +12,15 @@ define(["jquery", "js/spec_helpers/create_sinon", "js/spec_helpers/modal_helpers
editorModeButtonTemplate
=
readFixtures
(
'editor-mode-button.underscore'
),
installMockXBlock
,
uninstallMockXBlock
,
hasSavedMockXBlock
,
installMockXModule
,
uninstallMockXModule
,
hasSavedMockXModule
,
installEditTemplates
,
showEditModal
;
installMockXBlock
=
function
(
mockResult
)
{
window
.
MockXBlock
=
function
(
runtime
,
element
)
{
return
{
save
:
function
()
{
return
mockResult
;
}
runtime
:
runtime
};
};
};
...
...
@@ -58,9 +54,9 @@ define(["jquery", "js/spec_helpers/create_sinon", "js/spec_helpers/modal_helpers
appendSetFixtures
(
$
(
"<script>"
,
{
id
:
"metadata-string-entry"
,
type
:
"text/template"
}).
text
(
stringEntryTemplate
));
};
showEditModal
=
function
(
requests
,
xblockElement
,
model
,
mockHtml
)
{
showEditModal
=
function
(
requests
,
xblockElement
,
model
,
mockHtml
,
options
)
{
var
modal
=
new
EditXBlockModal
({});
modal
.
edit
(
xblockElement
,
model
);
modal
.
edit
(
xblockElement
,
model
,
options
);
create_sinon
.
respondWithJson
(
requests
,
{
html
:
mockHtml
,
"resources"
:
[]
...
...
cms/static/js/spec_helpers/modal_helpers.js
View file @
1b2d1858
...
...
@@ -47,7 +47,7 @@ define(["jquery"],
cancelModal
=
function
(
modal
)
{
var
modalElement
,
cancelButton
;
modalElement
=
getModalElement
(
modal
);
cancelButton
=
modalElement
.
find
(
'.action-cancel'
);
cancelButton
=
modalElement
.
find
(
'.action-cancel
:visible
'
);
expect
(
cancelButton
.
length
).
toBe
(
1
);
cancelButton
.
click
();
};
...
...
cms/static/js/views/modals/base_modal.js
View file @
1b2d1858
...
...
@@ -71,8 +71,10 @@ define(["jquery", "underscore", "gettext", "js/views/baseview"],
},
cancel
:
function
(
event
)
{
event
.
preventDefault
();
event
.
stopPropagation
();
// Make sure parent modals don't see the click
if
(
event
)
{
event
.
preventDefault
();
event
.
stopPropagation
();
// Make sure parent modals don't see the click
}
this
.
hide
();
},
...
...
@@ -98,7 +100,21 @@ define(["jquery", "underscore", "gettext", "js/views/baseview"],
name
:
name
,
isPrimary
:
isPrimary
});
this
.
$
(
'.modal-actions ul'
).
append
(
html
);
this
.
getActionBar
().
find
(
'ul'
).
append
(
html
);
},
/**
* Returns the action bar that contains the modal's action buttons.
*/
getActionBar
:
function
()
{
return
this
.
$
(
'.modal-window > div > .modal-actions'
);
},
/**
* Returns the action button of the specified type.
*/
getActionButton
:
function
(
type
)
{
return
this
.
getActionBar
().
find
(
'.action-'
+
type
);
},
resize
:
function
()
{
...
...
cms/static/js/views/modals/edit_xblock.js
View file @
1b2d1858
...
...
@@ -37,6 +37,10 @@ define(["jquery", "underscore", "gettext", "js/views/modals/base_modal",
this
.
editOptions
=
options
;
this
.
render
();
this
.
show
();
// Hide the action bar until we know which buttons we want
this
.
getActionBar
().
hide
();
// Display the xblock after the modal is shown as there are some xblocks
// that depend upon being visible when they initialize, e.g. the problem xmodule.
this
.
displayXBlock
();
...
...
@@ -60,7 +64,17 @@ define(["jquery", "underscore", "gettext", "js/views/modals/base_modal",
onDisplayXBlock
:
function
()
{
var
editorView
=
this
.
editorView
,
title
=
this
.
getTitle
();
title
=
this
.
getTitle
(),
xblock
=
editorView
.
xblock
,
runtime
=
xblock
.
runtime
;
// Notify the runtime that the modal has been shown
if
(
runtime
)
{
this
.
runtime
=
runtime
;
runtime
.
notify
(
"edit-modal-shown"
,
this
);
}
// Update the modal's header
if
(
editorView
.
hasCustomTabs
())
{
// Hide the modal's header as the custom editor provides its own
this
.
$
(
'.modal-header'
).
hide
();
...
...
@@ -74,9 +88,28 @@ define(["jquery", "underscore", "gettext", "js/views/modals/base_modal",
this
.
selectMode
(
editorView
.
mode
);
}
}
// If the xblock is not using custom buttons then choose which buttons to show
if
(
!
editorView
.
hasCustomButtons
())
{
// If the xblock does not support save then disable the save button
if
(
!
xblock
.
save
)
{
this
.
disableSave
();
}
this
.
getActionBar
().
show
();
}
// Resize the modal to fit the window
this
.
resize
();
},
disableSave
:
function
()
{
var
saveButton
=
this
.
getActionButton
(
'save'
),
cancelButton
=
this
.
getActionButton
(
'cancel'
);
saveButton
.
hide
();
cancelButton
.
text
(
gettext
(
'OK'
));
cancelButton
.
addClass
(
'action-primary'
);
},
getTitle
:
function
()
{
var
displayName
=
this
.
xblockElement
.
find
(
'.xblock-header .header-details'
).
text
().
trim
();
// If not found, try the old unit page style rendering
...
...
@@ -117,23 +150,28 @@ define(["jquery", "underscore", "gettext", "js/views/modals/base_modal",
},
save
:
function
(
event
)
{
var
self
=
this
,
xblockInfo
=
this
.
xblockInfo
,
refresh
=
self
.
editOptions
.
refresh
;
event
.
preventDefault
();
this
.
editorView
.
save
({
success
:
function
()
{
self
.
hide
();
if
(
refresh
)
{
refresh
(
xblockInfo
);
}
}
success
:
_
.
bind
(
this
.
onSave
,
this
)
});
},
onSave
:
function
()
{
var
refresh
=
this
.
editOptions
.
refresh
;
this
.
hide
();
if
(
refresh
)
{
refresh
(
this
.
xblockInfo
);
}
},
hide
:
function
()
{
BaseModal
.
prototype
.
hide
.
call
(
this
);
// Notify the runtime that the modal has been hidden
if
(
this
.
runtime
)
{
this
.
runtime
.
notify
(
'edit-modal-hidden'
);
}
// Completely clear the contents of the modal
this
.
undelegateEvents
();
this
.
$el
.
html
(
""
);
...
...
cms/static/js/views/xblock_editor.js
View file @
1b2d1858
...
...
@@ -49,6 +49,10 @@ define(["jquery", "underscore", "gettext", "js/views/feedback_notification", "js
return
this
.
$
(
'.editor-with-tabs'
).
length
>
0
;
},
hasCustomButtons
:
function
()
{
return
this
.
$
(
'.editor-with-buttons'
).
length
>
0
;
},
createMetadataEditor
:
function
()
{
var
metadataEditor
,
metadataData
,
...
...
@@ -88,27 +92,36 @@ define(["jquery", "underscore", "gettext", "js/views/feedback_notification", "js
var
xblockInfo
=
this
.
model
,
data
,
saving
;
data
=
this
.
getXBlockData
();
saving
=
new
NotificationView
.
Mini
({
title
:
gettext
(
'Saving…'
)
});
saving
.
show
();
return
xblockInfo
.
save
(
data
).
done
(
function
()
{
var
success
=
options
.
success
;
saving
.
hide
();
if
(
success
)
{
success
();
}
});
data
=
this
.
getXModuleData
();
if
(
data
)
{
saving
=
new
NotificationView
.
Mini
({
title
:
gettext
(
'Saving…'
)
});
saving
.
show
();
return
xblockInfo
.
save
(
data
).
done
(
function
()
{
var
success
=
options
.
success
;
saving
.
hide
();
if
(
success
)
{
success
();
}
});
}
},
getXBlockData
:
function
()
{
/**
* Returns the data saved for the xmodule. Note that this *does not* work for XBlocks.
*/
getXModuleData
:
function
()
{
var
xblock
=
this
.
xblock
,
metadataEditor
=
this
.
getMetadataEditor
(),
data
;
data
=
xblock
.
save
();
if
(
metadataEditor
)
{
data
.
metadata
=
_
.
extend
(
data
.
metadata
||
{},
this
.
getChangedMetadata
());
data
=
null
;
if
(
xblock
.
save
)
{
data
=
xblock
.
save
();
if
(
metadataEditor
)
{
data
.
metadata
=
_
.
extend
(
data
.
metadata
||
{},
this
.
getChangedMetadata
());
}
}
else
{
console
.
error
(
'Cannot save xblock as it has no save method'
);
}
return
data
;
},
...
...
cms/static/sass/elements/_modal-window.scss
View file @
1b2d1858
...
...
@@ -18,7 +18,6 @@
.modal-content
{
position
:
relative
;
box-shadow
:
0
0
3px
$shadow-d1
;
background-color
:
$white
;
padding
:
5%
;
}
...
...
@@ -52,7 +51,9 @@
}
}
// TODO: need to sync up (alongside general editing mode) with xblocks.scss UI
.modal-chin
,
.xblock-actions
,
.modal-actions
{
padding
:
(
$baseline
*
0
.75
)
2%
(
$baseline
/
2
)
2%
;
...
...
@@ -190,6 +191,22 @@
}
}
// xblock custom actions
.modal-window
.editor-with-buttons
{
margin-bottom
:
(
$baseline
*
3
);
// TODO: need to sync up (alongside general editing mode) with xblocks.scss UI
.xblock-actions
{
background-color
:
$gray-l4
;
position
:
absolute
;
width
:
100%
;
bottom
:
0
;
}
}
// special overrides for video module editor/hidden tab editors
.modal-lg.modal-type-video
{
...
...
cms/templates/component.html
View file @
1b2d1858
<
%!
from
django
.
utils
.
translation
import
ugettext
as
_
%
>
<
%
namespace
name=
'static'
file=
'static_content.html'
/>
<div
class=
"wrapper wrapper-component-editor"
>
<div
class=
"component-editor"
>
<div
class=
"component-edit-header"
>
<span
class=
"component-name"
></span>
<ul
class=
"nav-edit-modes"
>
<li
id=
"editor-mode"
class=
"mode active-mode"
aria-controls=
"editor-tab"
role=
"tab"
>
<a
href=
"#"
>
${_("Editor")}
</a>
</li>
<li
id=
"settings-mode"
class=
"mode active-mode"
aria-controls=
"settings-tab"
role=
"tab"
>
<a
href=
"#"
>
${_("Settings")}
</a>
</li>
</ul>
</div>
<!-- Editor Header -->
<div
class=
"component-edit-modes"
>
<div
class=
"module-editor"
/>
</div>
## Native XBlocks render their own save/cancel buttons
% if show_save_cancel:
<div
class=
"row module-actions"
>
<a
href=
"#"
class=
"save-button action-primary action"
>
${_("Save")}
</a>
<a
href=
"#"
class=
"cancel-button action-secondary action"
>
${_("Cancel")}
</a>
</div>
<!-- Module Actions-->
% endif
</div>
</div>
<div
class=
"wrapper wrapper-component-action-header"
>
<div
class=
"component-header"
>
${label}
...
...
cms/templates/container.html
View file @
1b2d1858
...
...
@@ -130,7 +130,4 @@ main_xblock_info = {
</section>
</div>
</div>
<div
class=
"edit-xblock-modal"
></div>
</
%
block>
cms/templates/edit-tabs.html
View file @
1b2d1858
...
...
@@ -178,6 +178,4 @@
<span
class=
"label"
>
${_("close modal")}
</span>
</a>
</div>
<div
class=
"edit-xblock-modal"
></div>
</
%
block>
cms/templates/js/basic-modal.underscore
View file @
1b2d1858
...
...
@@ -5,7 +5,7 @@
role="dialog">
<div class="modal-window-overlay"></div>
<div class="modal-window confirm modal-editor modal-<%= size %> modal-type-<%= type %>">
<div class="<%= name %>-modal"
action="#"
>
<div class="<%= name %>-modal">
<div class="modal-header">
<h2 class="title modal-window-title"><%= title %></h2>
<ul class="editor-modes action-list action-modes">
...
...
cms/templates/js/mock/mock-xblock-editor-with-custom-buttons.underscore
0 → 100644
View file @
1b2d1858
<div class="xblock xblock-studio_view" data-runtime-version="1" data-usage-id="i4x:;_;_edX;_mock"
data-init="MockXBlock" data-runtime-class="StudioRuntime" data-block-type="mock" tabindex="0">
<div class="mock-xblock editor-with-buttons">
<h3>Mock XBlock Editor</h3>
<div class="modal-actions">
<h3 class="sr">Actions</h3>
<ul>
<li class="action-item">
<a href="#" class="button action-primary action-save">Save</a>
</li>
<li class="action-item">
<a href="#" class="button action-cancel">Cancel</a>
</li>
</ul>
</div>
</div>
</div>
cms/templates/unit.html
View file @
1b2d1858
...
...
@@ -225,5 +225,4 @@ require(["domReady!", "jquery", "js/models/module_info", "coffee/src/views/unit"
</div>
</div>
</div>
<div
class=
"edit-xblock-modal"
></div>
</
%
block>
common/static/coffee/src/xblock/core.coffee
View file @
1b2d1858
...
...
@@ -12,6 +12,7 @@
runtime
=
new
window
[
runtime
][
"v
#{
version
}
"
]
initFn
=
window
[
initFnName
]
block
=
initFn
(
runtime
,
element
)
?
{}
block
.
runtime
=
runtime
else
elementTag
=
$
(
'<div>'
).
append
(
$element
.
clone
()).
html
();
console
.
log
(
"Block
#{
elementTag
}
is missing data-runtime, data-runtime-version or data-init, and can't be initialized"
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment