first pass at pdfbook
This commit is contained in:
@@ -352,6 +352,13 @@ class CourseDescriptor(SequenceDescriptor):
|
||||
"""
|
||||
return self.metadata.get('tabs')
|
||||
|
||||
@property
|
||||
def pdf_textbooks(self):
|
||||
"""
|
||||
Return the pdf_textbooks config, as a python object, or None if not specified.
|
||||
"""
|
||||
return self.metadata.get('pdf_textbooks')
|
||||
|
||||
@tabs.setter
|
||||
def tabs(self, value):
|
||||
self.metadata['tabs'] = value
|
||||
|
||||
@@ -119,6 +119,16 @@ def _textbooks(tab, user, course, active_page):
|
||||
for index, textbook in enumerate(course.textbooks)]
|
||||
return []
|
||||
|
||||
def _pdf_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('pdf_book', args=[course.id, index]),
|
||||
active_page == "pdftextbook/{0}".format(index))
|
||||
for index, textbook in enumerate(course.pdf_textbooks)]
|
||||
return []
|
||||
|
||||
def _staff_grading(tab, user, course, active_page):
|
||||
if has_access(user, course, 'staff'):
|
||||
@@ -198,6 +208,7 @@ VALID_TAB_TYPES = {
|
||||
'discussion': TabImpl(need_name, _discussion),
|
||||
'external_link': TabImpl(key_checker(['name', 'link']), _external_link),
|
||||
'textbooks': TabImpl(null_validator, _textbooks),
|
||||
'pdf_textbooks': TabImpl(null_validator, _pdf_textbooks),
|
||||
'progress': TabImpl(need_name, _progress),
|
||||
'static_tab': TabImpl(key_checker(['name', 'url_slug']), _static_tab),
|
||||
'peer_grading': TabImpl(null_validator, _peer_grading),
|
||||
|
||||
@@ -30,3 +30,23 @@ def index(request, course_id, book_index, page=None):
|
||||
|
||||
def index_shifted(request, course_id, page):
|
||||
return index(request, course_id=course_id, page=int(page) + 24)
|
||||
|
||||
|
||||
@login_required
|
||||
def pdf_index(request, course_id, book_index, chapter=None, page=None):
|
||||
course = get_course_with_access(request.user, course_id, 'load')
|
||||
staff_access = has_access(request.user, course, 'staff')
|
||||
|
||||
book_index = int(book_index)
|
||||
textbook = course.pdf_textbooks[book_index]
|
||||
|
||||
# if page is None:
|
||||
# page = textbook.start_page
|
||||
|
||||
return render_to_response('static_pdfbook.html',
|
||||
{'book_index': book_index,
|
||||
'course': course,
|
||||
'textbook': textbook,
|
||||
'page': page,
|
||||
'chapter': chapter,
|
||||
'staff_access': staff_access})
|
||||
|
||||
111
lms/templates/static_pdfbook.html
Normal file
111
lms/templates/static_pdfbook.html
Normal file
@@ -0,0 +1,111 @@
|
||||
<%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">
|
||||
|
||||
<!-- Use latest PDF.js build from Github -->
|
||||
<script type="text/javascript" src="https://raw.github.com/mozilla/pdf.js/gh-pages/build/pdf.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
//
|
||||
// NOTE:
|
||||
// Modifying the URL below to another server will likely *NOT* work. Because of browser
|
||||
// security restrictions, we have to use a file server with special headers
|
||||
// (CORS) - most servers don't support cross-origin browser requests.
|
||||
//
|
||||
var url = "${textbook['url']}";
|
||||
|
||||
//
|
||||
// Disable workers to avoid yet another cross-origin issue (workers need the URL of
|
||||
// the script to be loaded, and currently do not allow cross-origin scripts)
|
||||
//
|
||||
PDFJS.disableWorker = true;
|
||||
|
||||
var pdfDoc = null,
|
||||
pageNum = 1,
|
||||
scale = 0.8,
|
||||
canvas = document.getElementById('the-canvas'),
|
||||
ctx = canvas.getContext('2d');
|
||||
|
||||
//
|
||||
// Get page info from document, resize canvas accordingly, and render page
|
||||
//
|
||||
function renderPage(num) {
|
||||
// Using promise to fetch the page
|
||||
pdfDoc.getPage(num).then(function(page) {
|
||||
var viewport = page.getViewport(scale);
|
||||
canvas.height = viewport.height;
|
||||
canvas.width = viewport.width;
|
||||
|
||||
// Render PDF page into canvas context
|
||||
var renderContext = {
|
||||
canvasContext: ctx,
|
||||
viewport: viewport
|
||||
};
|
||||
page.render(renderContext);
|
||||
});
|
||||
|
||||
// Update page counters
|
||||
document.getElementById('page_num').textContent = pageNum;
|
||||
document.getElementById('page_count').textContent = pdfDoc.numPages;
|
||||
}
|
||||
|
||||
//
|
||||
// Go to previous page
|
||||
//
|
||||
function goPrevious() {
|
||||
if (pageNum <= 1)
|
||||
return;
|
||||
pageNum--;
|
||||
renderPage(pageNum);
|
||||
}
|
||||
|
||||
//
|
||||
// Go to next page
|
||||
//
|
||||
function goNext() {
|
||||
if (pageNum >= pdfDoc.numPages)
|
||||
return;
|
||||
pageNum++;
|
||||
renderPage(pageNum);
|
||||
}
|
||||
|
||||
//
|
||||
// Asynchronously download PDF as an ArrayBuffer
|
||||
//
|
||||
PDFJS.getDocument(url).then(function getPdfHelloWorld(_pdfDoc) {
|
||||
pdfDoc = _pdfDoc;
|
||||
renderPage(pageNum);
|
||||
});
|
||||
</script>
|
||||
</%block>
|
||||
|
||||
<!-- TODO: what should active_page be set to here???? textbook/0 or pdftextbook/0? -->
|
||||
<%include file="/courseware/course_navigation.html" args="active_page='textbook/{0}'.format(book_index)" />
|
||||
|
||||
<section class="container">
|
||||
<div class="book-wrapper">
|
||||
|
||||
<section class="book">
|
||||
<section class="page">
|
||||
<div>
|
||||
<button id="prev" onclick="goPrevious()">Previous</button>
|
||||
<button id="next" onclick="goNext()">Next</button>
|
||||
|
||||
<span>Page: <span id="page_num"></span> / <span id="page_count"></span></span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<canvas id="the-canvas" style="border:1px solid black"></canvas>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
12
lms/urls.py
12
lms/urls.py
@@ -233,6 +233,17 @@ if settings.COURSEWARE_ENABLED:
|
||||
'staticbook.views.index'),
|
||||
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/book-shifted/(?P<page>[^/]*)$',
|
||||
'staticbook.views.index_shifted'),
|
||||
|
||||
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/pdfbook/(?P<book_index>[^/]*)/$',
|
||||
'staticbook.views.pdf_index', name="pdf_book"),
|
||||
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/pdfbook/(?P<book_index>[^/]*)/(?P<page>[^/]*)$',
|
||||
'staticbook.views.pdf_index'),
|
||||
|
||||
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/pdfbook/(?P<book_index>[^/]*)/chapter/(?P<chapter>[^/]*)/$',
|
||||
'staticbook.views.pdf_index'),
|
||||
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>[^/]+/[^/]+/[^/]+)/courseware/?$',
|
||||
'courseware.views.index', name="courseware"),
|
||||
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/courseware/(?P<chapter>[^/]*)/$',
|
||||
@@ -241,6 +252,7 @@ if settings.COURSEWARE_ENABLED:
|
||||
'courseware.views.index', name="courseware_section"),
|
||||
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/courseware/(?P<chapter>[^/]*)/(?P<section>[^/]*)/(?P<position>[^/]*)/?$',
|
||||
'courseware.views.index', name="courseware_position"),
|
||||
|
||||
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/progress$',
|
||||
'courseware.views.progress', name="progress"),
|
||||
# Takes optional student_id for instructor use--shows profile as that student sees it.
|
||||
|
||||
Reference in New Issue
Block a user