Merge pull request #1636 from MITx/feature/brian/htmlbook
Initial implementation of htmlbook.
This commit is contained in:
@@ -356,7 +356,14 @@ class CourseDescriptor(SequenceDescriptor):
|
||||
"""
|
||||
Return the pdf_textbooks config, as a python object, or None if not specified.
|
||||
"""
|
||||
return self.metadata.get('pdf_textbooks')
|
||||
return self.metadata.get('pdf_textbooks', [])
|
||||
|
||||
@property
|
||||
def html_textbooks(self):
|
||||
"""
|
||||
Return the html_textbooks config, as a python object, or None if not specified.
|
||||
"""
|
||||
return self.metadata.get('html_textbooks', [])
|
||||
|
||||
@tabs.setter
|
||||
def tabs(self, value):
|
||||
|
||||
@@ -130,6 +130,17 @@ def _pdf_textbooks(tab, user, course, active_page):
|
||||
for index, textbook in enumerate(course.pdf_textbooks)]
|
||||
return []
|
||||
|
||||
def _html_textbooks(tab, user, course, active_page):
|
||||
"""
|
||||
Generates one tab per textbook. Only displays if user is authenticated.
|
||||
"""
|
||||
if user.is_authenticated():
|
||||
# since there can be more than one textbook, active_page is e.g. "book/0".
|
||||
return [CourseTab(textbook['tab_title'], reverse('html_book', args=[course.id, index]),
|
||||
active_page == "htmltextbook/{0}".format(index))
|
||||
for index, textbook in enumerate(course.html_textbooks)]
|
||||
return []
|
||||
|
||||
def _staff_grading(tab, user, course, active_page):
|
||||
if has_access(user, course, 'staff'):
|
||||
link = reverse('staff_grading', args=[course.id])
|
||||
@@ -209,6 +220,7 @@ VALID_TAB_TYPES = {
|
||||
'external_link': TabImpl(key_checker(['name', 'link']), _external_link),
|
||||
'textbooks': TabImpl(null_validator, _textbooks),
|
||||
'pdf_textbooks': TabImpl(null_validator, _pdf_textbooks),
|
||||
'html_textbooks': TabImpl(null_validator, _html_textbooks),
|
||||
'progress': TabImpl(need_name, _progress),
|
||||
'static_tab': TabImpl(key_checker(['name', 'url_slug']), _static_tab),
|
||||
'peer_grading': TabImpl(null_validator, _peer_grading),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from lxml import etree
|
||||
|
||||
# from django.conf import settings
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.http import Http404
|
||||
from mitxmako.shortcuts import render_to_response
|
||||
|
||||
from courseware.access import has_access
|
||||
@@ -15,6 +15,8 @@ def index(request, course_id, book_index, page=None):
|
||||
staff_access = has_access(request.user, course, 'staff')
|
||||
|
||||
book_index = int(book_index)
|
||||
if book_index < 0 or book_index >= len(course.textbooks):
|
||||
raise Http404("Invalid book index value: {0}".format(book_index))
|
||||
textbook = course.textbooks[book_index]
|
||||
table_of_contents = textbook.table_of_contents
|
||||
|
||||
@@ -40,6 +42,8 @@ def pdf_index(request, course_id, book_index, chapter=None, page=None):
|
||||
staff_access = has_access(request.user, course, 'staff')
|
||||
|
||||
book_index = int(book_index)
|
||||
if book_index < 0 or book_index >= len(course.pdf_textbooks):
|
||||
raise Http404("Invalid book index value: {0}".format(book_index))
|
||||
textbook = course.pdf_textbooks[book_index]
|
||||
|
||||
def remap_static_url(original_url, course):
|
||||
@@ -57,13 +61,49 @@ def pdf_index(request, course_id, book_index, chapter=None, page=None):
|
||||
# then remap all the chapter URLs as well, if they are provided.
|
||||
if 'chapters' in textbook:
|
||||
for entry in textbook['chapters']:
|
||||
entry['url'] = remap_static_url(entry['url'], course)
|
||||
entry['url'] = remap_static_url(entry['url'], course)
|
||||
|
||||
|
||||
return render_to_response('static_pdfbook.html',
|
||||
{'book_index': book_index,
|
||||
'course': course,
|
||||
{'book_index': book_index,
|
||||
'course': course,
|
||||
'textbook': textbook,
|
||||
'chapter': chapter,
|
||||
'page': page,
|
||||
'staff_access': staff_access})
|
||||
|
||||
@login_required
|
||||
def html_index(request, course_id, book_index, chapter=None, anchor_id=None):
|
||||
course = get_course_with_access(request.user, course_id, 'load')
|
||||
staff_access = has_access(request.user, course, 'staff')
|
||||
|
||||
book_index = int(book_index)
|
||||
if book_index < 0 or book_index >= len(course.html_textbooks):
|
||||
raise Http404("Invalid book index value: {0}".format(book_index))
|
||||
textbook = course.html_textbooks[book_index]
|
||||
|
||||
def remap_static_url(original_url, course):
|
||||
input_url = "'" + original_url + "'"
|
||||
output_url = replace_static_urls(
|
||||
input_url,
|
||||
course.metadata['data_dir'],
|
||||
course_namespace=course.location
|
||||
)
|
||||
# strip off the quotes again...
|
||||
return output_url[1:-1]
|
||||
|
||||
if 'url' in textbook:
|
||||
textbook['url'] = remap_static_url(textbook['url'], course)
|
||||
# then remap all the chapter URLs as well, if they are provided.
|
||||
if 'chapters' in textbook:
|
||||
for entry in textbook['chapters']:
|
||||
entry['url'] = remap_static_url(entry['url'], course)
|
||||
|
||||
|
||||
return render_to_response('static_htmlbook.html',
|
||||
{'book_index': book_index,
|
||||
'course': course,
|
||||
'textbook': textbook,
|
||||
'chapter': chapter,
|
||||
'anchor_id': anchor_id,
|
||||
'staff_access': staff_access})
|
||||
|
||||
@@ -158,6 +158,19 @@ div.book-wrapper {
|
||||
img {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
div {
|
||||
text-align: left;
|
||||
line-height: 1.6em;
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
|
||||
.Paragraph, h2 {
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
135
lms/templates/static_htmlbook.html
Normal file
135
lms/templates/static_htmlbook.html
Normal file
@@ -0,0 +1,135 @@
|
||||
<%inherit file="main.html" />
|
||||
<%namespace name='static' file='static_content.html'/>
|
||||
<%block name="title"><title>${course.number} Textbook</title>
|
||||
</%block>
|
||||
|
||||
<%block name="headextra">
|
||||
<%static:css group='course'/>
|
||||
<%static:js group='courseware'/>
|
||||
</%block>
|
||||
|
||||
<%block name="js_extra">
|
||||
<script type="text/javascript">
|
||||
(function($) {
|
||||
$.fn.myHTMLViewer = function(options) {
|
||||
var urlToLoad = null;
|
||||
if (options.url) {
|
||||
urlToLoad = options.url;
|
||||
}
|
||||
var chapterUrls = null;
|
||||
if (options.chapters) {
|
||||
chapterUrls = options.chapters;
|
||||
}
|
||||
var chapterToLoad = 1;
|
||||
if (options.chapterNum) {
|
||||
// TODO: this should only be specified if there are
|
||||
// chapters, and it should be in-bounds.
|
||||
chapterToLoad = options.chapterNum;
|
||||
}
|
||||
var anchorToLoad = null;
|
||||
if (options.chapters) {
|
||||
anchorToLoad = options.anchor_id;
|
||||
}
|
||||
|
||||
loadUrl = function htmlViewLoadUrl(url, anchorId) {
|
||||
// clear out previous load, if any:
|
||||
parentElement = document.getElementById('bookpage');
|
||||
while (parentElement.hasChildNodes())
|
||||
parentElement.removeChild(parentElement.lastChild);
|
||||
// load new URL in:
|
||||
$('#bookpage').load(url);
|
||||
|
||||
// if there is an anchor set, then go to that location:
|
||||
if (anchorId != null) {
|
||||
// TODO: add implementation....
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
loadChapterUrl = function htmlViewLoadChapterUrl(chapterNum, anchorId) {
|
||||
if (chapterNum < 1 || chapterNum > chapterUrls.length) {
|
||||
return;
|
||||
}
|
||||
var chapterUrl = chapterUrls[chapterNum-1];
|
||||
loadUrl(chapterUrl, anchorId);
|
||||
};
|
||||
|
||||
// define navigation links for chapters:
|
||||
if (chapterUrls != null) {
|
||||
var loadChapterUrlHelper = function(i) {
|
||||
return function(event) {
|
||||
// when opening a new chapter, always open to the top:
|
||||
loadChapterUrl(i, null);
|
||||
};
|
||||
};
|
||||
for (var index = 1; index <= chapterUrls.length; index += 1) {
|
||||
$("#htmlchapter-" + index).click(loadChapterUrlHelper(index));
|
||||
}
|
||||
}
|
||||
|
||||
// finally, load the appropriate url/page
|
||||
if (urlToLoad != null) {
|
||||
loadUrl(urlToLoad, anchorToLoad);
|
||||
} else {
|
||||
loadChapterUrl(chapterToLoad, anchorToLoad);
|
||||
}
|
||||
|
||||
}
|
||||
})(jQuery);
|
||||
|
||||
$(document).ready(function() {
|
||||
var options = {};
|
||||
%if 'url' in textbook:
|
||||
options.url = "${textbook['url']}";
|
||||
%endif
|
||||
%if 'chapters' in textbook:
|
||||
var chptrs = [];
|
||||
%for chap in textbook['chapters']:
|
||||
chptrs.push("${chap['url']}");
|
||||
%endfor
|
||||
options.chapters = chptrs;
|
||||
%endif
|
||||
%if chapter is not None:
|
||||
options.chapterNum = ${chapter};
|
||||
%endif
|
||||
%if anchor_id is not None:
|
||||
options.anchor_id = ${anchor_id};
|
||||
%endif
|
||||
|
||||
$('#outerContainer').myHTMLViewer(options);
|
||||
});
|
||||
</script>
|
||||
</%block>
|
||||
|
||||
<%include file="/courseware/course_navigation.html" args="active_page='htmltextbook/{0}'.format(book_index)" />
|
||||
|
||||
<div id="outerContainer">
|
||||
<div id="mainContainer" class="book-wrapper">
|
||||
|
||||
%if 'chapters' in textbook:
|
||||
<section aria-label="Textbook Navigation" class="book-sidebar">
|
||||
<ul id="booknav" class="treeview-booknav">
|
||||
<%def name="print_entry(entry, index_value)">
|
||||
<li id="htmlchapter-${index_value}">
|
||||
<a class="chapter">
|
||||
${entry.get('title')}
|
||||
</a>
|
||||
</li>
|
||||
</%def>
|
||||
|
||||
%for (index, entry) in enumerate(textbook['chapters']):
|
||||
${print_entry(entry, index+1)}
|
||||
% endfor
|
||||
</ul>
|
||||
</section>
|
||||
%endif
|
||||
|
||||
<section id="viewerContainer" class="book">
|
||||
<section class="page">
|
||||
<div id="bookpage" />
|
||||
</section>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -280,6 +280,15 @@ if settings.COURSEWARE_ENABLED:
|
||||
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/pdfbook/(?P<book_index>[^/]*)/chapter/(?P<chapter>[^/]*)/(?P<page>[^/]*)$',
|
||||
'staticbook.views.pdf_index'),
|
||||
|
||||
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/htmlbook/(?P<book_index>[^/]*)/$',
|
||||
'staticbook.views.html_index', name="html_book"),
|
||||
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/htmlbook/(?P<book_index>[^/]*)/chapter/(?P<chapter>[^/]*)/$',
|
||||
'staticbook.views.html_index'),
|
||||
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/htmlbook/(?P<book_index>[^/]*)/chapter/(?P<chapter>[^/]*)/(?P<anchor_id>[^/]*)/$',
|
||||
'staticbook.views.html_index'),
|
||||
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/htmlbook/(?P<book_index>[^/]*)/(?P<anchor_id>[^/]*)/$',
|
||||
'staticbook.views.html_index'),
|
||||
|
||||
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/courseware/?$',
|
||||
'courseware.views.index', name="courseware"),
|
||||
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/courseware/(?P<chapter>[^/]*)/$',
|
||||
|
||||
Reference in New Issue
Block a user