Skip to content
Snippets Groups Projects
Commit 69290c18 authored by Calen Pennington's avatar Calen Pennington
Browse files

Live editing of html modules implemented, but no saving to the backend

parent 1b7b7e91
No related branches found
No related tags found
No related merge requests found
Showing
with 170 additions and 117 deletions
...@@ -10,6 +10,7 @@ import os.path ...@@ -10,6 +10,7 @@ import os.path
from StringIO import StringIO from StringIO import StringIO
from mako.template import Template from mako.template import Template
from mako.lookup import TemplateLookup from mako.lookup import TemplateLookup
from collections import defaultdict
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from keystore.django import keystore from keystore.django import keystore
...@@ -42,9 +43,7 @@ class Command(BaseCommand): ...@@ -42,9 +43,7 @@ class Command(BaseCommand):
# Simple lists # Simple lists
'chapter': 'Week', 'chapter': 'Week',
'course': 'Course', 'course': 'Course',
'sequential': 'LectureSequence', 'section': defaultdict(lambda: 'Section', {
'vertical': 'ProblemSet',
'section': {
'Lab': 'Lab', 'Lab': 'Lab',
'Lecture Sequence': 'LectureSequence', 'Lecture Sequence': 'LectureSequence',
'Homework': 'Homework', 'Homework': 'Homework',
...@@ -52,8 +51,13 @@ class Command(BaseCommand): ...@@ -52,8 +51,13 @@ class Command(BaseCommand):
'Video': 'VideoSegment', 'Video': 'VideoSegment',
'Midterm': 'Exam', 'Midterm': 'Exam',
'Final': 'Exam', 'Final': 'Exam',
None: 'Section', 'Problems': 'ProblemSet',
}, }),
'videosequence': 'VideoSequence',
'problemset': 'ProblemSet',
'vertical': 'Section',
'sequential': 'Section',
'tab': 'Section',
# True types # True types
'video': 'VideoSegment', 'video': 'VideoSegment',
'html': 'HTML', 'html': 'HTML',
...@@ -78,6 +82,8 @@ class Command(BaseCommand): ...@@ -78,6 +82,8 @@ class Command(BaseCommand):
e.set('url', 'i4x://mit.edu/6002xs12/{category}/{name}'.format( e.set('url', 'i4x://mit.edu/6002xs12/{category}/{name}'.format(
category=category, category=category,
name=name)) name=name))
else:
print "Skipping element with tag", e.tag
def handle_skip(e): def handle_skip(e):
...@@ -150,6 +156,9 @@ class Command(BaseCommand): ...@@ -150,6 +156,9 @@ class Command(BaseCommand):
'sequential': handle_list, 'sequential': handle_list,
'vertical': handle_list, 'vertical': handle_list,
'section': handle_list, 'section': handle_list,
'videosequence': handle_list,
'problemset': handle_list,
'tab': handle_list,
# True types # True types
'video': handle_video, 'video': handle_video,
'html': handle_html, 'html': handle_html,
......
...@@ -17,4 +17,4 @@ def index(request): ...@@ -17,4 +17,4 @@ def index(request):
def edit_item(request): def edit_item(request):
item_id = request.GET['id'] item_id = request.GET['id']
item = keystore().get_item(item_id) item = keystore().get_item(item_id)
return HttpResponse("<section>Problem content</section>") return HttpResponse(item.get_html())
...@@ -21,6 +21,7 @@ Longer TODO: ...@@ -21,6 +21,7 @@ Longer TODO:
import sys import sys
import tempfile import tempfile
import os.path
from path import path from path import path
############################ FEATURE CONFIGURATION ############################# ############################ FEATURE CONFIGURATION #############################
...@@ -154,11 +155,30 @@ PIPELINE_CSS = { ...@@ -154,11 +155,30 @@ PIPELINE_CSS = {
PIPELINE_ALWAYS_RECOMPILE = ['sass/base-style.scss'] 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 = { PIPELINE_JS = {
'main': { 'main': {
'source_filenames': ['coffee/main.coffee'], 'source_filenames': ['coffee/main.coffee'],
'output_filename': 'js/main.js', 'output_filename': 'js/main.js',
}, },
'module-js': {
'source_filenames': module_js_sources,
'output_filename': 'js/modules.js',
}
} }
PIPELINE_COMPILERS = [ PIPELINE_COMPILERS = [
......
tmp*js
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() $('section.main-content').children().hide()
$('.editable').inlineEdit() $('.editable').inlineEdit()
...@@ -20,12 +34,6 @@ $ -> ...@@ -20,12 +34,6 @@ $ ->
$(this).parent().parent().hide() $(this).parent().parent().hide()
return false 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 = -> setHeight = ->
windowHeight = $(this).height() windowHeight = $(this).height()
...@@ -55,10 +63,6 @@ $ -> ...@@ -55,10 +63,6 @@ $ ->
$(document).ready(setHeight) $(document).ready(setHeight)
$(window).bind('resize', setHeight) $(window).bind('resize', setHeight)
$('a.module-edit').click ->
edit_item($(this).attr('id'))
return false
$('.video-new a').click -> $('.video-new a').click ->
$('section.edit-pane').show() $('section.edit-pane').show()
return false return false
...@@ -66,3 +70,6 @@ $ -> ...@@ -66,3 +70,6 @@ $ ->
$('.problem-new a').click -> $('.problem-new a').click ->
$('section.edit-pane').show() $('section.edit-pane').show()
return false return false
bind_edit_links()
This diff is collapsed.
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
<%block name="content"></%block> <%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/jquery.markitup.js"></script>
<script type="text/javascript" src="${ STATIC_URL }/js/markitup/sets/wiki/set.js"></script> <script type="text/javascript" src="${ STATIC_URL }/js/markitup/sets/wiki/set.js"></script>
% if settings.MITX_FEATURES['USE_DJANGO_PIPELINE']: % if settings.MITX_FEATURES['USE_DJANGO_PIPELINE']:
...@@ -31,6 +31,8 @@ ...@@ -31,6 +31,8 @@
% else: % else:
<script src="${ STATIC_URL }/js/main.js"></script> <script src="${ STATIC_URL }/js/main.js"></script>
% endif % endif
<%static:js group='module-js'/>
<script src="${ STATIC_URL }/js/jquery.inlineedit.js"></script> <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.leanModal.min.js"></script>
<script src="${ STATIC_URL }/js/jquery.tablednd.js"></script> <script src="${ STATIC_URL }/js/jquery.tablednd.js"></script>
......
...@@ -7,14 +7,9 @@ ...@@ -7,14 +7,9 @@
<%include file="widgets/navigation.html"/> <%include file="widgets/navigation.html"/>
<section class="main-content"> <section class="main-content">
<%include file="widgets/week-edit.html"/> <section class="edit-pane">
<%include file="widgets/week-new.html"/> <div id="module-html"/>
<%include file="widgets/sequnce-edit.html"/> </section>
<%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> </section>
</section> </section>
......
<%namespace name='static' file='../static_content.html'/>
<section class="html-edit">
<header>
<a href="#" class="cancel">Cancel</a>
<a href="#" class="save-update">Save &amp; 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 &amp; Update</a>
</section>
</section>
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
<section> <section>
<header> <header>
<h1 class="editable">New Problem</h1> <h1 class="editable">${module.name}</h1>
<section class="author"> <section class="author">
<div> <div>
<h2>Last modified:</h2> <h2>Last modified:</h2>
......
<section class="sequence-edit"> <section class="sequence-edit">
<header> <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> <div>
<h1 class="editable">Lecture sequence</h1> <h1 class="editable">${module.name}</h1>
<p><strong>Group type:</strong> Ordered Sequence</p> <p><strong>Module Type:</strong>${module.type}</p>
</div> </div>
</header> </header>
...@@ -51,72 +43,12 @@ ...@@ -51,72 +43,12 @@
<ol> <ol>
<li> <li>
<ol> <ol>
% for child in module.get_children():
<li> <li>
<a href="" class="problem-edit">Problem title 11</a> <a href="#" class="module-edit" id="${child.url}">${child.name}</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="draggable">handle</a> <a href="#" class="draggable">handle</a>
</li> </li>
%endfor
</ol> </ol>
</li> </li>
......
...@@ -10,7 +10,8 @@ import StringIO ...@@ -10,7 +10,8 @@ import StringIO
from datetime import timedelta from datetime import timedelta
from lxml import etree 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 progress import Progress
from capa.capa_problem import LoncapaProblem from capa.capa_problem import LoncapaProblem
from capa.responsetypes import StudentInputError from capa.responsetypes import StudentInputError
...@@ -63,8 +64,14 @@ class ComplexEncoder(json.JSONEncoder): ...@@ -63,8 +64,14 @@ class ComplexEncoder(json.JSONEncoder):
return json.JSONEncoder.default(self, obj) return json.JSONEncoder.default(self, obj)
class ModuleDescriptor(XModuleDescriptor): class CapaModuleDescriptor(MakoModuleDescriptor):
pass """
Module implementing problems in the LON-CAPA format,
as implemented by capa.capa_problem
"""
mako_template = 'widgets/problem-edit.html'
class Module(XModule): class Module(XModule):
......
import json import json
import logging import logging
from x_module import XModule, XModuleDescriptor from x_module import XModule
from mako_module import MakoModuleDescriptor
from lxml import etree from lxml import etree
log = logging.getLogger("mitx.courseware") log = logging.getLogger("mitx.courseware")
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
class ModuleDescriptor(XModuleDescriptor): class HtmlModuleDescriptor(MakoModuleDescriptor):
pass """
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): class Module(XModule):
id_attribute = 'filename' id_attribute = 'filename'
......
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 ...@@ -3,7 +3,8 @@ import logging
from lxml import etree 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 from xmodule.progress import Progress
log = logging.getLogger("mitx.common.lib.seq_module") log = logging.getLogger("mitx.common.lib.seq_module")
...@@ -12,9 +13,6 @@ log = logging.getLogger("mitx.common.lib.seq_module") ...@@ -12,9 +13,6 @@ log = logging.getLogger("mitx.common.lib.seq_module")
# OBSOLETE: This obsoletes 'type' # OBSOLETE: This obsoletes 'type'
class_priority = ['video', 'problem'] class_priority = ['video', 'problem']
class ModuleDescriptor(XModuleDescriptor):
pass
class Module(XModule): class Module(XModule):
''' Layout module which lays out content in a temporal sequence ''' Layout module which lays out content in a temporal sequence
''' '''
...@@ -117,5 +115,5 @@ class Module(XModule): ...@@ -117,5 +115,5 @@ class Module(XModule):
self.rendered = False self.rendered = False
class SectionDescriptor(XModuleDescriptor): class SectionDescriptor(MakoModuleDescriptor):
pass mako_template = 'widgets/sequence-edit.html'
...@@ -19,6 +19,9 @@ setup( ...@@ -19,6 +19,9 @@ setup(
"TutorialIndex = seq_module:SectionDescriptor", "TutorialIndex = seq_module:SectionDescriptor",
"Exam = seq_module:SectionDescriptor", "Exam = seq_module:SectionDescriptor",
"VideoSegment = video_module:VideoSegmentDescriptor", "VideoSegment = video_module:VideoSegmentDescriptor",
"ProblemSet = seq_module:SectionDescriptor",
"Problem = capa_module:CapaModuleDescriptor",
"HTML = html_module:HtmlModuleDescriptor",
] ]
} }
) )
...@@ -3,7 +3,6 @@ import pkg_resources ...@@ -3,7 +3,6 @@ import pkg_resources
import logging import logging
from keystore import Location from keystore import Location
from progress import Progress
log = logging.getLogger('mitx.' + __name__) log = logging.getLogger('mitx.' + __name__)
...@@ -30,6 +29,12 @@ class Plugin(object): ...@@ -30,6 +29,12 @@ class Plugin(object):
return classes[0].load() 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): class XModule(object):
''' Implements a generic learning module. ''' Implements a generic learning module.
...@@ -154,6 +159,7 @@ class XModuleDescriptor(Plugin): ...@@ -154,6 +159,7 @@ class XModuleDescriptor(Plugin):
and can generate XModules (which do know about student state). and can generate XModules (which do know about student state).
""" """
entry_point = "xmodule.v1" entry_point = "xmodule.v1"
js = {}
@staticmethod @staticmethod
def load_from_json(json_data, system): def load_from_json(json_data, system):
...@@ -178,6 +184,19 @@ class XModuleDescriptor(Plugin): ...@@ -178,6 +184,19 @@ class XModuleDescriptor(Plugin):
""" """
return cls(system=system, **json_data) 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, def __init__(self,
system, system,
definition=None, definition=None,
...@@ -221,21 +240,27 @@ class XModuleDescriptor(Plugin): ...@@ -221,21 +240,27 @@ class XModuleDescriptor(Plugin):
else: else:
return [child for child in self._child_instances if child.type in categories] 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): def get_xml(self):
''' For conversions between JSON and legacy XML representations. ''' For conversions between JSON and legacy XML representations.
''' '''
if self.xml: if self.xml:
return self.xml return self.xml
else: else:
raise NotImplementedError("JSON->XML Translation not implemented") raise NotImplementedError("JSON->XML Translation not implemented")
def get_json(self): def get_json(self):
''' For conversions between JSON and legacy XML representations. ''' For conversions between JSON and legacy XML representations.
''' '''
if self.json: if self.json:
raise NotImplementedError raise NotImplementedError
return self.json # TODO: Return context as well -- files, etc. return self.json # TODO: Return context as well -- files, etc.
else: else:
raise NotImplementedError("XML->JSON Translation not implemented") raise NotImplementedError("XML->JSON Translation not implemented")
#def handle_cms_json(self): #def handle_cms_json(self):
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment