diff --git a/fields/pod.py b/fields/pod.py index 9c8c86e..f64104a 100644 --- a/fields/pod.py +++ b/fields/pod.py @@ -15,7 +15,7 @@ # Appy. If not, see . # ------------------------------------------------------------------------------ -import time, os, os.path, StringIO +import time, os, os.path from appy.fields import Field from appy.px import Px from file import File @@ -43,7 +43,7 @@ class Pod(Field): - @@ -58,7 +58,7 @@ class Pod(Field): maxChars=None, colspan=1, master=None, masterValue=None, focus=False, historized=False, mapping=None, label=None, template=None, context=None, action=None, askAction=False, - stylesMapping={}, freezeFormat='pdf'): + stylesMapping={}, formats=None, freezeFormat='pdf'): # The following param stores the path to a POD template self.template = template # The context is a dict containing a specific pod context, or a method @@ -72,7 +72,14 @@ class Pod(Field): self.askAction = askAction # A global styles mapping that would apply to the whole template self.stylesMapping = stylesMapping - # Freeze format is by PDF by default + # What are the output formats when generating documents from this pod ? + if not formats: + # Compute default ones + if template.endswith('.ods'): + self.formats = ('xls', 'ods') + else: + self.formats = ('pdf', 'doc', 'odt') + # Freeze format is PDF by default. self.freezeFormat = freezeFormat Field.__init__(self, None, (0,1), default, show, page, group, layouts, move, indexed, searchable, specificReadPermission, @@ -86,26 +93,13 @@ class Pod(Field): value = getattr(obj.o.aq_base, self.name, None) return isinstance(value, obj.o.getProductConfig().File) - def getToolInfo(self, obj): - '''Gets information related to this field (p_self) that is available in - the tool: the POD template and the available output formats. If this - field is frozen, available output formats are not available anymore: - only the format of the frozen doc is returned.''' - tool = obj.tool - appyClass = tool.o.getAppyClass(obj.o.meta_type) - # Get the output format(s) - if self.isFrozen(obj): - # The only available format is the one from the frozen document - fileName = getattr(obj.o.aq_base, self.name).filename - formats = (os.path.splitext(fileName)[1][1:],) - else: - # Available formats are those which are selected in the tool. - name = tool.getAttributeName('formats', appyClass, self.name) - formats = getattr(tool, name) - # Get the POD template - name = tool.getAttributeName('podTemplate', appyClass, self.name) - template = getattr(tool, name) - return (template, formats) + def getOutputFormats(self, obj): + '''Returns self.formats, excepted if there is a frozen document: in + this case, only the format of the frozen doc is returned.''' + if not self.isFrozen(obj): return self.formats + # The only available format is the one from the frozen document + fileName = getattr(obj.o.aq_base, self.name).filename + return (os.path.splitext(fileName)[1][1:],) def getValue(self, obj): '''Gets, on_obj, the value conforming to self's type definition. For a @@ -121,17 +115,18 @@ class Pod(Field): # 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. + # precisions about the way to produce the file from the request object. + # 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) + diskFolder = tool.getDiskFolder() + # Get the path to the pod template. + templatePath = os.path.join(diskFolder, self.template) + if not os.path.isfile(templatePath): + raise Exception('Pod template not found at %s.' % templatePath) # Get the output format - defaultFormat = 'pdf' - if defaultFormat not in availFormats: defaultFormat = availFormats[0] - outputFormat = getattr(rq, 'podFormat', defaultFormat) + outputFormat = getattr(rq, 'podFormat', 'odt') # Get or compute the specific POD context specificContext = None if callable(self.context): @@ -144,7 +139,7 @@ class Pod(Field): # 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()} + '_': obj.translate, 'projectFolder': diskFolder} # 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) @@ -175,9 +170,8 @@ class Pod(Field): stylesMapping = self.callMethod(obj, self.stylesMapping) else: stylesMapping = self.stylesMapping - rendererParams = {'template': StringIO.StringIO(template.content), - 'context': podContext, 'result': tempFileName, - 'stylesMapping': stylesMapping, + rendererParams = {'template': templatePath, 'context': podContext, + 'result': tempFileName, 'stylesMapping':stylesMapping, 'imageResolver': tool.o.getApp()} if tool.unoEnabledPython: rendererParams['pythonWithUnoPath'] = tool.unoEnabledPython @@ -191,7 +185,7 @@ class Pod(Field): 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') + obj.log(str(pe).strip(), type='error') return Pod.POD_ERROR # Give a friendly name for this file fileName = obj.translate(self.labelId) @@ -209,9 +203,9 @@ class Pod(Field): try: os.remove(tempFileName) except OSError, oe: - obj.log(Pod.DELETE_TEMP_DOC_ERROR % str(oe), type='warning') + obj.log(Pod.DELETE_TEMP_DOC_ERROR % str(oe).strip(), type='warning') except IOError, ie: - obj.log(Pod.DELETE_TEMP_DOC_ERROR % str(ie), type='warning') + obj.log(Pod.DELETE_TEMP_DOC_ERROR % str(ie).strip(), type='warning') return res def store(self, obj, value): diff --git a/gen/descriptors.py b/gen/descriptors.py index 308b627..ac12a3e 100644 --- a/gen/descriptors.py +++ b/gen/descriptors.py @@ -6,7 +6,7 @@ import types, copy import appy.gen as gen import po -from model import ModelClass, toolFieldPrefixes +from model import ModelClass from utils import produceNiceMessage, getClassName TABS = 4 # Number of blanks in a Python indentation. @@ -260,21 +260,6 @@ class FieldDescriptor: def __repr__(self): return '' % (self.fieldName, self.classDescr) - def getToolFieldMessage(self, fieldName): - '''Some attributes generated on the Tool class need a specific - default message, returned by this method.''' - res = fieldName - for prefix in toolFieldPrefixes: - fullPrefix = prefix + 'For' - if fieldName.startswith(fullPrefix): - messageId = 'MSG_%s' % prefix - res = getattr(po, messageId) - if res.find('%s') != -1: - # I must complete the message with the field name. - res = res % fieldName.split('_')[-1] - break - return res - def produceMessage(self, msgId, isLabel=True): '''Gets the default label, description or help (depending on p_msgType) for i18n message p_msgId.''' @@ -283,10 +268,6 @@ class FieldDescriptor: if isLabel: niceDefault = True default = self.fieldName - # Some attributes need a specific predefined message - if isinstance(self.classDescr, ToolClassDescriptor): - default = self.getToolFieldMessage(self.fieldName) - if default != self.fieldName: niceDefault = False return msgId, default, niceDefault def walkString(self): @@ -318,8 +299,6 @@ class FieldDescriptor: if self.appyType.askAction: label = '%s_%s_askaction' % (self.classDescr.name, self.fieldName) self.i18n(label, po.POD_ASKACTION, nice=False) - # Add the POD-related fields on the Tool - self.generator.tool.addPodRelatedFields(self) def walkList(self): # Add i18n-specific messages @@ -417,43 +396,6 @@ class ToolClassDescriptor(ClassDescriptor): def isFolder(self, klass=None): return True def isRoot(self): return False - def addPodRelatedFields(self, fieldDescr): - '''Adds the fields needed in the Tool for configuring a Pod field.''' - className = fieldDescr.classDescr.name - # On what page and group to display those fields ? - pg = {'page': 'documents', - 'group':gen.Group(fieldDescr.classDescr.klass.__name__,['50%']*2)} - # Add the field that will store the pod template. - fieldName = 'podTemplateFor%s_%s' % (className, fieldDescr.fieldName) - fieldType = gen.File(**pg) - self.addField(fieldName, fieldType) - # Add the field that will store the output format(s) - fieldName = 'formatsFor%s_%s' % (className, fieldDescr.fieldName) - fieldType = gen.String(validator=gen.Selection('getPodOutputFormats'), - multiplicity=(1,None), default=('odt',), **pg) - self.addField(fieldName, fieldType) - - def addSearchRelatedFields(self, classDescr): - '''Adds, for class p_classDescr, attributes related to the search - functionality for class p_classDescr.''' - className = classDescr.name - # Field that defines how many columns are shown on the custom search - # screen. - fieldName = 'numberOfSearchColumnsFor%s' % className - fieldType = gen.Integer(default=3, page='userInterface', - group=classDescr.klass.__name__) - self.addField(fieldName, fieldType) - # Field that allows to select, among all indexed fields, what fields - # must really be used in the search screen. - fieldName = 'searchFieldsFor%s' % className - defaultValue = [a[0] for a in classDescr.getOrderedAppyAttributes( - condition='attrValue.indexed')] - if 'title' not in defaultValue: defaultValue.insert(0, 'title') - fieldType = gen.String(multiplicity=(0,None), validator=gen.Selection( - '_appy_getSearchableFields*%s' % className), default=defaultValue, - page='userInterface', group=classDescr.klass.__name__) - self.addField(fieldName, fieldType) - def addImportRelatedFields(self, classDescr): '''Adds, for class p_classDescr, attributes related to the import functionality for class p_classDescr.''' diff --git a/gen/generator.py b/gen/generator.py index 8e2f238..71ba4f4 100644 --- a/gen/generator.py +++ b/gen/generator.py @@ -716,8 +716,6 @@ class ZopeGenerator(Generator): # import-related fields. for classDescr in self.getClasses(include='allButTool'): if not classDescr.isRoot(): continue - # Add the search-related fields. - self.tool.addSearchRelatedFields(classDescr) importMean = classDescr.getCreateMean('Import') if importMean: self.tool.addImportRelatedFields(classDescr) diff --git a/gen/installer.py b/gen/installer.py index 7ac2d2d..bec62ca 100644 --- a/gen/installer.py +++ b/gen/installer.py @@ -189,37 +189,6 @@ class ZopeInstaller: appyTool.log('Group "%s", related to global role "%s", was ' \ 'created.' % (groupId, role)) - # Create POD templates within the tool if required - for contentType in self.config.attributes.iterkeys(): - appyClass = tool.getAppyClass(contentType) - if not appyClass: continue # May be an abstract class - wrapperClass = tool.getAppyClass(contentType, wrapper=True) - for appyType in wrapperClass.__fields__: - if appyType.type != 'Pod': continue - # Find the attribute that stores the template, and store on - # it the default one specified in the appyType if no - # template is stored yet. - attrName = appyTool.getAttributeName('podTemplate', appyClass, - appyType.name) - fileObject = getattr(appyTool, attrName) - if not fileObject or (fileObject.size == 0): - # There is no file. Put the one specified in the appyType. - fileName = os.path.join(appyTool.getDiskFolder(), - appyType.template) - if os.path.exists(fileName): - setattr(appyTool, attrName, fileName) - # If the template is ods, set the default format to ods - # (because default is odt) - if fileName.endswith('.ods'): - formats = appyTool.getAttributeName('formats', - appyClass, appyType.name) - setattr(appyTool, formats, ['ods']) - appyTool.log('Imported "%s" in the tool in ' \ - 'attribute "%s"'% (fileName, attrName)) - else: - appyTool.log('Template "%s" was not found!' % \ - fileName, type='error') - # Create or update Translation objects translations = [t.o.id for t in appyTool.translations] # We browse the languages supported by this application and check diff --git a/gen/mixins/ToolMixin.py b/gen/mixins/ToolMixin.py index f63c06e..c5166ae 100644 --- a/gen/mixins/ToolMixin.py +++ b/gen/mixins/ToolMixin.py @@ -203,15 +203,6 @@ class ToolMixin(BaseMixin): cfg = self.getProductConfig() return [self.getAppyClass(k) for k in cfg.rootClasses] - def _appy_getSearchableFields(self, className): - '''Returns the (translated) names of fields that may be searched on - objects of type p_className (=indexed fields).''' - res = [] - for field in self.getAllAppyTypes(className=className): - if field.indexed: - res.append((field.name, self.translate(field.labelId))) - return res - def getSearchInfo(self, className, refInfo=None): '''Returns, as an object: - the list of searchable fields (some among all indexed fields); @@ -225,9 +216,13 @@ class ToolMixin(BaseMixin): nbOfColumns = refField.queryNbCols else: # The search is triggered from an app-wide search. - tool = self.appy() - fieldNames = getattr(tool, 'searchFieldsFor%s' % className,()) - nbOfColumns = getattr(tool, 'numberOfSearchColumnsFor%s' %className) + klass = self.getAppyClass(className) + fieldNames = getattr(klass, 'searchFields', None) + if not fieldNames: + # Gather all the indexed fields on this class. + fieldNames = [f.name for f in self.getAllAppyTypes(className) \ + if f.indexed] + nbOfColumns = getattr(klass, 'numberOfSearchColumns', 3) for name in fieldNames: field = self.getAppyType(name, className=className) fields.append(field) @@ -250,8 +245,7 @@ class ToolMixin(BaseMixin): '''Must we show, on pxQueryResult, instances of p_className as a list or as a grid?''' klass = self.getAppyClass(className) - if hasattr(klass, 'resultMode'): return klass.resultMode - return 'list' # The default mode + return getattr(klass, 'resultMode', 'list') def getImportElements(self, className): '''Returns the list of elements that can be imported from p_path for diff --git a/gen/model.py b/gen/model.py index 608e65e..e344f35 100644 --- a/gen/model.py +++ b/gen/model.py @@ -214,13 +214,12 @@ setattr(Page, Page.pages.back.attribute, Page.pages.back) # The Tool class --------------------------------------------------------------- # Prefixes of the fields generated on the Tool. -toolFieldPrefixes = ('podTemplate', 'formats', 'numberOfSearchColumns', - 'searchFields') defaultToolFields = ('title', 'mailHost', 'mailEnabled', 'mailFrom', - 'appyVersion', 'dateFormat', 'hourFormat', 'users', - 'connectedUsers', 'groups', 'translations', - 'loadTranslationsAtStartup', 'pages', 'unoEnabledPython', - 'openOfficePort', 'numberOfResultsPerPage') + 'appyVersion', 'dateFormat', 'hourFormat', + 'unoEnabledPython', 'openOfficePort', + 'numberOfResultsPerPage', 'users', 'connectedUsers', + 'groups', 'translations', 'loadTranslationsAtStartup', + 'pages') class Tool(ModelClass): # In a ModelClass we need to declare attributes in the following list. @@ -239,6 +238,9 @@ class Tool(ModelClass): appyVersion = gen.String(**lf) dateFormat = gen.String(default='%d/%m/%Y', **lf) hourFormat = gen.String(default='%H:%M', **lf) + unoEnabledPython = gen.String(default='/usr/bin/python', **lf) + openOfficePort = gen.Integer(default=2002, **lf) + numberOfResultsPerPage = gen.Integer(default=30, **lf) # Ref(User) will maybe be transformed into Ref(CustomUserClass). userPage = gen.Page('users', show=isManager) @@ -265,16 +267,6 @@ class Tool(ModelClass): show='view', back=gen.Ref(attribute='toTool3', show=False), page=gen.Page('pages', show=isManager)) - # Document generation page - dgp = {'page': gen.Page('documents', show=isManagerEdit)} - def validPythonWithUno(self, value): pass # Real method in the wrapper - unoEnabledPython = gen.String(default='/usr/bin/python', show=False, - validator=validPythonWithUno, **dgp) - openOfficePort = gen.Integer(default=2002, show=False, **dgp) - # User interface page - numberOfResultsPerPage = gen.Integer(default=30, - page=gen.Page('userInterface', show=False)) - @classmethod def _appy_clean(klass): toClean = [] diff --git a/gen/po.py b/gen/po.py index 503e782..5382880 100644 --- a/gen/po.py +++ b/gen/po.py @@ -216,13 +216,6 @@ appyLabels = [ # Some default values for labels whose ids are not fixed (so they can't be # included in the previous variable). CONFIG = "Configuration panel for product '%s'" -# The following messages (starting with MSG_) correspond to tool -# attributes added for every gen-class (warning: the message IDs correspond -# to MSG_). -MSG_podTemplate = "POD template for field '%s'" -MSG_formats = "Output format(s) for field '%s'" -MSG_numberOfSearchColumns = "Number of search columns" -MSG_searchFields = "Search fields" POD_ASKACTION = 'Trigger related action' EMAIL_SUBJECT = '${siteTitle} - Action \\"${transitionName}\\" has been ' \ 'performed on element entitled \\"${objectTitle}\\".' diff --git a/gen/wrappers/ToolWrapper.py b/gen/wrappers/ToolWrapper.py index 83fb911..af0f3bc 100644 --- a/gen/wrappers/ToolWrapper.py +++ b/gen/wrappers/ToolWrapper.py @@ -6,17 +6,6 @@ from appy.shared.utils import executeCommand from appy.gen.wrappers import AbstractWrapper from appy.px import Px -# ------------------------------------------------------------------------------ -_PY = 'Please specify a file corresponding to a Python interpreter ' \ - '(ie "/usr/bin/python").' -FILE_NOT_FOUND = 'Path "%s" was not found.' -VALUE_NOT_FILE = 'Path "%s" is not a file. ' + _PY -NO_PYTHON = "Name '%s' does not starts with 'python'. " + _PY -NOT_UNO_ENABLED_PYTHON = '"%s" is not a UNO-enabled Python interpreter. ' \ - 'To check if a Python interpreter is UNO-enabled, ' \ - 'launch it and type "import uno". If you have no ' \ - 'ImportError exception it is ok.' - # ------------------------------------------------------------------------------ class ToolWrapper(AbstractWrapper): @@ -649,19 +638,6 @@ class ToolWrapper(AbstractWrapper):

''', template=AbstractWrapper.pxTemplate, hook='content') - def validPythonWithUno(self, value): - '''This method represents the validator for field unoEnabledPython.''' - if value: - if not os.path.exists(value): - return FILE_NOT_FOUND % value - if not os.path.isfile(value): - return VALUE_NOT_FILE % value - if not os.path.basename(value).startswith('python'): - return NO_PYTHON % value - if os.system('%s -c "import uno"' % value): - return NOT_UNO_ENABLED_PYTHON % value - return True - def isManager(self): '''Some pages on the tool can only be accessed by managers.''' if self.user.has_role('Manager'): return 'view' @@ -686,11 +662,6 @@ class ToolWrapper(AbstractWrapper): (user.o.absolute_url(), user.title,access)) return res + '\n'.join(rows) + '' - podOutputFormats = ('odt', 'pdf', 'doc', 'rtf', 'ods', 'xls') - def getPodOutputFormats(self): - '''Gets the available output formats for POD documents.''' - return [(of, self.translate(of)) for of in self.podOutputFormats] - def getInitiator(self, field=False): '''Retrieves the object that triggered the creation of the object being currently created (if any), or the name of the field in this @@ -712,32 +683,6 @@ class ToolWrapper(AbstractWrapper): '''Gets the Appy class corresponding to technical p_zopeName.''' return self.o.getAppyClass(zopeName) - def getAttributeName(self, attributeType, klass, attrName=None): - '''Some names of Tool attributes are not easy to guess. This method - generates the attribute name based on p_attributeType, a p_klass from - the application, and a p_attrName (given only if needed). - p_attributeType may be: - - "podTemplate" - Stores the pod template for p_attrName. - - "formats" - Stores the output format(s) of a given pod template for - p_attrName. - - "numberOfSearchColumns" - Determines in how many columns the search screen for p_klass - is rendered. - - "searchFields" - Determines, among all indexed fields for p_klass, which one will - really be used in the search screen. - ''' - fullClassName = self.o.getPortalType(klass) - res = '%sFor%s' % (attributeType, fullClassName) - if attrName: res += '_%s' % attrName - return res - def getAvailableLanguages(self): '''Returns the list of available languages for this application.''' return [(t.id, t.title) for t in self.translations] @@ -801,36 +746,4 @@ class ToolWrapper(AbstractWrapper): except Exception, e: failed.append(startObject) return nb, failed - - def validate(self, new, errors): - '''Validates that uploaded POD templates and output types are - compatible.''' - page = self.request.get('page', 'main') - if page == 'documents': - # Check that uploaded templates and output formats are compatible. - for fieldName in dir(new): - # Ignore fields which are not POD templates. - if not fieldName.startswith('podTemplate'): continue - # Get the file name, either from the newly uploaded file or - # from the existing file stored in the database. - if getattr(new, fieldName): - fileName = getattr(new, fieldName).filename - else: - fileName = getattr(self, fieldName).name - # Get the extension of the uploaded file. - ext = os.path.splitext(fileName)[1][1:] - # Get the chosen output formats for this template. - formatsFieldName = 'formatsFor%s' % fieldName[14:] - formats = getattr(new, formatsFieldName) - error = False - if ext == 'odt': - error = ('ods' in formats) or ('xls' in formats) - elif ext == 'ods': - error = ('odt' in formats) or ('pdf' in formats) or \ - ('doc' in formats) or ('rtf' in formats) - if error: - msg = 'This (these) format(s) cannot be used with ' \ - 'this template.' - setattr(errors, formatsFieldName, msg) - return self._callCustom('validate', new, errors) # ------------------------------------------------------------------------------