# ===========================================================================
# eXe
# Copyright 2004-2006, University of Auckland
# Copyright 2004-2008 eXe Project, http://eXeLearning.org/
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# ===========================================================================
"""
Classes to XHTML elements. Used by GenericBlock
"""
import os
import logging
import re
import urllib
from exe.webui import common
from exe.engine.path import Path
from exe import globals as G
log = logging.getLogger(__name__)
def replaceLinks(matchobj, package_name):
""" replace external links with calls to user's preferred browser """
anchor = matchobj.group(0)
do = re.search(r'(?i)href\s*=\s*"?([^>"]+)"?', anchor)
# only modify links to external targets
if do \
and do.group(1).find('http://') >=0 \
and not do.group(1).find('http://127.0.0.1') >= 0:
return re.sub(r'(?i)href\s*=\s*"?([^>"]+)"?',
r'''href="\1" onclick="window.parent.browseURL('\1'); return false"''',
anchor)
elif do \
and do.group(1).startswith('resources/'):
clean_url = urllib.quote(package_name.encode('utf-8'))
return re.sub(r'(?i)href\s*=\s*"?([^>"]+)"?',
r'''href="\1" onclick="window.parent.browseURL('http://127.0.0.1:%d/%s/\1'); return false"''' % (G.application.config.port, clean_url),
anchor)
else:
return anchor
# ===========================================================================
class Element(object):
"""
Base class for a XHTML element. Used by GenericBlock
"""
def __init__(self, field):
"""
Initialize
"""
self.field = field
self.id = field.id
def process(self, request):
"""
Process arguments from the web server.
"""
msg = x_(u"ERROR Element.process called directly with %s class")
log.error(msg % self.__class__.__name__)
return _(msg) % self.__class__.__name__
def renderEdit(self):
"""
Returns an XHTML string for editing this element
"""
msg = _(u"ERROR Element.renderEdit called directly with %s class")
log.error(msg % self.__class__.__name__)
return _(msg) % self.__class__.__name__
def renderPreview(self):
"""
Returns an XHTML string for previewing this element
(Defaults to calling renderView.)
"""
return self.renderView()
def renderView(self):
"""
Returns an XHTML string for viewing this element
"""
msg = x_(u"ERROR Element.renderView called directly with %s class")
log.error(msg % self.__class__.__name__)
return _(msg) % self.__class__.__name__
# ===========================================================================
class ElementWithResources(Element):
"""
Another Base class for a XHTML element.
Used by TextAreaElement, FeedbackElement, and ClozeElement,
to handle all processing of the multiple images (and any other resources)
which can now be included by the tinyMCE RichTextArea.
NOTE: while this was originally where all the of embedding took place,
it has since been moved into FieldWithResources, and this is now a rather
empty class, but still remains in case more processing to occur here later.
"""
def __init__(self, field):
"""
Initialize
"""
Element.__init__(self, field)
# hold onto the field's idevice for easy future reference:
self.field_idevice = field.idevice
# ===========================================================================
class TextElement(Element):
"""
TextElement is a single line of text
"""
def __init__(self, field):
"""
Initialize
"""
Element.__init__(self, field)
def process(self, request):
"""
Process arguments from the web server.
"""
is_cancel = common.requestHasCancel(request)
if self.id in request.args \
and not is_cancel:
self.field.content = request.args[self.id][0]
def renderEdit(self):
"""
Returns an XHTML string with the form element for editing this field
"""
# package not needed for the TextElement, only for rich-text fields:
this_package = None
html = common.formField('textInput', this_package, self.field.name, '',
self.id, '', self.field.content)
return html
def renderView(self):
"""
Returns an XHTML string for viewing or previewing this element
"""
return self.field.content
# ===========================================================================
class TextAreaElement(ElementWithResources):
"""
TextAreaElement is responsible for a block of text
"""
def __init__(self, field):
"""
Initialize
"""
ElementWithResources.__init__(self, field)
self.width = "100%"
if (hasattr(field.idevice, 'class_') and
field.idevice.class_ in \
("activity", "objectives", "preknowledge")):
self.height = 250
else:
self.height = 100
def process(self, request):
"""
Process arguments from the web server.
"""
is_cancel = common.requestHasCancel(request)
if is_cancel:
self.field.idevice.edit = False
# but double-check for first-edits, and ensure proper attributes:
if not hasattr(self.field, 'content_w_resourcePaths'):
self.field.content_w_resourcePaths = ""
if not hasattr(self.field, 'content_wo_resourcePaths'):
self.field.content_wo_resourcePaths = ""
self.field.content = self.field.content_wo_resourcePaths
return
if self.id in request.args:
# process any new images and other resources courtesy of tinyMCE:
self.field.content_w_resourcePaths \
= self.field.ProcessPreviewed(request.args[self.id][0])
# likewise determining the paths for exports, etc.:
self.field.content_wo_resourcePaths \
= self.field.MassageContentForRenderView( \
self.field.content_w_resourcePaths)
# and begin by choosing the content for preview mode, WITH paths:
self.field.content = self.field.content_w_resourcePaths
def renderEdit(self):
"""
Returns an XHTML string with the form element for editing this field
"""
# to render, choose the content with the preview-able resource paths:
self.field.content = self.field.content_w_resourcePaths
log.debug("renderEdit content="+self.field.content+
", height="+unicode(self.height))
this_package = None
if self.field_idevice is not None \
and self.field_idevice.parentNode is not None:
this_package = self.field_idevice.parentNode.package
html = common.formField('richTextArea', this_package,
self.field.name,'',
self.id, self.field.instruc,
self.field.content,
str(self.width), str(self.height))
return html
def renderPreview(self, visible=True, class_="block"):
"""
Returns an XHTML string for previewing this element
"""
# to render, choose the content with the preview-able resource paths:
self.field.content = self.field.content_w_resourcePaths
content = re.sub(r'(?i)<\s*a[^>]+>',
lambda mo: replaceLinks(mo, self.field.idevice.parentNode.package.name),
self.field.content)
return self.renderView(content=content, visible=visible, \
class_=class_, preview=True)
def renderView(self, visible=True, class_="block", content=None,
preview=False):
"""
Returns an XHTML string for viewing or previewing this element
"""
if visible:
visible = 'style="display:block"'
else:
visible = 'style="display:none"'
if content is None:
if preview:
# render the resource content with resource paths:
if not hasattr(self.field, 'content_w_resourcePaths'):
# safety measure, in case not yet set. could set to "" or:
self.field.content_w_resourcePaths = self.field.content
self.field.content = self.field.content_w_resourcePaths
else:
# render with the flattened content, withOUT resource paths:
if not hasattr(self.field, 'content_wo_resourcePaths'):
# safety measure, in case not yet set. could set to "" or:
self.field.content_wo_resourcePaths = self.field.content
self.field.content = self.field.content_wo_resourcePaths
content = self.field.content
return '
%s
' % (
self.id, class_, visible, content)
# ===========================================================================
class FeedbackElement(ElementWithResources):
"""
FeedbackElement is a text which can be show and hide
"""
def __init__(self, field):
"""
Initialize
"""
ElementWithResources.__init__(self, field)
def process(self, request):
"""
Process arguments from the web server.
"""
is_cancel = common.requestHasCancel(request)
if is_cancel:
self.field.idevice.edit = False
# but double-check for first-edits, and ensure proper attributes:
if not hasattr(self.field, 'content_w_resourcePaths'):
self.field.content_w_resourcePaths = ""
if not hasattr(self.field, 'content_wo_resourcePaths'):
self.field.content_wo_resourcePaths = ""
self.field.content = self.field.content_wo_resourcePaths
return
if self.id in request.args:
# process any new images and other resources courtesy of tinyMCE:
self.field.content_w_resourcePaths = \
self.field.ProcessPreviewed(request.args[self.id][0])
# likewise determining the paths for exports, etc.:
self.field.content_wo_resourcePaths = \
self.field.MassageContentForRenderView( \
self.field.content_w_resourcePaths)
# and begin by choosing the content for preview mode, WITH paths:
self.field.feedback = self.field.content_w_resourcePaths
def renderEdit(self):
"""
Returns an XHTML string with the form element for editing this field
"""
# to render, choose the content with the preview-able resource paths:
self.field.feedback = self.field.content_w_resourcePaths
this_package = None
if self.field_idevice is not None \
and self.field_idevice.parentNode is not None:
this_package = self.field_idevice.parentNode.package
html = common.formField('richTextArea', this_package,
self.field.name,'',
self.id, self.field.instruc,
self.field.feedback)
return html
def renderView(self):
"""
Returns an XHTML string for viewing this question element
"""
return self.doRender(preview=False)
def renderPreview(self):
"""
Returns an XHTML string for previewing this question element
"""
return self.doRender(preview=True)
def doRender(self, preview=False):
"""
Returns an XHTML string for viewing or previewing this element
"""
if preview:
# to render, use the content with the preview-able resource paths:
self.field.feedback = self.field.content_w_resourcePaths
else:
# to render, use the flattened content, withOUT resource paths:
self.field.feedback = self.field.content_wo_resourcePaths
html = ""
if self.field.feedback != "":
html += '
\n '
html += common.feedbackButton('btn' + self.id,
self.field.buttonCaption,
onclick="toggleFeedback('%s')" % self.id)
html += '
\n '
html += '
'\
% self.id
html += self.field.feedback
html += "
\n"
return html
# ===========================================================================
class PlainTextAreaElement(Element):
"""
PlainTextAreaElement is responsible for a block of text
"""
def __init__(self, field):
"""
Initialize
"""
Element.__init__(self, field)
self.cols = "80"
self.rows = "10"
def process(self, request):
"""
Process arguments from the web server.
"""
if self.id in request.args:
self.field.content = request.args[self.id][0]
def renderEdit(self):
"""
Returns an XHTML string with the form element for editing this field
"""
log.debug("renderEdit content="+self.field.content+
", height="+unicode(self.height))
# package not needed for PlainTextArea, only for rich-text fields:
this_package = None
html = common.formField('textArea', this_package, self.field.name,'',
self.id, self.field.instruc,
self.field.content, '',
self.cols, self.rows)
return html
def renderPreview(self):
content = re.sub(r'(?i)<\s*a[^>]+>',
lambda mo: replaceLinks(mo, self.field.idevice.parentNode.package.name),
self.field.content)
return self.renderView(content=content)
def renderView(self, visible=True, class_="block", content=None):
"""
Returns an XHTML string for viewing or previewing this element
"""
if content is None:
content = self.field.content
return content + ' '
# ===========================================================================
class ImageElement(Element):
"""
for image element processing
"""
def __init__(self, field):
"""
Initialize
"""
Element.__init__(self, field)
def process(self, request):
"""
Process arguments from the web server.
"""
is_cancel = common.requestHasCancel(request)
if "path"+self.id in request.args \
and not is_cancel:
self.field.setImage(request.args["path"+self.id][0])
if "width"+self.id in request.args \
and not is_cancel:
self.field.width = request.args["width"+self.id][0]
if "height"+self.id in request.args \
and not is_cancel:
self.field.height = request.args["height"+self.id][0]
if "action" in request.args and request.args["action"][0]=="addImage" and \
request.args["object"][0]==self.id:
self.field.idevice.edit = True
self.field.idevice.undo = False
def renderEdit(self):
"""
Returns an XHTML string with the form element for editing this field
"""
log.debug("renderEdit")
if not self.field.imageResource:
self.field.setDefaultImage()
function = ""
if hasattr(self.field, 'isFeedback') and self.field.isFeedback:
function = "addFeedbackImage"
else:
function = "addImage"
html = u'
'
html += u''+self.field.name+':\n'
html += common.elementInstruc(self.field.instruc)
html += u"
\n"
html += u'
'
html += u'\n"
html += u"
"
html += u'\n'
html += u'
'
html += common.textInput("path"+self.id, "", 50)
html += u'' % _(u"Select an image")
if self.field.imageResource and not self.field.isDefaultImage:
html += '
'+ self.field.imageResource.storageName + '
'
html += u'
%s
\n' % _(u"Display as:")
html += u"px "
html += u"by \n"
html += u"px \n"
html += u"(%s) \n" % _(u"blank for original size")
html += u"
"
return html
def renderPreview(self):
"""
Returns an XHTML string for previewing this image
"""
if not self.field.imageResource:
self.field.setDefaultImage()
html = common.image("img"+self.id,
"resources/%s" % (self.field.imageResource.storageName),
self.field.width,
self.field.height)
return html
def renderView(self):
"""
Returns an XHTML string for viewing this image
"""
if not self.field.imageResource:
self.field.setDefaultImage()
html = common.image("img"+self.id,
self.field.imageResource.storageName,
self.field.width,
self.field.height)
return html
# ===========================================================================
class MultimediaElement(Element):
"""
for media element processing
"""
def __init__(self, field):
"""
Initialize
"""
Element.__init__(self, field)
def process(self, request):
"""
Process arguments from the web server.
"""
if "path"+self.id in request.args:
self.field.setMedia(request.args["path"+self.id][0])
if "caption" + self.id in request.args:
self.field.caption = request.args["caption"+self.id][0]
def renderEdit(self):
"""
Returns an XHTML string with the form element for editing this field
"""
log.debug("renderEdit")
html = ""
html += u''+self.field.name+':\n'
html += common.elementInstruc(self.field.instruc)+' '
html += common.textInput("path"+self.id, "", 50)
html += u'' % _(u"Select an MP3")
if self.field.mediaResource:
html += '
'+ self.field.mediaResource.storageName + '
'
html += ' %s ' % _(u"Caption:")
html += common.textInput("caption" + self.id, self.field.caption)
html += common.elementInstruc(self.field.captionInstruc)+ ' '
return html
def renderPreview(self):
"""
Returns an XHTML string for previewing this image
"""
html = ""
if self.field.mediaResource:
html = self.renderMP3(
'../%s/resources/%s' % (
self.field.idevice.parentNode.package.name,
self.field.mediaResource.storageName),
"../templates/xspf_player.swf")
return html
def renderView(self):
"""
Returns an XHTML string for viewing this image
"""
html = ""
if self.field.mediaResource:
html += self.renderMP3(self.field.mediaResource.storageName,
"xspf_player.swf")
return html
def renderMP3(self, filename, mp3player):
path = Path(filename)
fileExtension =path.ext.lower()
mp3Str_mat = common.flash(filename, self.field.width, self.field.height,
id="mp3player",
params = {
'movie': '%s?song_url=%s&song_title=%s' % (mp3player, filename, self.field.caption),
'quality': 'high',
'bgcolor': '#E6E6E6'})
mp3Str = """
""" % {'mp3player': mp3player,
'url': filename,
'caption': self.field.caption}
wmvStr = common.flash(filename, self.field.width, self.field.height,
id="mp3player",
params = {
'Filename': filename,
'ShowControls': 'true',
'AutoRewind': 'true',
'AutoStart': 'false',
'AutoSize': 'true',
'EnableContextMenu': 'true',
'TransparentAtStart': 'false',
'AnimationAtStart': 'false',
'ShowGotoBar': 'false',
'EnableFullScreenControls': 'true'})
wmvStr = """
""" %(filename, filename)
aviStr = """
""" % (filename, filename)
mpgStr = """
""" % (filename, filename)
wavStr = r"""
""" % (self.id, filename, self.id)
movStr = """
""" %(filename, filename)
mpgStr = """
"""
if fileExtension == ".mp3":
return mp3Str
elif fileExtension == ".wav":
return wavStr
elif fileExtension == ".wmv" or fileExtension == ".wma":
return wmvStr
elif fileExtension == ".mov":
return movStr
#elif fileExtension == ".mpg":
#return mpgStr
elif fileExtension == ".avi":
return aviStr
else:
return ""
#============================================================================
class AttachmentElement(Element):
"""
for attachment element processing
"""
def __init__(self, field):
"""
Initialize
"""
Element.__init__(self, field)
def process(self, request):
"""
Process arguments from the web server.
"""
if "path"+self.id in request.args:
self.field.setAttachment(request.args["path"+self.id][0])
def renderEdit(self):
"""
Returns an XHTML string with the form element for editing this field
"""
log.debug("renderEdit")
html = ""
label = _(u'Filename:')
if self.field.attachResource:
label += u': '
label += u''
label += self.field.attachResource.storageName
label += u'\n'
html += '%s' % label
html += common.elementInstruc(self.field.instruc)+' '
html += common.textInput("path"+self.id, "", 50)
html += u' \n' % _(u"Select a file")
return html
def renderPreview(self):
"""
Returns an XHTML string for previewing this image
"""
html = ""
if self.field.attachResource:
html += u" "
html += self.field.attachResource.storageName
html += u"\n"
return html
def renderView(self):
"""
Returns an XHTML string for previewing this image
"""
html = ""
if self.field.attachResource:
html += u" "
html += u""
html += self.field.attachResource.storageName
html += u" \n"
return html
#================================================================================
class MagnifierElement(Element):
"""
for magnifier element processing
"""
def __init__(self, field):
"""
Initialize
"""
Element.__init__(self, field)
def process(self, request):
"""
Process arguments from the web server.
"""
is_cancel = common.requestHasCancel(request)
self.field.message = ""
if "path"+self.id in request.args:
path = request.args["path"+self.id][0]
if path.lower().endswith(".jpg") or path.lower().endswith(".jpeg"):
self.field.setImage(request.args["path"+self.id][0])
elif path <> "":
self.field.message = _(u"Please select a .jpg file.")
self.field.idevice.edit = True
# disabling Undo once an image has been added:
self.field.idevice.undo = False
if "width"+self.id in request.args \
and not is_cancel:
self.field.width = request.args["width"+self.id][0]
if "height"+self.id in request.args \
and not is_cancel:
self.field.height = request.args["height"+self.id][0]
if "action" in request.args and request.args["action"][0]=="addJpgImage" and \
request.args["object"][0]==self.id:
# disabling Undo once an image has been added:
self.field.idevice.undo = False
self.field.idevice.edit = True
def renderEdit(self):
"""
Returns an XHTML string with the form element for editing this field
"""
log.debug("renderEdit")
if not self.field.imageResource:
self.field.setDefaultImage()
html = u'
\n'
html += u""+self.field.name+":\n"
html += common.elementInstruc(self.field.instruc)
html += u"
\n"
html += u'
\n'
html += u'\n"
html += u"
\n"
html += u'\n'
html += u'
\n'
html += common.textInput("path"+self.id, "", 50)
html += u'' % _(u"Select an image (JPG file)")
if self.field.imageResource and not self.field.isDefaultImage:
html += '
'+ self.field.imageResource.storageName + '
'
if self.field.message <> "":
html += '' + self.field.message + ''
html += u'
%s' % _(u"Display as:")
html += common.elementInstruc(self.field.idevice.dimensionInstruc)
html += u'
\n'
html += u' pixels by '
html += u' pixels.\n'
html += u'\n'
html += u'(%s) \n' % _(u'blank for original size')
html += u'
\n'
return html
def renderPreview(self):
"""
Returns an XHTML string for previewing this image
"""
if not self.field.imageResource:
self.field.setDefaultImage()
html = self.renderMagnifier(
'../%s/resources/%s' % (
self.field.idevice.parentNode.package.name,
self.field.imageResource.storageName),
"../templates/magnifier.swf")
return html
def renderView(self):
"""
Returns an XHTML string for viewing this image
"""
if not self.field.imageResource:
self.field.setDefaultImage()
html = self.renderMagnifier(self.field.imageResource.storageName,
"magnifier.swf")
return html
def renderMagnifier(self, imageFile, magnifierFile):
"""
Renders the magnifier flash thingie
"""
field = self.field
flashVars = {
'file': imageFile,
'width': field.width,
'height': field.height,
'borderWidth': '12',
'glassSize': field.glassSize,
'initialZoomSize': field.initialZSize,
'maxZoomSize': field.maxZSize,
'targetColor': '#FF0000'}
# Format the flash vars
flashVars = '&'.join(
['%s=%s' % (name, value) for
name, value in flashVars.items()])
return common.flash(magnifierFile, field.width, field.height,
id='magnifier%s' % self.id,
params = {
'movie': magnifierFile,
'quality': 'high',
'scale': 'noscale',
'salign': 'lt',
'bgcolor': '#888888',
'FlashVars': flashVars})
#============================================================================
class ClozeElement(ElementWithResources):
"""
Allows the user to enter a passage of text and declare that some words
should be added later by the student
"""
# Properties
@property
def editorId(self):
"""
Returns the id string for our midas editor
"""
return 'editorArea%s' % self.id
@property
def editorJs(self):
"""
Returns the js that gets the editor document
"""
return "document.getElementById('%s').contentWindow.document" % \
self.editorId
@property
def hiddenFieldJs(self):
"""
Returns the js that gets the hiddenField document
"""
return "document.getElementById('cloze%s')" % self.id
# Public Methods
def process(self, request):
"""
Sets the encodedContent of our field
"""
is_cancel = common.requestHasCancel(request)
if is_cancel:
self.field.idevice.edit = False
# but double-check for first-edits, and ensure proper attributes:
if not hasattr(self.field, 'content_w_resourcePaths'):
self.field.content_w_resourcePaths = ""
self.field.idevice.edit = True
if not hasattr(self.field, 'content_wo_resourcePaths'):
self.field.content_wo_resourcePaths = ""
self.field.idevice.edit = True
return
if self.editorId in request.args:
# process any new images and other resources courtesy of tinyMCE:
self.field.content_w_resourcePaths = \
self.field.ProcessPreviewed(request.args[self.editorId][0])
# likewise determining the paths for exports, etc.:
self.field.content_wo_resourcePaths = \
self.field.MassageContentForRenderView(\
self.field.content_w_resourcePaths)
# and begin by choosing the content for preview mode, WITH paths:
self.field.encodedContent = self.field.content_w_resourcePaths
self.field.strictMarking = \
'strictMarking%s' % self.id in request.args
self.field.checkCaps = \
'checkCaps%s' % self.id in request.args
self.field.instantMarking = \
'instantMarking%s' % self.id in request.args
def renderEdit(self):
"""
Enables the user to set up their passage of text
"""
# to render, choose the content with the preview-able resource paths:
self.field.encodedContent = self.field.content_w_resourcePaths
this_package = None
if self.field_idevice is not None \
and self.field_idevice.parentNode is not None:
this_package = self.field_idevice.parentNode.package
html = [
# Render the iframe box
common.formField('richTextArea', this_package, _('Cloze Text'),'',
self.editorId, self.field.instruc,
self.field.encodedContent),
# Render our toolbar
u'
',
]
return '\n '.join(html)
def renderPreview(self, feedbackId=None, preview=True):
"""
Just a front-end wrapper around renderView..
"""
# set up the content for preview mode:
preview = True
return self.renderView(feedbackId, preview)
def renderView(self, feedbackId=None, preview=False):
"""
Shows the text with inputs for the missing parts
"""
if preview:
# to render, use the content with the preview-able resource paths:
self.field.encodedContent = self.field.content_w_resourcePaths
else:
# to render, use the flattened content, withOUT resource paths:
self.field.encodedContent = self.field.content_wo_resourcePaths
html = ['
' % self.id]
# Store our args in some hidden fields
def storeValue(name):
value = str(bool(getattr(self.field, name))).lower()
return common.hiddenField('clozeFlag%s.%s' % (self.id, name), value)
html.append(storeValue('strictMarking'))
html.append(storeValue('checkCaps'))
html.append(storeValue('instantMarking'))
if feedbackId:
html.append(common.hiddenField('clozeVar%s.feedbackId' % self.id,
'ta'+feedbackId))
# Mix the parts together
words = ""
def encrypt(word):
"""
Simple XOR encryptions
"""
result = ''
key = 'X'
for letter in word:
result += unichr(ord(key) ^ ord(letter))
key = letter
# Encode for javascript
output = ''
for char in result:
output += '%%u%04x' % ord(char[0])
return output.encode('base64')
for i, (text, missingWord) in enumerate(self.field.parts):
if text:
html.append(text)
if missingWord:
words += "'" + missingWord + "',"
# The edit box for the user to type into
inputHtml = [
' \n' % len(missingWord)]
if self.field.instantMarking:
inputHtml.insert(2, ' onKeyUp="onClozeChange(this)"')
html += inputHtml
# Hidden span with correct answer
html += [
'%s' % (
self.id, i, encrypt(missingWord))]
# Score string
html += ['
\n']
if self.field.instantMarking:
html += ['\n' % (self.id)]
if feedbackId is not None:
html += [common.feedbackButton('feedback'+self.id,
_(u"Show/Hide Feedback"),
style="margin: 0;",
onclick="toggleClozeFeedback('%s')" % self.id)]
# Set the show/hide answers button attributes
style = 'display: inline;'
value = _(u"Show/Clear Answers")
onclick = "toggleClozeAnswers('%s')" % self.id
else:
html += [common.button('submit%s' % self.id,
_(u"Submit"),
id='submit%s' % self.id,
onclick="clozeSubmit('%s')" % self.id),
common.button(
'restart%s' % self.id,
_(u"Restart"),
id='restart%s' % self.id,
style="display: none;",
onclick="clozeRestart('%s')" % self.id),
]
# Set the show/hide answers button attributes
style = 'display: none;'
value = _(u"Show Answers")
onclick = "fillClozeInputs('%s')" % self.id
# Show/hide answers button
html += [' ',
common.button(
'%sshowAnswersButton' % self.id,
value,
id='showAnswersButton%s' % self.id,
style=style,
onclick=onclick),
]
html += ['' % self.id]
html += ['
\n']
return '\n'.join(html) + '
'
def renderText(self):
"""
Shows the text with gaps for text file export
"""
html = ""
for text, missingWord in self.field.parts:
if text:
html += text
if missingWord:
for x in missingWord:
html += "_"
return html
def renderAnswers(self):
"""
Shows the answers for text file export
"""
html = ""
html += "
%s:
" % _(u"Answsers")
answers = ""
for i, (text, missingWord) in enumerate(self.field.parts):
if missingWord:
answers += str(i+1) + '.' + missingWord + ' '
if answers <> "":
html += answers +'
'
else:
html = ""
return html
# ===========================================================================
class FlashElement(Element):
"""
for flash element processing
"""
def __init__(self, field):
"""
Initialize
"""
Element.__init__(self, field)
def process(self, request):
"""
Process arguments from the web server.
"""
if "path"+self.id in request.args:
self.field.setFlash(request.args["path"+self.id][0])
if "width"+self.id in request.args:
self.field.width = request.args["width"+self.id][0]
if "height"+self.id in request.args:
self.field.height = request.args["height"+self.id][0]
def renderEdit(self):
"""
Returns an XHTML string with the form element for editing this field
"""
log.debug("renderEdit")
html = u'
'
html += u""+self.field.name+":\n"
html += common.elementInstruc(self.field.instruc)
html += u"
\n"
html += common.textInput("path"+self.id, "", 50)
html += u'' % _(u"Select Flash Object")
html += common.elementInstruc(self.field.fileInstruc)
if self.field.flashResource:
html += '
'+ self.field.flashResource.storageName + '
'
html += u'
%s
\n' % _(u"Display as:")
html += u"px\n"
html += u"by \n"
html += u"px\n"
return html
def renderPreview(self):
"""
Returns an XHTML string for previewing this image
"""
if self.field.flashResource:
flashFile = 'resources/' + self.field.flashResource.storageName
else:
flashFile = ""
html = common.flash(flashFile, self.field.width, self.field.height)
return html
def renderView(self):
"""
Returns an XHTML string for viewing this flash
"""
if self.field.flashResource:
flashFile = self.field.flashResource.storageName
else:
flashFile = ""
html = common.flash(flashFile, self.field.width, self.field.height)
return html
# ===========================================================================
class FlashMovieElement(Element):
"""
for flash movie element processing
"""
def __init__(self, field):
"""
Initialize
"""
Element.__init__(self, field)
def process(self, request):
"""
Process arguments from the web server.
"""
self.field.message = ""
if "path"+self.id in request.args:
path = request.args["path"+self.id][0]
if path.endswith(".flv"):
self.field.setFlash(request.args["path"+self.id][0])
elif path <> "":
self.field.message = _(u"Please select a .flv file.")
self.field.idevice.edit = True
def renderEdit(self):
"""
Returns an XHTML string with the form element for editing this field
"""
log.debug("renderEdit")
html = u'
'
html += u""+self.field.name+":"
html += common.elementInstruc(self.field.fileInstruc)
html += u"
"
html += common.textInput("path"+self.id, "", 50)
html += u'\n' % _(u"Select a flash video")
if self.field.flashResource:
html += '
'+ self.field.flashResource.storageName + '
'
if self.field.message <> "":
html += '' + self.field.message + ''
return html
def renderPreview(self):
"""
Returns an XHTML string for previewing this image
"""
if self.field.flashResource:
flashFile = 'resources/%s' % (
self.field.flashResource.storageName)
else:
flashFile = ""
return common.flashMovie(flashFile, self.field.width,
self.field.height, '../templates/',
autoplay='false')
def renderView(self):
"""
Returns an XHTML string for viewing this flash
"""
if self.field.flashResource:
flashFile = self.field.flashResource.storageName
else:
flashFile = ""
html = common.flashMovie(flashFile,
self.field.width,
self.field.height,
autoplay='true')
return html
# ===========================================================================
class MathElement(Element):
"""
MathElement is a single line of text
"""
def __init__(self, field):
"""
Initialize
"""
Element.__init__(self, field)
def process(self, request):
"""
Process arguments from the web server.
"""
if 'fontsize'+self.id in request.args:
self.field.fontsize = int(request.args["fontsize"+self.id][0])
if 'preview'+self.id in request.args:
self.field.idevice.edit = True
if 'input'+self.id in request.args and \
not(request.args[u"action"][0] == u"delete" and
request.args[u"object"][0]==self.field.idevice.id):
self.field.latex = request.args['input'+self.id][0]
def renderEdit(self):
"""
Returns an XHTML string with the form element for editing this field
"""
webDir = G.application.config.webDir
greekDir = Path(webDir+'/images/maths/greek letters')
oprationDir = Path(webDir+'/images/maths/binary oprations')
relationDir = Path(webDir+'/images/maths/relations')
html = u'
'
html += u""+self.field.name+":\n"
html += common.elementInstruc(self.field.instruc)
html += u"
\n"
# Latex input
html += '
\n'
for file in greekDir.files():
if file.ext == ".gif" or file.ext == ".png":
symbol = file.namebase
html += common.insertSymbol("input"+self.id,
u"/images/maths/greek letters/%s",
"%s", r"\\%s") % (symbol, symbol,
file.basename())
html += u" "
for file in oprationDir.files():
if file.ext == ".gif" or file.ext == ".png":
symbol = file.namebase
html += common.insertSymbol("input"+self.id,
u"/images/maths/binary oprations/%s",
"%s", r"\\%s") % (symbol, symbol,
file.basename())
html += u" "
for file in relationDir.files():
if file.ext == ".gif" or file.ext == ".png":
symbol = file.namebase
html += common.insertSymbol("input"+self.id,
u"/images/maths/relations/%s",
"%s", r"\\%s") % (symbol, symbol,
file.basename())
html += " "
html += common.insertSymbol("input"+self.id, "", "",
r"\\begin{verbatim}\\end{verbatim}", _("text"), 14)
html += common.insertSymbol("input"+self.id, "", "", r"\\\\\n", _("newline"))
# font size select
html += ' '
html += _("Select a font size: ")
html += "\n"
html += "
\n"
html += common.textArea('input'+self.id, self.field.latex)
# Preview
html += '
\n'
html += common.submitButton('preview'+self.id, _('Preview'))
html += common.elementInstruc(self.field.previewInstruc) + ' '
if self.field.gifResource:
html += '
'
html += '
' % (self.field.gifResource.storageName)
html += "
\n"
else:
html += ' '
return html
def renderPreview(self):
"""
Returns an XHTML string for viewing or previewing this element
"""
html = ""
if self.field.gifResource:
html += '
\n'
html += '
'
html += '
' % (self.field.gifResource.storageName)
html += '
\n'
return html
def renderView(self):
"""
Returns an XHTML string for viewing or previewing this element
"""
html = ""
if self.field.gifResource:
html += '
\n'
html += '
'
html += '
' % (self.field.gifResource.storageName)
html += "
\n"
return html
# ===========================================================================
class SelectOptionElement(Element):
"""
SelectOptionElement is responsible for a block of option. Used by
SelectQuestionElement.
Which is used as part of the Multi-Select iDevice.
"""
def __init__(self, field):
"""
Initialize
"""
Element.__init__(self, field)
self.index = 0
# to compensate for the strange unpickling timing when objects are
# loaded from an elp, ensure that proper idevices are set:
if field.answerTextArea.idevice is None:
field.answerTextArea.idevice = idevice
self.answerElement = TextAreaElement(field.answerTextArea)
self.answerId = "ans"+self.id
self.answerElement.id = self.answerId
def process(self, request):
"""
Process arguments from the web server.
Return any which apply to this element.
"""
log.debug("process " + repr(request.args))
is_cancel = common.requestHasCancel(request)
if self.answerId in request.args:
self.answerElement.process(request)
if "c"+self.id in request.args \
and not is_cancel:
self.field.isCorrect = True
log.debug("option " + repr(self.field.isCorrect))
elif "ans"+self.id in request.args \
and not is_cancel:
self.field.isCorrect = False
if "action" in request.args and \
request.args["action"][0] == "del"+self.id:
# before deleting the option object, remove any internal anchors:
for o_field in self.field.getRichTextFields():
o_field.ReplaceAllInternalAnchorsLinks()
o_field.RemoveAllInternalLinks()
self.field.question.options.remove(self.field)
self.field.idevice.undo = False
def renderEdit(self):
"""
Returns an XHTML string for editing this option element
code is pretty much straight from the Multi-Option aka QuizOption
"""
html = u"
%s" % _("Option")
html += common.elementInstruc(self.field.question.optionInstruc)
header = ""
if self.index == 0:
header = _("Correct Option")
html += u"
%s\n" % header
html += u"
\n"
if self.index == 0:
html += common.elementInstruc(self.field.question.correctAnswerInstruc)
html += "
\n"
# rather than using answerElement.renderEdit(),
# access the appropriate content_w_resourcePaths attribute directly,
# since this is in a customised output format
# (in a table, with an extra delete-option X to the right)
this_package = None
if self.answerElement.field_idevice is not None \
and self.answerElement.field_idevice.parentNode is not None:
this_package = self.answerElement.field_idevice.parentNode.package
html += common.richTextArea("ans"+self.id,
self.answerElement.field.content_w_resourcePaths,
package=this_package)
html += "
\n"
html += common.checkbox("c"+self.id,
self.field.isCorrect, self.index)
html += "
\n"
html += common.submitImage("del"+self.id, self.field.idevice.id,
"/images/stock-cancel.png",
_(u"Delete option"))
html += "
\n"
return html
def renderView(self, preview=False):
"""
Returns an XHTML string for viewing this option element
"""
log.debug("renderView called with preview = " + str(preview))
ident = self.field.question.id + str(self.index)
html = '
'
html += u'\n' %str(self.field.isCorrect)
ansIdent = "ans" + self.field.question.id + str(self.index)
html += '
\n' % ansIdent
if preview:
html += self.answerElement.renderPreview()
else:
html += self.answerElement.renderView()
html += "
\n"
html += '
\n' %(ident + '_1')
html += _("Correct") + "
"
html += '
\n' %(ident + '_0')
html += _("Incorrect") + "
"
html += "
"
return html
# ===========================================================================
class SelectquestionElement(Element):
"""
SelectQuestionElement is responsible for a block of question.
Used by QuizTestBlock
Which is used as part of the Multi-Select iDevice.
"""
def __init__(self, field):
"""
Initialize
"""
Element.__init__(self, field)
# to compensate for the strange unpickling timing when objects are
# loaded from an elp, ensure that proper idevices are set:
if field.questionTextArea.idevice is None:
field.questionTextArea.idevice = idevice
if field.feedbackTextArea.idevice is None:
field.feedbackTextArea.idevice = idevice
self.questionElement = TextAreaElement(field.questionTextArea)
self.questionId = "question"+self.id
self.questionElement.id = self.questionId
self.feedbackElement = TextAreaElement(field.feedbackTextArea)
self.feedbackId = "feedback"+self.id
self.feedbackElement.id = self.feedbackId
self.options = []
i = 0
for option in self.field.options:
ele = SelectOptionElement(option)
ele.index = i
self.options.append(ele)
i += 1
def process(self, request):
"""
Process the request arguments from the web server
"""
log.info("process " + repr(request.args))
is_cancel = common.requestHasCancel(request)
if self.questionId in request.args:
self.questionElement.process(request)
if ("addOption"+unicode(self.id)) in request.args:
self.field.addOption()
self.field.idevice.edit = True
self.field.idevice.undo = False
if self.feedbackId in request.args:
self.feedbackElement.process(request)
if "action" in request.args and \
request.args["action"][0] == "del" + self.id:
# before deleting the questions object, remove any internal anchors:
for q_field in self.field.getRichTextFields():
q_field.ReplaceAllInternalAnchorsLinks()
q_field.RemoveAllInternalLinks()
self.field.idevice.questions.remove(self.field)
self.field.idevice.undo = False
for element in self.options:
element.process(request)
def renderEdit(self):
"""
Returns an XHTML string with the form element for editing this element
"""
html = u"
\n"
html += u"" + _("Question:") + " "
html += common.elementInstruc(self.field.questionInstruc)
html += u" " + common.submitImage("del" + self.id,
self.field.idevice.id,
"/images/stock-cancel.png",
_("Delete question"))
# rather than using questionElement.renderEdit(),
# access the appropriate content_w_resourcePaths attribute directly,
# since this is in a customised output format
# (an extra delete-question X to the right of the question-mark)
this_package = None
if self.questionElement.field_idevice is not None \
and self.questionElement.field_idevice.parentNode is not None:
this_package = self.questionElement.field_idevice.parentNode.package
html += common.richTextArea("question"+self.id,
self.questionElement.field.content_w_resourcePaths,
package=this_package)
html += u"
"
html += u""
html += u"
"
html += u"
%s " % _("Options")
html += common.elementInstruc(self.field.optionInstruc)
html += u"
"
html += u"
"
html += u""
html += u""
for element in self.options:
html += element.renderEdit()
html += u""
html += u"
\n"
value = _(u"Add another Option")
html += common.submitButton("addOption"+self.id, value)
html += u" "
html += self.feedbackElement.renderEdit()
html += u"
\n"
return html
def renderView(self,img=None):
"""
Returns an XHTML string for viewing this question element
"""
html = "
\n"
html += self.doRender(img, preview=False)
html += "
\n"
return html
def renderPreview(self,img=None):
"""
Returns an XHTML string for viewing this question element
"""
return self.doRender(img, preview=True)
def doRender(self, img, preview=False):
"""
Returns an XHTML string for viewing this element
"""
if preview:
html = self.questionElement.renderPreview()
else:
html = self.questionElement.renderView()
html += "
"
for element in self.options:
html += element.renderView(preview)
html += "
"
html += ' ' %(len(self.field.options),self.field.id)
html += " \n"
html += '
' % ("f"+self.field.id)
if preview:
html += self.feedbackElement.renderPreview()
else:
html += self.feedbackElement.renderView()
html += '
'
return html
# ===========================================================================
class QuizOptionElement(Element):
"""
QuizOptionElement is responsible for a block of option. Used by
QuizQuestionElement.
Which is used as part of the Multi-Choice iDevice.
"""
def __init__(self, field):
"""
Initialize
"""
Element.__init__(self, field)
self.index = 0
# to compensate for the strange unpickling timing when objects are
# loaded from an elp, ensure that proper idevices are set:
if field.answerTextArea.idevice is None:
field.answerTextArea.idevice = idevice
if field.feedbackTextArea.idevice is None:
field.feedbackTextArea.idevice = idevice
self.answerElement = TextAreaElement(field.answerTextArea)
self.answerId = "ans"+self.id
self.answerElement.id = self.answerId
self.feedbackElement = TextAreaElement(field.feedbackTextArea)
self.feedbackId = "f"+self.id
self.feedbackElement.id = self.feedbackId
def process(self, request):
"""
Process arguments from the web server.
Return any which apply to this element.
"""
log.debug("process " + repr(request.args))
is_cancel = common.requestHasCancel(request)
if self.answerId in request.args:
self.answerElement.process(request)
if self.feedbackId in request.args:
self.feedbackElement.process(request)
if ("c"+self.field.question.id in request.args \
and request.args["c"+self.field.question.id][0]==str(self.index) \
and not is_cancel):
self.field.isCorrect = True
elif "ans"+self.id in request.args \
and not is_cancel:
self.field.isCorrect = False
if "action" in request.args and \
request.args["action"][0] == "del"+self.id:
# before deleting the option object, remove any internal anchors:
for o_field in self.field.getRichTextFields():
o_field.ReplaceAllInternalAnchorsLinks()
o_field.RemoveAllInternalLinks()
self.field.question.options.remove(self.field)
# disable Undo once an option has been deleted:
self.field.idevice.undo = False
def renderEdit(self):
"""
Returns an XHTML string for editing this option element
"""
html = u"
%s" % _("Option")
html += common.elementInstruc(self.field.idevice.answerInstruc)
header = ""
if self.index == 0:
header = _("Correct Option")
html += u"
%s\n" % header
html += u"
\n"
if self.index == 0:
html += common.elementInstruc(self.field.idevice.keyInstruc)
html += "
\n"
# rather than using answerElement.renderEdit(),
# access the appropriate content_w_resourcePaths attribute directly,
# since this is in a customised output format
# (in a table, with an extra delete-option X to the right)
this_package = None
if self.answerElement.field_idevice is not None \
and self.answerElement.field_idevice.parentNode is not None:
this_package = self.answerElement.field_idevice.parentNode.package
html += common.richTextArea("ans"+self.id,
self.answerElement.field.content_w_resourcePaths,
package=this_package)
html += "
\n"
html += common.option("c"+self.field.question.id,
self.field.isCorrect, self.index)
html += "
\n"
html += common.submitImage("del"+self.id, self.field.idevice.id,
"/images/stock-cancel.png",
_(u"Delete option"))
html += "
\n"
html += "
%s" % _("Feedback")
html += common.elementInstruc(self.field.idevice.feedbackInstruc)
html += "
\n"
# likewise, rather than using feedbackElement.renderEdit(),
# access the appropriate content_w_resourcePaths attribute directly,
# since this is in a customised output format
# (though less so now that the header isn't even centered)
this_package = None
if self.feedbackElement.field_idevice is not None \
and self.feedbackElement.field_idevice.parentNode is not None:
this_package = self.feedbackElement.field_idevice.parentNode.package
html += common.richTextArea('f'+self.id,
self.feedbackElement.field.content_w_resourcePaths,
package=this_package)
html += "
\n"
return html
def renderAnswerView(self, preview=False):
"""
Returns an XHTML string for viewing and previewing this option element
"""
log.debug("renderView called with preview = " + str(preview))
length = len(self.field.question.options)
html = '
'
html += '' \
% (self.index, length, self.field.question.id)
html += '
\n'
if preview:
html += self.answerElement.renderPreview()
else:
html += self.answerElement.renderView()
html += "
\n"
return html
def renderFeedbackView(self, preview=False):
"""
return xhtml string for display this option's feedback
"""
feedbackStr = ""
if hasattr(self.feedbackElement.field, 'content')\
and self.feedbackElement.field.content.strip() != "":
if preview:
feedbackStr = self.feedbackElement.renderPreview()
else:
feedbackStr = self.feedbackElement.renderView()
else:
if self.field.isCorrect:
feedbackStr = _("Correct")
else:
feedbackStr = _("Wrong")
html = '
' % (str(to_even))
html += feedbackStr
html += '
\n'
return html
# ===========================================================================
class QuizQuestionElement(Element):
"""
QuizQuestionElement is responsible for a block of question.
Used by QuizTestBlock
Which is used as part of the Multi-Choice iDevice.
"""
def __init__(self, field):
"""
Initialize
"""
Element.__init__(self, field)
# to compensate for the strange unpickling timing when objects are
# loaded from an elp, ensure that proper idevices are set:
if field.questionTextArea.idevice is None:
field.questionTextArea.idevice = idevice
if field.hintTextArea.idevice is None:
field.hintTextArea.idevice = idevice
self.questionElement = TextAreaElement(field.questionTextArea)
self.questionId = "question"+self.id
self.questionElement.id = self.questionId
self.hintElement = TextAreaElement(field.hintTextArea)
self.hintId = "hint"+self.id
self.hintElement.id = self.hintId
self.options = []
i = 0
for option in self.field.options:
ele = QuizOptionElement(option)
ele.index = i
self.options.append(ele)
i += 1
def process(self, request):
"""
Process the request arguments from the web server
"""
log.info("process " + repr(request.args))
if self.questionId in request.args:
self.questionElement.process(request)
if self.hintId in request.args:
self.hintElement.process(request)
if ("addOption"+unicode(self.id)) in request.args:
self.field.addOption()
self.field.idevice.edit = True
# disable Undo once an option has been added:
self.field.idevice.undo = False
if "action" in request.args and \
request.args["action"][0] == "del" + self.id:
# before deleting the question object, remove any internal anchors:
for q_field in self.field.getRichTextFields():
q_field.ReplaceAllInternalAnchorsLinks()
q_field.RemoveAllInternalLinks()
self.field.idevice.questions.remove(self.field)
# disable Undo once a question has been deleted:
self.field.idevice.undo = False
for element in self.options:
element.process(request)
def renderEdit(self):
"""
Returns an XHTML string with the form element for editing this element
"""
html = u" "+common.submitImage("del"+self.id, self.field.idevice.id,
"/images/stock-cancel.png",
_("Delete question"))
html += self.questionElement.renderEdit()
html += self.hintElement.renderEdit()
html += "
"
html += ""
for element in self.options:
html += element.renderEdit()
html += ""
html += "
\n"
value = _("Add another option")
html += common.submitButton("addOption"+unicode(self.id), value)
return html
def renderView(self, img1=None, img2=None):
"""
Returns an XHTML string for viewing this question element
"""
html = "
\n"
html += self.doRender(img1, img2, preview=False)
html += "
\n"
return html
def renderPreview(self, img1=None, img2=None):
"""
Returns an XHTML string for viewing this question element
"""
return self.doRender(img1, img2, preview=True)
def doRender(self, img1, img2, preview=False):
"""
Returns an XHTML string for viewing this element
"""
if preview:
html = self.questionElement.renderPreview()
else:
html = self.questionElement.renderView()
html += " \n"
if self.hintElement.field.content.strip():
html += '' % img1
html += '\n'
html += ''
html += '
'
html += '
'
html += '
"
html += '
'
html += _("Hint")
html += '
\n'
if preview:
html += self.hintElement.renderPreview()
else:
html += self.hintElement.renderView()
html += "
\n"
html += "
\n"
html += "\n"
for element in self.options:
html += element.renderAnswerView(preview)
html += "\n"
html += "
\n"
for element in self.options:
html += element.renderFeedbackView(preview)
return html