Commit c52c61ff by Chris Dodge

Got basics of HTML editor with the Asset widget working. Can query/display all…

Got basics of HTML editor with the Asset widget working. Can query/display all existing assets as well as embedding them into the editor. No upload, search, sorting, filtering yet.
parent 1dfba0bc
...@@ -339,6 +339,7 @@ def edit_unit(request, location): ...@@ -339,6 +339,7 @@ def edit_unit(request, location):
'create_new_unit_template': Location('i4x', 'edx', 'templates', 'vertical', 'Empty'), 'create_new_unit_template': Location('i4x', 'edx', 'templates', 'vertical', 'Empty'),
'unit_state': unit_state, 'unit_state': unit_state,
'published_date': published_date, 'published_date': published_date,
'course_location' : course.location
}) })
...@@ -911,6 +912,49 @@ def not_found(request): ...@@ -911,6 +912,49 @@ def not_found(request):
def server_error(request): def server_error(request):
return render_to_response('error.html', {'error': '500'}) return render_to_response('error.html', {'error': '500'})
@login_required
@ensure_csrf_cookie
def assets(request, location):
"""
An AJAX handler to return a list of all assets for a course
"""
course_location = Location(location)
if not has_access(request.user, course_location):
raise PermissionDenied()
course_reference = StaticContent.compute_location(course_location.org,
course_location.course, course_location.name)
assets = contentstore().get_all_content_for_course(course_reference)
# sort in reverse upload date order
assets = sorted(assets, key=lambda asset: asset['uploadDate'], reverse=True)
# now iterate and put into the format that clients will expect
asset_display = []
for asset in assets:
id = asset['_id']
display_info = {}
asset_location = StaticContent.compute_location(id['org'], id['course'], id['name'])
display_info['id'] = asset_location.url()
display_info['display_name'] = asset['displayname']
display_info['upload_date'] = get_date_display(asset['uploadDate'])
display_info['url'] = StaticContent.get_url_path_from_location(asset_location)
# note, due to the schema change we may not have a 'thumbnail_location' in the result set
_thumbnail_location = asset.get('thumbnail_location', None)
thumbnail_location = Location(_thumbnail_location) if _thumbnail_location is not None else None
display_info['thumb_url'] = StaticContent.get_url_path_from_location(thumbnail_location) if thumbnail_location is not None else None
display_info['markup'] = "<img src='{0}' />".format(display_info['url'])
asset_display.append(display_info)
logging.debug("assets = {0}".format(asset_display))
return HttpResponse(json.dumps(asset_display))
@login_required @login_required
@ensure_csrf_cookie @ensure_csrf_cookie
......
class LibraryEntry extends Backbone.Model
defaults:
id: null
thumb_url: null
url: null
display_name: null
upload_date: null
markup: null
mime_type: null
class LibraryCollection extends Backbone.Collection
url: '/assets/'
model: LibraryEntry
initialize: (course_location) ->
@url = @url + course_location
class CMS.Views.AssetWidget extends Backbone.View
initialize: ->
@editor = @options.editor
@library = new CMS.Views.AssetWidget.Library(
el: @$('.library')
model: @model
assetWidget: @
)
@uploadForm = new CMS.Views.AssetWidget.UploadForm(
el: @$('.upload-form')
model: @model
assetWidget: @
)
insertAssetAndClose: (markup) ->
@editor.focus()
@editor.selection.setContent(markup)
@close()
close: ->
@$el.remove();
# modalCover is defined in html-editor.js
$modalCover.css('z-index', '1000');
openUploadDialog: ->
@library.$el.hide()
@uploadForm.$el.show()
closeUploadDialog: ->
@library.$el.show()
@uploadForm.$el.hide()
class CMS.Views.AssetWidget.Library extends Backbone.View
events:
"click .upload-button": 'clickUploadButton'
"keyup .search" : 'searchBarChange'
initialize: ->
@assetWidget = @options.assetWidget
@assetList = new CMS.Views.AssetWidget.Library.AssetList(
el: @$('table')
sortCol:'date'
sortOrder: 'desc'
typeFilter: ''
itemsPerPage: 15
widget: @assetWidget
model: @model
)
clickUploadButton: (event) ->
event.preventDefault()
@assetWidget.openUploadDialog()
searchBarChange: (event) ->
@assetList.loadList
class CMS.Views.AssetWidget.Library.AssetItem extends Backbone.View
events:
"click .insert-asset-button": 'clickInsertAssetButton'
initialize: ->
@widget = @options.widget
clickInsertAssetButton: ->
@widget.insertAssetAndClose(@model.get('markup'))
class CMS.Views.AssetWidget.Library.AssetList extends Backbone.View
initialize: ->
@widget = @options.widget
# this is how to tie in Underscore to render the templates
_.bindAll @
@sortCol = @options.sortCol
@sortOrder = @options.sortOrder
@typeFilter = @options.typeFilter
@itemPerPage = @options.itemsPerPage
@pageNum = 1
# set up the collection data model
@collection = new LibraryCollection(@model.get('course_location'))
# set up a callback when something is added to the collection
# @collection.bind 'add', @renderItem
# make the trip to the server to populate the collection
@collection.fetch(
success: @render
)
render: ->
@collection.models.forEach(@renderItem)
return
renderItem: (item) ->
template = _.template $('#asset-library-entry').html()
html = template(item.toJSON())
$tbody = $(@el).find('tbody')
$tbody.append html
$_el = $tbody.find('[data-id="' + item.id + '"]')
new CMS.Views.AssetWidget.Library.AssetItem(
el: $_el
model: item
widget: @widget
)
return
class CMS.Views.AssetWidget.UploadForm extends Backbone.View
events:
"click .close-button" : 'closeUploadForm'
initialize: ->
@assetWidget = @options.assetWidget
closeUploadForm: (event) ->
event.preventDefault()
@assetWidget.closeUploadDialog()
...@@ -19,7 +19,8 @@ class CMS.Views.HTMLModuleEdit extends CMS.Views.ModuleEdit ...@@ -19,7 +19,8 @@ class CMS.Views.HTMLModuleEdit extends CMS.Views.ModuleEdit
@$componentItem = $('<li>').addClass('editing') @$componentItem = $('<li>').addClass('editing')
@$editor = $($('#html-editor').html()) @$editor = $($('#html-editor').html())
# initHTMLEditor is in a separate .js file # initHTMLEditor is in a separate .js file
initHTMLEditor(@$editor, html) # id here is the location_id
initHTMLEditor(@$editor, html, @model.get('course_location'))
@$editor.find('.cancel-button').bind('click', @closeEditor) @$editor.find('.cancel-button').bind('click', @closeEditor)
@$editor.find('.save-button').bind('click', @saveEditor) @$editor.find('.save-button').bind('click', @saveEditor)
......
...@@ -51,6 +51,7 @@ class CMS.Views.UnitEdit extends Backbone.View ...@@ -51,6 +51,7 @@ class CMS.Views.UnitEdit extends Backbone.View
onDelete: @deleteComponent, onDelete: @deleteComponent,
model: new CMS.Models.Module( model: new CMS.Models.Module(
id: $(element).data('id'), id: $(element).data('id'),
course_location: @model.get('course_location')
) )
) )
) )
...@@ -101,9 +102,10 @@ class CMS.Views.UnitEdit extends Backbone.View ...@@ -101,9 +102,10 @@ class CMS.Views.UnitEdit extends Backbone.View
moduleEditor = new CMS.Views.HTMLModuleEdit( moduleEditor = new CMS.Views.HTMLModuleEdit(
el: $editView, el: $editView,
onDelete: @deleteComponent, onDelete: @deleteComponent,
isNew: true,
parent: parent, parent: parent,
model: new CMS.Models.Module() model: new CMS.Models.Module(
course_location: @model.get('course_location')
)
) )
moduleEditor.enterEditMode() moduleEditor.enterEditMode()
return return
...@@ -117,7 +119,7 @@ class CMS.Views.UnitEdit extends Backbone.View ...@@ -117,7 +119,7 @@ class CMS.Views.UnitEdit extends Backbone.View
$componentActions = $($('#component-actions').html()) $componentActions = $($('#component-actions').html())
@$componentItem.append(@$editor) @$componentItem.append(@$editor)
#@$componentItem.append($preview) @$componentItem.append($preview)
@$componentItem.append($componentActions) @$componentItem.append($componentActions)
@$componentItem.hide() @$componentItem.hide()
...@@ -132,7 +134,9 @@ class CMS.Views.UnitEdit extends Backbone.View ...@@ -132,7 +134,9 @@ class CMS.Views.UnitEdit extends Backbone.View
@closeEditor() @closeEditor()
module = new CMS.Views.HTMLModuleEdit( module = new CMS.Views.HTMLModuleEdit(
onDelete: @deleteComponent onDelete: @deleteComponent
model: new CMS.Models.Module() model: new CMS.Models.Module(
@model.course_location
)
) )
@$newComponentItem.before(module.$el) @$newComponentItem.before(module.$el)
module.cloneTemplate( module.cloneTemplate(
......
...@@ -7,7 +7,7 @@ var visualEditor; ...@@ -7,7 +7,7 @@ var visualEditor;
var htmlEditor; var htmlEditor;
var htmlEditorTargetId; var htmlEditorTargetId;
function initHTMLEditor($editor, html) { function initHTMLEditor($editor, html, course_location) {
$htmlEditor = $editor; $htmlEditor = $editor;
htmlEditorTargetId = null; htmlEditorTargetId = null;
var _html = html; var _html = html;
...@@ -38,10 +38,17 @@ function initHTMLEditor($editor, html) { ...@@ -38,10 +38,17 @@ function initHTMLEditor($editor, html) {
$modalCover.unbind('click'); $modalCover.unbind('click');
$modalCover.bind('click', closeAssetWidget); $modalCover.bind('click', closeAssetWidget);
$modalCover.css('z-index', '99999'); $modalCover.css('z-index', '99999');
$('.upload-button', $assetWidget).bind('click', uploadFromWidget); // $('.upload-button', $assetWidget).bind('click', uploadFromWidget);
$('.close-button', $assetWidget).bind('click', closeAssetWidget); //$('.close-button', $assetWidget).bind('click', closeAssetWidget);
$('.insert-asset-button', $assetWidget).bind('click', { editor: ed }, insertAsset); //$('.insert-asset-button', $assetWidget).bind('click', { editor: ed }, insertAsset);
$body.append($assetWidget); $body.append($assetWidget);
$el = $body.find('.asset-library');
$moduleEditor = new CMS.Views.AssetWidget({
el: $el,
model: new Backbone.Model({course_location: course_location}),
editor: ed
});
} }
}); });
ed.addButton('studio.link', { ed.addButton('studio.link', {
......
<script type="text/template" id="discussion-editor">
<div class="discussion-editor editor">
<div class="row">
<label>Category Name:</label>
<input type="text" value="Discussion Topic" class="discussion-id-field">
</div>
<div class="row">
<a href="#" class="save-button">Save</a><a href="#" class="cancel-button">Cancel</a>
</div>
</div>
</script>
<script type="text/template" id="discussion-preview">
<div class="discussion-preview">
<div class="discussion-module" data-discussion-id="">
<a class="discussion-show control-button" href="#" data-discussion-id=""><span class="show-hide-discussion-icon"></span><span class="button-text">Show Discussion</span></a>
<a href="#" class="new-post-btn"><span class="new-post-icon"></span>New Post</a>
</div>
</div>
</script>
\ No newline at end of file
<script type="text/template" id="simple-editor-cheatsheet">
<article class="simple-editor-cheatsheet">
<div class="cheatsheet-wrapper">
<div class="row">
<h6>Multiple Choice</h6>
<div class="col sample">
<img src="/static/img/choice-example.png" />
</div>
<div class="col">
<pre><code>( ) red
( ) green
(x) blue</code></pre>
</div>
</div>
<div class="row">
<h6>Multiple Check</h6>
<div class="col sample">
<img src="/static/img/multi-example.png" />
</div>
<div class="col">
<pre><code>[ ] earth
[ ] wind
[x] water</code></pre>
</div>
</div>
<div class="row">
<h6>String Response</h6>
<div class="col sample">
<img src="/static/img/string-example.png" />
</div>
<div class="col">
<pre><code>= dog</code></pre>
</div>
</div>
<div class="row">
<h6>Numerical Response</h6>
<div class="col sample">
<img src="/static/img/number-example.png" />
</div>
<div class="col">
<pre><code>= 3.14 +- 2%</code></pre>
</div>
</div>
<div class="row">
<h6>Option Response</h6>
<div class="col sample">
<img src="/static/img/select-example.png" />
</div>
<div class="col">
<pre><code>[[wrong, (right)]]</code></pre>
</div>
</div>
</div>
</article>
</script>
<script type="text/template" id="problem-editor">
<div class="problem-editor editor">
<div class="row">
<div class="editor-bar">
<ul class="format-buttons">
<li><a href="#" class="multiple-choice-button" data-tooltip="Multiple Choice"><span class="problem-editor-icon multiple-choice"></span></a></li>
<li><a href="#" class="checks-button" data-tooltip="Check Multiple"><span class="problem-editor-icon checks"></span></a></li>
<li><a href="#" class="string-button" data-tooltip="String Response"><span class="problem-editor-icon string"></span></a></li>
<li><a href="#" class="number-button" data-tooltip="Numerical Response"><span class="problem-editor-icon number"></span></a></li>
<li><a href="#" class="dropdown-button" data-tooltip="Dropdown"><span class="problem-editor-icon dropdown"></span></a></li>
</ul>
<ul class="editor-tabs">
<li><a href="#" class="simple-tab tab current" data-tab="simple">Simple</a></li>
<li><a href="#" class="xml-tab tab" data-tab="xml">XML</a></li>
<li><a href="#" class="cheatsheet-toggle" data-tooltip="Toggle Cheatsheet">?</a></li>
</ul>
</div>
<textarea class="edit-box"></textarea>
<textarea class="xml-box"></textarea>
</div>
<div class="row">
<div class="problem-settings">
<div class="row">
<label>Show Answer:</label>
<select>
<option>attempted</option>
<option>always</option>
<option>never</option>
<option>closed</option>
</select>
</div>
<div class="row">
<label>Rerandomize:</label>
<select>
<option>always</option>
<option>never</option>
<option>per_student</option>
</select>
</div>
</div>
<a href="#" class="problem-settings-button"><span class="settings-icon"></span><span class="button-label">Show Advanced Settings</span></a>
</div>
<div class="row">
<a href="#" class="save-button">Save</a><a href="#" class="cancel-button">Cancel</a>
</div>
</div>
</script>
<script type="text/template" id="problem-preview">
<section class="xmodule_display xmodule_CapaModule" data-type="Problem">
<section id="problem_i4x-Giannattasio-313-problem-9e62f3fb1fa14ba6a671b3de73c02eab" class="problems-wrapper" data-problem-id="i4x://Giannattasio/313/problem/9e62f3fb1fa14ba6a671b3de73c02eab" data-url="/preview/modx/0/i4x://Giannattasio/313/problem/9e62f3fb1fa14ba6a671b3de73c02eab">
<section class="problem"></section>
<section class="action">
<input type="hidden" name="problem_id" value="Option Response">
<input class="check Check" type="button" value="Check">
<input class="show" type="button" value="Show Answer">
</section>
</section>
</section>
</script>
\ No newline at end of file
<script type="text/template" id="video-editor">
<div class="video-editor editor">
<div class="row">
<label>YouTube ID(s):</label>
<input type="text" value="0.75:JMD_ifUUfsU,1.0:OEoXaMPEzfM,1.25:AKqURZnYqpk,1.50:DYpADpL7jAY" class="video-id-field">
<a href="#" class="browse-button">Browse</a>
</div>
<div class="row">
<label>Transcript:</label>
<input type="text" class="transcript-field">
<a href="#" class="browse-button">Browse</a>
</div>
<div class="row">
<a href="#" class="save-button">Save</a><a href="#" class="cancel-button">Cancel</a>
</div>
</div>
</script>
<script type="text/template" id="video-preview">
<div class="video-preview">
<section class="xmodule_display xmodule_VideoModule" data-type="Video">
<div id="video_i4x-Giannattasio-313-video-39532ac1a90742bbbd70148122a8fcfa" class="video video-load-complete" data-streams="0.75:JMD_ifUUfsU,1.0:OEoXaMPEzfM,1.25:AKqURZnYqpk,1.50:DYpADpL7jAY" data-caption-data-dir="0458bf5495cf4072adde09918f6a9941" data-show-captions="true">
<div class="tc-wrapper">
<article class="video-wrapper">
<section class="video-player">
<iframe frameborder="0" allowfullscreen="" id="i4x-Giannattasio-313-video-39532ac1a90742bbbd70148122a8fcfa" title="YouTube video player" height="390" width="640" src="http://www.youtube.com/embed/OEoXaMPEzfM?controls=0&amp;wmode=transparent&amp;rel=0&amp;showinfo=0&amp;enablejsapi=1&amp;modestbranding=1&amp;origin=http%3A%2F%2Flocalhost%3A8001"></iframe>
</section>
<section class="video-controls"><div class="slider ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all"><div class="ui-slider-range ui-widget-header ui-slider-range-min" style="width: 2.586206896551724%; "></div><a class="ui-slider-handle ui-state-default ui-corner-all" href="#" style="left: 2.586206896551724%; "></a></div>
<div>
<ul class="vcr">
<li><a class="video_control play" href="#">Play</a></li>
<li>
<div class="vidtime">0:02 / 1:56</div>
</li>
</ul>
<div class="secondary-controls"><div class="speeds">
<a href="#">
<h3>Speed</h3>
<p class="active">1.0x</p>
</a>
<ol class="video_speeds"><li data-speed="1.50"><a href="#">1.50x</a></li><li data-speed="1.25"><a href="#">1.25x</a></li><li data-speed="1.0" class="active"><a href="#">1.0x</a></li><li data-speed="0.75"><a href="#">0.75x</a></li></ol>
</div><div class="volume">
<a href="#"></a>
<div class="volume-slider-container">
<div class="volume-slider ui-slider ui-slider-vertical ui-widget ui-widget-content ui-corner-all"><div class="ui-slider-range ui-widget-header ui-slider-range-min" style="height: 100%; "></div><a class="ui-slider-handle ui-state-default ui-corner-all" href="#" style="bottom: 100%; "></a></div>
</div>
</div>
<a href="#" class="add-fullscreen" oldtitle="Fill browser">Fill Browser</a>
<a href="#" class="quality_control" title="HD">HD</a><a href="#" class="hide-subtitles" oldtitle="Turn off captions">Captions</a></div>
</div></section>
</article><ol class="subtitles" style="max-height: 342px; "></ol>
</div>
</div>
</section>
</div>
</script>
\ No newline at end of file
...@@ -18,6 +18,9 @@ urlpatterns = ('', ...@@ -18,6 +18,9 @@ urlpatterns = ('',
url(r'^unpublish_unit$', 'contentstore.views.unpublish_unit', name='unpublish_unit'), url(r'^unpublish_unit$', 'contentstore.views.unpublish_unit', name='unpublish_unit'),
url(r'^create_new_course', 'contentstore.views.create_new_course', name='create_new_course'), url(r'^create_new_course', 'contentstore.views.create_new_course', name='create_new_course'),
# this is a ajax handler for a list of assets for a course
url(r'^assets/(?P<location>.*?)$', 'contentstore.views.assets', name='assets'),
url(r'^(?P<org>[^/]+)/(?P<course>[^/]+)/course/(?P<name>[^/]+)$', url(r'^(?P<org>[^/]+)/(?P<course>[^/]+)/course/(?P<name>[^/]+)$',
'contentstore.views.course_index', name='course_index'), 'contentstore.views.course_index', name='course_index'),
url(r'^(?P<org>[^/]+)/(?P<course>[^/]+)/import/(?P<name>[^/]+)$', url(r'^(?P<org>[^/]+)/(?P<course>[^/]+)/import/(?P<name>[^/]+)$',
......
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