def dump(self, obj, filePath=None, format=None): '''Exports this file to disk (outside the db-controller filesystem). The tied Appy p_obj(ect) is required. If p_filePath is specified, it is the path name where the file will be dumped; folders mentioned in it must exist. If not, the file will be dumped in the OS temp folder. The absolute path name of the dumped file is returned. If an error occurs, the method returns None. If p_format is specified, LibreOffice will be called for converting the dumped file to the desired format.''' if not filePath: filePath = '%s/file%f.%s' % (sutils.getOsTempFolder(), time.time(), self.fsName) # Copies the file to disk. shutil.copyfile(self.getFilePath(obj), filePath) if format: # Convert the dumped file using LibreOffice errorMessage = obj.tool.convert(filePath, format) # Even if we have an "error" message, it could be a simple warning. # So we will continue here and, as a subsequent check for knowing if # an error occurred or not, we will test the existence of the # converted file (see below). os.remove(filePath) # Return the name of the converted file. baseName, ext = os.path.splitext(filePath) if (ext == '.%s' % format): filePath = '%s.res.%s' % (baseName, format) else: filePath = '%s.%s' % (baseName, format) if not os.path.exists(filePath): obj.log(CONVERSION_ERROR % errorMessage, type='error') return return filePath
def generateDocument(self): '''Generates the document from field-related info. UID of object that is the template target is given in the request.''' rq = self.REQUEST appyTool = self.appy() # Get the object objectUid = rq.get('objectUid') obj = self.uid_catalog(UID=objectUid)[0].getObject() appyObj = obj.appy() # Get information about the document to render. specificPodContext = None fieldName = rq.get('fieldName') format = rq.get('podFormat') podInfo = self.getPodInfo(obj, fieldName) template = podInfo['template'].content podTitle = podInfo['title'] if podInfo['context']: if type(podInfo['context']) == types.FunctionType: specificPodContext = podInfo['context'](appyObj) else: specificPodContext = podInfo['context'] doAction = rq.get('askAction') == 'True' # Temporary file where to generate the result tempFileName = '%s/%s_%f.%s' % ( getOsTempFolder(), obj.UID(), time.time(), format) # Define parameters to pass to the appy.pod renderer currentUser = self.portal_membership.getAuthenticatedMember() podContext = {'tool': appyTool, 'user': currentUser, 'self': appyObj, 'now': self.getProductConfig().DateTime(), 'projectFolder': appyTool.getDiskFolder(), } if specificPodContext: podContext.update(specificPodContext) rendererParams = {'template': StringIO.StringIO(template), 'context': podContext, 'result': tempFileName} if appyTool.unoEnabledPython: rendererParams['pythonWithUnoPath'] = appyTool.unoEnabledPython if appyTool.openOfficePort: rendererParams['ooPort'] = appyTool.openOfficePort # Launch the renderer try: renderer = Renderer(**rendererParams) renderer.run() except appy.pod.PodError, pe: if not os.path.exists(tempFileName): # In some (most?) cases, when OO returns an error, the result is # nevertheless generated. appyTool.log(str(pe), type='error') appyTool.say(POD_ERROR) return self.goto(rq.get('HTTP_REFERER'))
def tempFile(self): '''A temp file has been created in a temp folder. This method returns this file to the browser.''' rq = self.REQUEST baseFolder = os.path.join(getOsTempFolder(), self.getAppName()) baseFolder = os.path.join(baseFolder, rq.SESSION.id) fileName = os.path.join(baseFolder, rq.get('name', '')) if os.path.exists(fileName): f = file(fileName) content = f.read() f.close() # Remove the temp file os.remove(fileName) return content return 'File does not exist'
def getPoFile(self): '''Computes and returns the PO file corresponding to this translation.''' tool = self.tool fileName = os.path.join(getOsTempFolder(), '%s-%s.po' % (tool.o.getAppName(), self.id)) poFile = PoFile(fileName) for field in self.fields: if (field.name == 'title') or (field.type != 'String'): continue # Adds the PO message corresponding to this field msg = field.getValue(self.o) or '' for old, new in self.poReplacements: msg = msg.replace(old, new) poFile.addMessage(PoMessage(field.name, msg, '')) poFile.generate() return True, file(fileName)
def migrateBinaryFields(obj): '''Ensures all file and frozen pod fields on p_obj are FileInfo instances.''' migrated = 0 # Count the number of migrated fields for field in obj.fields: if field.type == 'File': oldValue = getattr(obj, field.name) if oldValue and not isinstance(oldValue, FileInfo): # A legacy File object. Convert it to a FileInfo instance # and extract the binary to the filesystem. setattr(obj, field.name, oldValue) migrated += 1 elif field.type == 'Pod': frozen = getattr(obj.o, field.name, None) if frozen: # Dump this file on disk. tempFolder = sutils.getOsTempFolder() fmt = os.path.splitext(frozen.filename)[1][1:] fileName = os.path.join(tempFolder, '%f.%s' % (time.time(), fmt)) f = file(fileName, 'wb') if frozen.data.__class__.__name__ == 'Pdata': # The file content is splitted in several chunks. f.write(frozen.data.data) nextPart = frozen.data.next while nextPart: f.write(nextPart.data) nextPart = nextPart.next else: # Only one chunk f.write(frozen.data) f.close() f = file(fileName) field.freeze(obj, template=field.template[0], format=fmt, noSecurity=True, upload=f, freezeOdtOnError=False) f.close() # Remove the legacy in-zodb file object setattr(obj.o, field.name, None) migrated += 1 return migrated
def dump(self, filePath=None, format=None, tool=None): '''Writes the file on disk. If p_filePath is specified, it is the path name where the file will be dumped; folders mentioned in it must exist. If not, the file will be dumped in the OS temp folder. The absolute path name of the dumped file is returned. If an error occurs, the method returns None. If p_format is specified, OpenOffice will be called for converting the dumped file to the desired format. In this case, p_tool, a Appy tool, must be provided. Indeed, any Appy tool contains parameters for contacting OpenOffice in server mode.''' if not filePath: filePath = '%s/file%f.%s' % (getOsTempFolder(), time.time(), normalizeString(self.name)) f = file(filePath, 'w') if self.content.__class__.__name__ == 'Pdata': # The file content is splitted in several chunks. f.write(self.content.data) nextPart = self.content.next while nextPart: f.write(nextPart.data) nextPart = nextPart.next else: # Only one chunk f.write(self.content) f.close() if format: if not tool: return # Convert the dumped file using OpenOffice errorMessage = tool.convert(filePath, format) # Even if we have an "error" message, it could be a simple warning. # So we will continue here and, as a subsequent check for knowing if # an error occurred or not, we will test the existence of the # converted file (see below). os.remove(filePath) # Return the name of the converted file. baseName, ext = os.path.splitext(filePath) if (ext == '.%s' % format): filePath = '%s.res.%s' % (baseName, format) else: filePath = '%s.%s' % (baseName, format) if not os.path.exists(filePath): tool.log(CONVERSION_ERROR % (cmd, errorMessage), type='error') return return filePath
def export(self, at='string', format='xml', include=None, exclude=None): '''Creates an "exportable" version of this object. p_format is "xml" by default, but can also be "csv". If p_format is: * "xml", if p_at is "string", this method returns the XML version, without the XML prologue. Else, (a) if not p_at, the XML will be exported on disk, in the OS temp folder, with an ugly name; (b) else, it will be exported at path p_at. * "csv", if p_at is "string", this method returns the CSV data as a string. If p_at is an opened file handler, the CSV line will be appended in it. If p_include is given, only fields whose names are in it will be included. p_exclude, if given, contains names of fields that will not be included in the result. ''' if format == 'xml': # Todo: take p_include and p_exclude into account. # Determine where to put the result toDisk = (at != 'string') if toDisk and not at: at = getOsTempFolder() + '/' + self.o.UID() + '.xml' # Create the XML version of the object marshaller = XmlMarshaller(cdata=True, dumpUnicode=True, dumpXmlPrologue=toDisk, rootTag=self.klass.__name__) xml = marshaller.marshall(self.o, objectType='appy') # Produce the desired result if toDisk: f = file(at, 'w') f.write(xml.encode('utf-8')) f.close() return at else: return xml elif format == 'csv': if isinstance(at, basestring): marshaller = CsvMarshaller(include=include, exclude=exclude) return marshaller.marshall(self) else: marshaller = CsvMarshaller(at, include=include, exclude=exclude) marshaller.marshall(self)
def export(self, at="string", format="xml", include=None, exclude=None): """Creates an "exportable" version of this object. p_format is "xml" by default, but can also be "csv". If p_format is: * "xml", if p_at is "string", this method returns the XML version, without the XML prologue. Else, (a) if not p_at, the XML will be exported on disk, in the OS temp folder, with an ugly name; (b) else, it will be exported at path p_at. * "csv", if p_at is "string", this method returns the CSV data as a string. If p_at is an opened file handler, the CSV line will be appended in it. If p_include is given, only fields whose names are in it will be included. p_exclude, if given, contains names of fields that will not be included in the result. """ if format == "xml": # Todo: take p_include and p_exclude into account. # Determine where to put the result toDisk = at != "string" if toDisk and not at: at = getOsTempFolder() + "/" + self.o.UID() + ".xml" # Create the XML version of the object marshaller = XmlMarshaller( cdata=True, dumpUnicode=True, dumpXmlPrologue=toDisk, rootTag=self.klass.__name__ ) xml = marshaller.marshall(self.o, objectType="appy") # Produce the desired result if toDisk: f = file(at, "w") f.write(xml.encode("utf-8")) f.close() return at else: return xml elif format == "csv": if isinstance(at, basestring): marshaller = CsvMarshaller(include=include, exclude=exclude) return marshaller.marshall(self) else: marshaller = CsvMarshaller(at, include=include, exclude=exclude) marshaller.marshall(self)
def export(self, at='string'): '''Creates an "exportable", XML version of this object. If p_at is "string", this method returns the XML version, without the XML prologue. Else, (a) if not p_at, the XML will be exported on disk, in the OS temp folder, with an ugly name; (b) else, it will be exported at path p_at.''' # Determine where to put the result toDisk = (at != 'string') if toDisk and not at: at = getOsTempFolder() + '/' + self.o.UID() + '.xml' # Create the XML version of the object marshaller = XmlMarshaller(cdata=True, dumpUnicode=True, dumpXmlPrologue=toDisk, rootTag=self.klass.__name__) xml = marshaller.marshall(self.o, objectType='appy') # Produce the desired result if toDisk: f = file(at, 'w') f.write(xml.encode('utf-8')) f.close() return at else: return xml
def __init__(self, fileOrFolder, keyword): self.fileOrFolder = fileOrFolder self.keyword = keyword self.tempFolder = getOsTempFolder()
def run(self): '''Generates the Debian package.''' curdir = os.getcwd() j = os.path.join tempFolder = getOsTempFolder() # Create, in the temp folder, the required sub-structure for the Debian # package. debFolder = j(tempFolder, 'debian') if os.path.exists(debFolder): FolderDeleter.delete(debFolder) # Copy the Python package into it srcFolder = j(debFolder, 'usr', 'lib') for version in self.pythonVersions: libFolder = j(srcFolder, 'python%s' % version) os.makedirs(libFolder) destFolder = j(libFolder, self.appName) shutil.copytree(self.app, destFolder) # Clean dest folder (.svn/.bzr files) cleanFolder(destFolder, folders=('.svn', '.bzr')) # When packaging Appy itself, everything is in /usr/lib/pythonX. When # packaging an Appy app, we will generate more files for creating a # running instance. if self.appName != 'appy': # Create the folders that will collectively represent the deployed # Zope instance. binFolder = j(debFolder, 'usr', 'bin') os.makedirs(binFolder) # <app>ctl name = '%s/%sctl' % (binFolder, self.appNameLower) f = file(name, 'w') f.write(appCtl % self.appNameLower) os.chmod(name, 0744) # Make it executable by owner. f.close() # <app>run name = '%s/%srun' % (binFolder, self.appNameLower) f = file(name, 'w') f.write(appRun % self.appNameLower) os.chmod(name, 0744) # Make it executable by owner. f.close() # startlo name = '%s/startlo' % binFolder f = file(name, 'w') f.write(loStart) f.close() os.chmod(name, 0744) # Make it executable by owner. # /var/lib/<app> (will store Data.fs, lock files, etc) varLibFolder = j(debFolder, 'var', 'lib', self.appNameLower) os.makedirs(varLibFolder) f = file('%s/README' % varLibFolder, 'w') f.write('This folder stores the %s database.\n' % self.appName) f.close() # /var/log/<app> (will store event.log and Z2.log) varLogFolder = j(debFolder, 'var', 'log', self.appNameLower) os.makedirs(varLogFolder) f = file('%s/README' % varLogFolder, 'w') f.write('This folder stores the log files for %s.\n' % self.appName) f.close() # /etc/<app>.conf (Zope configuration file) etcFolder = j(debFolder, 'etc') os.makedirs(etcFolder) name = '%s/%s.conf' % (etcFolder, self.appNameLower) n = self.appNameLower f = file(name, 'w') productsFolder = '/usr/lib/python%s/%s/zope' % \ (self.pythonVersions[0], self.appName) f.write(zopeConf % ('/var/lib/%s' % n, '/var/lib/%s' % n, '/var/log/%s' % n, str(self.zopePort), 'products %s\n' % productsFolder)) f.close() # /etc/init.d/<app> (start the app at boot time) initdFolder = j(etcFolder, 'init.d') os.makedirs(initdFolder) name = '%s/%s' % (initdFolder, self.appNameLower) f = file(name, 'w') n = self.appNameLower f.write(initScript % (n, n, 'Start Zope with the Appy-based %s ' \ 'application.' % n, '%sctl start' % n, '%sctl restart' % n, '%sctl stop' % n)) f.close() os.chmod(name, 0744) # Make it executable by owner # /etc/init.d/lo (start LibreOffice at boot time) name = '%s/lo' % initdFolder f = file(name, 'w') f.write(initScript % ('lo','lo', 'Start LibreOffice in server mode', 'startlo', 'startlo', "#Can't stop LO.")) f.write('\n') f.close() os.chmod(name, 0744) # Make it executable by owner. # Get the size of the app, in Kb. os.chdir(tempFolder) out, err = executeCommand(['du', '-b', '-s', 'debian']) size = int(int(out.split()[0])/1024.0) os.chdir(debFolder) # Create data.tar.gz based on it executeCommand(['tar', 'czvf', 'data.tar.gz', '*']) # Create the control file f = file('control', 'w') nameSuffix = '' dependencies = [] if self.appName != 'appy': nameSuffix = '-%s' % self.appNameLower dependencies.append('python-appy') if self.depends: for d in self.depends: dependencies.append(d) depends = '' if dependencies: depends = ', ' + ', '.join(dependencies) f.write(debianInfo % (nameSuffix, self.appVersion, size, self.pythonVersions[0], depends)) f.close() # Create md5sum file f = file('md5sums', 'w') toWalk = ['usr'] if self.appName != 'appy': toWalk += ['etc', 'var'] for folderToWalk in toWalk: for dir, dirnames, filenames in os.walk(folderToWalk): for name in filenames: m = md5.new() pathName = j(dir, name) currentFile = file(pathName, 'rb') while True: data = currentFile.read(8096) if not data: break m.update(data) currentFile.close() # Add the md5 sum to the file f.write('%s %s\n' % (m.hexdigest(), pathName)) f.close() # Create postinst, a script that will: # - bytecompile Python files after the Debian install # - change ownership of some files if required # - [in the case of an app-package] call update-rc.d for starting it at # boot time. f = file('postinst', 'w') content = '#!/bin/sh\nset -e\n' for version in self.pythonVersions: bin = '/usr/bin/python%s' % version lib = '/usr/lib/python%s' % version cmds = ' %s -m compileall -q %s/%s 2> /dev/null\n' % (bin, lib, self.appName) content += 'if [ -e %s ]\nthen\n%sfi\n' % (bin, cmds) if self.appName != 'appy': # Allow user "zope", that runs the Zope instance, to write the # database and log files. content += 'chown -R zope:root /var/lib/%s\n' % self.appNameLower content += 'chown -R zope:root /var/log/%s\n' % self.appNameLower # Call update-rc.d for starting the app at boot time content += 'update-rc.d %s defaults\n' % self.appNameLower content += 'update-rc.d lo defaults\n' # (re-)start the app content += '%sctl restart\n' % self.appNameLower # (re-)start lo content += 'startlo\n' f.write(content) f.close() # Create prerm, a script that will remove all pyc files before removing # the Debian package. f = file('prerm', 'w') content = '#!/bin/sh\nset -e\n' for version in self.pythonVersions: content += 'find /usr/lib/python%s/%s -name "*.pyc" -delete\n' % \ (version, self.appName) f.write(content) f.close() # Create control.tar.gz executeCommand(['tar', 'czvf', 'control.tar.gz', './control', './md5sums', './postinst', './prerm']) # Create debian-binary f = file('debian-binary', 'w') f.write('2.0\n') f.close() # Create the signature if required if self.sign: # Create the concatenated version of all files within the deb out, err = executeCommand(['cat', 'debian-binary', 'control.tar.gz', 'data.tar.gz']) f = file('/tmp/combined-contents', 'wb') f.write(out) f.close() executeCommand(['gpg', '-abs', '-o', '_gpgorigin', '/tmp/combined-contents']) signFile = '_gpgorigin' os.remove('/tmp/combined-contents') # Export the public key and name it according to its ID as found by # analyzing the result of command "gpg --fingerprint". out, err = executeCommand(['gpg', '--fingerprint']) fingerprint = out.split('\n') id = 'pubkey' for line in fingerprint: if '=' not in line: continue id = line.split('=')[1].strip() id = ''.join(id.split()[-4:]) break out, err = executeCommand(['gpg', '--export', '-a']) f = file('%s/%s.asc' % (self.out, id), 'w') f.write(out) f.close() else: signFile = None # Create the .deb package debName = 'python-appy%s-%s.deb' % (nameSuffix, self.appVersion) cmd = ['ar', '-r', debName] if signFile: cmd.append(signFile) cmd += ['debian-binary', 'control.tar.gz', 'data.tar.gz'] out, err = executeCommand(cmd) # Move it to self.out os.rename(j(debFolder, debName), j(self.out, debName)) # Clean temp files FolderDeleter.delete(debFolder) os.chdir(curdir)
def getValue(self, obj, name=None, template=None, format=None, result=None, queryData=None, customContext=None, noSecurity=False): '''For a pod field, getting its value means computing a pod document or returning a frozen one. A pod field differs from other field types because there can be several ways to produce the field value (ie: self.template can hold various templates; output file format can be odt, pdf,.... We get those precisions about the way to produce the file, either from params, or from default values. * p_template is the specific template, among self.template, that must be used as base for generating the document; * p_format is the output format of the resulting document; * p_result, if given, must be the absolute path of the document that will be computed by pod. If not given, pod will produce a doc in the OS temp folder; * if the pod document is related to a query, the query parameters needed to re-trigger the query are given in p_queryData; * dict p_customContext may be specified and will override any other value available in the context, including values from the field-specific context. ''' obj = obj.appy() template = template or self.template[0] format = format or 'odt' # Security check if not noSecurity and not queryData: if self.showTemplate and not self.showTemplate(obj, template): raise Exception(UNAUTHORIZED) # Return the possibly frozen document (not applicable for query-related # pods). if not queryData: frozen = self.isFrozen(obj, template, format) if frozen: fileName = self.getDownloadName(obj, template, format, False) return FileInfo(frozen, inDb=False, uploadName=fileName) # We must call pod to compute a pod document from "template" tool = obj.tool ztool = tool.o diskFolder = tool.getDiskFolder() # Get the path to the pod template templatePath = self.getTemplatePath(diskFolder, template) # Get or compute the specific POD context specificContext = None if callable(self.context): specificContext = self.callMethod(obj, self.context) else: specificContext = self.context # Compute the name of the result file if not result: result = '%s/%s_%f.%s' % (sutils.getOsTempFolder(), obj.id, time.time(), format) # Define parameters to give to the appy.pod renderer podContext = {'tool': tool, 'user': obj.user, 'self': obj, 'field':self, 'now': ztool.getProductConfig().DateTime(), '_': obj.translate, 'projectFolder': diskFolder, 'template': template, 'request': tool.request} # If the pod document is related to a query, re-trigger it and put the # result in the pod context. if queryData: # Retrieve query params cmd = ', '.join(Pod.queryParams) cmd += " = queryData.split(';')" exec cmd # (re-)execute the query, but without any limit on the number of # results; return Appy objects. objs = ztool.executeQuery(obj.o.portal_type, searchName=search, sortBy=sortKey, sortOrder=sortOrder, filterKey=filterKey, filterValue=filterValue, maxResults='NO_LIMIT') podContext['objects'] = [o.appy() for o in objs.objects] podContext['queryData'] = queryData.split(';') # Add the field-specific and custom contexts if present. if specificContext: podContext.update(specificContext) if customContext: podContext.update(customContext) # Variable "_checked" can be expected by a template but absent (ie, # when generating frozen documents). if '_checked' not in podContext: podContext['_checked'] = Object() # Define a potential global styles mapping if callable(self.stylesMapping): stylesMapping = self.callMethod(obj, self.stylesMapping) else: stylesMapping = self.stylesMapping rendererParams = {'template': templatePath, 'context': podContext, 'result': result, 'stylesMapping': stylesMapping, 'imageResolver': ztool.getApp(), 'overwriteExisting': True, 'forceOoCall': self.forceOoCall} cfg = ztool.getProductConfig(True) if cfg.unoEnabledPython: rendererParams['pythonWithUnoPath'] = cfg.unoEnabledPython if cfg.libreOfficePort: rendererParams['ooPort'] = cfg.libreOfficePort # Launch the renderer try: renderer = Renderer(**rendererParams) renderer.run() except PodError, pe: if not os.path.exists(result): # In some (most?) cases, when OO returns an error, the result is # nevertheless generated. obj.log(str(pe).strip(), type='error') return POD_ERROR
def run(self): '''Generates the Debian package.''' curdir = os.getcwd() j = os.path.join tempFolder = getOsTempFolder() # Create, in the temp folder, the required sub-structure for the Debian # package. debFolder = j(tempFolder, 'debian') if os.path.exists(debFolder): FolderDeleter.delete(debFolder) # Copy the Python package into it srcFolder = j(debFolder, 'usr', 'lib') for version in self.pythonVersions: libFolder = j(srcFolder, 'python%s' % version) os.makedirs(libFolder) destFolder = j(libFolder, self.appName) shutil.copytree(self.app, destFolder) # Clean dest folder (.svn/.bzr files) cleanFolder(destFolder, folders=('.svn', '.bzr')) # When packaging Appy itself, everything is in /usr/lib/pythonX. When # packaging an Appy app, we will generate more files for creating a # running instance. if self.appName != 'appy': # Create the folders that will collectively represent the deployed # Zope instance. binFolder = j(debFolder, 'usr', 'bin') os.makedirs(binFolder) # <app>ctl name = '%s/%sctl' % (binFolder, self.appNameLower) f = file(name, 'w') f.write(appCtl % self.appNameLower) os.chmod(name, 0744) # Make it executable by owner. f.close() # <app>run name = '%s/%srun' % (binFolder, self.appNameLower) f = file(name, 'w') f.write(appRun % self.appNameLower) os.chmod(name, 0744) # Make it executable by owner. f.close() # startoo name = '%s/startoo' % binFolder f = file(name, 'w') f.write(ooStart) f.close() os.chmod(name, 0744) # Make it executable by owner. # /var/lib/<app> (will store Data.fs, lock files, etc) varLibFolder = j(debFolder, 'var', 'lib', self.appNameLower) os.makedirs(varLibFolder) f = file('%s/README' % varLibFolder, 'w') f.write('This folder stores the %s database.\n' % self.appName) f.close() # /var/log/<app> (will store event.log and Z2.log) varLogFolder = j(debFolder, 'var', 'log', self.appNameLower) os.makedirs(varLogFolder) f = file('%s/README' % varLogFolder, 'w') f.write('This folder stores the log files for %s.\n' % self.appName) f.close() # /etc/<app>.conf (Zope configuration file) etcFolder = j(debFolder, 'etc') os.makedirs(etcFolder) name = '%s/%s.conf' % (etcFolder, self.appNameLower) n = self.appNameLower f = file(name, 'w') productsFolder = '/usr/lib/python%s/%s/zope' % \ (self.pythonVersions[0], self.appName) f.write(zopeConf % ('/var/lib/%s' % n, '/var/lib/%s' % n, '/var/log/%s' % n, str(self.zopePort), 'products %s\n' % productsFolder)) f.close() # /etc/init.d/<app> (start the app at boot time) initdFolder = j(etcFolder, 'init.d') os.makedirs(initdFolder) name = '%s/%s' % (initdFolder, self.appNameLower) f = file(name, 'w') n = self.appNameLower f.write(initScript % (n, n, 'Start Zope with the Appy-based %s ' \ 'application.' % n, '%sctl start' % n, '%sctl restart' % n, '%sctl stop' % n)) f.close() os.chmod(name, 0744) # Make it executable by owner. # /etc/init.d/oo (start OpenOffice at boot time) name = '%s/oo' % initdFolder f = file(name, 'w') f.write(initScript % ('oo', 'oo', 'Start OpenOffice in server mode', 'startoo', 'startoo', "#Can't stop OO.")) f.write('\n') f.close() os.chmod(name, 0744) # Make it executable by owner. # Get the size of the app, in Kb. os.chdir(tempFolder) cmd = subprocess.Popen(['du', '-b', '-s', 'debian'], stdout=subprocess.PIPE) size = int(int(cmd.stdout.read().split()[0]) / 1024.0) os.chdir(debFolder) # Create data.tar.gz based on it. os.system('tar czvf data.tar.gz *') # Create the control file f = file('control', 'w') nameSuffix = '' dependencies = [] if self.appName != 'appy': nameSuffix = '-%s' % self.appNameLower dependencies.append('python-appy') if self.depends: for d in self.depends: dependencies.append(d) depends = '' if dependencies: depends = ', ' + ', '.join(dependencies) f.write(debianInfo % (nameSuffix, self.appVersion, size, self.pythonVersions[0], depends)) f.close() # Create md5sum file f = file('md5sums', 'w') toWalk = ['usr'] if self.appName != 'appy': toWalk += ['etc', 'var'] for folderToWalk in toWalk: for dir, dirnames, filenames in os.walk(folderToWalk): for name in filenames: m = md5.new() pathName = j(dir, name) currentFile = file(pathName, 'rb') while True: data = currentFile.read(8096) if not data: break m.update(data) currentFile.close() # Add the md5 sum to the file f.write('%s %s\n' % (m.hexdigest(), pathName)) f.close() # Create postinst, a script that will: # - bytecompile Python files after the Debian install # - change ownership of some files if required # - [in the case of an app-package] call update-rc.d for starting it at # boot time. f = file('postinst', 'w') content = '#!/bin/sh\nset -e\n' for version in self.pythonVersions: bin = '/usr/bin/python%s' % version lib = '/usr/lib/python%s' % version cmds = ' %s -m compileall -q %s/%s 2> /dev/null\n' % (bin, lib, self.appName) content += 'if [ -e %s ]\nthen\n%sfi\n' % (bin, cmds) if self.appName != 'appy': # Allow user "zope", that runs the Zope instance, to write the # database and log files. content += 'chown -R zope:root /var/lib/%s\n' % self.appNameLower content += 'chown -R zope:root /var/log/%s\n' % self.appNameLower # Call update-rc.d for starting the app at boot time content += 'update-rc.d %s defaults\n' % self.appNameLower content += 'update-rc.d oo defaults\n' # (re-)start the app content += '%sctl restart\n' % self.appNameLower # (re-)start oo content += 'startoo\n' f.write(content) f.close() # Create prerm, a script that will remove all pyc files before removing # the Debian package. f = file('prerm', 'w') content = '#!/bin/sh\nset -e\n' for version in self.pythonVersions: content += 'find /usr/lib/python%s/%s -name "*.pyc" -delete\n' % \ (version, self.appName) f.write(content) f.close() # Create control.tar.gz os.system('tar czvf control.tar.gz ./control ./md5sums ./postinst ' \ './prerm') # Create debian-binary f = file('debian-binary', 'w') f.write('2.0\n') f.close() # Create the signature if required if self.sign: # Create the concatenated version of all files within the deb os.system('cat debian-binary control.tar.gz data.tar.gz > ' \ '/tmp/combined-contents') os.system('gpg -abs -o _gpgorigin /tmp/combined-contents') signFile = '_gpgorigin ' os.remove('/tmp/combined-contents') # Export the public key and name it according to its ID as found by # analyzing the result of command "gpg --fingerprint". cmd = subprocess.Popen(['gpg', '--fingerprint'], stdout=subprocess.PIPE) fingerprint = cmd.stdout.read().split('\n') id = 'pubkey' for line in fingerprint: if '=' not in line: continue id = line.split('=')[1].strip() id = ''.join(id.split()[-4:]) break os.system('gpg --export -a > %s/%s.asc' % (self.out, id)) else: signFile = '' # Create the .deb package debName = 'python-appy%s-%s.deb' % (nameSuffix, self.appVersion) os.system('ar -r %s %sdebian-binary control.tar.gz data.tar.gz' % \ (debName, signFile)) # Move it to self.out os.rename(j(debFolder, debName), j(self.out, debName)) # Clean temp files FolderDeleter.delete(debFolder) os.chdir(curdir)
def getValue( self, obj, template=None, format=None, result=None, queryData=None, customParams=None, noSecurity=False ): """For a pod field, getting its value means computing a pod document or returning a frozen one. A pod field differs from other field types because there can be several ways to produce the field value (ie: self.template can hold various templates; output file format can be odt, pdf,.... We get those precisions about the way to produce the file, either from params, or from default values. * p_template is the specific template, among self.template, that must be used as base for generating the document; * p_format is the output format of the resulting document; * p_result, if given, must be the absolute path of the document that will be computed by pod. If not given, pod will produce a doc in the OS temp folder; * if the pod document is related to a query, the query parameters needed to re-trigger the query are given in p_queryData; * p_customParams may be specified. Every custom param must have form "name:value". Custom params override any other value available in the context, including values from the field-specific context. """ obj = obj.appy() template = template or self.template[0] format = format or "odt" # Security check. if not noSecurity and not queryData: if self.showTemplate and not self.showTemplate(obj, template): raise Exception(self.UNAUTHORIZED) # Return the possibly frozen document (not applicable for query-related # pods). if not queryData: frozen = self.isFrozen(obj, template, format) if frozen: fileName = self.getDownloadName(obj, template, format, False) return FileInfo(frozen, inDb=False, uploadName=fileName) # We must call pod to compute a pod document from "template". tool = obj.tool diskFolder = tool.getDiskFolder() # Get the path to the pod template. templatePath = os.path.join(diskFolder, template) if not os.path.isfile(templatePath): raise Exception(self.TEMPLATE_NOT_FOUND % templatePath) # Get or compute the specific POD context specificContext = None if callable(self.context): specificContext = self.callMethod(obj, self.context) else: specificContext = self.context # Compute the name of the result file. if not result: result = "%s/%s_%f.%s" % (sutils.getOsTempFolder(), obj.uid, time.time(), format) # Define parameters to give to the appy.pod renderer podContext = { "tool": tool, "user": obj.user, "self": obj, "field": self, "now": obj.o.getProductConfig().DateTime(), "_": obj.translate, "projectFolder": diskFolder, } # If the pod document is related to a query, re-trigger it and put the # result in the pod context. if queryData: # Retrieve query params cmd = ", ".join(tool.o.queryParamNames) cmd += " = queryData.split(';')" exec cmd # (re-)execute the query, but without any limit on the number of # results; return Appy objects. objs = tool.o.executeQuery( obj.o.portal_type, searchName=search, sortBy=sortKey, sortOrder=sortOrder, filterKey=filterKey, filterValue=filterValue, maxResults="NO_LIMIT", ) podContext["objects"] = [o.appy() for o in objs.objects] # Add the field-specific context if present. if specificContext: podContext.update(specificContext) # If a custom param comes from the request, add it to the context. if customParams: paramsDict = eval(customParams) podContext.update(paramsDict) # Define a potential global styles mapping if callable(self.stylesMapping): stylesMapping = self.callMethod(obj, self.stylesMapping) else: stylesMapping = self.stylesMapping rendererParams = { "template": templatePath, "context": podContext, "result": result, "stylesMapping": stylesMapping, "imageResolver": tool.o.getApp(), "overwriteExisting": True, } if tool.unoEnabledPython: rendererParams["pythonWithUnoPath"] = tool.unoEnabledPython if tool.openOfficePort: rendererParams["ooPort"] = tool.openOfficePort # Launch the renderer try: renderer = Renderer(**rendererParams) renderer.run() except PodError, pe: if not os.path.exists(result): # In some (most?) cases, when OO returns an error, the result is # nevertheless generated. obj.log(str(pe).strip(), type="error") return Pod.POD_ERROR
def __init__(self, fileOrFolder, script): self.fileOrFolder = fileOrFolder self.script = script self.tempFolder = getOsTempFolder()
def getValue(self, obj): '''Gets, on_obj, the value conforming to self's type definition. For a Pod field, if a file is stored in the field, it means that the field has been frozen. Else, it means that the value must be retrieved by calling pod to compute the result.''' rq = getattr(obj, 'REQUEST', None) res = getattr(obj.aq_base, self.name, None) if res and res.size: # Return the frozen file. return sutils.FileWrapper(res) # If we are here, it means that we must call pod to compute the file. # A Pod field differs from other field types because there can be # several ways to produce the field value (ie: output file format can be # odt, pdf,...; self.action can be executed or not...). We get those # precisions about the way to produce the file from the request object # and from the tool. If we don't find the request object (or if it does # not exist, ie, when Zope runs in test mode), we use default values. obj = obj.appy() tool = obj.tool # Get POD template and available formats from the tool. template, availFormats = self.getToolInfo(obj) # Get the output format defaultFormat = 'pdf' if defaultFormat not in availFormats: defaultFormat = availFormats[0] outputFormat = getattr(rq, 'podFormat', defaultFormat) # Get or compute the specific POD context specificContext = None if callable(self.context): specificContext = self.callMethod(obj, self.context) else: specificContext = self.context # Temporary file where to generate the result tempFileName = '%s/%s_%f.%s' % (sutils.getOsTempFolder(), obj.uid, time.time(), outputFormat) # Define parameters to give to the appy.pod renderer podContext = { 'tool': tool, 'user': obj.user, 'self': obj, 'field': self, 'now': obj.o.getProductConfig().DateTime(), '_': obj.translate, 'projectFolder': tool.getDiskFolder() } # If the POD document is related to a query, get it from the request, # execute it and put the result in the context. isQueryRelated = rq.get('queryData', None) if isQueryRelated: # Retrieve query params from the request cmd = ', '.join(tool.o.queryParamNames) cmd += " = rq['queryData'].split(';')" exec cmd # (re-)execute the query, but without any limit on the number of # results; return Appy objects. objs = tool.o.executeQuery(obj.o.portal_type, searchName=search, sortBy=sortKey, sortOrder=sortOrder, filterKey=filterKey, filterValue=filterValue, maxResults='NO_LIMIT') podContext['objects'] = [o.appy() for o in objs['objects']] # Add the field-specific context if present. if specificContext: podContext.update(specificContext) # If a custom param comes from the request, add it to the context. A # custom param must have format "name:value". Custom params override any # other value in the request, including values from the field-specific # context. customParams = rq.get('customParams', None) if customParams: paramsDict = eval(customParams) podContext.update(paramsDict) # Define a potential global styles mapping if callable(self.stylesMapping): stylesMapping = self.callMethod(obj, self.stylesMapping) else: stylesMapping = self.stylesMapping rendererParams = { 'template': StringIO.StringIO(template.content), 'context': podContext, 'result': tempFileName, 'stylesMapping': stylesMapping, 'imageResolver': tool.o.getApp() } if tool.unoEnabledPython: rendererParams['pythonWithUnoPath'] = tool.unoEnabledPython if tool.openOfficePort: rendererParams['ooPort'] = tool.openOfficePort # Launch the renderer try: renderer = Renderer(**rendererParams) renderer.run() except PodError, pe: if not os.path.exists(tempFileName): # In some (most?) cases, when OO returns an error, the result is # nevertheless generated. obj.log(str(pe), type='error') return Pod.POD_ERROR
def getValue(self, obj, template=None, format=None, result=None, queryData=None, customParams=None, noSecurity=False): '''For a pod field, getting its value means computing a pod document or returning a frozen one. A pod field differs from other field types because there can be several ways to produce the field value (ie: self.template can hold various templates; output file format can be odt, pdf,.... We get those precisions about the way to produce the file, either from params, or from default values. * p_template is the specific template, among self.template, that must be used as base for generating the document; * p_format is the output format of the resulting document; * p_result, if given, must be the absolute path of the document that will be computed by pod. If not given, pod will produce a doc in the OS temp folder; * if the pod document is related to a query, the query parameters needed to re-trigger the query are given in p_queryData; * p_customParams may be specified. Every custom param must have form "name:value". Custom params override any other value available in the context, including values from the field-specific context. ''' obj = obj.appy() template = template or self.template[0] format = format or 'odt' # Security check. if not noSecurity and not queryData: if self.showTemplate and not self.showTemplate(obj, template): raise Exception(self.UNAUTHORIZED) # Return the possibly frozen document (not applicable for query-related # pods). if not queryData: frozen = self.isFrozen(obj, template, format) if frozen: fileName = self.getDownloadName(obj, template, format, False) return FileInfo(frozen, inDb=False, uploadName=fileName) # We must call pod to compute a pod document from "template". tool = obj.tool diskFolder = tool.getDiskFolder() # Get the path to the pod template. templatePath = os.path.join(diskFolder, template) if not os.path.isfile(templatePath): raise Exception(self.TEMPLATE_NOT_FOUND % templatePath) # Get or compute the specific POD context specificContext = None if callable(self.context): specificContext = self.callMethod(obj, self.context) else: specificContext = self.context # Compute the name of the result file. if not result: result = '%s/%s_%f.%s' % (sutils.getOsTempFolder(), obj.uid, time.time(), format) # Define parameters to give to the appy.pod renderer podContext = {'tool': tool, 'user': obj.user, 'self': obj, 'field':self, 'now': obj.o.getProductConfig().DateTime(), '_': obj.translate, 'projectFolder': diskFolder} # If the pod document is related to a query, re-trigger it and put the # result in the pod context. if queryData: # Retrieve query params cmd = ', '.join(tool.o.queryParamNames) cmd += " = queryData.split(';')" exec cmd # (re-)execute the query, but without any limit on the number of # results; return Appy objects. objs = tool.o.executeQuery(obj.o.portal_type, searchName=search, sortBy=sortKey, sortOrder=sortOrder, filterKey=filterKey, filterValue=filterValue, maxResults='NO_LIMIT') podContext['objects'] = [o.appy() for o in objs.objects] # Add the field-specific context if present. if specificContext: podContext.update(specificContext) # If a custom param comes from the request, add it to the context. if customParams: paramsDict = eval(customParams) podContext.update(paramsDict) # Define a potential global styles mapping if callable(self.stylesMapping): stylesMapping = self.callMethod(obj, self.stylesMapping) else: stylesMapping = self.stylesMapping rendererParams = {'template': templatePath, 'context': podContext, 'result': result, 'stylesMapping': stylesMapping, 'imageResolver': tool.o.getApp(), 'overwriteExisting': True} if tool.unoEnabledPython: rendererParams['pythonWithUnoPath'] = tool.unoEnabledPython if tool.openOfficePort: rendererParams['ooPort'] = tool.openOfficePort # Launch the renderer try: renderer = Renderer(**rendererParams) renderer.run() except PodError, pe: if not os.path.exists(result): # In some (most?) cases, when OO returns an error, the result is # nevertheless generated. obj.log(str(pe).strip(), type='error') return Pod.POD_ERROR
def getValue(self, obj): '''Gets, on_obj, the value conforming to self's type definition. For a Pod field, if a file is stored in the field, it means that the field has been frozen. Else, it means that the value must be retrieved by calling pod to compute the result.''' rq = getattr(obj, 'REQUEST', None) res = getattr(obj.aq_base, self.name, None) if res and res.size: # Return the frozen file. return sutils.FileWrapper(res) # If we are here, it means that we must call pod to compute the file. # A Pod field differs from other field types because there can be # several ways to produce the field value (ie: output file format can be # odt, pdf,...; self.action can be executed or not...). We get those # precisions about the way to produce the file from the request object # and from the tool. If we don't find the request object (or if it does # not exist, ie, when Zope runs in test mode), we use default values. obj = obj.appy() tool = obj.tool # Get POD template and available formats from the tool. template, availFormats = self.getToolInfo(obj) # Get the output format defaultFormat = 'pdf' if defaultFormat not in availFormats: defaultFormat = availFormats[0] outputFormat = getattr(rq, 'podFormat', defaultFormat) # Get or compute the specific POD context specificContext = None if callable(self.context): specificContext = self.callMethod(obj, self.context) else: specificContext = self.context # Temporary file where to generate the result tempFileName = '%s/%s_%f.%s' % ( sutils.getOsTempFolder(), obj.uid, time.time(), outputFormat) # Define parameters to give to the appy.pod renderer podContext = {'tool': tool, 'user': obj.user, 'self': obj, 'field':self, 'now': obj.o.getProductConfig().DateTime(), '_': obj.translate, 'projectFolder': tool.getDiskFolder()} # If the POD document is related to a query, get it from the request, # execute it and put the result in the context. isQueryRelated = rq.get('queryData', None) if isQueryRelated: # Retrieve query params from the request cmd = ', '.join(tool.o.queryParamNames) cmd += " = rq['queryData'].split(';')" exec cmd # (re-)execute the query, but without any limit on the number of # results; return Appy objects. objs = tool.o.executeQuery(obj.o.portal_type, searchName=search, sortBy=sortKey, sortOrder=sortOrder, filterKey=filterKey, filterValue=filterValue, maxResults='NO_LIMIT') podContext['objects'] = [o.appy() for o in objs['objects']] # Add the field-specific context if present. if specificContext: podContext.update(specificContext) # If a custom param comes from the request, add it to the context. A # custom param must have format "name:value". Custom params override any # other value in the request, including values from the field-specific # context. customParams = rq.get('customParams', None) if customParams: paramsDict = eval(customParams) podContext.update(paramsDict) # Define a potential global styles mapping if callable(self.stylesMapping): stylesMapping = self.callMethod(obj, self.stylesMapping) else: stylesMapping = self.stylesMapping rendererParams = {'template': StringIO.StringIO(template.content), 'context': podContext, 'result': tempFileName, 'stylesMapping': stylesMapping, 'imageResolver': tool.o.getApp()} if tool.unoEnabledPython: rendererParams['pythonWithUnoPath'] = tool.unoEnabledPython if tool.openOfficePort: rendererParams['ooPort'] = tool.openOfficePort # Launch the renderer try: renderer = Renderer(**rendererParams) renderer.run() except PodError, pe: if not os.path.exists(tempFileName): # In some (most?) cases, when OO returns an error, the result is # nevertheless generated. obj.log(str(pe), type='error') return Pod.POD_ERROR