# =========================================================================== # 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 += '\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'%s\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'%s\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'', u'', u'', u'', u'', u'', u'
', u' """ u'', common.checkbox('strictMarking%s' % self.id, self.field.strictMarking, title=_(u'Strict Marking?'), instruction=self.field.strictMarkingInstruc), u'', common.checkbox('checkCaps%s' % self.id, self.field.checkCaps, title=_(u'Check Caps?'), instruction=self.field.checkCapsInstruc), u'', common.checkbox('instantMarking%s' % self.id, self.field.instantMarking, title=_(u'Instant Marking?'), instruction=self.field.instantMarkingInstruc), 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 += '" html += '" 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"" html += u"" html += u"" html += u"" for element in self.options: html += element.renderEdit() html += u"" html += u"
%s " % _("Options") html += common.elementInstruc(self.field.optionInstruc) 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 += '
' 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 += _("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