class EditorPane(object): """ The EditorPane is responsible for creating new idevice """ def __init__(self, webServer, parent): """ Initialize JR: anado parente para poder acceder a algunos atributos de editorpag, en concreto a showHide """ self.ideviceStore = webServer.application.ideviceStore self.webDir = webServer.application.config.webDir self.styles = webServer.application.config.styleStore.getStyles() self.elements = [] self.idevice = GenericIdevice("", "", "", "", "") self.idevice.id = self.ideviceStore.getNewIdeviceId() self.originalIdevice = GenericIdevice("", "", "", "", "") self.purpose = "" self.tip = "" self.message = "" self.parent = parent self._nameInstruc = \ x_(u"Your new iDevice will appear in the iDevice " u"pane with this title. This is a compulsory field " u"and you will be prompted to enter a label if you try " u"to submit your iDevice without one.") self._authorInstruc = x_(u"This is an optional field.") self._purposeInstruc = x_( u"The purpose dialogue allows you to describe" u" your intended purpose of the iDevice to other" u" potential users.") self._emphasisInstruc = x_( u"Use this pulldown to select whether or not " u" the iDevice should have any formatting " u" applied to it to distinguish " u"it; ie. a border and an icon.") self._tipInstruc = x_(u"Use this field to describe " u"your intended use and the pedagogy behind " u"the device's development. This can be useful " u"if your iDevice is to be exported for others " u"to use.") self._lineInstruc = x_(u"Add a single text line to an iDevice. " u"Useful if you want the ability to place a " u"label within the iDevice.") self._textBoxInstruc = x_( u"Add a text entry box to an iDevice. " u"Used for entering larger amounts of textual " u"content.") self._feedbackInstruc = x_( u"Add an interactive feedback field to your iDevice.") self._flashInstruc = x_(u"Add a flash video to your iDevice.") self._mp3Instruc = x_(u"Add an mp3 file to your iDevice.") self._attachInstruc = x_(u"Add an attachment file to your iDevice.") self.style = self.styles[0] # Properties nameInstruc = lateTranslate('nameInstruc') authorInstruc = lateTranslate('authorInstruc') purposeInstruc = lateTranslate('purposeInstruc') emphasisInstruc = lateTranslate('emphasisInstruc') tipInstruc = lateTranslate('tipInstruc') lineInstruc = lateTranslate('lineInstruc') textBoxInstruc = lateTranslate('textBoxInstruc') feedbackInstruc = lateTranslate('feedbackInstruc') flashInstruc = lateTranslate('flashInstruc') mp3Instruc = lateTranslate('mp3Instruc') attachInstruc = lateTranslate('attachInstruc') def setIdevice(self, idevice): """ Sets the iDevice to edit """ self.idevice = idevice.clone() self.idevice.id = idevice.id self.originalIdevice = idevice def process(self, request, status): """ Process """ log.debug("process " + repr(request.args)) self.message = "" if status == "old": for element in self.elements: element.process(request) if "title" in request.args: self.idevice.title = unicode(request.args["title"][0], 'utf8') if "tip" in request.args: self.idevice.tip = unicode(request.args["tip"][0], 'utf8') if "emphasis" in request.args: self.idevice.emphasis = int(request.args["emphasis"][0]) if self.idevice.emphasis == 0: self.idevice.icon = "" if "addText" in request.args: field = TextField(_(u"Enter the label here"), _(u"Enter instructions for completion here")) field.setIDevice(self.idevice) self.idevice.addField(field) self.idevice.edit = True if "addTextArea" in request.args: field = TextAreaField( _(u"Enter the label here"), _(u"Enter the instructions for completion here")) field.setIDevice(self.idevice) self.idevice.addField(field) self.idevice.edit = True if "addFeedback" in request.args: field = FeedbackField( _(u"Enter the label here"), _(u"""Feedback button will not appear if no data is entered into this field.""")) field.setIDevice(self.idevice) self.idevice.addField(field) self.idevice.edit = True #if "addFlash" in request.args: #print "add a flash" #field = FlashField(_(u"Enter the label here"), #_(u"Enter the instructions for completion here")) #field.setIDevice(self.idevice) #self.idevice.addField(field) if "addMP3" in request.args: field = MultimediaField( _(u"Enter the label here"), _(u"Enter the instructions for completion here")) field.setIDevice(self.idevice) self.idevice.addField(field) if not 'xspf_player.swf' in self.idevice.systemResources: self.idevice.systemResources += ['xspf_player.swf'] self.idevice.edit = True if "addAttachment" in request.args: field = AttachmentField( _(u"Enter the label here"), _(u"Enter the instructions for completion here")) field.setIDevice(self.idevice) self.idevice.addField(field) self.idevice.edit = True if ("action" in request.args and request.args["action"][0] == "selectIcon"): self.idevice.icon = request.args["object"][0] if "preview" in request.args: if self.idevice.title == "": self.message = _("Please enter<br />an idevice name.") else: self.idevice.edit = False if "edit" in request.args: self.idevice.edit = True if "cancel" in request.args: ideviceId = self.idevice.id self.idevice = self.originalIdevice.clone() self.idevice.id = ideviceId self.parent.showHide = False if ("action" in request.args and request.args["action"][0] == "changeStyle"): self.style = self.styles[int(request.args["object"][0])] self.__buildElements() def __buildElements(self): """ Building up element array """ self.elements = [] elementTypeMap = { TextField: TextEditorElement, TextAreaField: TextAreaEditorElement, ImageField: ImageEditorElement, FeedbackField: FeedbackEditorElement, MultimediaField: MultimediaEditorElement, FlashField: FlashEditorElement, AttachmentField: AttachmentEditorElement } for field in self.idevice.fields: elementType = elementTypeMap.get(field.__class__) if elementType: # Create an instance of the appropriate element class log.debug(u"createElement " + elementType.__class__.__name__ + u" for " + field.__class__.__name__) self.elements.append(elementType(field)) else: log.error(u"No element type registered for " + field.__class__.__name__) def renderButtons(self, request): """ Render the idevice being edited """ html = "<font color=\"red\"><b>" + self.message + "</b></font>" html += "<fieldset><legend><b>" + _("Add Field") + "</b></legend>" html += common.submitButton("addText", _("Text Line")) html += common.elementInstruc(self.lineInstruc) + "<br/>" html += common.submitButton("addTextArea", _("Text Box")) html += common.elementInstruc(self.textBoxInstruc) + "<br/>" html += common.submitButton("addFeedback", _("Feedback")) html += common.elementInstruc(self.feedbackInstruc) + "<br/>" # Attachments are now embeddable: #html += common.submitButton("addAttachment", _("Attachment")) #html += common.elementInstruc(self.attachInstruc) + "<br/>" # MP3 fields are now embeddable: #html += common.submitButton("addMP3", _("MP3")) #html += common.elementInstruc(self.mp3Instruc) + "<br/>" html += "</fieldset>\n" html += "<fieldset><legend><b>" + _("Actions") + "</b></legend>" if self.idevice.edit: html += common.submitButton("preview", _("Preview"), not self.parent.isGeneric) else: html += common.submitButton("edit", _("Edit")) html += "<br/>" html += common.submitButton("cancel", _("Cancel")) #html += "</fieldset>" return html def renderIdevice(self, request): """ Returns an XHTML string for rendering the new idevice """ html = "<div id=\"editorWorkspace\">\n" html += "<script type=\"text/javascript\">\n" html += "<!--\n" html += """ function submitLink(action, object, changed) { var form = document.getElementById("contentForm") form.action.value = action; form.object.value = object; form.isChanged.value = changed; form.submit(); }\n""" html += """ function submitIdevice() { var form = document.getElementById("contentForm") if (form.ideviceSelect.value == "newIdevice") form.action.value = "newIdevice" else form.action.value = "changeIdevice" form.object.value = form.ideviceSelect.value; form.isChanged.value = 1; form.submit(); }\n""" html += """ function submitStyle() { var form = document.getElementById("contentForm") form.action.value = "changeStyle"; form.object.value = form.styleSelect.value; form.isChanged.value = 0; form.submit(); }\n""" html += "//-->\n" html += "</script>\n" self.purpose = self.idevice.purpose.replace("\r", "") self.purpose = self.purpose.replace("\n", "\\n") self.tip = self.idevice.tip.replace("\r", "") self.tip = self.tip.replace("\n", "\\n") if self.idevice.edit: html += "<b>" + _("Name") + ": </b>\n" html += common.elementInstruc(self.nameInstruc) + "<br/>" html += '<input type="text" name= "title" id="title" value="%s"/>' % self.idevice.title this_package = None html += common.formField('richTextArea', this_package, _(u"Pedagogical Tip"), 'tip', '', self.tipInstruc, self.tip) html += "<b>" + _("Emphasis") + ":</b> " html += "<select onchange=\"submit();\" name=\"emphasis\">\n" emphasisValues = { Idevice.NoEmphasis: _(u"No emphasis"), Idevice.SomeEmphasis: _(u"Some emphasis") } for value, description in emphasisValues.items(): html += "<option value=\"" + unicode(value) + "\" " if self.idevice.emphasis == value: html += "selected " html += ">" + description + "</option>\n" html += "</select> \n" html += common.elementInstruc(self.emphasisInstruc) html += "<br/><br/>\n" if self.idevice.emphasis > 0: html += self.__renderStyles() + " " html += u'<a href="#" ' html += u'onmousedown="Javascript:updateCoords(event);"\n' html += u'onclick="Javascript:showMe(\'iconpanel\', 350, 100);">' html += u'Select an icon:</a> \n' icon = self.idevice.icon if icon != "": html += '<img align="middle" ' html += 'src="/style/%s/icon_%s' % ( self.style.get_dirname(), icon) html += '.gif"/><br/>' html += u'<div id="iconpanel" style="display:none; z-index:99;">' html += u'<div style="float:right;" >\n' html += u'<img alt="%s" ' % _("Close") html += u'src="/images/stock-stop.png" title="%s"\n' % _( "Close") html += u'onmousedown="Javascript:hideMe();"/></div><br/> \n' html += u'<div align="center"><b>%s:</b></div><br/>' % _( "Icons") html += self.__renderIcons() html += u'</div><br/>\n' for element in self.elements: html += element.renderEdit() else: html += "<b>" + self.idevice.title + "</b><br/><br/>" for element in self.elements: html += element.renderPreview() if self.idevice.purpose != "" or self.idevice.tip != "": html += "<a title=\"" + _("Pedagogical Help") + "\" " html += "onmousedown=\"Javascript:updateCoords(event);\" \n" html += "onclick=\"Javascript:showMe('phelp', 380, 240);\" \n" html += "href=\"Javascript:void(0)\" style=\"cursor:help;\">\n " html += '<img alt="%s" src="/images/info.png" border="0" \n' % _( 'Info') html += "align=\"middle\" /></a>\n" html += "<div id=\"phelp\" style=\"display:none;\">\n" html += "<div style=\"float:right;\" " html += '<img alt="%s" src="/images/stock-stop.png" \n' % _( 'Close') html += " title='" + _( "Close") + "' border='0' align='middle' \n" html += "onmousedown=\"Javascript:hideMe();\"/></div>\n" if self.idevice.purpose != "": html += "<b>Purpose:</b><br/>%s<br/>" % self.purpose if self.idevice.tip != "": html += "<b>Tip:</b><br/>%s<br/>" % self.idevice.tip html += "</div>\n" html += "</div>\n" self.message = "" return html def __renderStyles(self): """ Return xhtml string for rendering styles select """ html = '<select onchange="submitStyle();" name="styleSelect">\n' idx = 0 for style in self.styles: html += "<option value='%d' " % idx if self.style.get_name() == style.get_name(): html += "selected " html += ">" + style.get_name() + "</option>\n" idx = idx + 1 html += "</select> \n" return html def __renderIcons(self): """ Return xhtml string for dispay all icons """ iconpath = self.style.get_style_dir() iconfiles = iconpath.files("icon_*") html = "" for iconfile in iconfiles: iconname = iconfile.namebase icon = iconname.split("_", 1)[1] filename = "/style/%s/%s.gif" % (self.style.get_dirname(), iconname) html += u'<div style="float:left; text-align:center; width:80px;\n' html += u'margin-right:10px; margin-bottom:10px" > ' html += u'<img src="%s" \n' % filename html += u' alt="%s" ' % _("Submit") html += u"onclick=\"submitLink('selectIcon','%s',1)\">\n" % icon html += u'<br/>%s.gif</div>\n' % icon return html
class AppletIdevice(Idevice): """ Java Applet Idevice. Enables you to embed java applet in the browser """ persistenceVersion = 1 def __init__(self, parentNode=None): """ Sets up the idevice title and instructions etc """ Idevice.__init__(self, x_(u"Java Applet"), x_(u"University of Auckland"), u"", u"", u"", parentNode) self.emphasis = Idevice.NoEmphasis self.appletCode = u"" self.type = u"other" self._fileInstruc = x_(u"""Add all the files provided for the applet except the .txt file one at a time using the add files and upload buttons. The files, once loaded will be displayed beneath the Applet code field.""") self._codeInstruc = x_(u""""Find the .txt file (in the applet file) and open it. Copy the contents of this file <ctrl A, ctrl C> into the applet code field.""") self._typeInstruc = x_(u""" <p>If the applet you're adding was generated by one of the programs in this drop down, please select it, then add the data/applet file generated by your program. </p> <p>eg. For Geogebra applets, select geogebra, then add the .ggb file that you created in Geogebra.</p>""") self.message = "" # Properties fileInstruc = lateTranslate('fileInstruc') codeInstruc = lateTranslate('codeInstruc') typeInstruc = lateTranslate('typeInstruc') def getResourcesField(self, this_resource): """ implement the specific resource finding mechanism for this iDevice: """ # if this_resource is listed within the iDevice's userResources, # then we can assume that this_resource is indeed a valid resource, # even though that has no direct field. # As such, merely return the resource itself, to indicate that # it DOES belong to this iDevice, but is not a FieldWithResources: if this_resource in self.userResources: return this_resource return None def getRichTextFields(self): """ Like getResourcesField(), a general helper to allow nodes to search through all of their fields without having to know the specifics of each iDevice type. """ # Applet iDevice has no rich-text fields: return [] def burstHTML(self, i): """ takes a BeautifulSoup fragment (i) and bursts its contents to import this idevice from a CommonCartridge export """ # Java Applet Idevice: #title = i.find(name='span', attrs={'class' : 'iDeviceTitle' }) #idevice.title = title.renderContents().decode('utf-8') # no title for this idevice. # =====> WARNING: not yet loading any of the files! # # BEWARE also of the appletCode line breaks loading as <br/>, # may want change this back to \n or \r\n? # AND: also need to load the applet type: Geogebra or Other. inner = i.find(name='div', attrs={'class' : 'iDevice emphasis0' }) self.appletCode= inner.renderContents().decode('utf-8') def uploadFile(self, filePath): """ Store the upload files in the package Needs to be in a package to work. """ log.debug(u"uploadFile "+unicode(filePath)) resourceFile = Path(filePath) assert(self.parentNode, _('file %s has no parentNode') % self.id) assert(self.parentNode.package, _('iDevice %s has no package') % self.parentNode.id) if resourceFile.isfile(): self.message = "" Resource(self, resourceFile) if self.type == "geogebra": self.appletCode = self.getAppletcode(resourceFile.basename()) else: log.error('File %s is not a file' % resourceFile) def deleteFile(self, fileName): """ Delete a selected file """ for resource in self.userResources: if resource.storageName == fileName: resource.delete() break def getAppletcode(self, filename): """ xhtml string for GeoGebraApplet """ html = """ <applet code="geogebra.GeoGebraApplet.class" archive="geogebra.jar" width="750" height="450"> <param name="filename" value="%s"> <param name="framePossible" value="false"> Please <a href="http://java.sun.com/getjava"> install Java 1.4</a> (or later) to use this page. </applet> """ % filename return html def copyFiles(self): """ if geogebra, then copy all jar files, otherwise delete all jar files. """ for resource in reversed(self.userResources): resource.delete() self.appletCode = "" self.message = "" if self.type == "geogebra": #from exe.application import application from exe import globals ideviceDir = globals.application.config.webDir/'templates' for file in GEOGEBRA_FILE_NAMES: filename = ideviceDir/file self.uploadFile(filename) self.appletCode = self.getAppletcode("") def upgradeToVersion1(self): """ Called to upgrade to 0.23 release """ self.message = "" self.type = u"other" self._typeInstruc = x_(u""" <p>If the applet you're adding was generated by one of the programs in this drop down, please select it, then add the data/applet file generated by your program. </p> <p>eg. For Geogebra applets, select geogebra, then add the .ggb file that you created in Geogebra.</p>""")
class CitasparapensarfpdIdevice(Idevice): """ El iDevice Citas para pensar permite al alumnado reflexionar sobre algunas citas propuestas """ persistenceVersion = 7 def __init__(self, activity="", answer=""): """ Initialize """ Idevice.__init__( self, x_(u"FPD - Citas Para Pensar"), x_(u"Jose Ramon Jimenez Reyes"), x_(u"""Citas para pensar es un iDevice que permite al alumnado reflexionar sobre algunas citas propuestas.""" ), u"", u"citasparapensarfpd") # self.emphasis = Idevice.SomeEmphasis self.emphasis = "_citasparapensarfpd" self._activityInstruc = x_( u"""Introduce el texto que aparecerá en este iDevice""") # self.systemResources += ["common.js"] self.activityTextArea = TextAreaField(x_(u'Texto Citas para pensar:'), self._activityInstruc, activity) self.activityTextArea.idevice = self # Properties activityInstruc = lateTranslate('activityInstruc') def getResourcesField(self, this_resource): """ implement the specific resource finding mechanism for this iDevice: """ # be warned that before upgrading, this iDevice field could not exist: if hasattr(self, 'activityTextArea')\ and hasattr(self.activityTextArea, 'images'): for this_image in self.activityTextArea.images: if hasattr(this_image, '_imageResource') \ and this_resource == this_image._imageResource: return self.activityTextArea return None def getRichTextFields(self): fields_list = [] if hasattr(self, 'activityTextArea'): fields_list.append(self.activityTextArea) return fields_list def burstHTML(self, i): # Citasparapensarfpd Idevice: title = i.find(name='span', attrs={'class': 'iDeviceTitle'}) self.title = title.renderContents().decode('utf-8') reflections = i.findAll(name='div', attrs={'id': re.compile('^ta')}) # should be exactly two of these: # 1st = field[0] == Activity if len(reflections) >= 1: self.activityTextArea.content_wo_resourcePaths = \ reflections[0].renderContents().decode('utf-8') # and add the LOCAL resource paths back in: self.activityTextArea.content_w_resourcePaths = \ self.activityTextArea.MassageResourceDirsIntoContent( \ self.activityTextArea.content_wo_resourcePaths) self.activityTextArea.content = \ self.activityTextArea.content_w_resourcePaths def upgradeToVersion1(self): """ Upgrades the node from version 0 to 1. """ log.debug(u"Upgrading iDevice") self.icon = u"citasparapensarfpd" def upgradeToVersion2(self): """ Upgrades the node from 1 (v0.5) to 2 (v0.6). Old packages will loose their icons, but they will load. """ log.debug(u"Upgrading iDevice") # self.emphasis = Idevice.SomeEmphasis self.emphasis = "_citasparapensarfpd" def upgradeToVersion3(self): """ Upgrades v0.6 to v0.7. """ self.lastIdevice = False def upgradeToVersion4(self): """ Upgrades to exe v0.10 """ self._upgradeIdeviceToVersion1() self._activityInstruc = self.__dict__['activityInstruc'] def upgradeToVersion5(self): """ Upgrades to exe v0.10 """ self._upgradeIdeviceToVersion1() def upgradeToVersion6(self): """ Upgrades to v0.12 """ self._upgradeIdeviceToVersion2() self.systemResources += ["common.js"] def upgradeToVersion7(self): """ Upgrades to somewhere before version 0.25 (post-v0.24) Taking the old unicode string fields, and converting them into image-enabled TextAreaFields: """ self.activityTextArea = TextAreaField(x_(u'Texto para pensar:'), self._activityInstruc, self.activity) self.activityTextArea.idevice = self
class MultimediaIdevice(Idevice): """ A Multimedia Idevice is one built up from an Multimedia file and free text. """ persistenceVersion = 2 def __init__(self, defaultMedia=None): Idevice.__init__( self, x_(u"MP3"), x_(u"Auckland University of Technology"), x_(u"The MP3 iDevice allows you to attach an MP3 " "media file to your content along with relevant textual" "learning instructions."), u"", u"") self.emphasis = Idevice.NoEmphasis self.media = MultimediaField( x_(u"Choose an MP3 file"), x_(u"" "<ol>" " <li>Click <Select an MP3> and browse to the MP3 " " file you wish to insert</li>" " <li>Click on the dropdown menu to select the position " " that you want the file displayed on screen.</li>" " <li>Enter an optional caption for your file.</li>" " <li>Associate any relevant text to the MP3 file.</li>" " <li>Choose the type of style you would like the iDevice to" " display e.g. 'Some emphasis' " "applies a border and icon to the iDevice content displayed.</li>" "</ol>")) self.media.idevice = self self.text = TextAreaField( x_(u"Text"), x_("""Enter the text you wish to associate with the file.""")) self.text.idevice = self self.float = u"left" self.caption = u"" self.icon = u"multimedia" self._captionInstruc = x_(u"""Provide a caption for the MP3 file. This will appear in the players title bar as well.""") self._alignInstruc = x_(u"""Alignment allows you to choose where on the screen the media player will be positioned.""") self.systemResources += ['xspf_player.swf'] # Properties captionInstruc = lateTranslate('captionInstruc') alignInstruc = lateTranslate('alignInstruc') def getResourcesField(self, this_resource): """ implement the specific resource finding mechanism for this iDevice: """ # be warned that before upgrading, this iDevice field could not exist: if hasattr(self, 'media') and hasattr(self.media, 'mediaResource'): if this_resource == self.media.mediaResource: return self.media # be warned that before upgrading, this iDevice field could not exist: if hasattr(self, 'text') and hasattr(self.text, 'images'): for this_image in self.text.images: if hasattr(this_image, '_imageResource') \ and this_resource == this_image._imageResource: return self.text return None def getRichTextFields(self): """ Like getResourcesField(), a general helper to allow nodes to search through all of their fields without having to know the specifics of each iDevice type. """ fields_list = [] if hasattr(self, 'text'): fields_list.append(self.text) return fields_list def upgradeToVersion2(self): """ (We skipped version 1 by accident) Upgrades to 0.22 """ self.systemResources += ['xspf_player.swf']
class ImageWithTextIdevice(Idevice): """ A ImageWithText Idevice is one built up from an image and free text. """ persistenceVersion = 9 def __init__(self, defaultImage = None): Idevice.__init__(self, x_(u"Image with Text"), x_(u"University of Auckland"), x_(u"""<p> The image with text iDevice can be used in a number of ways to support both the emotional (affective) and learning task (cognitive) dimensions of eXe content. </p><p> <b>Integrating visuals with verbal summaries</b> </p><p> Cognitive psychologists indicate that presenting learners with a representative image and corresponding verbal summary (that is presented simultaneously) can reduce cognitive load and enhance learning retention. This iDevice can be used to present an image (photograph, diagram or graphic) with a brief verbal summary covering the main points relating to the image. For example, if you were teaching the functions of a four-stroke combustion engine, you could have a visual for each of the four positions of the piston with a brief textual summary of the key aspects of each visual. </p>"""), u"", u"") self.emphasis = Idevice.NoEmphasis self.group = Idevice.Media self.image = ImageField(x_(u"Image"), u"") self.image.idevice = self self.image.defaultImage = defaultImage self.text = TextAreaField(x_(u"Text"), x_("""Enter the text you wish to associate with the image.""")) self.text.idevice = self self.float = u"left" self.caption = u"" self._captionInstruc = x_(u"""Provide a caption for the image you have just inserted.""") # Properties captionInstruc = lateTranslate('captionInstruc') def upgradeToVersion1(self): """ Called to upgrade from 0.5 release """ self.float = u"left" def upgradeToVersion2(self): """ Called to upgrade from 0.6 release """ self.caption = u"" self.emphasis = Idevice.NoEmphasis def upgradeToVersion3(self): """ Upgrades v0.6 to v0.7. """ self.lastIdevice = False def upgradeToVersion4(self): """ Upgrades to exe v0.10 """ self._upgradeIdeviceToVersion1() def upgradeToVersion5(self): """ Upgrades to v0.12 """ log.debug("upgrade to version 5") self._upgradeIdeviceToVersion2() self.image._upgradeFieldToVersion2() def upgradeToVersion6(self): """ Called to upgrade from 0.13 release """ self._captionInstruc = x_(u"""Provide a caption for the image you have just inserted.""") def upgradeToVersion7(self): """ Called to upgrade to version 0.24 """ self.image.isFeedback = False def upgradeToVersion8(self): """ Converting ImageWithTextIdevice -> FreeTextIdevice, now that FreeText can hold embeddded images. BUT - due to the inconsistent loading of the objects via unpickling, since the resources aren't necessarily properly loaded and upgraded, NOR is the package necessarily, as it might not even have a list of resources yet, all of this conversion code must be done in an afterUpgradeHandler """ G.application.afterUpgradeHandlers.append(self.convertToFreeText) def upgradeToVersion9(self): """ Adds group to idevice """ self.group = Idevice.Media def convertToFreeText(self): """ Actually do the Converting of ImageWithTextIdevice -> FreeTextIdevice, now that FreeText can hold embeddded images. """ new_content = "" # ensure that an image resource still exists on this ImageWithText, # before trying to add it into the FreeText idevice. # Why? corrupt packages have been seen missing resources... # (usually in with extra package objects as well, probably # from old code doing faulty Extracts, or somesuch nonesense) imageResource_exists = False if self.image.imageResource: # also ensure that it has the correct md5 checksum, since there was # a period in which resource checksums were being created before # the resource zip file was fully closed, and not flushed out: self.image.imageResource.checksumCheck() if os.path.exists(self.image.imageResource.path) and \ os.path.isfile(self.image.imageResource.path): imageResource_exists = True else: log.warn("Couldn't find ImageWithText image when upgrading "\ + self.image.imageResource.storageName) if imageResource_exists: new_content += "<img src=\"resources/" \ + self.image.imageResource.storageName + "\" " if self.image.height: new_content += "height=\"" + self.image.height + "\" " if self.image.width: new_content += "width=\"" + self.image.width + "\" " new_content += "/> \n" elif self.image.imageResource: new_content += "<BR>\n[WARNING: missing image: " \ + self.image.imageResource.storageName + "]\n" if self.caption != "": new_content += "<BR>\n[" + self.caption + "]\n" if self.text.content != "": new_content += "<P>\n" + self.text.content + "\n" # note: this is given a text field which itself did NOT yet have # any embedded media! easier, eh? replacementIdev = FreeTextIdevice(new_content) ########### # now, copy that content field's content into its _w_resourcePaths, # and properly remove the resource directory via Massage.... # for its _wo_resourcePaths: # note that replacementIdev's content field's content # is automatically set at its constructor (above), # as is the default content_w_resourcePaths (a copy of content) # AND the default content_wo_resourcePaths (a copy of content), # so only need to update the content_wo_resourcePaths: replacementIdev.content.content_wo_resourcePaths = \ replacementIdev.content.MassageContentForRenderView( \ replacementIdev.content.content_w_resourcePaths) # Design note: ahhhhh, the above is a good looking reason to possibly # have the MassageContentForRenderView() method # just assume its content_w_resourcePaths as the input # and write the output to its content_wo_resourcePaths..... ####### # next step, add the new IDevice into the same node as this one self.parentNode.addIdevice(replacementIdev) # in passing GalleryImage into the FieldWithResources, # that content field needs to be sure to have an updated # parentNode, courtesy of its idevice: replacementIdev.content.setParentNode() # and semi-manually add/create the current image # resource into the FreeTextIdevice's TextAreaField, content. # the content text will have already been taken care of above, # including the ideal <img src=...> including resources path, # but still need the actual image resource: if imageResource_exists: # Not sure why this can't be imported up top, but it gives # ImportError: cannot import name GalleryImages, # so here it be: from exe.engine.galleryidevice import GalleryImage full_image_path = self.image.imageResource.path new_GalleryImage = GalleryImage(replacementIdev.content, \ self.caption, full_image_path, mkThumbnail=False) # and move it up to the position following this node! while ( self.parentNode.idevices.index(replacementIdev) \ > ( (self.parentNode.idevices.index(self) + 1))): replacementIdev.movePrev() # finally: delete THIS idevice itself, deleting it from the node self.delete() def getResourcesField(self, this_resource): """ implement the specific resource finding mechanism for this iDevice: """ # be warned that before upgrading, this iDevice field could not exist: if hasattr(self, 'image') and hasattr(self.image, 'imageResource'): if this_resource == self.image.imageResource: return self.image # be warned that before upgrading, this iDevice field could not exist: if hasattr(self, 'text') and hasattr(self.text, 'images'): for this_image in self.text.images: if hasattr(this_image, '_imageResource') \ and this_resource == this_image._imageResource: return self.text return None def getRichTextFields(self): """ Like getResourcesField(), a general helper to allow nodes to search through all of their fields without having to know the specifics of each iDevice type. """ fields_list = [] if hasattr(self, 'text'): fields_list.append(self.text) return fields_list
class ImageMagnifierIdevice(Idevice): """ A ImageMagnifier Idevice is one built up from an image and free text. """ persistenceVersion = 3 def __init__(self, defaultImage=None): Idevice.__init__( self, x_(u"Image Magnifier"), x_(u"University of Auckland"), x_(u"""The image magnifier is a magnifying tool enabling learners to magnify the view of the image they have been given. Moving the magnifying glass over the image allows larger detail to be studied."""), u"", u"") self.emphasis = Idevice.NoEmphasis self.group = Idevice.Media self.imageMagnifier = MagnifierField( x_(u"Choose an Image"), x_(u"""Click on the picture below or the "Add Image" button to select an image file to be magnified.""")) self.imageMagnifier.idevice = self self.imageMagnifier.defaultImage = defaultImage self.text = TextAreaField( x_(u"Text"), x_("""Enter the text you wish to associate with the file.""")) self.text.idevice = self self.float = u"left" self.caption = u"" self._captionInstruc = x_(u"""Provide a caption for the image to be magnified.""") self._dimensionInstruc = x_(u"""Choose the size you want your image to display at. The measurements are in pixels. Generally, 100 pixels equals approximately 3cm. Leave both fields blank if you want the image to display at its original size.""") self._alignInstruc = x_(u"""Alignment allows you to choose where on the screen the image will be positioned.""") self._initialZoomInstruc = x_(u"""Set the initial level of zoom when the IDevice loads, as a percentage of the original image size""") self._maxZoomInstruc = x_(u"""Set the maximum level of zoom, as a percentage of the original image size""") self._glassSizeInstruc = x_( u"""Select the size of the magnifying glass""") self.systemResources += ['magnifier.swf'] # Properties captionInstruc = lateTranslate('captionInstruc') dimensionInstruc = lateTranslate('dimensionInstruc') alignInstruc = lateTranslate('alignInstruc') initialZoomInstruc = lateTranslate('initialZoomInstruc') maxZoomInstruc = lateTranslate('maxZoomInstruc') glassSizeInstruc = lateTranslate('glassSizeInstruc') def getResourcesField(self, this_resource): """ implement the specific resource finding mechanism for this iDevice: """ # be warned that before upgrading, this iDevice field could not exist: if hasattr(self, 'imageMagnifier')\ and hasattr(self.imageMagnifier, 'imageResource'): if this_resource == self.imageMagnifier.imageResource: return self.imageMagnifier # be warned that before upgrading, this iDevice field could not exist: if hasattr(self, 'text') and hasattr(self.text, 'images'): for this_image in self.text.images: if hasattr(this_image, '_imageResource') \ and this_resource == this_image._imageResource: return self.text return None def getRichTextFields(self): """ Like getResourcesField(), a general helper to allow nodes to search through all of their fields without having to know the specifics of each iDevice type. """ fields_list = [] if hasattr(self, 'text'): fields_list.append(self.text) return fields_list def burstHTML(self, i): """ takes a BeautifulSoup fragment (i) and bursts its contents to import this idevice from a CommonCartridge export """ # ImageMagnifier Idevice: #======> WARNING - NOT YET BURSTING!!!!!!!! #title = i.find(name='span', attrs={'class' : 'iDeviceTitle' }) #idevice.title = title.renderContents().decode('utf-8') # no title for this idevice # WARNING: not yet loading the image or its parameters: # Could be in the following tag: # <param name="FlashVars" \ # value="glassSize=2&height=189&width=267 \ # &initialZoomSize=100&file=sunflowers.jpg \ # &maxZoomSize=150&targetColor=#FF0000&borderWidth=12 #inner = i.find(name='div', attrs={'class' : 'iDevice_inner' }) #idevice.fields[0].content = inner.renderContents().decode('utf-8') #idevice.fields[0].content_w_resourcePaths = inner.renderContents().decode('utf-8') #idevice.fields[0].content_wo_resourcePaths = inner.renderContents().decode('utf-8') def upgradeToVersion1(self): """ Upgrades to v0.14 """ self._alignInstruc = x_(u"""Alignment allows you to choose where on the screen the image will be positioned.""") self._initialZoomInstruc = x_(u"""Set the initial level of zoom when the IDevice loads, as a percentage of the original image size""") self._maxZoomInstruc = x_(u"""Set the maximum level of zoom, as a percentage of the original image size""") self._glassSizeInstruc = x_(u"""This chooses the initial size of the magnifying glass""") def upgradeToVersion2(self): """ Upgrades to v0.24 """ self.imageMagnifier.isDefaultImage = False def upgradeToVersion3(self): """ Adds group to idevice """ self.group = Idevice.Media
class ExternalUrlIdevice(Idevice): """ ExternalUrlIdevice: just has a field for the url """ persistenceVersion = 3 def __init__(self, content=""): Idevice.__init__( self, x_(u"External Web Site"), x_(u"University of Auckland"), x_(u"""The external website iDevice loads an external website into an inline frame in your eXe content rather then opening it in a popup box. This means learners are not having to juggle windows. This iDevice should only be used if your content will be viewed by learners online."""), "", "") self.emphasis = Idevice.NoEmphasis self.url = "" self.height = "300" self._urlInstruc = x_(u"""Enter the URL you wish to display and select the size of the area to display it in.""") #Properties urlInstruc = lateTranslate('urlInstruc') def getResourcesField(self, this_resource): """ implement the specific resource finding mechanism for this iDevice: """ # NOTE that the ExternalURL iDevice has NO additional resources: return None def getRichTextFields(self): """ Like getResourcesField(), a general helper to allow nodes to search through all of its fields without having to know the specifics of each iDevice type. """ # ExternalURL has no rich-text fields: return [] def burstHTML(self, i): """ takes a BeautifulSoup fragment (i) and bursts its contents to import this idevice from a CommonCartridge export """ # External Web Site Idevice: #title = i.find(name='span', attrs={'class' : 'iDeviceTitle' }) #idevice.title = title.renderContents().decode('utf-8') # no title for this iDevice. inner = i.find(name='iframe').__str__() # 1. the url: <iframe src="HERE" ... ></iframe> url_start_pos = inner.find('src=\"') if url_start_pos >= 0: url_start_pos += len('src=\"') url_end_pos = inner.find('\"', url_start_pos) if url_end_pos >= url_start_pos: self.url = inner[url_start_pos:url_end_pos].decode('utf-8') # 2. the height: <iframe height="###px" ... ></iframe> height_start_pos = inner.find('height=\"') if height_start_pos >= 0: height_start_pos += len('height=\"') height_end_pos = inner.find('px\"', height_start_pos) if height_end_pos >= height_start_pos: self.height = \ inner[height_start_pos : height_end_pos].decode('utf-8') def upgradeToVersion1(self): """ Upgrades exe to v0.10 """ self._upgradeIdeviceToVersion1() def upgradeToVersion2(self): """ Upgrades to v0.12 """ self._upgradeIdeviceToVersion2() def upgradeToVersion3(self): """ add _urlInstruc """ self._urlInstruc = x_(u"""Enter the URL you wish to display and select the size of the area to display it in.""")
class ReflectionfpdIdevice(Idevice): """ A Reflection Idevice presents question/s for the student to think about before they look at the answer/s """ persistenceVersion = 7 def __init__(self, activity="", answer=""): """ Initialize """ Idevice.__init__( self, x_(u"FPD - Reflexiona (con Retroalimentacion)"), x_(u"University of Auckland"), x_(u"""Reflection is a teaching method often used to connect theory to practice. Reflection tasks often provide learners with an opportunity to observe and reflect on their observations before presenting these as a piece of academic work. Journals, diaries, profiles and portfolios are useful tools for collecting observation data. Rubrics and guides can be effective feedback tools."""), u"", u"reflexionfpd") # self.emphasis = Idevice.SomeEmphasis self.emphasis = "_reflexionfpd" self._activityInstruc = x_(u"""Enter a question for learners to reflect upon.""") self._answerInstruc = x_(u"""Describe how learners will assess how they have done in the exercise. (Rubrics are useful devices for providing reflective feedback.)""") self.systemResources += ["common.js"] self.activityTextArea = TextAreaField(x_(u'Reflective question:'), self._activityInstruc, activity) self.activityTextArea.idevice = self self.answerTextArea = TextAreaField(x_(u'Feedback:'), self._answerInstruc, answer) self.answerTextArea.idevice = self # Properties activityInstruc = lateTranslate('activityInstruc') answerInstruc = lateTranslate('answerInstruc') def getResourcesField(self, this_resource): """ implement the specific resource finding mechanism for this iDevice: """ # be warned that before upgrading, this iDevice field could not exist: if hasattr(self, 'activityTextArea')\ and hasattr(self.activityTextArea, 'images'): for this_image in self.activityTextArea.images: if hasattr(this_image, '_imageResource') \ and this_resource == this_image._imageResource: return self.activityTextArea # be warned that before upgrading, this iDevice field could not exist: if hasattr(self, 'answerTextArea')\ and hasattr(self.answerTextArea, 'images'): for this_image in self.answerTextArea.images: if hasattr(this_image, '_imageResource') \ and this_resource == this_image._imageResource: return self.answerTextArea return None def getRichTextFields(self): """ Like getResourcesField(), a general helper to allow nodes to search through all of their fields without having to know the specifics of each iDevice type. """ fields_list = [] if hasattr(self, 'activityTextArea'): fields_list.append(self.activityTextArea) if hasattr(self, 'answerTextArea'): fields_list.append(self.answerTextArea) return fields_list def burstHTML(self, i): """ takes a BeautifulSoup fragment (i) and bursts its contents to import this idevice from a CommonCartridge export """ # Reflection Idevice: title = i.find(name='span', attrs={'class': 'iDeviceTitle'}) self.title = title.renderContents().decode('utf-8') reflections = i.findAll(name='div', attrs={'id': re.compile('^ta')}) # should be exactly two of these: # 1st = field[0] == Activity if len(reflections) >= 1: self.activityTextArea.content_wo_resourcePaths = \ reflections[0].renderContents().decode('utf-8') # and add the LOCAL resource paths back in: self.activityTextArea.content_w_resourcePaths = \ self.activityTextArea.MassageResourceDirsIntoContent( \ self.activityTextArea.content_wo_resourcePaths) self.activityTextArea.content = \ self.activityTextArea.content_w_resourcePaths # 2nd = field[1] == Answer if len(reflections) >= 2: self.answerTextArea.content_wo_resourcePaths = \ reflections[1].renderContents().decode('utf-8') # and add the LOCAL resource paths back in: self.answerTextArea.content_w_resourcePaths = \ self.answerTextArea.MassageResourceDirsIntoContent( \ self.answerTextArea.content_wo_resourcePaths) self.answerTextArea.content = \ self.answerTextArea.content_w_resourcePaths def upgradeToVersion1(self): """ Upgrades the node from version 0 to 1. """ log.debug(u"Upgrading iDevice") self.icon = u"reflexionfpd" def upgradeToVersion2(self): """ Upgrades the node from 1 (v0.5) to 2 (v0.6). Old packages will loose their icons, but they will load. """ log.debug(u"Upgrading iDevice") # self.emphasis = Idevice.SomeEmphasis self.emphasis = "_reflexionfpd" def upgradeToVersion3(self): """ Upgrades v0.6 to v0.7. """ self.lastIdevice = False def upgradeToVersion4(self): """ Upgrades to exe v0.10 """ self._upgradeIdeviceToVersion1() self._activityInstruc = self.__dict__['activityInstruc'] self._answerInstruc = self.__dict__['answerInstruc'] def upgradeToVersion5(self): """ Upgrades to exe v0.10 """ self._upgradeIdeviceToVersion1() def upgradeToVersion6(self): """ Upgrades to v0.12 """ self._upgradeIdeviceToVersion2() self.systemResources += ["common.js"] def upgradeToVersion7(self): """ Upgrades to somewhere before version 0.25 (post-v0.24) Taking the old unicode string fields, and converting them into image-enabled TextAreaFields: """ self.activityTextArea = TextAreaField(x_(u'Reflective question:'), self._activityInstruc, self.activity) self.activityTextArea.idevice = self self.answerTextArea = TextAreaField(x_(u'Feedback:'), self._answerInstruc, self.answer) self.answerTextArea.idevice = self
class MultichoiceIdevice(Idevice): """ A multichoice Idevice is one built up from question and options """ persistenceVersion = 7 def __init__(self, question=""): """ Initialize """ Idevice.__init__(self, x_(u"Multi-choice"), x_(u"University of Auckland"), x_(u"""Although more often used in formal testing situations MCQs can be used as a testing tool to stimulate thought and discussion on topics students may feel a little reticent in responding to. When designing a MCQ test consider the following: <ul> <li> What learning outcomes are the questions testing</li> <li> What intellectual skills are being tested</li> <li> What are the language skills of the audience</li> <li> Gender and cultural issues</li> <li> Avoid grammar language and question structures that might provide clues</li> </ul> """), x_(u"""When building an MCQ consider the following: <ul> <li> Use phrases that learners are familiar with and have encountered in their study </li> <li> Keep responses concise </li> <li> There should be some consistency between the stem and the responses </li> <li> Provide enough options to challenge learners to think about their response </li> <li> Try to make sure that correct responses are not more detailed than the distractors </li> <li> Distractors should be incorrect but plausible </li> </ul> """), u"question") self.emphasis = Idevice.SomeEmphasis self.questions = [] self.options = [] # question and hint appear to be left over, and no longer applicable, # since they are now on a per-question basis, and part of the child # QuizQuestionField, but here their old attributes remain: self.question = "" self.hint = "" # eventually: somebody should confirm this and remove them, will you? self._hintInstruc = x_(u"""Enter a hint here. If you do not want to provide a hint, leave this field blank.""") self._questionInstruc = x_(u"""Enter the question stem. The quest should be clear and unambiguous. Avoid negative premises as these can tend to be ambiguous.""") self._keyInstruc = x_(u"""Select the correct option by clicking on the radio button.""") self._answerInstruc = x_(u"""Enter the available choices here. You can add options by clicking the "Add Another Option" button. Delete options by clicking the red "X" next to the Option.""") self._feedbackInstruc = x_(u"""Type in the feedback that you want the student to see when selecting the particular option. If you don't complete this box, eXe will automatically provide default feedback as follows: "Correct answer" as indicated by the selection for the correct answer; or "Wrong answer" for the other options.""") self.systemResources += ["common.js", "libot_drag.js", "panel-amusements.png", "stock-stop.png"] self.message = "" self.addQuestion() # Properties hintInstruc = lateTranslate('hintInstruc') questionInstruc = lateTranslate('questionInstruc') keyInstruc = lateTranslate('keyInstruc') answerInstruc = lateTranslate('answerInstruc') feedbackInstruc = lateTranslate('feedbackInstruc') def addQuestion(self): """ Add a new question to this iDevice. """ question = QuizQuestionField(self, x_(u'Question')) question.addOption() self.questions.append(question) def getResourcesField(self, this_resource): """ implement the specific resource finding mechanism for this iDevice: """ for this_question in self.questions: this_field = this_question.getResourcesField(this_resource) if this_field is not None: return this_field return None def getRichTextFields(self): """ Like getResourcesField(), a general helper to allow nodes to search through all of their fields without having to know the specifics of each iDevice type. """ fields_list = [] for this_question in self.questions: fields_list.extend(this_question.getRichTextFields()) return fields_list def burstHTML(self, i): """ takes a BeautifulSoup fragment (i) and bursts its contents to import this idevice from a CommonCartridge export """ # Multi-choice Idevice: title = i.find(name='h2', attrs={'class' : 'iDeviceTitle' }) self.title = title.renderContents().decode('utf-8') inner = i.find(name='div', attrs={'class' : 'iDevice_inner' }) # copied and modified from CaseStudy: mc_questions = inner.findAll(name='div', attrs={'class' : 'question'}) if len(mc_questions) < 1: # need to remove the default 1st question del self.questions[0] for question_num in range(len(mc_questions)): if question_num > 0: # only created with the first question, add others: self.addQuestion() question = mc_questions[question_num] questions = question.findAll(name='div', attrs={'class' : 'block' , 'id' : re.compile('^taquestion') }) if len(questions) == 1: # ELSE: should warn of unexpected result! inner_question = questions[0] self.questions[question_num].questionTextArea.content_wo_resourcePaths \ = inner_question.renderContents().decode('utf-8') # and add the LOCAL resource paths back in: self.questions[question_num].questionTextArea.content_w_resourcePaths \ = self.questions[question_num].questionTextArea.MassageResourceDirsIntoContent( \ self.questions[question_num].questionTextArea.content_wo_resourcePaths) self.questions[question_num].questionTextArea.content = \ self.questions[question_num].questionTextArea.content_w_resourcePaths hints = question.findAll(name='div', attrs={'class' : 'block' , 'id' : re.compile('^tahint') }) if len(hints) == 1: # no warning otherwise, since hint is optional inner_hint = hints[0] self.questions[question_num].hintTextArea.content_wo_resourcePaths \ = inner_hint.renderContents().decode('utf-8') # and add the LOCAL resource paths back in: self.questions[question_num].hintTextArea.content_w_resourcePaths \ = self.questions[question_num].hintTextArea.MassageResourceDirsIntoContent( \ self.questions[question_num].hintTextArea.content_wo_resourcePaths) self.questions[question_num].hintTextArea.content = \ self.questions[question_num].hintTextArea.content_w_resourcePaths else: self.questions[question_num].hintTextArea.content = "" self.questions[question_num].hintTextArea.content_w_resourcePaths \ = "" self.questions[question_num].hintTextArea.content_wo_resourcePaths \ = "" options = question.findAll(name='div', attrs={'class' : 'block' , 'id' : re.compile('^taans') }) feedbacks = question.findAll(name='div', attrs={'id' : re.compile('^sa') }) # the feedbacks now have the correctness # of the option embedded as well, in the even_steven :-) ##### # NOTE also that feedbacks will just have Correct or Wrong # (perhaps translated? but no divs, that's the important part!) # but if one was defined, it will appear in a div such as: # <div id="taf18_5" class="block" style="display:block">FB</div> if len(options) < 1: # need to remove the default 1st option del self.questions[question_num].options[0] for option_loop in range(0, len(options)): if option_loop >= 1: # more options than created by default: self.questions[question_num].addOption() self.questions[question_num].options[option_loop].answerTextArea.content_wo_resourcePaths \ = options[option_loop].renderContents().decode('utf-8') # and add the LOCAL resource paths back in: self.questions[question_num].options[option_loop].answerTextArea.content_w_resourcePaths \ = self.questions[question_num].options[option_loop].answerTextArea.MassageResourceDirsIntoContent( \ self.questions[question_num].options[option_loop].answerTextArea.content_wo_resourcePaths) self.questions[question_num].options[option_loop].answerTextArea.content \ = self.questions[question_num].options[option_loop].answerTextArea.content_w_resourcePaths inner_feedback = feedbacks[option_loop].find(name='div', id=re.compile('^taf')) if inner_feedback: self.questions[question_num].options[option_loop].feedbackTextArea.content_wo_resourcePaths \ = inner_feedback.renderContents().decode('utf-8') # and add the LOCAL resource paths back in: self.questions[question_num].options[option_loop].feedbackTextArea.content_w_resourcePaths \ = self.questions[question_num].options[option_loop].feedbackTextArea.MassageResourceDirsIntoContent( \ self.questions[question_num].options[option_loop].feedbackTextArea.content_wo_resourcePaths) self.questions[question_num].options[option_loop].feedbackTextArea.content \ = self.questions[question_num].options[option_loop].feedbackTextArea.content_w_resourcePaths else: # no user-defined feedback, just using the default: self.questions[question_num].options[option_loop].feedbackTextArea.content \ = "" self.questions[question_num].options[option_loop].feedbackTextArea.content_w_resourcePaths \ = "" self.questions[question_num].options[option_loop].feedbackTextArea.content_wo_resourcePaths \ = "" # and finally, see if this is a correct answer: even_score = int(feedbacks[option_loop].attrMap['even_steven']) if not (even_score % 2): # i.e., if it IS even, then this is correct: self.questions[question_num].options[option_loop].isCorrect \ = True def upgradeToVersion1(self): """ Called to upgrade from 0.4 release """ self.hint = "" self.icon = "multichoice" self.__dict__['hintInstruc'] = \ x_(u"Enter a hint here. If you do not want to provide a " u"hint, leave this field blank.") def upgradeToVersion2(self): """ Upgrades the node from 1 (v0.5) to 2 (v0.6). Old packages will loose their icons, but they will load. """ log.debug(u"Upgrading iDevice") self.emphasis = Idevice.SomeEmphasis def upgradeToVersion3(self): """ Upgrades the node from 1 (v0.6) to 2 (v0.7). Change icon from 'multichoice' to 'question' """ log.debug(u"Upgrading iDevice icon") self.icon = "question" def upgradeToVersion4(self): """ Upgrades v0.6 to v0.7. """ self.lastIdevice = False def upgradeToVersion5(self): """ Upgrades to exe v0.10 """ self._upgradeIdeviceToVersion1() self._hintInstruc = self.__dict__['hintInstruc'] self._questionInstruc = self.__dict__['questionInstruc'] self._keyInstruc = self.__dict__['keyInstruc'] self._answerInstruc = self.__dict__['answerInstruc'] self._feedbackInstruc = self.__dict__['feedbackInstruc'] def upgradeToVersion6(self): """ Upgrades to v0.12 """ self._upgradeIdeviceToVersion2() self.systemResources += ["common.js", "libot_drag.js", "panel-amusements.png", "stock-stop.png"] def upgradeToVersion7(self): """ Upgrades to v0.19 """ self.questions = [] length = len(self.options) if length >0: self.addQuestion() self.questions[0].hint = self.hint self.questions[0].question = self.question for i in range(1, length): self.questions[0].addOption() i += 1 for i in range(0, length): self.questions[0].options[i].answer = self.options[i].answer self.questions[0].options[i].feedback = self.options[i].feedback self.questions[0].options[i].isCorrect = self.options[i].isCorrect self.questions[0].options[i].question = self.questions[0] self.questions[0].options[i].idevice = self i += 1 self.question = "" self.options = [] self.hint = "" def upgradeTo8SafetyCheck(self): """ Handles the post-upgrade issues which require all of its child objects to have already been upgraded, not just this multichoiceidevice itself. But this is essentially a missing upgradeToVersion8 (as described in, and called by, its TwistedRePersist, to follow) """ if not hasattr(self, 'questions'): # not sure why it wouldn't even have this, but define it: self.questions = [] if len(self.questions) == 0: # no question defined yet, nothing to upgrade there: return if not hasattr(self.questions[0], 'question'): # does NOT have a self.questions[0].question anymore # -> already new enough. return if (self.questions[0].question != self.questions[0].questionTextArea.content): # .question is the original pre-upgrade string, # and .questionTextArea has been created to hold its content, # but is now created as the latest persistenceVersion,==1. # Therefore the actual data was not yet properly migrated # from .question to .questionTextArea, and this is caused by # forcing the upgrade call that we know to be needed: self.questions[0].upgradeToVersion1() # next up, ensure that each options has been properly upgraded: length = len(self.questions[0].options) if length >0: for i in range(0, length): # note that even though it is an older multichoice, # newer options could have been added, which might # not have the .answer or .feedback: if hasattr(self.questions[0].options[i], 'answer'): # from the original self.options[i].answer if (self.questions[0].options[i].answer != self.questions[0].options[i].answerTextArea): # As with .question -> .questionTextArea, .answer # was not yet properly migrated to .answerTextArea, # and this is caused by forcing the upgrade call # that we know to be needed: self.questions[0].options[i].upgradeToVersion1() # finally, delete the old answer: del self.questions[0].options[i].answer if hasattr(self.questions[0].options[i], 'feedback'): # and finally, delete the old feedback: del self.questions[0].options[i].feedback # finally, delete the old question and hint: if hasattr(self.questions[0], 'question'): del self.questions[0].question if hasattr(self.questions[0], 'hint'): del self.questions[0].hint def TwistedRePersist(self): """ Handles any post-upgrade issues (such as typically re-persisting non-persistent data) In this case, this is to handle a MultiChoiceIdevice Upgrade case that slipped between the cracks.... """ # A missing upgrade path should have been implemented as # upgradeToVersion8 # but it's now too late to put in a version8, # primarily because we have frozen our persistence versions: # as of v1.00, so that all >= v1.00 elps are compatible. # But rather than doing the missing upgrade right here, # after multichoiceidevice itself has gone through its other upgrades, # we need to ensure that all of its related objects have also gone # through their upgrades. # So, put this as an afterUpgradeHandler: G.application.afterUpgradeHandlers.append(self.upgradeTo8SafetyCheck)
class EditorPane(object): """ The EditorPane is responsible for creating new idevice """ def __init__(self, webServer, parent): """ Initialize JR: anado parente para poder acceder a algunos atributos de editorpag, en concreto a showHide """ self.ideviceStore = webServer.application.ideviceStore self.webDir = webServer.application.config.webDir self.styles = webServer.application.config.styleStore.getStyles() self.elements = [] self.idevice = GenericIdevice("", "", "", "", "") self.idevice.id = self.ideviceStore.getNewIdeviceId() self.originalIdevice = GenericIdevice("", "", "", "", "") self.purpose = "" self.tip = "" self.message = "" self.parent = parent self._nameInstruc = \ x_(u"Your new iDevice will appear in the iDevice " u"pane with this title. This is a compulsory field " u"and you will be prompted to enter a label if you try " u"to submit your iDevice without one.") self._authorInstruc = x_(u"This is an optional field.") self._purposeInstruc = x_( u"The purpose dialogue allows you to describe" u" your intended purpose of the iDevice to other" u" potential users.") self._emphasisInstruc = x_( u"Use this pulldown to select whether or not " u" the iDevice should have any formatting " u" applied to it to distinguish " u"it; ie. a border and an icon.") self._tipInstruc = x_(u"Use this field to describe " u"your intended use and the pedagogy behind " u"the device's development. This can be useful " u"if your iDevice is to be exported for others " u"to use.") self._lineInstruc = x_(u"Add a single text line to an iDevice. " u"Useful if you want the ability to place a " u"label within the iDevice.") self._textBoxInstruc = x_( u"Add a text entry box to an iDevice. " u"Used for entering larger amounts of textual " u"content.") self._feedbackInstruc = x_( u"Add an interactive feedback field to your iDevice.") self._flashInstruc = x_(u"Add a flash video to your iDevice.") self._mp3Instruc = x_(u"Add an mp3 file to your iDevice.") self._attachInstruc = x_(u"Add an attachment file to your iDevice.") self.style = self.styles[0] # Properties nameInstruc = lateTranslate('nameInstruc') authorInstruc = lateTranslate('authorInstruc') purposeInstruc = lateTranslate('purposeInstruc') emphasisInstruc = lateTranslate('emphasisInstruc') tipInstruc = lateTranslate('tipInstruc') lineInstruc = lateTranslate('lineInstruc') textBoxInstruc = lateTranslate('textBoxInstruc') feedbackInstruc = lateTranslate('feedbackInstruc') flashInstruc = lateTranslate('flashInstruc') mp3Instruc = lateTranslate('mp3Instruc') attachInstruc = lateTranslate('attachInstruc') def setIdevice(self, idevice): """ Sets the iDevice to edit """ self.idevice = idevice.clone() self.idevice.id = idevice.id self.originalIdevice = idevice def process(self, request, status): """ Process """ log.debug("process " + repr(request.args)) self.message = "" if status == "old": for element in self.elements: element.process(request) if "title" in request.args: self.idevice.title = unicode(request.args["title"][0], 'utf8') if "tip" in request.args: self.idevice.tip = unicode(request.args["tip"][0], 'utf8') if "emphasis" in request.args: self.idevice.emphasis = int(request.args["emphasis"][0]) if self.idevice.emphasis == 0: self.idevice.icon = "" if "addText" in request.args: field = TextField(_(u"Enter the label here"), _(u"Enter instructions for completion here")) field.setIDevice(self.idevice) self.idevice.addField(field) self.idevice.edit = True if "addTextArea" in request.args: field = TextAreaField( _(u"Enter the label here"), _(u"Enter the instructions for completion here")) field.setIDevice(self.idevice) self.idevice.addField(field) self.idevice.edit = True if "addFeedback" in request.args: field = FeedbackField( _(u"Enter the label here"), _(u"""Feedback button will not appear if no data is entered into this field.""")) field.setIDevice(self.idevice) self.idevice.addField(field) self.idevice.edit = True #if "addFlash" in request.args: #print "add a flash" #field = FlashField(_(u"Enter the label here"), #_(u"Enter the instructions for completion here")) #field.setIDevice(self.idevice) #self.idevice.addField(field) if "addMP3" in request.args: field = MultimediaField( _(u"Enter the label here"), _(u"Enter the instructions for completion here")) field.setIDevice(self.idevice) self.idevice.addField(field) if not 'xspf_player.swf' in self.idevice.systemResources: self.idevice.systemResources += ['xspf_player.swf'] self.idevice.edit = True if "addAttachment" in request.args: field = AttachmentField( _(u"Enter the label here"), _(u"Enter the instructions for completion here")) field.setIDevice(self.idevice) self.idevice.addField(field) self.idevice.edit = True if ("action" in request.args and request.args["action"][0] == "selectIcon"): self.idevice.icon = request.args["object"][0] if "preview" in request.args: if self.idevice.title == "": self.message = _("Please enter<br />an idevice name.") else: self.idevice.edit = False if "edit" in request.args: self.idevice.edit = True if "cancel" in request.args: ideviceId = self.idevice.id self.idevice = self.originalIdevice.clone() self.idevice.id = ideviceId self.parent.showHide = False if ("action" in request.args and request.args["action"][0] == "changeStyle"): self.style = self.styles[int(request.args["object"][0])] self.__buildElements() def __buildElements(self): """ Building up element array """ self.elements = [] elementTypeMap = { TextField: TextEditorElement, TextAreaField: TextAreaEditorElement, ImageField: ImageEditorElement, FeedbackField: FeedbackEditorElement, MultimediaField: MultimediaEditorElement, FlashField: FlashEditorElement, AttachmentField: AttachmentEditorElement } for field in self.idevice.fields: elementType = elementTypeMap.get(field.__class__) if elementType: # Create an instance of the appropriate element class log.debug(u"createElement " + elementType.__class__.__name__ + u" for " + field.__class__.__name__) self.elements.append(elementType(field)) else: log.error(u"No element type registered for " + field.__class__.__name__) def renderButtons(self, request): """ Render the idevice being edited """ html = "<font color=\"red\"><b>" + self.message + "</b></font>" html += "<fieldset><legend><b>" + _("Add Field") + "</b></legend>" html += common.submitButton("addText", _("Text Line")) html += common.elementInstruc(self.lineInstruc) + "<br/>" html += common.submitButton("addTextArea", _("Text Box")) html += common.elementInstruc(self.textBoxInstruc) + "<br/>" html += common.submitButton("addFeedback", _("Feedback")) html += common.elementInstruc(self.feedbackInstruc) + "<br/>" # Attachments are now embeddable: #html += common.submitButton("addAttachment", _("Attachment")) #html += common.elementInstruc(self.attachInstruc) + "<br/>" # MP3 fields are now embeddable: #html += common.submitButton("addMP3", _("MP3")) #html += common.elementInstruc(self.mp3Instruc) + "<br/>" html += "</fieldset>\n" html += "<fieldset><legend><b>" + _("Actions") + "</b></legend>" if self.idevice.edit: html += common.submitButton("preview", _("Preview"), not self.parent.isGeneric) else: html += common.submitButton("edit", _("Edit")) html += "<br/>" html += common.submitButton("cancel", _("Cancel")) #html += "</fieldset>" return html def renderIdevice(self, request): """ Returns an XHTML string for rendering the new idevice """ html = "<div id=\"editorWorkspace\">\n" html += "<script type=\"text/javascript\">\n" html += "<!--\n" html += """ function selectStyleIcon(icon,e) { var div = document.getElementById("styleIcons"); var imgs = div.getElementsByTagName("IMG"); for (var i=0;i<imgs.length;i++) { imgs[i].style.border = "1px solid #E8E8E8"; } e.style.border = "1px solid #333333"; submitLink("selectIcon",icon,1); } function submitLink(action, object, changed) { var form = document.getElementById("contentForm") form.action.value = action; form.object.value = object; form.isChanged.value = changed; form.submit(); }\n""" html += """ function submitIdevice() { var form = document.getElementById("contentForm") if (form.ideviceSelect.value == "newIdevice") form.action.value = "newIdevice" else form.action.value = "changeIdevice" form.object.value = form.ideviceSelect.value; form.isChanged.value = 1; form.submit(); }\n""" html += """ function submitStyle() { var form = document.getElementById("contentForm") form.action.value = "changeStyle"; form.object.value = form.styleSelect.value; form.isChanged.value = 0; form.submit(); }\n""" html += "//-->\n" html += "</script>\n" self.purpose = self.idevice.purpose.replace("\r", "") self.purpose = self.purpose.replace("\n", "\\n") self.tip = self.idevice.tip.replace("\r", "") self.tip = self.tip.replace("\n", "\\n") if self.idevice.edit: html += "<b>" + _("Name") + ": </b>\n" html += common.elementInstruc(self.nameInstruc) + "<br/>" html += '<input type="text" name= "title" id="title" value="%s"/>' % self.idevice.title this_package = None html += common.formField('richTextArea', this_package, _(u"Pedagogical Tip"), 'tip', '', self.tipInstruc, self.tip) html += "<b>" + _("Emphasis") + ":</b> " html += "<select onchange=\"submit();\" name=\"emphasis\">\n" emphasisValues = { Idevice.NoEmphasis: _(u"No emphasis"), Idevice.SomeEmphasis: _(u"Some emphasis") } for value, description in emphasisValues.items(): html += "<option value=\"" + unicode(value) + "\" " if self.idevice.emphasis == value: html += "selected " html += ">" + description + "</option>\n" html += "</select> \n" html += common.elementInstruc(self.emphasisInstruc) html += "<br/><br/>\n" if self.idevice.emphasis > 0: html += self.__renderStyles() + " " html += u'<a style="margin-right:.5em" href="javascript:void(0)" ' html += u'onclick="showMessageBox(\'iconpanel\');">' html += u'%s</a>' % _('Select an icon') icon = self.idevice.icon if icon != "": iconExists = False myIcon = Path(G.application.config.stylesDir / self.style.get_dirname() / "icon_" + self.idevice.icon + ".gif") if myIcon.exists(): iconExists = True else: myIcon = Path(G.application.config.stylesDir / self.style.get_dirname() / "icon_" + self.idevice.icon + ".png") if myIcon.exists(): iconExists = True if iconExists: html += '<img style="vertical-align:middle;max-width:60px;height:auto" ' html += 'src="/style/%s/icon_%s' % ( self.style.get_dirname(), icon) html += '%s"/><br />' % myIcon.ext html += u'<div style="display:none;z-index:99;">' html += u'<div id="iconpaneltitle">' + _("Icons") + '</div>' html += u'<div id="iconpanelcontent">' html += self.__renderIcons() html += u'</div>' html += u'</div>\n' html += u'<br style="clear:both;margin-bottom:10px" />' for element in self.elements: html += element.renderEdit() else: html += "<b>" + self.idevice.title + "</b><br/><br/>" for element in self.elements: html += element.renderPreview() if self.idevice.purpose != "" or self.idevice.tip != "": html += "<a title=\"" + _("Pedagogical Help") + "\" " html += "onclick=\"showMessageBox('phelp');\" " html += "href=\"javascript:void(0)\" style=\"cursor:help;\">\n " html += '<img alt="%s" src="/images/info.png" border="0" ' % _( 'Info') html += "align=\"middle\" /></a>\n" html += "<div style='display:none;'>" if self.idevice.purpose != "": html += "<div id='phelptitle'>" + _('Purpose') + "</div>" html += "<div id='phelpcontent'>" + self.purpose + "</div>" if self.idevice.tip != "": html += "<div id='phelptitle'>" + _('Tip:') + "</div>" html += "<div id='phelpcontent'>" + self.idevice.tip + "</div>" html += "</div>" html += "</div>\n" html += "</div>\n" self.message = "" return html def __renderStyles(self): """ Return xhtml string for rendering styles select """ html = '<select onchange="submitStyle();" style="display:none" name="styleSelect" id="styleSelect">\n' idx = 0 isSelected = False for style in self.styles: html += "<option value='%d' " % idx if self.style.get_name() == style.get_name(): html += "selected " isSelected = True html += ">" + style.get_name() + "~" + style.get_dirname( ) + "</option>\n" idx = idx + 1 html += "</select> \n" # Auto-select the current style # This should be done with Python, not JavaScript # It's just a provisional solution so the user does not have to choose the right Style html += "<script type='text/javascript'>\ function autoSelectStyle(){\ var autoSelectStyle = document.getElementById('styleSelect');\ if (autoSelectStyle.options[autoSelectStyle.value].innerHTML.split('~')[1]==top.exe_style_dirname) return false;\ var currentStyleFolder;\ for (var i=0; i<autoSelectStyle.options.length; i++) {\ currentStyleFolder = autoSelectStyle.options[i].innerHTML.split('~')[1];\ if (top.exe_style_dirname==currentStyleFolder) {\ autoSelectStyle.value=i;\ submitStyle();\ }\ }\ }\ if (typeof(top.exe_style_dirname)!='undefined') {\ autoSelectStyle();\ }\ \ </script>" return html def __renderIcons(self): """ Return xhtml string for dispay all icons """ iconpath = self.style.get_style_dir() iconfiles = iconpath.files("icon_*") html = '<div id="styleIcons"><div style="height:300px;overflow:auto">' for iconfile in iconfiles: iconname = iconfile.namebase icon = iconname.split("_", 1)[1] iconExists = False iconExtension = "gif" myIcon = Path(G.application.config.stylesDir / self.style.get_dirname() / iconname + ".gif") if myIcon.exists(): iconExists = True else: myIcon = Path(G.application.config.stylesDir / self.style.get_dirname() / iconname + ".png") if myIcon.exists(): iconExists = True iconExtension = "png" if iconExists: filename = "/style/%s/%s.%s" % (self.style.get_dirname(), iconname, iconExtension) html += u'<div style="float:left; text-align:center; width:105px;\n' html += u'margin-right:10px; margin-bottom:15px" > ' html += u'<img src="%s" \n' % filename # html += u' alt="%s" ' % _("Submit") # window[1] because we use Ext.MessageBox instead of libot_drag.js html += u"style=\"border:1px solid #E8E8E8;padding:5px;cursor:pointer;max-width:60px;height:auto\" onclick=\"window[1].selectStyleIcon('%s',this)\" title=\"%s.%s\">\n" % ( icon, icon, iconExtension) # html += u"style=\"cursor:pointer\" onclick=\"window[1].submitLink('selectIcon','%s',1)\">\n" % icon html += u'<br /><span style="display:inline-block;width:100px;overflow:hidden;text-overflow:ellipsis">%s.%s</span></div>\n' % ( icon, iconExtension) html += '</div></div>' return html
class WikipediaIdevice(Idevice): """ A Wikipedia Idevice is one built from a Wikipedia article. """ persistenceVersion = 8 def __init__(self, defaultSite): Idevice.__init__(self, x_(u"Wiki Article"), x_(u"University of Auckland"), x_(u"""<p>The Wikipedia iDevice allows you to locate existing content from within Wikipedia and download this content into your eXe resource. The Wikipedia Article iDevice takes a snapshot copy of the article content. Changes in Wikipedia will not automatically update individual snapshot copies in eXe, a fresh copy of the article will need to be taken. Likewise, changes made in eXe will not be updated in Wikipedia. </p> <p>Wikipedia content is covered by the GNU free documentation license.</p>"""), u"", u"") self.emphasis = Idevice.NoEmphasis self.articleName = u"" self.article = TextAreaField(x_(u"Article")) self.article.idevice = self self.images = {} self.site = defaultSite self.icon = u"inter" self.systemResources += ["fdl.html"] self._langInstruc = x_(u"""Select the appropriate language version of Wikipedia to search and enter search term.""") self._searchInstruc = x_("""Enter a phrase or term you wish to search within Wikipedia.""") self.ownUrl = "" # Properties langInstruc = lateTranslate('langInstruc') searchInstruc = lateTranslate('searchInstruc') def loadArticle(self, name): """ Load the article from Wikipedia """ self.articleName = name url = "" name = urllib.quote(name.replace(" ", "_").encode('utf-8')) try: url = (self.site or self.ownUrl) if not url.endswith('/') and name <> '': url += '/' if '://' not in url: url = 'http://' + url url += name net = urllib.urlopen(url) page = net.read() net.close() except IOError, error: log.warning(unicode(error)) self.article.content = _(u"Unable to download from %s <br/>Please check the spelling and connection and try again.") % url self.article.content_w_resourcePaths = self.article.content self.article.content_wo_resourcePaths = self.article.content return page = unicode(page, "utf8") # FIXME avoid problems with numeric entities in attributes page = page.replace(u' ', u' ') # avoidParserProblems is set to False because BeautifulSoup's # cleanup was causing a "concatenating Null+Str" error, # and Wikipedia's HTML doesn't need cleaning up. # BeautifulSoup is faster this way too. soup = BeautifulSoup(page, False) content = soup.first('div', {'id': "content"}) # remove the wiktionary, wikimedia commons, and categories boxes # and the protected icon and the needs citations box if content: infoboxes = content.findAll('div', {'class' : 'infobox sisterproject'}) [infobox.extract() for infobox in infoboxes] catboxes = content.findAll('div', {'id' : 'catlinks'}) [catbox.extract() for catbox in catboxes] amboxes = content.findAll('table', {'class' : re.compile(r'.*\bambox\b.*')}) [ambox.extract() for ambox in amboxes] protecteds = content.findAll('div', {'id' : 'protected-icon'}) [protected.extract() for protected in protecteds] else: content = soup.first('body') if not content: log.error("no content") self.article.content = _(u"Unable to download from %s <br/>Please check the spelling and connection and try again.") % url # set the other elements as well self.article.content_w_resourcePaths = self.article.content self.article.content_wo_resourcePaths = self.article.content return # clear out any old images while self.userResources: self.userResources[0].delete() self.images = {} # Download the images bits = url.split('/') netloc = '%s//%s' % (bits[0], bits[2]) path = '/'.join(bits[3:-1]) tmpDir = TempDirPath() for imageTag in content.fetch('img'): imageSrc = unicode(imageTag['src']) imageName = imageSrc.split('/')[-1] # Search if we've already got this image if imageName not in self.images: if not imageSrc.startswith("http://"): if imageSrc.startswith("/"): imageSrc = netloc + imageSrc else: imageSrc = '%s/%s/%s' % (netloc, path, imageSrc) urllib.urlretrieve(imageSrc, tmpDir/imageName) new_resource = Resource(self, tmpDir/imageName) if new_resource._storageName != imageName: # looks like it was changed due to a possible conflict, # so reset the imageName accordingly for the content: imageName = new_resource._storageName self.images[imageName] = True # We have to use absolute URLs if we want the images to # show up in edit mode inside FCKEditor imageTag['src'] = (u"/" + self.parentNode.package.name + u"/resources/" + imageName) self.article.content = self.reformatArticle(netloc, unicode(content)) # now that these are supporting images, any direct manipulation # of the content field must also store this updated information # into the other corresponding fields of TextAreaField: # (perhaps eventually a property should be made for TextAreaField # such that these extra set's are not necessary, but for now, here:) self.article.content_w_resourcePaths = self.article.content self.article.content_wo_resourcePaths = self.article.content
class FlashMovieIdevice(Idevice): """ A FlashMovie Idevice is one built up from a flash file and free text. """ persistenceVersion = 5 def __init__(self): Idevice.__init__( self, x_(u"Flash Movie"), x_(u"University of Auckland"), x_(u"""\ This iDevice only supports the Flash Video File (.FLV) format, and will not accept other video formats. You can however convert other movie formats (e.g. mov, wmf etc) into the .FLV format using third party encoders. These are not supplied with eXe. Users will also need to download the Flash 8 player from http://www.macromedia.com/ to play the video."""), u"", u"") self.emphasis = Idevice.NoEmphasis self.group = Idevice.Media self.flash = FlashMovieField(x_(u"Flash Movie")) self.flash.idevice = self self.text = TextAreaField( x_(u"Description"), x_("""Enter the text you wish to associate with the file.""")) self.text.idevice = self self.float = u"left" self.caption = "" self._captionInstruc = x_(u"""Provide a caption for the flash movie you have just inserted.""") self.systemResources += ['flowPlayer.swf'] # Properties captionInstruc = lateTranslate('captionInstruc') def getResourcesField(self, this_resource): """ implement the specific resource finding mechanism for this iDevice: """ # be warned that before upgrading, this iDevice field could not exist: if hasattr(self, 'flash') and hasattr(self.flash, 'flashResource'): if this_resource == self.flash.flashResource: return self.flash # be warned that before upgrading, this iDevice field could not exist: if hasattr(self, 'text') and hasattr(self.text, 'images'): for this_image in self.text.images: if hasattr(this_image, '_imageResource') \ and this_resource == this_image._imageResource: return self.text return None def getRichTextFields(self): """ Like getResourcesField(), a general helper to allow nodes to search through all of their fields without having to know the specifics of each iDevice type. """ fields_list = [] if hasattr(self, 'text'): fields_list.append(self.text) return fields_list def upgradeToVersion2(self): """ Upgrades to v0.12 """ self._upgradeIdeviceToVersion2() self.flash._upgradeFieldToVersion2() self.systemResources += ['videoContainer.swf'] def upgradeToVersion3(self): """ Upgrades to v0.14 """ self._captionInstruc = x_(u"""Provide a caption for the flash movie you have just inserted.""") self.flash._upgradeFieldToVersion3() def upgradeToVersion4(self): """ Upgrades to v0.23 """ self.systemResources.remove('videoContainer.swf') self.systemResources += ['flowPlayer.swf'] def upgradeToVersion5(self): """ Adds group to idevice """ self.group = Idevice.Media
class FlashWithTextIdevice(Idevice): """ A FlashWithText Idevice is one built up from a flash file and free text. """ persistenceVersion = 4 def __init__(self): Idevice.__init__( self, x_(u"Flash with Text"), x_(u"University of Auckland"), x_(u"""The flash with text idevice allows you to associate additional textual information to a flash file. This may be useful where you wish to provide educational instruction regarding the flash file the learners will view."""), u"", u"") self.emphasis = Idevice.NoEmphasis self.flash = FlashField(x_(u"Flash with Text"), u"") self.flash.idevice = self self.text = TextAreaField( x_(u"Description"), x_("""Enter the text you wish to associate with the image.""")) self.text.idevice = self self.float = u"left" self.caption = u"" self._captionInstruc = x_(u"""Provide a caption for the flash you have just inserted.""") self._dimensionInstruc = x_(u"""Enter the flash display dimensions (in pixels) and determine the alignment of the image on screen. The width and height dimensions will alter proportionally.""") # Properties captionInstruc = lateTranslate('captionInstruc') dimensionInstruc = lateTranslate('dimensionInstruc') def getResourcesField(self, this_resource): """ implement the specific resource finding mechanism for this iDevice: """ # be warned that before upgrading, this iDevice field could not exist: if hasattr(self, 'flash') and hasattr(self.flash, 'flashResource'): if this_resource == self.flash.flashResource: return self.flash # be warned that before upgrading, this iDevice field could not exist: if hasattr(self, 'text') and hasattr(self.text, 'images'): for this_image in self.text.images: if hasattr(this_image, '_imageResource') \ and this_resource == this_image._imageResource: return self.text return None def getRichTextFields(self): """ Like getResourcesField(), a general helper to allow nodes to search through all of their fields without having to know the specifics of each iDevice type. """ fields_list = [] if hasattr(self, 'text'): fields_list.append(self.text) return fields_list def upgradeToVersion1(self): """ Upgrades exe to v0.10 """ self._upgradeIdeviceToVersion1() def upgradeToVersion2(self): """ Upgrades to v0.12 """ self._upgradeIdeviceToVersion2() self.flash._upgradeFieldToVersion2() def upgradeToVersion3(self): """ Upgrades to v0.13 """ self._captionInstruc = x_(u"""Provide a caption for the flash you have just inserted.""") self._dimensionInstruc = x_(u"""Enter the flash display dimensions (in pixels) and determine the alignment of the image on screen. The width and height dimensions will alter proportionally.""") self.flash._upgradeFieldToVersion3() def NoLonger_upgradeToVersion4(self): """ NOTE: upgradeToVersion4 (from r3051) no longer enabled, because: FlashWithTextIdevice is now deprecated, leaving its functionality fully in place, but hidden from standard usage. Before doing the deprecation, however, there had previously been a very brief period of converting FlashWithTextIdevice -> FreeTextIdevice, since FreeText can hold embeddded .swf Flash Object media. It was decided, though, that the behaviour was just different enough that FlashWithTextIdevice should still be available, if needed. Leaving the persistence version to 4, for those few intermediate elps. """ G.application.afterUpgradeHandlers.append(self.convertToFreeText)
class WikipediaIdevice(Idevice): """ A Wikipedia Idevice is one built from a Wikipedia article. """ persistenceVersion = 9 def __init__(self, defaultSite): Idevice.__init__( self, x_(u"Wiki Article"), x_(u"University of Auckland"), x_(u"""<p>The Wikipedia iDevice allows you to locate existing content from within Wikipedia and download this content into your eXe resource. The Wikipedia Article iDevice takes a snapshot copy of the article content. Changes in Wikipedia will not automatically update individual snapshot copies in eXe, a fresh copy of the article will need to be taken. Likewise, changes made in eXe will not be updated in Wikipedia. </p> <p>Wikipedia content is covered by the GNU Free Documentation 1.2 License, and since 2009 additionally by the Creative Commons Attribution-ShareAlike 3.0 Unported License.</p>"""), u"", u"") self.emphasis = Idevice.NoEmphasis self.articleName = u"" self.article = TextAreaField(x_(u"Article")) self.article.idevice = self self.images = {} self.site = defaultSite self.icon = u"inter" self._langInstruc = x_(u"""Select the appropriate language version of Wikipedia to search and enter search term.""") self._searchInstruc = x_("""Enter a phrase or term you wish to search within Wikipedia.""") self.ownUrl = "" self.systemResources += ['exe_wikipedia.css'] # Properties langInstruc = lateTranslate('langInstruc') searchInstruc = lateTranslate('searchInstruc') def loadArticle(self, name): """ Load the article from Wikipedia """ self.articleName = name url = '' name = urllib.quote(name.replace(" ", "_").encode('utf-8')) # Get the full URL of the site url = self.site or self.ownUrl if not url.endswith('/') and name != '': url += '/' if '://' not in url: url = 'http://' + url url += name # Get the site content try: net = urllib.urlopen(url) page = net.read() net.close() except IOError, error: log.warning(unicode(error)) self.article.content = _( u"Unable to download from %s <br/>Please check the spelling and connection and try again." ) % url self.article.content_w_resourcePaths = self.article.content self.article.content_wo_resourcePaths = self.article.content return page = unicode(page, "utf8") # FIXME avoid problems with numeric entities in attributes page = page.replace(u' ', u' ') # avoidParserProblems is set to False because BeautifulSoup's # cleanup was causing a "concatenating Null+Str" error, # and Wikipedia's HTML doesn't need cleaning up. # BeautifulSoup is faster this way too. soup = BeautifulSoup(page, False) content = soup.first('div', {'id': "content"}) # Fix bug #1359: El estilo ITE no respeta ancho de página al exportar # a páginas web si se usa iDevice wikipedia content['id'] = "wikipedia-content" # remove the wiktionary, wikimedia commons, and categories boxes # and the protected icon and the needs citations box if content: infoboxes = content.findAll('div', {'class': 'infobox sisterproject'}) [infobox.extract() for infobox in infoboxes] catboxes = content.findAll('div', {'id': 'catlinks'}) [catbox.extract() for catbox in catboxes] amboxes = content.findAll('table', {'class': re.compile(r'.*\bambox\b.*')}) [ambox.extract() for ambox in amboxes] protecteds = content.findAll('div', {'id': 'protected-icon'}) [protected.extract() for protected in protecteds] # Extract HTML comments comments = content.findAll( text=lambda text: isinstance(text, Comment)) [comment.extract() for comment in comments] else: content = soup.first('body') # If we still don't have content it means there is a problem with the article if not content: log.error(u"No content on Wikipedia article: %s" % url) self.article.content = _( u"Unable to download from %s <br/>Please check the spelling and connection and try again." ) % url # Set the other elements as well self.article.content_w_resourcePaths = self.article.content self.article.content_wo_resourcePaths = self.article.content return # Clear out any old images while self.userResources: self.userResources[0].delete() self.images = {} # Download the images bits = url.split('/') netloc = '%s//%s' % (bits[0], bits[2]) path = '/'.join(bits[3:-1]) tmpDir = TempDirPath() # Fetch all images from content for imageTag in content.fetch('img'): # Get src and image filename imageSrc = unicode(imageTag['src']) imageName = imageSrc.split('/')[-1] imageName = imageName.replace('>', '>') imageName = imageName.replace('<', '<') imageName = imageName.replace('"', '"') imageName = imageName.replace(' ', '') imageName = imageName.replace('%2C', ',') imageName = imageName.replace('%22', '"') imageName = imageName.replace('%28', '(') imageName = imageName.replace('%29', ')') imageName = imageName.replace('%C3%A5', 'å') # Decode image name imageName = urllib.unquote(imageName) # Search if we've already got this image if imageName not in self.images: if not re.match("^http(s)?:\/\/", imageSrc): if imageSrc.startswith("/"): imageSrc = bits[0] + imageSrc else: imageSrc = '%s/%s/%s' % (netloc, path, imageSrc) try: # download with its original name... in ASCII: ## er... just because some repositories do not undestand no ascii names of files: imageName = imageName.encode('ascii', 'ignore') # If the image file doesn't have an extension try to guess it if not re.match('^.*\.(.){1,}', imageName): # Open a conecction with the image and get the headers online_resource = urllib.urlopen(imageSrc) image_info = online_resource.info() online_resource.close() # Try to guess extension from mimetype extension = mimetypes.guess_extension( image_info['content-type']) # Wikimedia uses mainly SVG images so we can safely say that # this image is in svg (if it wasn't if wouldn't be shown anyway) extension = extenion or '.svg' imageName = imageName + extension # Download image urllib.urlretrieve(imageSrc, tmpDir / imageName) # Add the new resource new_resource = Resource(self, tmpDir / imageName) except: log.error(u'Unable to download file: %s' % imageSrc) # If there is an exception try to get the next one continue if new_resource._storageName != imageName: # looks like it was changed due to a possible conflict, # so reset the imageName accordingly for the content: imageName = new_resource._storageName self.images[imageName] = True imageTag['src'] = u"resources/" + imageName self.article.content = self.reformatArticle(netloc, unicode(content)) # now that these are supporting images, any direct manipulation # of the content field must also store this updated information # into the other corresponding fields of TextAreaField: # (perhaps eventually a property should be made for TextAreaField # such that these extra set's are not necessary, but for now, here:) self.article.content_w_resourcePaths = self.article.content self.article.content_wo_resourcePaths = self.article.content
class ScormClozeField(FieldWithResources): """ This field handles a passage with words that the student must fill in And can now support multiple images (and any other resources) via tinyMCE """ regex = re.compile('(%u)((\d|[A-F]){4})', re.UNICODE) persistenceVersion = 3 # these will be recreated in FieldWithResources' TwistedRePersist: nonpersistant = ['content', 'content_wo_resourcePaths'] def __init__(self, name, instruc): """ Initialise """ FieldWithResources.__init__(self, name, instruc) self.parts = [] self._encodedContent = '' self.rawContent = '' self._setVersion2Attributes() def _setVersion2Attributes(self): """ Sets the attributes that were added in persistenceVersion 2 """ self.strictMarking = False self._strictMarkingInstruc = \ x_(u"<p>If left unchecked a small number of spelling and " "capitalization errors will be accepted. If checked only " "an exact match in spelling and capitalization will be accepted." "</p>" "<p><strong>For example:</strong> If the correct answer is " "<code>Elephant</code> then both <code>elephant</code> and " "<code>Eliphant</code> will be judged " "<em>\"close enough\"</em> by the algorithm as it only has " "one letter wrong, even if \"Check Capitilization\" is on." "</p>" "<p>If capitalization checking is off in the above example, " "the lowercase <code>e</code> will not be considered a " "mistake and <code>eliphant</code> will also be accepted." "</p>" "<p>If both \"Strict Marking\" and \"Check Capitalization\" " "are set, the only correct answer is \"Elephant\". If only " "\"Strict Marking\" is checked and \"Check Capitalization\" " "is not, \"elephant\" will also be accepted." "</p>") self.checkCaps = False self._checkCapsInstruc = \ x_(u"<p>If this option is checked, submitted answers with " "different capitalization will be marked as incorrect." "</p>") self.instantMarking = False self._instantMarkingInstruc = \ x_(u"""<p>If this option is set, each word will be marked as the learner types it rather than all the words being marked the end of the exercise.</p>""") # Property handlers def set_encodedContent(self, value): """ Cleans out the encoded content as it is passed in. Makes clean XHTML. """ for key, val in name2codepoint.items(): value = value.replace('&%s;' % key, unichr(val)) # workaround for Microsoft Word which incorrectly quotes font names value = re.sub(r'font-family:\s*"([^"]+)"', r'font-family: \1', value) parser = ScormClozeHTMLParser() parser.feed(value) parser.close() self.parts = parser.result encodedContent = '' for shown, hidden in parser.result: encodedContent += shown if hidden: encodedContent += ' <u>' encodedContent += hidden encodedContent += '</u> ' self._encodedContent = encodedContent # Properties encodedContent = property(lambda self: self._encodedContent, set_encodedContent) strictMarkingInstruc = lateTranslate('strictMarkingInstruc') checkCapsInstruc = lateTranslate('checkCapsInstruc') instantMarkingInstruc = lateTranslate('instantMarkingInstruc') def upgradeToVersion1(self): """ Upgrades to exe v0.11 """ self.autoCompletion = True self.autoCompletionInstruc = _(u"""Allow auto completion when user filling the gaps.""") def upgradeToVersion2(self): """ Upgrades to exe v0.12 """ Field.upgradeToVersion2(self) strictMarking = not self.autoCompletion del self.autoCompletion del self.autoCompletionInstruc self._setVersion2Attributes() self.strictMarking = strictMarking def upgradeToVersion3(self): """ Upgrades to somewhere before version 0.25 (post-v0.24) to reflect that ClozeField now inherits from FieldWithResources, and will need its corresponding fields populated from content. """ self.content = self.encodedContent self.content_w_resourcePaths = self.encodedContent self.content_wo_resourcePaths = self.encodedContent
class ParasabermasfpdIdevice(Idevice): """ El iDevice Para saber permite al alumnado ampliar conocimientos voluntarios para su aprendizaje """ persistenceVersion = 9 def __init__(self, activity="", answer=""): """ Initialize """ Idevice.__init__( self, x_(u"FPD - A Step Ahead"), x_(u"Jose Ramon Jimenez Reyes"), x_(u"""A Step Ahead is an iDevice that permits students widen their knowledge with further contents.""" ), u"", u"parasabermasfpd") # self.emphasis = Idevice.SomeEmphasis self.emphasis = "_parasabermasfpd" self._activityInstruc = x_( u"""Enter the text that will appear on this iDevice""") # self.systemResources += ["common.js"] self.activityTextArea = TextAreaField(x_(u'A Step Ahead Text'), self._activityInstruc, activity) self.activityTextArea.idevice = self # Properties activityInstruc = lateTranslate('activityInstruc') def getResourcesField(self, this_resource): """ implement the specific resource finding mechanism for this iDevice: """ # be warned that before upgrading, this iDevice field could not exist: if hasattr(self, 'activityTextArea')\ and hasattr(self.activityTextArea, 'images'): for this_image in self.activityTextArea.images: if hasattr(this_image, '_imageResource') \ and this_resource == this_image._imageResource: return self.activityTextArea return None def getRichTextFields(self): fields_list = [] if hasattr(self, 'activityTextArea'): fields_list.append(self.activityTextArea) return fields_list def burstHTML(self, i): # Parasabermasfpd Idevice: title = i.find(name='span', attrs={'class': 'iDeviceTitle'}) self.title = title.renderContents().decode('utf-8') reflections = i.findAll(name='div', attrs={'id': re.compile('^ta')}) # should be exactly two of these: # 1st = field[0] == Activity if len(reflections) >= 1: self.activityTextArea.content_wo_resourcePaths = \ reflections[0].renderContents().decode('utf-8') # and add the LOCAL resource paths back in: self.activityTextArea.content_w_resourcePaths = \ self.activityTextArea.MassageResourceDirsIntoContent( \ self.activityTextArea.content_wo_resourcePaths) self.activityTextArea.content = \ self.activityTextArea.content_w_resourcePaths def upgradeToVersion1(self): """ Upgrades the node from version 0 to 1. """ log.debug(u"Upgrading iDevice") self.icon = u"activity" def upgradeToVersion2(self): """ Upgrades the node from 1 (v0.5) to 2 (v0.6). Old packages will loose their icons, but they will load. """ log.debug(u"Upgrading iDevice") # self.emphasis = Idevice.SomeEmphasis self.emphasis = "_parasabermasfpd" def upgradeToVersion3(self): """ Upgrades v0.6 to v0.7. """ self.lastIdevice = False def upgradeToVersion4(self): """ Upgrades to exe v0.10 """ self._upgradeIdeviceToVersion1() self._activityInstruc = self.__dict__['activityInstruc'] def upgradeToVersion5(self): """ Upgrades to exe v0.10 """ self._upgradeIdeviceToVersion1() def upgradeToVersion6(self): """ Upgrades to v0.12 """ self._upgradeIdeviceToVersion2() # self.systemResources += ["common.js"] def upgradeToVersion7(self): """ Upgrades to somewhere before version 0.25 (post-v0.24) Taking the old unicode string fields, and converting them into image-enabled TextAreaFields: """ self.activityTextArea = TextAreaField(x_(u'A Step Ahead Text'), self._activityInstruc, self.activity) self.activityTextArea.idevice = self def upgradeToVersion8(self): """ Delete icon from system resources """ self._upgradeIdeviceToVersion3() def upgradeToVersion9(self): if self._title == u"FPD - Para Saber Mas": self._title = u"FPD - A Step Ahead" if self._purpose == u"""Para saber más es un iDevice que permite al alumnado ampliar conocimientos, siendo estos voluntarios para su aprendizaje.""": self._purpose = u"""A Step Ahead is an iDevice that permits students widen their knowledge with further contents.""" if self._activityInstruc == u"""Introduce el texto que aparecerá en este iDevice""": self._activityInstruc = u"""Enter the text that will appear on this iDevice""" if self.activityTextArea._name == u'Texto Para saber más': self.activityTextArea._name = u'A Step Ahead Text'
class CasestudyIdevice(Idevice): """ A multichoice Idevice is one built up from question and options """ persistenceVersion = 9 def __init__(self, story="", defaultImage=None): """ Initialize """ Idevice.__init__( self, x_(u"Case Study"), x_(u"University of Auckland"), x_(u"""A case study is a device that provides learners with a simulation that has an educational basis. It takes a situation, generally based in reality, and asks learners to demonstrate or describe what action they would take to complete a task or resolve a situation. The case study allows learners apply their own knowledge and experience to completing the tasks assigned. when designing a case study consider the following:<ul> <li> What educational points are conveyed in the story</li> <li> What preparation will the learners need to do prior to working on the case study</li> <li> Where the case study fits into the rest of the course</li> <li> How the learners will interact with the materials and each other e.g. if run in a classroom situation can teams be setup to work on different aspects of the case and if so how are ideas feed back to the class</li></ul>"""), "", u"casestudy") self.emphasis = Idevice.SomeEmphasis self._storyInstruc = x_(u"""Create the case story. A good case is one that describes a controversy or sets the scene by describing the characters involved and the situation. It should also allow for some action to be taken in order to gain resolution of the situation.""") self.storyTextArea = TextAreaField(x_(u'Story:'), self._storyInstruc, story) self.storyTextArea.idevice = self self.questions = [] self._questionInstruc = x_(u"""Describe the activity tasks relevant to the case story provided. These could be in the form of questions or instructions for activity which may lead the learner to resolving a dilemma presented. """) self._feedbackInstruc = x_(u"""Provide relevant feedback on the situation.""") if defaultImage is None: defaultImage = G.application.config.webDir / 'images' / DEFAULT_IMAGE self.defaultImage = toUnicode(defaultImage) self.addQuestion() # Properties storyInstruc = lateTranslate('storyInstruc') questionInstruc = lateTranslate('questionInstruc') feedbackInstruc = lateTranslate('feedbackInstruc') storyInstruc = lateTranslate('storyInstruc') questionInstruc = lateTranslate('questionInstruc') feedbackInstruc = lateTranslate('feedbackInstruc') def addQuestion(self): """ Add a new question to this iDevice. """ self.questions.append(Question(self)) def getResourcesField(self, this_resource): """ implement the specific resource finding mechanism for this iDevice: """ # be warned that before upgrading, this iDevice field could not exist: if hasattr(self, 'storyTextArea')\ and hasattr(self.storyTextArea, 'images'): for this_image in self.storyTextArea.images: if hasattr(this_image, '_imageResource') \ and this_resource == this_image._imageResource: return self.storyTextArea for this_question in self.questions: this_field = this_question.getResourcesField(this_resource) if this_field is not None: return this_field return None def getRichTextFields(self): """ Like getResourcesField(), a general helper to allow nodes to search through all of their fields without having to know the specifics of each iDevice type. """ fields_list = [] if hasattr(self, 'storyTextArea'): fields_list.append(self.storyTextArea) for this_question in self.questions: fields_list.extend(this_question.getRichTextFields()) return fields_list def burstHTML(self, i): """ takes a BeautifulSoup fragment (i) and bursts its contents to import this idevice from a CommonCartridge export """ # CaseStudy Idevice: title = i.find(name='h2', attrs={'class': 'iDeviceTitle'}) self.title = title.renderContents().decode('utf-8') inner = i.find(name='div', attrs={'class': 'iDevice_inner'}) story = inner.find(name='div', attrs={ 'class': 'block', 'id': re.compile('^ta') }) self.storyTextArea.content_wo_resourcePaths = \ story.renderContents().decode('utf-8') # and add the LOCAL resource paths back in: self.storyTextArea.content_w_resourcePaths = \ self.storyTextArea.MassageResourceDirsIntoContent( \ self.storyTextArea.content_wo_resourcePaths) self.storyTextArea.content = self.storyTextArea.content_w_resourcePaths case_questions = inner.findAll(name='div', attrs={'class': 'question'}) for question_num in range(len(case_questions)): if question_num > 0: # only created with the first question, add others: self.addQuestion() question = case_questions[question_num] case_stories = question.findAll(name='div', attrs={ 'class': 'block', 'id': re.compile('^taquesQuestion') }) if len(case_stories) == 1: # ELSE: should warn of unexpected result! inner_question = case_stories[0] self.questions[question_num].questionTextArea.content = \ inner_question.renderContents().decode('utf-8') self.questions[question_num].questionTextArea.content_w_resourcePaths \ = inner_question.renderContents().decode('utf-8') self.questions[question_num].questionTextArea.content_wo_resourcePaths \ = inner_question.renderContents().decode('utf-8') # and add the LOCAL resource paths back in: self.questions[question_num].questionTextArea.content_w_resourcePaths \ = self.questions[question_num].questionTextArea.MassageResourceDirsIntoContent( \ self.questions[question_num].questionTextArea.content_wo_resourcePaths) self.questions[question_num].questionTextArea.content = \ self.questions[question_num].questionTextArea.content_w_resourcePaths case_feedbacks = question.findAll(name='div', attrs={ 'class': 'feedback', 'id': re.compile('^sq') }) if len(case_feedbacks) == 1: # no warning otherwise, since feedback is optional inner_feedback = case_feedbacks[0] self.questions[question_num].feedbackTextArea.content_wo_resourcePaths \ = inner_feedback.renderContents().decode('utf-8') # and add the LOCAL resource paths back in: self.questions[question_num].feedbackTextArea.content_w_resourcePaths \ = self.questions[question_num].feedbackTextArea.MassageResourceDirsIntoContent( \ self.questions[question_num].feedbackTextArea.content_wo_resourcePaths) self.questions[question_num].feedbackTextArea.content = \ self.questions[question_num].feedbackTextArea.content_w_resourcePaths _btfeedBack = inner.find( name='input', attrs={'name': re.compile('^toggle-feedback-')}) else: self.questions[question_num].feedbackTextArea.content = "" self.questions[question_num].feedbackTextArea.content_w_resourcePaths \ = "" self.questions[question_num].feedbackTextArea.content_wo_resourcePaths \ = "" _btfeedBack = inner.find( name='input', attrs={'name': re.compile('^toggle-feedback-')}) def upgradeToVersion1(self): """ Upgrades the node from version 0 to 1. Old packages will loose their icons, but they will load. """ log.debug(u"Upgrading iDevice") self.icon = "casestudy" def upgradeToVersion2(self): """ Upgrades the node from 1 (v0.5) to 2 (v0.6). Old packages will loose their icons, but they will load. """ log.debug(u"Upgrading iDevice") self.emphasis = Idevice.SomeEmphasis def upgradeToVersion3(self): """ Upgrades v0.6 to v0.7. """ self.lastIdevice = False def upgradeToVersion4(self): """ Upgrades to exe v0.10 """ self._upgradeIdeviceToVersion1() self._storyInstruc = self.__dict__['storyInstruc'] self._questionInstruc = self.__dict__['questionInstruc'] self._feedbackInstruc = self.__dict__['feedbackInstruc'] def upgradeToVersion5(self): """ Upgrades to v0.12 """ self._upgradeIdeviceToVersion2() def upgradeToVersion6(self): """ Upgrades for v0.18 """ self.defaultImage = toUnicode(G.application.config.webDir / 'images' / DEFAULT_IMAGE) for question in self.questions: question.setupImage(self) def upgradeToVersion7(self): """ Upgrades to somewhere before version 0.25 (post-v0.24) Taking the old unicode string fields, and converting them into a image-enabled TextAreaFields: """ self.storyTextArea = TextAreaField(x_(u'Story:'), self._storyInstruc, self.story) self.storyTextArea.idevice = self for question in self.questions: question.questionTextArea = TextAreaField(u'', u'', question.question) question.questionTextArea.idevice = self question.feedbackTextArea = TextAreaField(u'', u'', question.feedback) question.feedbackTextArea.idevice = self def upgradeToVersion8(self): """ Converting CaseStudyIdevice's image -> embedded image in its feedback field, a TextField than can now hold embedded images. BUT - due to the inconsistent loading of the objects via unpickling, since the resources aren't necessarily properly loaded and upgraded, NOR is the package necessarily, as it might not even have a list of resources yet, all of this conversion code must be done in an afterUpgradeHandler (as perhaps should have been done for the previous upgradeToVersion7) """ G.application.afterUpgradeHandlers.append(self.embedImagesInFeedback) def upgradeToVersion9(self): """ Delete icon from system resources """ self._upgradeIdeviceToVersion3() def embedImagesInFeedback(self): """ Loop through each question, to call their conversion: CaseStudyIdevice's image -> embedded in its feedback field, now that its TextField can hold embeddded images. """ for question in self.questions: question.embedImageInFeedback()
class TrueFalseIdevice(Idevice): """ A TrueFalse Idevice is one built up from question and options """ persistenceVersion = 11 def __init__(self): """ Initialize """ Idevice.__init__( self, x_(u"True-False Question"), x_(u"University of Auckland"), x_(u"""True/false questions present a statement where the learner must decide if the statement is true. This type of question works well for factual information and information that lends itself to either/or responses."""), u"", u"question") self.emphasis = Idevice.SomeEmphasis self._hintInstruc = x_(u"""A hint may be provided to assist the learner in answering the question.""") self.questions = [] self._questionInstruc = x_(u"""Type the question stem. The question should be clear and unambiguous. Avoid negative premises as these can tend to be ambiguous.""") self._keyInstruc = "" self._feedbackInstruc = x_(u"""Enter any feedback you wish to provide to the learner. This field may be left blank. if this field is left blank default feedback will be provided.""") self.questions.append(TrueFalseQuestion(self)) self.systemResources += [ "common.js", "panel-amusements.png", "stock-stop.png" ] self.instructionsForLearners = TextAreaField( x_(u'Instructions'), x_(u"""Provide instruction on how the True/False Question should be completed."""), u'') self.instructionsForLearners.idevice = self # Properties hintInstruc = lateTranslate('hintInstruc') questionInstruc = lateTranslate('questionInstruc') keyInstruc = lateTranslate('keyInstruc') feedbackInstruc = lateTranslate('feedbackInstruc') def addQuestion(self): """ Add a new question to this iDevice. """ self.questions.append(TrueFalseQuestion(self)) def getResourcesField(self, this_resource): """ implement the specific resource finding mechanism for this iDevice: """ # be warned that before upgrading, this iDevice field could not exist: if hasattr(self, 'instructionsForLearners')\ and hasattr(self.instructionsForLearners, 'images'): for this_image in self.instructionsForLearners.images: if hasattr(this_image, '_imageResource') \ and this_resource == this_image._imageResource: return self.instructionsForLearners for this_question in self.questions: this_field = this_question.getResourcesField(this_resource) if this_field is not None: return this_field return None def getRichTextFields(self): """ Like getResourcesField(), a general helper to allow nodes to search through all of their fields without having to know the specifics of each iDevice type. """ fields_list = [] if hasattr(self, 'instructionsForLearners'): fields_list.append(self.instructionsForLearners) for this_question in self.questions: fields_list.extend(this_question.getRichTextFields()) return fields_list def burstHTML(self, i): """ takes a BeautifulSoup fragment (i) and bursts its contents to import this idevice from a CommonCartridge export """ # True-False Idevice: title = i.find(name='h2', attrs={'class': 'iDeviceTitle'}) self.title = title.renderContents().decode('utf-8') inner = i.find(name='div', attrs={'class': 'iDevice_inner'}) instruct = inner.find(name='div', attrs={ 'class': 'block', 'style': 'display:block' }) self.instructionsForLearners.content_wo_resourcePaths = \ instruct.renderContents().decode('utf-8') # and add the LOCAL resource paths back in: self.instructionsForLearners.content_w_resourcePaths = \ self.instructionsForLearners.MassageResourceDirsIntoContent( \ self.instructionsForLearners.content_wo_resourcePaths) self.instructionsForLearners.content = \ self.instructionsForLearners.content_w_resourcePaths # copied and modified from Multi-Select, and others :-) : tf_questions = inner.findAll(name='div', attrs={'class': 'question'}) if len(tf_questions) < 1: # need to remove the default 1st question del self.questions[0] for question_num in range(len(tf_questions)): if question_num > 0: # only created with the first question, add others: self.addQuestion() question = tf_questions[question_num] questions = question.findAll(name='div', attrs={ 'class': 'block', 'id': re.compile('^taquestion') }) if len(questions) == 1: # ELSE: should warn of unexpected result! inner_question = questions[0] self.questions[question_num].questionTextArea.content_wo_resourcePaths \ = inner_question.renderContents().decode('utf-8') # and add the LOCAL resource paths back in: self.questions[question_num].questionTextArea.content_w_resourcePaths \ = self.questions[question_num].questionTextArea.MassageResourceDirsIntoContent( \ self.questions[question_num].questionTextArea.content_wo_resourcePaths) self.questions[question_num].questionTextArea.content = \ self.questions[question_num].questionTextArea.content_w_resourcePaths answer_true = question.find(name='div', attrs={'id': re.compile('^s0b')}) answer_false = question.find(name='div', attrs={'id': re.compile('^s1b')}) # true-false only has 1 feedback per question: feedbacks = question.findAll(name='div', attrs={'id': re.compile('^sfb')}) # true-false only has 1 hint per question: hints = question.findAll(name='div', attrs={'id': re.compile('^tahint')}) # and finally, see if this is a correct answer: even_score = int(answer_true.attrMap['even_steven']) if not (even_score % 2): # i.e., if it IS even, then this is correct: self.questions[question_num].isCorrect = True if len(hints) >= 1: inner_hint = hints[0] self.questions[question_num].hintTextArea.content_wo_resourcePaths \ = inner_hint.renderContents().decode('utf-8') # and add the LOCAL resource paths back in: self.questions[question_num].hintTextArea.content_w_resourcePaths \ = self.questions[question_num].hintTextArea.MassageResourceDirsIntoContent( \ self.questions[question_num].hintTextArea.content_wo_resourcePaths) self.questions[question_num].hintTextArea.content = \ self.questions[question_num].hintTextArea.content_w_resourcePaths else: # no user-defined feedback, just using the default: self.questions[question_num].hintTextArea.content = "" self.questions[question_num].hintTextArea.content_w_resourcePaths \ = "" self.questions[question_num].hintTextArea.content_wo_resourcePaths \ = "" if len(feedbacks) >= 1: inner_feedback = feedbacks[0] self.questions[question_num].feedbackTextArea.content_wo_resourcePaths \ = inner_feedback.renderContents().decode('utf-8') # and add the LOCAL resource paths back in: self.questions[question_num].feedbackTextArea.content_w_resourcePaths \ = self.questions[question_num].feedbackTextArea.MassageResourceDirsIntoContent( \ self.questions[question_num].feedbackTextArea.content_wo_resourcePaths) self.questions[question_num].feedbackTextArea.content = \ self.questions[question_num].feedbackTextArea.content_w_resourcePaths else: # no user-defined feedback, just using the default: self.questions[question_num].feedbackTextArea.content = "" self.questions[question_num].feedbackTextArea.content_w_resourcePaths \ = "" self.questions[question_num].feedbackTextArea.content_wo_resourcePaths \ = "" def upgradeToVersion1(self): """ Upgrades the node from version 0 to 1. Old packages will loose their icons, but they will load. """ log.debug(u"Upgrading iDevice") self.icon = u"multichoice" def upgradeToVersion2(self): """ Upgrades the node from 1 (v0.5) to 2 (v0.6). Old packages will loose their icons, but they will load. """ log.debug(u"Upgrading iDevice") self.emphasis = Idevice.SomeEmphasis def upgradeToVersion3(self): """ Upgrades the node from 1 (v0.6) to 2 (v0.7). Change icon from 'multichoice' to 'question' """ log.debug(u"Upgrading iDevice icon") self.icon = "question" def upgradeToVersion4(self): """ Upgrades v0.6 to v0.7. """ self.lastIdevice = False def upgradeToVersion5(self): """ Upgrades exe to v0.10 """ self._upgradeIdeviceToVersion1() self._hintInstruc = self.__dict__['hintInstruc'] self._questionInstruc = self.__dict__['questionInstruc'] self._keyInstruc = self.__dict__['keyInstruc'] def upgradeToVersion6(self): """ Upgrades exe to v0.11 """ self._feedbackInstruc = x_(u"""Type in the feedback that you want the student to see when selecting the particular question. If you don't complete this box, eXe will automatically provide default feedback as follows: "Correct answer" as indicated by the selection for the correct answer; or "Wrong answer" for the other alternatives.""") def upgradeToVersion7(self): """ Upgrades to v0.12 """ self._upgradeIdeviceToVersion2() self.systemResources += [ "common.js", "libot_drag.js", "panel-amusements.png", "stock-stop.png" ] def upgradeToVersion8(self): """ Upgrades to v0.15 """ self.instructionsForLearners = TextAreaField( x_(u'Instructions'), x_(u"""Provide instruction on how the True/False Question should be completed."""), x_(u'Read the paragraph below and ' 'fill in the missing words.')) self.instructionsForLearners.idevice = self def upgradeToVersion9(self): """ Upgrades to somewhere before version 0.25 (post-v0.24) Taking the TrueFalseQuestions' old unicode string fields, and converting them into a image-enabled TextAreaFields: """ for question in self.questions: question.upgrade_setIdevice(self) def upgradeToVersion10(self): if "libot_drag.js" in self.systemResources: self.systemResources.remove("libot_drag.js") def upgradeToVersion11(self): """ Delete icon from system resources """ self._upgradeIdeviceToVersion3()
class RssIdevice(Idevice): """ An RSS Idevice is one built from a RSS feed. """ def __init__(self): Idevice.__init__( self, x_(u"RSS"), x_(u"Auckland University of Technology"), x_(u"""The RSS iDevice is used to provide new content to an individual users machine. Using this iDevice you can provide links from a feed you select for learners to view."""), u"", u"") self.emphasis = Idevice.NoEmphasis self.rss = TextAreaField(x_(u"RSS")) self.rss.idevice = self self.icon = u"inter" self._urlInstruc = x_(u"""Enter an RSS URL for the RSS feed you want to attach to your content. Feeds are often identified by a small graphic icon (often like this <img src="/images/feed-icon.png" />) or the text "RSS". Clicking on the icon or text label will display an RSS feed right in your browser. You can copy and paste the URL into this field. Alternately, right clicking on the link or graphic will open a menu box; click on COPY LINK LOCATION or Copy Shortcut. Back in eXe open the RSS bookmark iDevice and Paste the URL into the RSS URL field and click the LOAD button. This will extract the titles from your feed and display them as links in your content. From here you can edit the bookmarks and add instructions or additional learning information.""") self.url = "" # Properties urlInstruc = lateTranslate('urlInstruc') def getResourcesField(self, this_resource): """ implement the specific resource finding mechanism for this iDevice: """ # be warned that before upgrading, this iDevice field could not exist: if hasattr(self, 'rss') and hasattr(self.rss, 'images'): for this_image in self.rss.images: if hasattr(this_image, '_imageResource') \ and this_resource == this_image._imageResource: return self.rss return None def getRichTextFields(self): """ Like getResourcesField(), a general helper to allow nodes to search through all of their fields without having to know the specifics of each iDevice type. """ fields_list = [] if hasattr(self, 'rss'): fields_list.append(self.rss) return fields_list def loadRss(self, url): """ Load the rss """ content = "" try: rssDic = feedparser.parse(url) length = len(rssDic['entries']) if length > 0: for i in range(0, length): content += '<p><A href="%s">%s</A></P>' % ( rssDic['entries'][i].link, rssDic['entries'][i].title) except IOError, error: log.warning(unicode(error)) content += _( u"Unable to load RSS feed from %s <br/>Please check the spelling and connection and try again." ) % url if content == "": content += _( u"Unable to load RSS feed from %s <br/>Please check the spelling and connection and try again." ) % url self.rss.content = unicode(content) # now that these are supporting images, any direct manipulation # of the content field must also store this updated information # into the other corresponding fields of TextAreaField: # (perhaps eventually a property should be made for TextAreaField # such that these extra set's are not necessary, but for now, here:) self.rss.content_w_resourcePaths = self.rss.content self.rss.content_wo_resourcePaths = self.rss.content
class AttachmentIdevice(Idevice): """ An Attachment Idevice allows a file to be attached to a package. """ persistenceVersion = 4 # 2013-11-04, JRF: added .odp examples to the .ppt references def __init__(self): Idevice.__init__( self, x_(u"Attachment"), x_(u"University of Auckland"), x_(u"The attachment iDevice is used to attach " "existing files to your .elp content. For example, " "you might have a PDF file or a PPT or .ODP presentation " "file that you wish the learners to have access " "to, these can be attached and labeled to indicate " "what the attachment is and how large the file is. " "Learners can click on the attachment link and can " "download the attachment."), u"", u"") self.emphasis = Idevice.NoEmphasis self.label = u'' self._descriptionInstruc = x_(u"Enter the text you wish to associate " u"with the downloaded file. You might " u"want to provide instructions on what " u"you require the learner to do once " u"the file is downloaded or how the " u"material should be used.") self.descriptionTextArea = TextAreaField(x_(u'Description:'), self._descriptionInstruc, u'') self.descriptionTextArea.idevice = self self._filenameInstruc = x_(u'Click <strong>Select a file</strong>, ' 'browse to the file you want ' 'to attach and select it.') self._labelInstruc = x_(u"<p>" "Assign a label for the attachment. It " "is useful to include the type of file. " "Eg. pdf, ppt, .odp, etc." "</p>" "<p>" "Including the size is also recommended so " "that after your package is exported " "to a web site, people will have an idea " "how long it would take to download this " "attachment." "</p>" "<p>" "For example: " "<code>Sales Forecast.doc (500kb)</code>" "</p>") # Properties filenameInstruc = lateTranslate('filenameInstruc') labelInstruc = lateTranslate('labelInstruc') descriptionInstruc = lateTranslate('descriptionInstruc') def setAttachment(self, attachmentPath): """ Store the attachment in the package Needs to be in a package to work. """ log.debug(u"setAttachment " + unicode(attachmentPath)) resourceFile = Path(attachmentPath) assert self.parentNode, \ _('Attachment %s has no parentNode') % self.id assert self.parentNode.package, \ _('iDevice %s has no package') % self.parentNode.id if resourceFile.isfile(): if self.userResources: # Clear out old attachment/s while self.userResources: self.userResources[0].delete() # Create the new resource Resource(self, resourceFile) else: log.error('File %s is not a file' % resourceFile) def getResourcesField(self, this_resource): """ implement the specific resource finding mechanism for this iDevice: """ # be warned that before upgrading, this iDevice field could not exist: if hasattr(self, 'descriptionTextArea')\ and hasattr(self.descriptionTextArea, 'images'): for this_image in self.descriptionTextArea.images: if hasattr(this_image, '_imageResource') \ and this_resource == this_image._imageResource: return self.descriptionTextArea # if this_resource wasn't found in the above TextArea, but is still # listed within the iDevice's userResources, then we can assume # that this_resource is the attached resource, even though that # has no direct field. # As such, merely return the resource itself, to indicate that # it DOES belong to this iDevice, but is not a FieldWithResources: if this_resource in self.userResources: return this_resource return None def getRichTextFields(self): """ Like getResourcesField(), a general helper to allow nodes to search through all of their fields without having to know the specifics of each iDevice type. """ fields_list = [] if hasattr(self, 'descriptionTextArea'): fields_list.append(self.descriptionTextArea) return fields_list def upgradeToVersion1(self): """ Upgrades v0.6 to v0.7. """ self.lastIdevice = False def upgradeToVersion2(self): """ Upgrades to v0.10 """ self._upgradeIdeviceToVersion1() self._filenameInstruc = self.__dict__.get('filenameInstruc', '') self._labelInstruc = self.__dict__.get('labelInstruc', '') self._descriptionInstruc = self.__dict__.get('descriptionInstruc', '') def upgradeToVersion3(self): """ Upgrades to v0.12 """ self._upgradeIdeviceToVersion2() if self.filename and self.parentNode: Resource(self, Path(self.filename)) del self.filename def upgradeToVersion4(self): """ Upgrades to somewhere before version 0.25 (post-v0.24) Taking the old .description unicode string field, and converting it into an image-enabled TextAreaField: """ self.descriptionTextArea = TextAreaField(x_(u'Description:'), self._descriptionInstruc, self.description) self.descriptionTextArea.idevice = self
class ListaField(FieldWithResources): """ This field handles a passage with words that the student must choose in And can now support multiple images (and any other resources) via tinyMCE """ regex = re.compile('(%u)((\d|[A-F]){4})', re.UNICODE) persistenceVersion = 3 # these will be recreated in FieldWithResources' TwistedRePersist: nonpersistant = ['content', 'content_wo_resourcePaths'] def __init__(self, name, instruc): """ Initialise """ FieldWithResources.__init__(self, name, instruc) self.parts = [] self._encodedContent = '' self.rawContent = '' self._setVersion2Attributes() self.otras = '' self.otrasInstruc = \ x_(u"<p>Optional: Write other words to complete the Dropdown activity.<br/> Use | (the vertical bar) to separate words.<br/> This field can be left blank. </p>") def _setVersion2Attributes(self): """ Sets the attributes that were added in persistenceVersion 2 """ #self.showScore = False #self._showScoreInstruc = \ #x_(u"""<p>Si esta opción esta marcada se muestra la puntuación obtenida.</p>""") # Property handlers def set_encodedContent(self, value): """ Cleans out the encoded content as it is passed in. Makes clean XHTML. """ for key, val in name2codepoint.items(): value = value.replace('&%s;' % key, unichr(val)) # workaround for Microsoft Word which incorrectly quotes font names value = re.sub(r'font-family:\s*"([^"]+)"', r'font-family: \1', value) parser = ListaHTMLParser() parser.feed(value) parser.close() self.parts = parser.result encodedContent = '' for shown, hidden in parser.result: encodedContent += shown if hidden: encodedContent += ' <u>' encodedContent += hidden encodedContent += '</u> ' self._encodedContent = encodedContent # Properties encodedContent = property(lambda self: self._encodedContent, set_encodedContent) #showScoreInstruc = lateTranslate('showScoreInstruc') otrasInstruc = lateTranslate('otrasInstruc') def upgradeToVersion1(self): """ Upgrades to exe v0.11 """ self.autoCompletion = True self.autoCompletionInstruc = _(u"""Allow auto completion when user filling the gaps.""") def upgradeToVersion2(self): """ Upgrades to exe v0.12 """ Field.upgradeToVersion2(self) strictMarking = not self.autoCompletion del self.autoCompletion del self.autoCompletionInstruc self._setVersion2Attributes() self.strictMarking = strictMarking def upgradeToVersion3(self): """ Upgrades to somewhere before version 0.25 (post-v0.24) to reflect that ClozeField now inherits from FieldWithResources, and will need its corresponding fields populated from content. """ self.content = self.encodedContent self.content_w_resourcePaths = self.encodedContent self.content_wo_resourcePaths = self.encodedContent
class TestQuestion(Persistable): """ A TestQuestion is built up of question and AnswerOptions. """ persistenceVersion = 3 def __init__(self, idevice, question=""): """ Initialize """ self.idevice = idevice self.options = [] self.correctAns = -2 self.userAns = -1 self._questionInstruc = x_(u"""Enter the question stem. The quest should be clear and unambiguous. Avoid negative premises as these can tend to be ambiguous.""") self._optionInstruc = x_(u"""Enter an answer option. Provide a range of plausible distractors (usually 3-4) as well as the correct answer. Click on the <Add another option> button to add another answer.""") self._correctAnswerInstruc = x_(u"""To indicate the correct answer, click the radio button next to the correct option.""") self.questionTextArea = TextAreaField(x_(u'Question:'), self._questionInstruc, u'') self.questionTextArea.idevice = self.idevice self.addOption() # Properties questionInstruc = lateTranslate('questionInstruc') optionInstruc = lateTranslate('optionInstruc') correctAnswerInstruc = lateTranslate('correctAnswerInstruc') def addOption(self): """ Add a new option to this question. """ self.options.append(AnswerOption(self, self.idevice)) def getResourcesField(self, this_resource): """ implement the specific resource finding mechanism for this iDevice: """ # be warned that before upgrading, this iDevice field could not exist: if hasattr(self, 'questionTextArea')\ and hasattr(self.questionTextArea, 'images'): for this_image in self.questionTextArea.images: if hasattr(this_image, '_imageResource') \ and this_resource == this_image._imageResource: return self.questionTextArea for this_option in self.options: this_field = this_option.getResourcesField(this_resource) if this_field is not None: return this_field return None def getRichTextFields(self): """ Like getResourcesField(), a general helper to allow nodes to search through all of their fields without having to know the specifics of each iDevice type. """ fields_list = [] if hasattr(self, 'questionTextArea'): fields_list.append(self.questionTextArea) for this_option in self.options: fields_list.extend(this_option.getRichTextFields()) return fields_list def upgradeToVersion1(self): """ Upgrades to v 0.13 """ self._optionInstruc = x_(u"""Enter an answer option. Provide a range of plausible distractors (usually 3-4) as well as the correct answer. Click on the <Add another option> button to add another answer.""") def upgradeToVersion2(self): """ Upgrades to v 0.13 """ self._questionInstruc = x_(u"""Enter the question stem. The quest should be clear and unambiguous. Avoid negative premises as these can tend to be ambiguous.""") def upgradeToVersion3(self): """ Upgrades to v 0.13 """ self._correctAnswerInstruc = x_(u"""To indicate the correct answer, click the radio button next to the correct option.""") def upgrade_setIdevice(self, idevice): """ While some of this might typically be done in an automatic upgrade method called from in increased persistence version, the problem with that approach is that the idevice was not previously stored, and cannot easily be gotten at that stage of operation. Rather than making such an upgrade method more messy than necessary, this method allows the parent TestQuestionIdevice to merely set itself on each of its TestQuestions during its own upgrade. Helps upgrade to somewhere before version 0.25 (post-v0.24), taking the old unicode string fields, and converting them into a image-enabled TextAreaFields: """ self.idevice = idevice self.questionTextArea = TextAreaField(x_(u'Question:'), self._questionInstruc, self.question) self.questionTextArea.idevice = self.idevice # and then, need to propagate the same upgrades # down through each of the options: for option in self.options: option.upgrade_setIdevice(self.idevice, self)
class AppletIdevice(Idevice): """ Java Applet Idevice. Enables you to embed java applet in the browser """ persistenceVersion = 1 def __init__(self, parentNode=None): """ Sets up the idevice title and instructions etc. """ Idevice.__init__(self, x_(u"Java Applet"), x_(u"University of Auckland"), u"", u"", u"", parentNode) self.emphasis = Idevice.NoEmphasis self.appletCode = u"" self.type = u"other" self._fileInstruc = x_(u"""Add all the files provided for the applet except the .txt file one at a time using the add files and upload buttons. The files, once loaded will be displayed beneath the Applet code field.""") self._codeInstruc = x_(u""""Find the .txt file (in the applet file) and open it. Copy the contents of this file <ctrl A, ctrl C> into the applet code field.""") self._typeInstruc = x_( u""" <p>If the applet you're adding was generated by one of the programs in this drop down, please select it, then add the data/applet file generated by your program. </p> <p>eg. For Geogebra applets, select geogebra, then add the .ggb file that you created in Geogebra.</p>""") self.message = "" # Properties fileInstruc = lateTranslate('fileInstruc') codeInstruc = lateTranslate('codeInstruc') typeInstruc = lateTranslate('typeInstruc') global DESC_PLUGIN DESC_PLUGIN = 0 def getResourcesField(self, this_resource): """ implement the specific resource finding mechanism for this iDevice: """ # if this_resource is listed within the iDevice's userResources, # then we can assume that this_resource is indeed a valid resource, # even though that has no direct field. # As such, merely return the resource itself, to indicate that # it DOES belong to this iDevice, but is not a FieldWithResources: if this_resource in self.userResources: return this_resource return None def getRichTextFields(self): """ Like getResourcesField(), a general helper to allow nodes to search through all of their fields without having to know the specifics of each iDevice type. """ # Applet iDevice has no rich-text fields: return [] def burstHTML(self, i): """ takes a BeautifulSoup fragment (i) and bursts its contents to import this idevice from a CommonCartridge export """ # Java Applet Idevice: #title = i.find(name='span', attrs={'class' : 'iDeviceTitle' }) #idevice.title = title.renderContents().decode('utf-8') # no title for this idevice. # =====> WARNING: not yet loading any of the files! # BEWARE also of the appletCode line breaks loading as <br/>, # may want change this back to \n or \r\n? # AND: also need to load the applet type: Geogebra or Other. inner = i.find(name='div', attrs={'class': 'iDevice emphasis0'}) self.appletCode = inner.renderContents().decode('utf-8') def verifyConn(self, site): """ verify if the URL indicated by the user is reachable """ import httplib import socket import re timeout = 30 socket.setdefaulttimeout(timeout) try: patron = '(?:http.*://)?(?P<host>[^:/ ]+).?(?P<port>[0-9]*).*' portandhost = re.search(patron, site) port = portandhost.group('port') host = portandhost.group('host') if not portandhost.group('port'): port = '80' conn = httplib.HTTPConnection(host, port) # now the real connection test: conn.connect() except (httplib.HTTPResponse, socket.error): return False try: # nothing more needed: conn.close() except: pass return True def uploadFile(self, filePath): """ Store the upload files in the package Needs to be in a package to work. """ if self.type == "descartes" and not filePath.endswith(".jar"): if filePath.find(",") == -1: global SCENE_NUM SCENE_NUM = 1 else: SCENE_NUM = int(filePath[:filePath.find(",")]) if self.type == "descartes" and (filePath.endswith(".htm") or filePath.endswith(".html")): global url url = filePath self.appletCode = self.getAppletcodeDescartes(filePath) # none scene was found: if self.appletCode == '': return None else: log.debug(u"uploadFile " + unicode(filePath)) resourceFile = Path(filePath) assert self.parentNode, _('file %s has no parentNode') % self.id assert self.parentNode.package, \ _('iDevice %s has no package') % self.parentNode.id if resourceFile.isfile(): self.message = "" Resource(self, resourceFile) if self.type == "geogebra": self.appletCode = self.getAppletcodeGeogebra( resourceFile.basename().replace(' ', '_').replace( ')', '').replace('(', '')) if self.type == "jclic": self.appletCode = self.getAppletcodeJClic( resourceFile.basename().replace(' ', '_').replace( ')', '').replace('(', '')) if self.type == "scratch": self.appletCode = self.getAppletcodeScratch( resourceFile.basename().replace(' ', '_').replace( ')', '').replace('(', '')) if self.type == "descartes": self.appletCode = self.getAppletcodeDescartes( resourceFile.basename()) ## next code should be used to load in the editor the HTML code of the html file: # if self.type == "other": # if filePath.endswith(".html") or filePath.endswith(".htm"): # content = open(filePath, 'r') # str = content.read() # self.appletCode = str # content.close() # else: # log.error('File %s is not a HTML file' % resourceFile) else: log.error('File %s is not a file' % resourceFile) def deleteFile(self, fileName): """ Delete a selected file """ for resource in self.userResources: if resource.storageName == fileName: resource.delete() break def getAppletcodeGeogebra(self, filename): """ xhtml string for GeoGebraApplet """ html = """ <applet code="geogebra.GeoGebraApplet.class" archive="geogebra.jar" width="750" height="450"> <param name="filename" value="%s"> <param name="framePossible" value="true"> <param name="java_arguments" value="-Xmx1024m"/> <param name="showResetIcon" value="true"/> <param name="showAnimationButton" value="true"/> <param name="errorDialogsActive" value="true"/> <param name="enableRightClick" value="true"/> <param name="enableLabelDrags" value="false"/> <param name="showMenuBar" value="false"/> <param name="showToolBar" value="true"/> <param name="showToolBarHelp" value="true"/> <param name="enableShiftDragZoom" value="true"/><param name="showAlgebraInput" value="false"/> <param name="useBrowserForJS" value="false" /> <param name="cache_archive" value="geogebra.jar,geogebra_main.jar, geogebra_gui.jar, geogebra_cas.jar,geogebra_algos.jar, geogebra_export.jar, geogebra_javascript.jar,jlatexmath.jar, jlm_greek.jar, jlm_cyrillic.jar,geogebra_properties.jar" /> Please <a href="http://java.sun.com/getjava"> install Java 1.4</a> (or later) to use this page. </applet> """ % filename return html def getAppletcodeJClic(self, filename): """ xhtml string for JClicApplet """ html = """ <applet code="JClicApplet" archive="jclic.jar" width="800" height="600"> <param name="name" value="JClicApplet"> <param name="activitypack" value="%s"> <param name="framePossible" value="false"> Please <a href="http://java.sun.com/getjava"> install Java 1.4</a> (or later) to use this page. </applet> """ % filename return html def getAppletcodeScratch(self, project): """ xhtml string for ScratchApplet """ html = """ <applet id="ProjectApplet" style="display:block" code="ScratchApplet" archive="ScratchApplet.jar" width="482" height="387"> <param name="project" value="%s"> <param name="useBrowserForJS" value="false" /> Please <a href="http://java.sun.com/getjava"> install Java 1.4</a> (or later) to use this page. </applet> """ % project return html def downloadFiles(self, stringapplet): """ only for DescartesApplet initially; three jobs: 1 look for image and macros files in the URL indicated by the user, 2 modify applet code for a correct exe detection of them after this, 3 download and store them into the exe project (absolute urls are required). Return the code modified. """ from BeautifulSoup import BeautifulSoup, BeautifulStoneSoup import re import urllib import urllib2 import string import os # import urllib.request stringappletmod = stringapplet soup = BeautifulSoup(stringapplet) # ONE: image files: key_image = [ 'archivo=', 'imagem_de_fundo=', 'imagem=', 'imagen=', 'file=', 'fitxer=', 'artxibo=', 'image=', 'bg_image=', 'imatge=', 'immagine=', 'irudia=', 'irundia=', 'fichier=', 'imaxe=', 'arquivo=', 'immagine_fondo=' ] # paths to the images indicated in the applet code: imageslist = [] for x in key_image: if string.find(stringapplet, x) != -1: expression = r"%s'([\w\./]+)'" % x patron = re.compile(expression) for tag in soup.findAll('param'): result = patron.search(tag['value']) if result: if result.group(1) not in imageslist: imageslist.append(result.group(1)) # modify applet code: urlimageslist = [] for im in imageslist: # put as locals the images' path inside exe editor... stringappletmod = stringappletmod.replace(im, im[im.rfind("/") + 1:]) # from imageslist, it's neccesary to create the list of absolute paths to the image # files because we want to download this images and load them in the project: # first quit scene number urlnoesc = url[url.find(",") + 1:] # cut the right side of the last /: urlcut = urlnoesc[:urlnoesc.rfind("/")] # and extend with the image from the applet code: urlimageslist.append(urlcut + "/" + im) # repeated no thanks: urlimageslist = list(set(urlimageslist)) # do not forget that it could be image_down and image_over versions # of the file in the same place, so... a new extended list: urlimgslistextended = [] for pathimg in urlimageslist: # we trick to urlimageslist adding files that haven't been detected really if pathimg not in urlimgslistextended: urlimgslistextended.append(pathimg) if string.find(pathimg, '.png') != -1: urlimgslistextended.append( pathimg.replace('.png', '_down.png')) urlimgslistextended.append( pathimg.replace('.png', '_over.png')) if string.find(pathimg, '.jpg') != -1: urlimgslistextended.append( pathimg.replace('.jpg', '_down.jpg')) urlimgslistextended.append( pathimg.replace('.jpg', '_over.jpg')) if string.find(pathimg, '.gif') != -1: urlimgslistextended.append( pathimg.replace('.gif', '_down.gif')) urlimgslistextended.append( pathimg.replace('.gif', '_over.gif')) urlimgslistextended = list(set(urlimgslistextended)) # now we can: download all you can find: for pathimgext in urlimgslistextended: # the clean name of the image file img = pathimgext[pathimgext.rfind("/") + 1:] # firstly to test the existence of the file: try: resp = urllib2.urlopen(pathimgext) except urllib2.URLError, e: if not hasattr(e, "code"): raise resp = e try: # download whith its original name: img_down = urllib.urlretrieve(pathimgext, img) except: print 'Unable to download file' # be sure the file was found: if img_down[1].maintype == 'image': self.uploadFile(img_down[0]) os.remove(img_down[0]) # change the path in the soup, now the file will be local: for x in imageslist: if x in soup: x = x[x.rfind("/") + 1:] # TWO: local macros (sometimes a macro that is called may reside inside the jar file) # paths to the macros indicated in the applet code: macroslist = [] key_typo = ['tipo=', 'tipus=', 'type=', 'mota='] for x in key_typo: if string.find(stringapplet, x) != -1: expression = r"%s'ma[ck]ro'" % x patron = re.compile(expression) for tag in soup.findAll('param'): result = patron.search(tag['value']) if result: # tipo = macro or makro finded, now we need expresion parameter inside value tag key_macro = [ 'expresión=', 'expresion=', 'adierazpen=', 'espressione=', 'expresión=', 'expresió=', 'expresi&oacute;n=', 'expressão=' ] for y in key_macro: if string.find(tag, y) != -1: # consider sometimes macro file has txt format but sometimes has not format... # and sometimes they are locals and sometimes they are inside the jar file wexpression = ur"%s'(?P<bla>[\w./]+)'" % y # notice unicode conversion wpatron = re.compile(wexpression) # convert tag to unicode string also: utag = unicode(tag) wresult = wpatron.search(utag) if wresult: macroslist.append(wresult.group('bla')) # repeated no thanks: macroslist = list(set(macroslist)) # from macroslist, it's neccesary to create the list of absolute # paths to the macros because we want to download them: urlmacroslist = [] for mac in macroslist: urlnoesc = url[url.find(",") + 1:] urlcut = urlnoesc[:urlnoesc.rfind("/")] urlmacroslist.append(urlcut + "/" + mac) urlmacroslist = list(set(urlmacroslist)) # we try to download them but really we do not know if they will be # physically, as locals -out of the jar file- so our code must look for them, # and if they seem not to be we we will asume they are in the jar file for pathmacro in urlmacroslist: macro = pathmacro[pathmacro.rfind("/") + 1:] try: resp = urllib2.urlopen(pathmacro) except urllib2.URLError, e: if not hasattr(e, "code"): raise resp = e if resp.code == 200: macro_down = urllib.urlretrieve(pathmacro, macro) self.uploadFile(macro_down[0]) os.remove(macro_down[0]) # and modify the applet code inside eXe, now the file will be local: for x in macroslist: if x.endswith('.txt'): if string.find(x, macro) != -1: stringappletmod = stringappletmod.replace(x, macro)
class Idevice(Persistable): """ The base class for all iDevices iDevices are mini templates which the user uses to create content in the package """ # Class attributes # see derieved classes for persistenceVersion nextId = 1 NoEmphasis, SomeEmphasis, StrongEmphasis = range(3) def __init__(self, title, author, purpose, tip, icon, parentNode=None): """Initialize a new iDevice, setting a unique id""" log.debug("Creating iDevice") self.edit = True self.lastIdevice = True self.emphasis = Idevice.NoEmphasis self.version = 0 self.id = unicode(Idevice.nextId) Idevice.nextId += 1 self.parentNode = parentNode self._title = title self._author = author self._purpose = purpose self._tip = tip self.icon = icon # userResources are copied into and stored in the package self.userResources = [] # systemResources are resources from whatever style dir we are using at render time self.systemResources = [] self.originalicon = icon # Properties def get_title(self): """ Gives a nicely encoded and translated title that can be put inside xul labels (eg. <label value="my "idevice"">) """ if not hasattr(self, '_title'): self._title = 'NO TITLE' if self._title: title = c_(self._title) title = title.replace('&', '&') title = title.replace('"', '"') return title else: return u'' def set_title(self, value): """ Sets self._title """ if c_(self._title) != value: self._title = value title = property(get_title, set_title) rawTitle = lateTranslate('title') author = lateTranslate('author') purpose = lateTranslate('purpose') tip = lateTranslate('tip') def get_klass(self): if hasattr(self, 'class_'): if self.class_ == '': # return u'customIdevice' # We add 3 classes to the custom iDevice # 1. customIdevice (common) customIdeviceClass = 'customIdevice ' # 2. Title (with no spaces, etc.) if self._title == '': customIdeviceClass += 'untitledIdevice' else: customIdeviceClass += 'Idevice' + re.sub( '\W+', '', self._title) # 3. Icon if self.icon: customIdeviceClass += ' icon' + self.icon else: customIdeviceClass += ' noIcon' return customIdeviceClass else: return self.class_ + u'Idevice' else: klass = str(self.__class__).split('.')[-1] return klass[:-2] klass = property(get_klass) def __cmp__(self, other): """ Compare this iDevice with other """ return cmp(self.id, other.id) def __deepcopy__(self, others): """ Override deepcopy because normal one seems to skip things when called from resource.__deepcopy__ """ # Create a new me miniMe = self.__class__.__new__(self.__class__) others[id(self)] = miniMe # Copy our resources first miniMe.userResources = [] for resource in self.userResources: miniMe.userResources.append(deepcopy(resource, others)) # Copy the rest of our attributes for key, val in self.__dict__.items(): if key != 'userResources': setattr(miniMe, key, deepcopy(val, others)) miniMe.id = unicode(Idevice.nextId) Idevice.nextId += 1 return miniMe # Public Methods def clone(self): """ Clone an iDevice just like this one """ log.debug("Cloning iDevice") newIdevice = copy.deepcopy(self) return newIdevice def delete(self): """ delete an iDevice from it's parentNode """ # Clear out old user resources # use reverse for loop to delete old user resources length = len(self.userResources) for i in range(length - 1, -1, -1): # possible bug fix, due to inconsistent order of loading: # ensure that this idevice IS attached to the resource: if self.userResources[i]._idevice is None: self.userResources[i]._idevice = self # and NOW we can finally properly delete it! self.userResources[i].delete() if self.parentNode: # first remove any internal anchors' links: self.ChangedParentNode(self.parentNode, None) self.parentNode.idevices.remove(self) self.parentNode = None def isFirst(self): """ Return true if this is the first iDevice in this node """ index = self.parentNode.idevices.index(self) return index == 0 def isLast(self): """ Return true if this is the last iDevice in this node """ index = self.parentNode.idevices.index(self) return index == len(self.parentNode.idevices) - 1 def movePrev(self): """ Move to the previous position """ parentNode = self.parentNode index = parentNode.idevices.index(self) if index > 0: temp = parentNode.idevices[index - 1] parentNode.idevices[index - 1] = self parentNode.idevices[index] = temp def moveNext(self): """ Move to the next position """ parentNode = self.parentNode index = parentNode.idevices.index(self) if index < len(parentNode.idevices) - 1: temp = parentNode.idevices[index + 1] parentNode.idevices[index + 1] = self parentNode.idevices[index] = temp def setParentNode(self, parentNode): """ Change parentNode Now includes support for renaming any internal anchors and their links. """ old_node = None if self.parentNode: old_node = self.parentNode self.parentNode.idevices.remove(self) old_id = self.id parentNode.addIdevice(self) self.id = old_id # and update any internal anchors and their links: self.ChangedParentNode(old_node, parentNode) def ChangedParentNode(self, old_node, new_node): """ To update all fo the anchors (if any) that are defined within any of this iDevice's various fields, and any internal links corresponding to those anchors. This is essentially a variation of Node:RenamedNode() It also removes any internal links from the data structures as well, if this iDevice is being deleted """ my_fields = self.getRichTextFields() num_fields = len(my_fields) for field_loop in range(num_fields - 1, -1, -1): this_field = my_fields[field_loop] if hasattr(this_field, 'anchor_names') \ and len(this_field.anchor_names) > 0: # okay, this is an applicable field with some anchors: this_field.ReplaceAllInternalAnchorsLinks(oldNode=old_node, newNode=new_node) if new_node: # add this particular anchor field into the new node's list: if not hasattr(new_node, 'anchor_fields'): new_node.anchor_fields = [] if this_field not in new_node.anchor_fields: new_node.anchor_fields.append(this_field) if new_package: if not hasattr(new_package, 'anchor_nodes'): new_package.anchor_nodes = [] if new_node not in new_package.anchor_nodes: new_package.anchor_nodes.append(new_node) # now, regardless of whether or not that field has any anchors, # if this idevice is being deleted (new_node is None), then # go ahead and remove any of its internal links # from the corresponding data structures: if not new_node \ and hasattr(this_field, 'intlinks_to_anchors') \ and len(this_field.intlinks_to_anchors) > 0: this_field.RemoveAllInternalLinks() return def getResourcesField(self, this_resource): """ Allow resources to easily find their specific corresponding field, to help out with loading and especially merging scenarios for resources with names already in use, for example. This method is expected to be overridden within each specific iDevice. """ # in the parent iDevice class, merely return a None, # and let each specific iDevice class implement its own version: log.warn("getResourcesField called on iDevice; no specific " + "implementation available for this particular iDevice " + "class: " + repr(self)) return None def getRichTextFields(self): """ Like getResourcesField(), a general helper to allow nodes to search through all of their fields without having to know the specifics of each iDevice type. Currently used by Extract to find all fields which have internal links. """ # in the parent iDevice class, merely return an empty list, # and let each specific iDevice class implement its own version: log.warn("getRichTextFields called on iDevice; no specific " + "implementation available for this particular iDevice " + "class: " + repr(self)) return [] # Protected Methods def _upgradeIdeviceToVersion1(self): """ Upgrades the Idevice class members from version 0 to version 1. Should be called in derived classes. """ log.debug("upgrading to version 1") self._title = self.__dict__.get('title', self.title) self._author = self.__dict__.get('author', self.title) self._purpose = self.__dict__.get('purpose', self.title) self._tip = self.__dict__.get('tip', self.title) def _upgradeIdeviceToVersion2(self): """ Upgrades the Idevice class members from version 1 to version 2. Should be called in derived classes. """ log.debug("upgrading to version 2, for 0.12") self.userResources = [] if self.icon: self.systemResources = ["icon_" + self.icon + ".gif"] else: self.systemResources = [] def _upgradeIdeviceToVersion3(self): if self.icon: icon = "icon_" + self.icon + ".gif" if icon in self.systemResources: self.systemResources.remove(icon) def _upgradeIdeviceToVersion4(self): pass
class NotaIdevice(Idevice): """ Note Idevice is for comments """ persistenceVersion = 7 def __init__(self, activity="", answer=""): """ Initialize """ Idevice.__init__(self, x_(u"Note"), x_(u"exelearning.net"), u"", u"Note", "") self.emphasis = Idevice.SomeEmphasis self._commentInstruc = u"" self.systemResources += ["common.js"] self.commentTextArea = TextAreaField(x_(u'Comment:'), self._commentInstruc, answer) self.commentTextArea.idevice = self # Properties commentInstruc = lateTranslate('commentInstruc') def getResourcesField(self, this_resource): """ implement the specific resource finding mechanism for this iDevice: """ # be warned that before upgrading, this iDevice field could not exist: # be warned that before upgrading, this iDevice field could not exist: if hasattr(self, 'commentTextArea')\ and hasattr(self.commentTextArea, 'images'): for this_image in self.commentTextArea.images: if hasattr(this_image, '_imageResource') \ and this_resource == this_image._imageResource: return self.commentTextArea return None def getRichTextFields(self): """ Like getResourcesField(), a general helper to allow nodes to search through all of their fields without having to know the specifics of each iDevice type. """ fields_list = [] if hasattr(self, 'commentTextArea'): fields_list.append(self.commentTextArea) return fields_list def upgradeToVersion1(self): """ Upgrades the node from version 0 to 1. """ log.debug(u"Upgrading iDevice") def upgradeToVersion2(self): """ Upgrades the node from 1 (v0.5) to 2 (v0.6). Old packages will loose their icons, but they will load. """ log.debug(u"Upgrading iDevice") self.emphasis = Idevice.SomeEmphasis def upgradeToVersion3(self): """ Upgrades v0.6 to v0.7. """ self.lastIdevice = False def upgradeToVersion4(self): """ Upgrades to exe v0.10 """ self._upgradeIdeviceToVersion1() self._commentInstruc = self.__dict__['commentInstruc'] def upgradeToVersion5(self): """ Upgrades to exe v0.10 """ self._upgradeIdeviceToVersion1() def upgradeToVersion6(self): """ Upgrades to v0.12 """ self._upgradeIdeviceToVersion2() self.systemResources += ["common.js"] def upgradeToVersion7(self): """ Upgrades to somewhere before version 0.25 (post-v0.24) Taking the old unicode string fields, and converting them into image-enabled TextAreaFields: """ self.commentTextArea = TextAreaField(x_(u'Feedback:'), self._commentInstruc, self.answer) self.commentTextArea.idevice = self def upgradeToVersion8(self): self.icon = ""