Live editing of html modules implemented, but no saving to the backend
This commit is contained in:
@@ -10,6 +10,7 @@ import os.path
|
||||
from StringIO import StringIO
|
||||
from mako.template import Template
|
||||
from mako.lookup import TemplateLookup
|
||||
from collections import defaultdict
|
||||
|
||||
from django.core.management.base import BaseCommand
|
||||
from keystore.django import keystore
|
||||
@@ -42,9 +43,7 @@ class Command(BaseCommand):
|
||||
# Simple lists
|
||||
'chapter': 'Week',
|
||||
'course': 'Course',
|
||||
'sequential': 'LectureSequence',
|
||||
'vertical': 'ProblemSet',
|
||||
'section': {
|
||||
'section': defaultdict(lambda: 'Section', {
|
||||
'Lab': 'Lab',
|
||||
'Lecture Sequence': 'LectureSequence',
|
||||
'Homework': 'Homework',
|
||||
@@ -52,8 +51,13 @@ class Command(BaseCommand):
|
||||
'Video': 'VideoSegment',
|
||||
'Midterm': 'Exam',
|
||||
'Final': 'Exam',
|
||||
None: 'Section',
|
||||
},
|
||||
'Problems': 'ProblemSet',
|
||||
}),
|
||||
'videosequence': 'VideoSequence',
|
||||
'problemset': 'ProblemSet',
|
||||
'vertical': 'Section',
|
||||
'sequential': 'Section',
|
||||
'tab': 'Section',
|
||||
# True types
|
||||
'video': 'VideoSegment',
|
||||
'html': 'HTML',
|
||||
@@ -78,6 +82,8 @@ class Command(BaseCommand):
|
||||
e.set('url', 'i4x://mit.edu/6002xs12/{category}/{name}'.format(
|
||||
category=category,
|
||||
name=name))
|
||||
else:
|
||||
print "Skipping element with tag", e.tag
|
||||
|
||||
|
||||
def handle_skip(e):
|
||||
@@ -150,6 +156,9 @@ class Command(BaseCommand):
|
||||
'sequential': handle_list,
|
||||
'vertical': handle_list,
|
||||
'section': handle_list,
|
||||
'videosequence': handle_list,
|
||||
'problemset': handle_list,
|
||||
'tab': handle_list,
|
||||
# True types
|
||||
'video': handle_video,
|
||||
'html': handle_html,
|
||||
|
||||
@@ -17,4 +17,4 @@ def index(request):
|
||||
def edit_item(request):
|
||||
item_id = request.GET['id']
|
||||
item = keystore().get_item(item_id)
|
||||
return HttpResponse("<section>Problem content</section>")
|
||||
return HttpResponse(item.get_html())
|
||||
|
||||
@@ -21,6 +21,7 @@ Longer TODO:
|
||||
|
||||
import sys
|
||||
import tempfile
|
||||
import os.path
|
||||
from path import path
|
||||
|
||||
############################ FEATURE CONFIGURATION #############################
|
||||
@@ -154,11 +155,30 @@ PIPELINE_CSS = {
|
||||
|
||||
PIPELINE_ALWAYS_RECOMPILE = ['sass/base-style.scss']
|
||||
|
||||
from x_module import XModuleDescriptor
|
||||
js_file_dir = tempfile.mkdtemp('js', dir=PROJECT_ROOT / "static")
|
||||
module_js_sources = []
|
||||
for xmodule in XModuleDescriptor.load_classes():
|
||||
js = xmodule.get_javascript()
|
||||
for filetype in ('coffee', 'js'):
|
||||
for idx, fragment in enumerate(js.get(filetype, [])):
|
||||
path = os.path.join(js_file_dir, "{name}.{idx}.{type}".format(
|
||||
name=xmodule.__name__,
|
||||
idx=idx,
|
||||
type=filetype))
|
||||
with open(path, 'w') as js_file:
|
||||
js_file.write(fragment)
|
||||
module_js_sources.append(path.replace(PROJECT_ROOT / "static/", ""))
|
||||
|
||||
PIPELINE_JS = {
|
||||
'main': {
|
||||
'source_filenames': ['coffee/main.coffee'],
|
||||
'output_filename': 'js/main.js',
|
||||
},
|
||||
'module-js': {
|
||||
'source_filenames': module_js_sources,
|
||||
'output_filename': 'js/modules.js',
|
||||
}
|
||||
}
|
||||
|
||||
PIPELINE_COMPILERS = [
|
||||
|
||||
1
cms/static/.gitignore
vendored
Normal file
1
cms/static/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
tmp*js
|
||||
@@ -1,3 +1,17 @@
|
||||
bind_edit_links = ->
|
||||
$('a.module-edit').click ->
|
||||
edit_item($(this).attr('id'))
|
||||
return false
|
||||
|
||||
edit_item = (id) ->
|
||||
$.get('/edit_item', {id: id}, (data) ->
|
||||
$('#module-html').empty().append(data)
|
||||
bind_edit_links()
|
||||
$('section.edit-pane').show()
|
||||
$('body').addClass('content')
|
||||
window['construct_html']('module-html')
|
||||
)
|
||||
|
||||
$ ->
|
||||
$('section.main-content').children().hide()
|
||||
$('.editable').inlineEdit()
|
||||
@@ -20,12 +34,6 @@ $ ->
|
||||
$(this).parent().parent().hide()
|
||||
return false
|
||||
|
||||
edit_item = (id) ->
|
||||
$.get('/edit_item', {id: id}, (data) ->
|
||||
$('section.edit-pane').empty().append(data)
|
||||
$('section.edit-pane').show()
|
||||
$('body').addClass('content')
|
||||
)
|
||||
|
||||
setHeight = ->
|
||||
windowHeight = $(this).height()
|
||||
@@ -55,10 +63,6 @@ $ ->
|
||||
$(document).ready(setHeight)
|
||||
$(window).bind('resize', setHeight)
|
||||
|
||||
$('a.module-edit').click ->
|
||||
edit_item($(this).attr('id'))
|
||||
return false
|
||||
|
||||
$('.video-new a').click ->
|
||||
$('section.edit-pane').show()
|
||||
return false
|
||||
@@ -66,3 +70,6 @@ $ ->
|
||||
$('.problem-new a').click ->
|
||||
$('section.edit-pane').show()
|
||||
return false
|
||||
|
||||
bind_edit_links()
|
||||
|
||||
|
||||
4
cms/static/js/jquery.min.js
vendored
Normal file
4
cms/static/js/jquery.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -23,7 +23,7 @@
|
||||
|
||||
<%block name="content"></%block>
|
||||
|
||||
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="${ STATIC_URL}/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="${ STATIC_URL }/js/markitup/jquery.markitup.js"></script>
|
||||
<script type="text/javascript" src="${ STATIC_URL }/js/markitup/sets/wiki/set.js"></script>
|
||||
% if settings.MITX_FEATURES['USE_DJANGO_PIPELINE']:
|
||||
@@ -31,6 +31,8 @@
|
||||
% else:
|
||||
<script src="${ STATIC_URL }/js/main.js"></script>
|
||||
% endif
|
||||
|
||||
<%static:js group='module-js'/>
|
||||
<script src="${ STATIC_URL }/js/jquery.inlineedit.js"></script>
|
||||
<script src="${ STATIC_URL }/js/jquery.leanModal.min.js"></script>
|
||||
<script src="${ STATIC_URL }/js/jquery.tablednd.js"></script>
|
||||
|
||||
@@ -7,14 +7,9 @@
|
||||
<%include file="widgets/navigation.html"/>
|
||||
|
||||
<section class="main-content">
|
||||
<%include file="widgets/week-edit.html"/>
|
||||
<%include file="widgets/week-new.html"/>
|
||||
<%include file="widgets/sequnce-edit.html"/>
|
||||
<%include file="widgets/video-edit.html"/>
|
||||
<%include file="widgets/video-new.html"/>
|
||||
<%include file="widgets/problem-edit.html"/>
|
||||
<%include file="widgets/problem-new.html"/>
|
||||
<section class="edit-pane"/>
|
||||
<section class="edit-pane">
|
||||
<div id="module-html"/>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
</section>
|
||||
|
||||
18
cms/templates/widgets/html-edit.html
Normal file
18
cms/templates/widgets/html-edit.html
Normal file
@@ -0,0 +1,18 @@
|
||||
<%namespace name='static' file='../static_content.html'/>
|
||||
|
||||
<section class="html-edit">
|
||||
<header>
|
||||
<a href="#" class="cancel">Cancel</a>
|
||||
<a href="#" class="save-update">Save & Update</a>
|
||||
</header>
|
||||
|
||||
<section>
|
||||
<header>
|
||||
<h1 class="editable">${module.name}</h1>
|
||||
<section>
|
||||
<textarea name="" id="edit-box" rows="8" cols="40">${module.definition['data']['text']}</textarea>
|
||||
<div id="edit-preview" class="preview">${module.definition['data']['text']}</div>
|
||||
</section>
|
||||
<a href="" class="save-update">Save & Update</a>
|
||||
</section>
|
||||
</section>
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
<section>
|
||||
<header>
|
||||
<h1 class="editable">New Problem</h1>
|
||||
<h1 class="editable">${module.name}</h1>
|
||||
<section class="author">
|
||||
<div>
|
||||
<h2>Last modified:</h2>
|
||||
|
||||
@@ -1,16 +1,8 @@
|
||||
<section class="sequence-edit">
|
||||
<header>
|
||||
<div class="week">
|
||||
<h2><a href="">Week 1</a></h2>
|
||||
<ul>
|
||||
<li>
|
||||
<p class="editable"><strong>Goal title:</strong> This is the goal body and is where the goal will be further explained</p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h1 class="editable">Lecture sequence</h1>
|
||||
<p><strong>Group type:</strong> Ordered Sequence</p>
|
||||
<h1 class="editable">${module.name}</h1>
|
||||
<p><strong>Module Type:</strong>${module.type}</p>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@@ -51,72 +43,12 @@
|
||||
<ol>
|
||||
<li>
|
||||
<ol>
|
||||
% for child in module.get_children():
|
||||
<li>
|
||||
<a href="" class="problem-edit">Problem title 11</a>
|
||||
<a href="#" class="draggable">handle</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="sequence-edit">Problem Group</a>
|
||||
<a href="#" class="draggable">handle</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="problem-edit">Problem title 14</a>
|
||||
<a href="#" class="draggable">handle</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="video-edit">Video 3</a>
|
||||
<a href="#" class="draggable">handle</a>
|
||||
</li>
|
||||
<li class="group">
|
||||
<header>
|
||||
<h3>
|
||||
<a href="#" class="problem-edit">Problem group</a>
|
||||
<a href="#" class="draggable">handle</a>
|
||||
</h3>
|
||||
</header>
|
||||
<ol>
|
||||
<li>
|
||||
<a href="#" class="problem-edit">Problem title 11</a>
|
||||
<a href="#" class="draggable">handle</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="problem-edit">Problem title 11</a>
|
||||
<a href="#" class="draggable">handle</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="problem-edit">Problem title 11</a>
|
||||
<a href="#" class="draggable">handle</a>
|
||||
</li>
|
||||
</ol>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="problem-edit">Problem title 13</a>
|
||||
<a href="#" class="draggable">handle</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="problem-edit">Problem title 14</a>
|
||||
<a href="#" class="draggable">handle</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="video-edit">Video 3</a>
|
||||
<a href="#" class="draggable">handle</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="" class="problem-edit">Problem title 11</a>
|
||||
<a href="#" class="draggable">handle</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="sequence-edit">Problem Group</a>
|
||||
<a href="#" class="draggable">handle</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="problem-edit">Problem title 14</a>
|
||||
<a href="#" class="draggable">handle</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="video-edit">Video 3</a>
|
||||
<a href="#" class="module-edit" id="${child.url}">${child.name}</a>
|
||||
<a href="#" class="draggable">handle</a>
|
||||
</li>
|
||||
%endfor
|
||||
</ol>
|
||||
</li>
|
||||
|
||||
@@ -10,7 +10,8 @@ import StringIO
|
||||
from datetime import timedelta
|
||||
from lxml import etree
|
||||
|
||||
from x_module import XModule, XModuleDescriptor
|
||||
from x_module import XModule
|
||||
from mako_module import MakoModuleDescriptor
|
||||
from progress import Progress
|
||||
from capa.capa_problem import LoncapaProblem
|
||||
from capa.responsetypes import StudentInputError
|
||||
@@ -63,8 +64,14 @@ class ComplexEncoder(json.JSONEncoder):
|
||||
return json.JSONEncoder.default(self, obj)
|
||||
|
||||
|
||||
class ModuleDescriptor(XModuleDescriptor):
|
||||
pass
|
||||
class CapaModuleDescriptor(MakoModuleDescriptor):
|
||||
"""
|
||||
Module implementing problems in the LON-CAPA format,
|
||||
as implemented by capa.capa_problem
|
||||
"""
|
||||
|
||||
mako_template = 'widgets/problem-edit.html'
|
||||
|
||||
|
||||
|
||||
class Module(XModule):
|
||||
|
||||
@@ -1,14 +1,28 @@
|
||||
import json
|
||||
import logging
|
||||
|
||||
from x_module import XModule, XModuleDescriptor
|
||||
from x_module import XModule
|
||||
from mako_module import MakoModuleDescriptor
|
||||
from lxml import etree
|
||||
|
||||
log = logging.getLogger("mitx.courseware")
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
class ModuleDescriptor(XModuleDescriptor):
|
||||
pass
|
||||
class HtmlModuleDescriptor(MakoModuleDescriptor):
|
||||
"""
|
||||
Module for putting raw html in a course
|
||||
"""
|
||||
mako_template = "widgets/html-edit.html"
|
||||
|
||||
# TODO (cpennington): Make this into a proper module
|
||||
js = {'coffee': ["""
|
||||
window.construct_html = (id) ->
|
||||
$('#' + id + " #edit-box").on('input', ->
|
||||
$('#' + id + ' #edit-preview').empty().append($(this).val())
|
||||
)
|
||||
"""]}
|
||||
|
||||
|
||||
class Module(XModule):
|
||||
id_attribute = 'filename'
|
||||
|
||||
18
common/lib/xmodule/mako_module.py
Normal file
18
common/lib/xmodule/mako_module.py
Normal file
@@ -0,0 +1,18 @@
|
||||
from x_module import XModuleDescriptor
|
||||
from mitxmako.shortcuts import render_to_string
|
||||
|
||||
|
||||
class MakoModuleDescriptor(XModuleDescriptor):
|
||||
"""
|
||||
Module descriptor intended as a mixin that uses a mako template
|
||||
to specify the module html.
|
||||
|
||||
Expects the descriptor to have the `mako_template` attribute set
|
||||
with the name of the template to render, and it will pass
|
||||
the descriptor as the `module` parameter to that template
|
||||
"""
|
||||
|
||||
def get_html(self):
|
||||
return render_to_string(self.mako_template, {
|
||||
'module': self
|
||||
})
|
||||
@@ -3,7 +3,8 @@ import logging
|
||||
|
||||
from lxml import etree
|
||||
|
||||
from x_module import XModule, XModuleDescriptor
|
||||
from x_module import XModule
|
||||
from mako_module import MakoModuleDescriptor
|
||||
from xmodule.progress import Progress
|
||||
|
||||
log = logging.getLogger("mitx.common.lib.seq_module")
|
||||
@@ -12,9 +13,6 @@ log = logging.getLogger("mitx.common.lib.seq_module")
|
||||
# OBSOLETE: This obsoletes 'type'
|
||||
class_priority = ['video', 'problem']
|
||||
|
||||
class ModuleDescriptor(XModuleDescriptor):
|
||||
pass
|
||||
|
||||
class Module(XModule):
|
||||
''' Layout module which lays out content in a temporal sequence
|
||||
'''
|
||||
@@ -117,5 +115,5 @@ class Module(XModule):
|
||||
self.rendered = False
|
||||
|
||||
|
||||
class SectionDescriptor(XModuleDescriptor):
|
||||
pass
|
||||
class SectionDescriptor(MakoModuleDescriptor):
|
||||
mako_template = 'widgets/sequence-edit.html'
|
||||
|
||||
@@ -19,6 +19,9 @@ setup(
|
||||
"TutorialIndex = seq_module:SectionDescriptor",
|
||||
"Exam = seq_module:SectionDescriptor",
|
||||
"VideoSegment = video_module:VideoSegmentDescriptor",
|
||||
"ProblemSet = seq_module:SectionDescriptor",
|
||||
"Problem = capa_module:CapaModuleDescriptor",
|
||||
"HTML = html_module:HtmlModuleDescriptor",
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
@@ -3,7 +3,6 @@ import pkg_resources
|
||||
import logging
|
||||
|
||||
from keystore import Location
|
||||
from progress import Progress
|
||||
|
||||
log = logging.getLogger('mitx.' + __name__)
|
||||
|
||||
@@ -30,6 +29,12 @@ class Plugin(object):
|
||||
|
||||
return classes[0].load()
|
||||
|
||||
@classmethod
|
||||
def load_classes(cls):
|
||||
return [class_.load()
|
||||
for class_
|
||||
in pkg_resources.iter_entry_points(cls.entry_point)]
|
||||
|
||||
|
||||
class XModule(object):
|
||||
''' Implements a generic learning module.
|
||||
@@ -154,6 +159,7 @@ class XModuleDescriptor(Plugin):
|
||||
and can generate XModules (which do know about student state).
|
||||
"""
|
||||
entry_point = "xmodule.v1"
|
||||
js = {}
|
||||
|
||||
@staticmethod
|
||||
def load_from_json(json_data, system):
|
||||
@@ -178,6 +184,19 @@ class XModuleDescriptor(Plugin):
|
||||
"""
|
||||
return cls(system=system, **json_data)
|
||||
|
||||
@classmethod
|
||||
def get_javascript(cls):
|
||||
"""
|
||||
Return a dictionary containing some of the following keys:
|
||||
coffee: A list of coffeescript fragments that should be compiled and
|
||||
placed on the page
|
||||
js: A list of javascript fragments that should be included on the page
|
||||
|
||||
All of these will be loaded onto the page in the CMS
|
||||
"""
|
||||
return cls.js
|
||||
|
||||
|
||||
def __init__(self,
|
||||
system,
|
||||
definition=None,
|
||||
@@ -221,21 +240,27 @@ class XModuleDescriptor(Plugin):
|
||||
else:
|
||||
return [child for child in self._child_instances if child.type in categories]
|
||||
|
||||
def get_html(self):
|
||||
"""
|
||||
Return the html used to edit this module
|
||||
"""
|
||||
raise NotImplementedError("get_html() must be provided by specific modules")
|
||||
|
||||
def get_xml(self):
|
||||
''' For conversions between JSON and legacy XML representations.
|
||||
'''
|
||||
if self.xml:
|
||||
if self.xml:
|
||||
return self.xml
|
||||
else:
|
||||
else:
|
||||
raise NotImplementedError("JSON->XML Translation not implemented")
|
||||
|
||||
def get_json(self):
|
||||
''' For conversions between JSON and legacy XML representations.
|
||||
'''
|
||||
if self.json:
|
||||
if self.json:
|
||||
raise NotImplementedError
|
||||
return self.json # TODO: Return context as well -- files, etc.
|
||||
else:
|
||||
return self.json # TODO: Return context as well -- files, etc.
|
||||
else:
|
||||
raise NotImplementedError("XML->JSON Translation not implemented")
|
||||
|
||||
#def handle_cms_json(self):
|
||||
|
||||
Reference in New Issue
Block a user