diff --git a/bin/generate.py b/bin/generate.py index 72375e4..c92faf2 100644 --- a/bin/generate.py +++ b/bin/generate.py @@ -3,18 +3,15 @@ # ------------------------------------------------------------------------------ import sys, os.path from optparse import OptionParser -from appy.gen.generator import GeneratorError +from appy.gen.generator import GeneratorError, ZopeGenerator from appy.shared.utils import LinesCounter import appy.version # ------------------------------------------------------------------------------ ERROR_CODE = 1 -VALID_PRODUCT_TYPES = ('zope', 'odt') APP_NOT_FOUND = 'Application not found at %s.' WRONG_NG_OF_ARGS = 'Wrong number of arguments.' WRONG_OUTPUT_FOLDER = 'Output folder not found. Please create it first.' -PRODUCT_TYPE_ERROR = 'Wrong product type. Product type may be one of the ' \ - 'following: %s' % str(VALID_PRODUCT_TYPES) C_OPTION = 'Removes from i18n files all labels that are not automatically ' \ 'generated from your gen-application. It can be useful during ' \ 'development, when you do lots of name changes (classes, ' \ @@ -37,7 +34,7 @@ S_OPTION = 'Sorts all i18n labels. If you use this option, among the ' \ 'set of translation files.' class GeneratorScript: - '''usage: %prog [options] app productType outputFolder + '''usage: %prog [options] app outputFolder "app" is the path to your Appy application, which must be a Python package (= a folder containing a file named @@ -47,44 +44,29 @@ class GeneratorScript: generated product, stored or symlinked in /Products. - "productType" is the kind of product you want to generate. "zope" is - the only available production-ready target. - "odt" is experimental. - "outputFolder" is the folder where the Zope product will be generated. For example, if you develop your application in /home/gdy/MyProject/MyProject, you typically specify "/home/gdy/MyProject/zope" as outputFolder. ''' - - def generateProduct(self, options, application, productType, outputFolder): - if productType == 'odt': - exec 'from appy.gen.odt.generator import Generator' - else: - from appy.gen.generator import ZopeGenerator as Generator - Generator(application, outputFolder, options).run() - def manageArgs(self, parser, options, args): # Check number of args - if len(args) != 3: + if len(args) != 2: print WRONG_NG_OF_ARGS parser.print_help() sys.exit(ERROR_CODE) - # Check productType - if args[1] not in VALID_PRODUCT_TYPES: - print PRODUCT_TYPE_ERROR - sys.exit(ERROR_CODE) # Check existence of application if not os.path.exists(args[0]): print APP_NOT_FOUND % args[0] sys.exit(ERROR_CODE) - # Check existence of outputFolder basic type - if not os.path.exists(args[2]): + # Check existence of outputFolder + if not os.path.exists(args[1]): print WRONG_OUTPUT_FOLDER sys.exit(ERROR_CODE) # Convert all paths in absolute paths - for i in (0,2): + for i in (0,1): args[i] = os.path.abspath(args[i]) + def run(self): optParser = OptionParser(usage=GeneratorScript.__doc__) optParser.add_option("-c", "--i18n-clean", action='store_true', @@ -95,8 +77,8 @@ class GeneratorScript: try: self.manageArgs(optParser, options, args) print 'Appy version:', appy.version.verbose - print 'Generating %s product in %s...' % (args[1], args[2]) - self.generateProduct(options, *args) + print 'Generating Zope product in %s...' % args[1] + ZopeGenerator(args[0], args[1], options).run() # Give the user some statistics about its code LinesCounter(args[0]).run() except GeneratorError, ge: diff --git a/gen/__init__.py b/gen/__init__.py index 5c88e7b..6322b28 100644 --- a/gen/__init__.py +++ b/gen/__init__.py @@ -1026,8 +1026,7 @@ class String(Type): it will be given by the Appy validation machinery, so it must be specified as parameter. The function returns True if the check is successful.''' - if not value: return True # Plone calls me erroneously for - # non-mandatory fields. + if not value: return True # First, remove any non-digit char v = '' for c in value: @@ -1058,8 +1057,7 @@ class String(Type): '''Checks that p_value corresponds to a valid IBAN number. IBAN stands for International Bank Account Number (ISO 13616). If the number is valid, the method returns True.''' - if not value: return True # Plone calls me erroneously for - # non-mandatory fields. + if not value: return True # First, remove any non-digit or non-letter char v = '' for c in value: @@ -1088,8 +1086,7 @@ class String(Type): '''Checks that p_value corresponds to a valid BIC number. BIC stands for Bank Identifier Code (ISO 9362). If the number is valid, the method returns True.''' - if not value: return True # Plone calls me erroneously for - # non-mandatory fields. + if not value: return True # BIC number must be 8 or 11 chars if len(value) not in (8, 11): return False # 4 first chars, representing bank name, must be letters @@ -1176,15 +1173,7 @@ class String(Type): else: return value if isinstance(value, basestring) and self.isMultiValued(): value = [value] - # Some backward compatibilities with Archetypes. - elif value.__class__.__name__ == 'BaseUnit': - try: - value = unicode(value) - except UnicodeDecodeError: - value = str(value) elif isinstance(value, tuple): - # When Appy storage was based on Archetype, multivalued string - # fields stored values as tuples of unicode strings. value = list(value) return value @@ -1207,12 +1196,6 @@ class String(Type): res = [t('%s_list_%s' % (self.labelId, v)) for v in value] else: res = t('%s_list_%s' % (self.labelId, value)) - elif not isinstance(value, basestring): - # Archetypes "Description" fields may hold a BaseUnit instance. - try: - res = unicode(value) - except UnicodeDecodeError: - res = str(value) # If value starts with a carriage return, add a space; else, it will # be ignored. if isinstance(res, basestring) and \ @@ -1384,8 +1367,8 @@ class Boolean(Type): return value def getFormattedValue(self, obj, value): - if value: res = obj.translate('yes', domain='plone') - else: res = obj.translate('no', domain='plone') + if value: res = obj.translate('yes') + else: res = obj.translate('no') return res def getStorableValue(self, value): @@ -1517,7 +1500,7 @@ class File(Type): def getFormattedValue(self, obj, value): if not value: return value - return value._atFile + return value._zopeFile def getRequestValue(self, request): return request.get('%s_file' % self.name) @@ -1591,7 +1574,7 @@ class File(Type): elif isinstance(value, OFSImageFile): setattr(obj, self.name, value) elif isinstance(value, FileWrapper): - setattr(obj, self.name, value._atFile) + setattr(obj, self.name, value._zopeFile) elif isinstance(value, basestring): setattr(obj, self.name, File.getFileObject(value, zope=True)) elif type(value) in sequenceTypes: @@ -2189,7 +2172,7 @@ class Pod(Type): def store(self, obj, value): '''Stores (=freezes) a document (in p_value) in the field.''' if isinstance(value, FileWrapper): - value = value._atFile + value = value._zopeFile setattr(obj, self.name, value) class List(Type): @@ -2283,19 +2266,18 @@ appyToZopePermissions = { class Role: '''Represents a role.''' - ploneRoles = ('Manager', 'Member', 'Owner', 'Reviewer', 'Anonymous', - 'Authenticated') - ploneLocalRoles = ('Owner',) - ploneUngrantableRoles = ('Anonymous', 'Authenticated') + zopeRoles = ('Manager', 'Owner', 'Anonymous', 'Authenticated') + zopeLocalRoles = ('Owner',) + zopeUngrantableRoles = ('Anonymous', 'Authenticated') def __init__(self, name, local=False, grantable=True): self.name = name self.local = local # True if it can be used as local role only. - # It is a standard Plone role or an application-specific one? - self.plone = name in self.ploneRoles - if self.plone and (name in self.ploneLocalRoles): + # It is a standard Zope role or an application-specific one? + self.zope = name in self.zopeRoles + if self.zope and (name in self.zopeLocalRoles): self.local = True self.grantable = grantable - if self.plone and (name in self.ploneUngrantableRoles): + if self.zope and (name in self.zopeUngrantableRoles): self.grantable = False # An ungrantable role is one that is, like the Anonymous or # Authenticated roles, automatically attributed to a user. @@ -2575,8 +2557,7 @@ class Transition: # Return a message to the user if needed if not doSay or (transitionName == '_init_'): return if not msg: - msg = obj.translate(u'Your content\'s status has been modified.', - domain='plone') + msg = obj.translate(u'Changes saved.') obj.say(msg) class Permission: diff --git a/gen/descriptors.py b/gen/descriptors.py index 3459b05..40b05dd 100644 --- a/gen/descriptors.py +++ b/gen/descriptors.py @@ -4,7 +4,7 @@ # ------------------------------------------------------------------------------ import types, copy -from appy.gen import State, Transition, Type +import appy.gen as gen from po import PoMessage from model import ModelClass, toolFieldPrefixes from utils import produceNiceMessage, getClassName @@ -27,8 +27,7 @@ class ClassDescriptor(Descriptor): '''This class gives information about an Appy class.''' def __init__(self, klass, orderedAttributes, generator): - appy.gen.descriptors.ClassDescriptor.__init__(self, klass, - orderedAttributes, generator) + Descriptor.__init__(self, klass, orderedAttributes, generator) self.methods = '' # Needed method definitions will be generated here # We remember here encountered pages and groups defined in the Appy # type. Indeed, after having parsed all application classes, we will @@ -70,7 +69,7 @@ class ClassDescriptor(Descriptor): except AttributeError: attrValue = getattr(self.modelClass, attrName) hookClass = self.modelClass - if isinstance(attrValue, Type): + if isinstance(attrValue, gen.Type): if not condition or eval(condition): attrs.append( (attrName, attrValue, hookClass) ) # Then, add attributes from parent classes @@ -142,7 +141,7 @@ class ClassDescriptor(Descriptor): attrValue = getattr(self.klass, attrName) except AttributeError: attrValue = getattr(self.modelClass, attrName) - if isinstance(attrValue, Type): + if isinstance(attrValue, gen.Type): if configClass: attrValue = copy.copy(attrValue) attrValue.optional = False @@ -184,13 +183,13 @@ class ClassDescriptor(Descriptor): res = [] if self.klass.__dict__.has_key('creators') and self.klass.creators: for creator in self.klass.creators: - if isinstance(creator, Role): + if isinstance(creator, gen.Role): if creator.local: raise 'Local role "%s" cannot be used as a creator.' % \ creator.name res.append(creator) else: - res.append(Role(creator)) + res.append(gen.Role(creator)) return res def getCreateMean(self, type='Import'): @@ -213,13 +212,17 @@ class ClassDescriptor(Descriptor): res = [] if klass.__dict__.has_key('search'): searches = klass.__dict__['search'] - if isinstance(searches, basestring): res.append(Search(searches)) - elif isinstance(searches, Search): res.append(searches) + if isinstance(searches, basestring): + res.append(gen.Search(searches)) + elif isinstance(searches, gen.Search): + res.append(searches) else: # It must be a list of searches. for search in searches: - if isinstance(search, basestring):res.append(Search(search)) - else: res.append(search) + if isinstance(search, basestring): + res.append(gen.Search(search)) + else: + res.append(search) return res @staticmethod @@ -268,11 +271,10 @@ class FieldDescriptor: '''This class gathers information about a specific typed attribute defined in a gen-class.''' - singleValuedTypes = ('Integer', 'Float', 'Boolean', 'Date', 'File') # Although Appy allows to specify a multiplicity[0]>1 for those types, it is - # not supported by Archetypes. So we will always generate single-valued type - # definitions for them. - specialParams = ('title', 'description') + # not currently. So we will always generate single-valued type definitions + # for them. + singleValuedTypes = ('Integer', 'Float', 'Boolean', 'Date', 'File') def __init__(self, fieldName, appyType, classDescriptor): self.appyType = appyType @@ -387,8 +389,7 @@ class FieldDescriptor: if self.appyType.editDefault: self.generator.tool.addDefaultField(self) # - put an index on this field? - if self.appyType.indexed and \ - (self.fieldName not in ('title', 'description')): + if self.appyType.indexed and (self.fieldName != 'title'): self.classDescr.addIndexMethod(self) # i18n labels messages = self.generator.labels @@ -477,7 +478,7 @@ class ToolClassDescriptor(ClassDescriptor): self.addField(fieldName, fieldType) fieldType.validator.append(fieldDescr.fieldName) fieldType.page.name = 'data' - fieldType.group = Group(fieldDescr.classDescr.klass.__name__) + fieldType.group = gen.Group(fieldDescr.classDescr.klass.__name__) def addDefaultField(self, fieldDescr): className = fieldDescr.classDescr.name @@ -485,22 +486,22 @@ class ToolClassDescriptor(ClassDescriptor): fieldType = fieldDescr.appyType.clone() self.addField(fieldName, fieldType) fieldType.page.name = 'data' - fieldType.group = Group(fieldDescr.classDescr.klass.__name__) + fieldType.group = gen.Group(fieldDescr.classDescr.klass.__name__) 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': 'documentGeneration', - 'group': Group(fieldDescr.classDescr.klass.__name__, ['50%']*2)} + '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 = File(**pg) + 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 = String(validator=Selection('getPodOutputFormats'), - multiplicity=(1,None), default=('odt',), **pg) + fieldType = gen.String(validator=gen.Selection('getPodOutputFormats'), + multiplicity=(1,None), default=('odt',), **pg) self.addField(fieldName, fieldType) def addQueryResultColumns(self, classDescr): @@ -508,7 +509,7 @@ class ToolClassDescriptor(ClassDescriptor): to select what default columns will be shown on query results.''' className = classDescr.name fieldName = 'resultColumnsFor%s' % className - fieldType = String(multiplicity=(0,None), validator=Selection( + fieldType = gen.String(multiplicity=(0,None), validator=gen.Selection( '_appy_getAllFields*%s' % className), page='userInterface', group=classDescr.klass.__name__) self.addField(fieldName, fieldType) @@ -520,21 +521,21 @@ class ToolClassDescriptor(ClassDescriptor): # Field that defines if advanced search is enabled for class # p_classDescr or not. fieldName = 'enableAdvancedSearchFor%s' % className - fieldType = Boolean(default=True, page='userInterface', - group=classDescr.klass.__name__) + fieldType = gen.Boolean(default=True, page='userInterface', + group=classDescr.klass.__name__) self.addField(fieldName, fieldType) # Field that defines how many columns are shown on the custom search # screen. fieldName = 'numberOfSearchColumnsFor%s' % className - fieldType = Integer(default=3, page='userInterface', - group=classDescr.klass.__name__) + 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')] - fieldType = String(multiplicity=(0,None), validator=Selection( + 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) @@ -546,8 +547,8 @@ class ToolClassDescriptor(ClassDescriptor): # Field that defines the path of the files to import. fieldName = 'importPathFor%s' % className defValue = classDescr.getCreateMean('Import').path - fieldType = String(page='data', multiplicity=(1,1), default=defValue, - group=classDescr.klass.__name__) + fieldType = gen.String(page='data', multiplicity=(1,1), + default=defValue,group=classDescr.klass.__name__) self.addField(fieldName, fieldType) def addWorkflowFields(self, classDescr): @@ -560,13 +561,13 @@ class ToolClassDescriptor(ClassDescriptor): if classDescr.isRoot() or issubclass(classDescr.klass, ModelClass): defaultValue = True fieldName = 'showWorkflowFor%s' % className - fieldType = Boolean(default=defaultValue, page='userInterface', - group=groupName) + fieldType = gen.Boolean(default=defaultValue, page='userInterface', + group=groupName) self.addField(fieldName, fieldType) # Adds the boolean field for showing or not the field "enter comments". fieldName = 'showWorkflowCommentFieldFor%s' % className - fieldType = Boolean(default=defaultValue, page='userInterface', - group=groupName) + fieldType = gen.Boolean(default=defaultValue, page='userInterface', + group=groupName) self.addField(fieldName, fieldType) # Adds the boolean field for showing all states in current state or not. # If this boolean is True but the current phase counts only one state, @@ -577,13 +578,12 @@ class ToolClassDescriptor(ClassDescriptor): if len(classDescr.getPhases()) > 1: defaultValue = True fieldName = 'showAllStatesInPhaseFor%s' % className - fieldType = Boolean(default=defaultValue, page='userInterface', - group=groupName) + fieldType = gen.Boolean(default=defaultValue, page='userInterface', + group=groupName) self.addField(fieldName, fieldType) class UserClassDescriptor(ClassDescriptor): - '''Represents an Archetypes-compliant class that corresponds to the User - for the generated application.''' + '''Appy-specific class for representing a user.''' def __init__(self, klass, generator): ClassDescriptor.__init__(self,klass,klass._appy_attributes[:],generator) self.modelClass = self.klass @@ -649,8 +649,8 @@ class TranslationClassDescriptor(ClassDescriptor): def addLabelField(self, messageId, page): '''Adds a Computed field that will display, in the source language, the content of the text to translate.''' - field = Computed(method=self.modelClass.label, plainText=False, - page=page, show=self.modelClass.show, layouts='f') + field = gen.Computed(method=self.modelClass.label, plainText=False, + page=page, show=self.modelClass.show, layouts='f') self.addField('%s_label' % messageId, field) def addMessageField(self, messageId, page, i18nFiles): @@ -683,7 +683,7 @@ class TranslationClassDescriptor(ClassDescriptor): params['width'] = width else: # This is a multi-line field, or a very-long-single-lined field - params['format'] = String.TEXT + params['format'] = gen.String.TEXT params['height'] = height - self.addField(messageId, String(**params)) + self.addField(messageId, gen.String(**params)) # ------------------------------------------------------------------------------ diff --git a/gen/generator.py b/gen/generator.py index fd7c58d..8ca3c75 100644 --- a/gen/generator.py +++ b/gen/generator.py @@ -2,7 +2,7 @@ import os, os.path, re, sys, parser, symbol, token, types import appy.pod, appy.pod.renderer from appy.shared.utils import FolderDeleter -#from appy.gen import * +import appy.gen as gen from po import PoMessage, PoFile, PoParser from descriptors import * from utils import produceNiceMessage, getClassName @@ -140,7 +140,7 @@ class Generator: self.user = None self.workflows = [] self.initialize() - self.config = Config.getDefault() + self.config = gen.Config.getDefault() self.modulesWithTests = set() self.totalNumberOfTests = 0 @@ -152,9 +152,9 @@ class Generator: workflow.''' res = 'none' for attrValue in klass.__dict__.itervalues(): - if isinstance(attrValue, Type): + if isinstance(attrValue, gen.Type): res = 'class' - elif isinstance(attrValue, State): + elif isinstance(attrValue, gen.State): res = 'workflow' if not res: for baseClass in klass.__bases__: @@ -219,13 +219,13 @@ class Generator: attrs = astClasses[moduleElem.__name__].attributes if appyType == 'class': # Determine the class type (standard, tool, user...) - if issubclass(moduleElem, Tool): + if issubclass(moduleElem, gen.Tool): if not self.tool: klass = self.descriptorClasses['tool'] self.tool = klass(moduleElem, attrs, self) else: self.tool.update(moduleElem, attrs) - elif issubclass(moduleElem, User): + elif issubclass(moduleElem, gen.User): if not self.user: klass = self.descriptorClasses['user'] self.user = klass(moduleElem, attrs, self) @@ -244,7 +244,7 @@ class Generator: self.workflows.append(descriptor) if self.containsTests(moduleElem): self.modulesWithTests.add(moduleObj.__name__) - elif isinstance(moduleElem, Config): + elif isinstance(moduleElem, gen.Config): self.config = moduleElem # Walk potential sub-modules @@ -461,7 +461,6 @@ class ZopeGenerator(Generator): self.generateTool() self.generateInit() self.generateTests() - self.generateConfigureZcml() # Create version.txt f = open(os.path.join(self.outputFolder, 'version.txt'), 'w') f.write(self.version) @@ -536,13 +535,13 @@ class ZopeGenerator(Generator): self.generateWrappers() self.generateConfig() - def getAllUsedRoles(self, plone=None, local=None, grantable=None): + def getAllUsedRoles(self, zope=None, local=None, grantable=None): '''Produces a list of all the roles used within all workflows and classes defined in this application. - If p_plone is True, it keeps only Plone-standard roles; if p_plone + If p_zope is True, it keeps only Zope-standard roles; if p_zope is False, it keeps only roles which are specific to this application; - if p_plone is None it has no effect (so it keeps both roles). + if p_zope is None it has no effect (so it keeps both roles). If p_local is True, it keeps only local roles (ie, roles that can only be granted locally); if p_local is False, it keeps only "global" @@ -557,8 +556,8 @@ class ZopeGenerator(Generator): for wfDescr in self.workflows: for attr in dir(wfDescr.klass): attrValue = getattr(wfDescr.klass, attr) - if isinstance(attrValue, State) or \ - isinstance(attrValue, Transition): + if isinstance(attrValue, gen.State) or \ + isinstance(attrValue, gen.Transition): for role in attrValue.getUsedRoles(): if role.name not in allRoles: allRoles[role.name] = role @@ -569,7 +568,7 @@ class ZopeGenerator(Generator): allRoles[role.name] = role res = allRoles.values() # Filter the result according to parameters - for p in ('plone', 'local', 'grantable'): + for p in ('zope', 'local', 'grantable'): if eval(p) != None: res = [r for r in res if eval('r.%s == %s' % (p, p))] return res @@ -613,17 +612,6 @@ class ZopeGenerator(Generator): res = configClasses return res - def generateConfigureZcml(self): - '''Generates file configure.zcml.''' - repls = self.repls.copy() - # Note every class as "deprecated". - depr = '' - for klass in self.getClasses(include='all'): - depr += '\n' % \ - (klass.name, klass.name) - repls['deprecated'] = depr - self.copyFile('configure.zcml', repls) - def generateConfig(self): repls = self.repls.copy() # Get some lists of classes @@ -677,9 +665,9 @@ class ZopeGenerator(Generator): attributes.append('"%s":[%s]' % (classDescr.name, ','.join(qNames))) repls['attributes'] = ',\n '.join(attributes) # Compute list of used roles for registering them if needed - specificRoles = self.getAllUsedRoles(plone=False) + specificRoles = self.getAllUsedRoles(zope=False) repls['roles'] = ','.join(['"%s"' % r.name for r in specificRoles]) - globalRoles = self.getAllUsedRoles(plone=False, local=False) + globalRoles = self.getAllUsedRoles(zope=False, local=False) repls['gRoles'] = ','.join(['"%s"' % r.name for r in globalRoles]) grantableRoles = self.getAllUsedRoles(local=False, grantable=True) repls['grRoles'] = ','.join(['"%s"' % r.name for r in grantableRoles]) @@ -780,7 +768,7 @@ class ZopeGenerator(Generator): self.copyFile('testAll.py', repls, destFolder='tests') def generateTool(self): - '''Generates the Plone tool that corresponds to this application.''' + '''Generates the tool that corresponds to this application.''' Msg = PoMessage # Create Tool-related i18n-related messages msg = Msg(self.tool.name, '', Msg.CONFIG % self.applicationName) @@ -874,7 +862,7 @@ class ZopeGenerator(Generator): poMsg.produceNiceDefault() if poMsg not in self.labels: self.labels.append(poMsg) - # Generate the resulting Archetypes class. + # Generate the resulting Zope class. self.copyFile('Class.py', repls, destName=fileName) def generateWorkflow(self, wfDescr): @@ -886,14 +874,14 @@ class ZopeGenerator(Generator): wfName = WorkflowDescriptor.getWorkflowName(wfDescr.klass) # Add i18n messages for states for name in dir(wfDescr.klass): - if not isinstance(getattr(wfDescr.klass, name), State): continue + if not isinstance(getattr(wfDescr.klass, name), gen.State): continue poMsg = PoMessage('%s_%s' % (wfName, name), '', name) poMsg.produceNiceDefault() self.labels.append(poMsg) # Add i18n messages for transitions for name in dir(wfDescr.klass): transition = getattr(wfDescr.klass, name) - if not isinstance(transition, Transition): continue + if not isinstance(transition, gen.Transition): continue poMsg = PoMessage('%s_%s' % (wfName, name), '', name) poMsg.produceNiceDefault() self.labels.append(poMsg) diff --git a/gen/installer.py b/gen/installer.py index 3ade52e..d32db31 100644 --- a/gen/installer.py +++ b/gen/installer.py @@ -5,7 +5,7 @@ import os, os.path, time import appy import appy.version -from appy.gen import Type, Ref, String, File +import appy.gen as gen from appy.gen.po import PoParser from appy.gen.utils import updateRolesForPermission, createObject from appy.shared.data import languages @@ -110,7 +110,7 @@ class ZopeInstaller: for name in files: baseName, ext = os.path.splitext(name) f = file(j(root, name)) - if ext in File.imageExts: + if ext in gen.File.imageExts: zopeFolder.manage_addImage(name, f) elif ext == '.pt': manage_addPageTemplate(zopeFolder, baseName, '', f.read()) @@ -302,13 +302,11 @@ class ZopeInstaller: # "po" file on disk. appFolder = self.config.diskFolder appName = self.config.PROJECTNAME - dn = os.path.dirname - jn = os.path.join - i18nFolder = jn(jn(jn(dn(dn(dn(appFolder))),'Products'),appName),'i18n') + i18nFolder = os.path.join(appFolder, 'tr') for translation in appyTool.translations: # Get the "po" file poName = '%s-%s.po' % (appName, translation.id) - poFile = PoParser(jn(i18nFolder, poName)).parse() + poFile = PoParser(os.path.join(i18nFolder, poName)).parse() for message in poFile.messages: setattr(translation, message.id, message.getMessage()) appyTool.log('Translation "%s" updated from "%s".' % \ @@ -359,7 +357,7 @@ class ZopeInstaller: wrapperClass = klass.wrapperClass if not hasattr(wrapperClass, 'title'): # Special field "type" is mandatory for every class. - title = String(multiplicity=(1,1), show='edit', indexed=True) + title = gen.String(multiplicity=(1,1), show='edit',indexed=True) title.init('title', None, 'appy') setattr(wrapperClass, 'title', title) names = self.config.attributes[wrapperClass.__name__[:-8]] @@ -368,8 +366,8 @@ class ZopeInstaller: for baseClass in klass.wrapperClass.__bases__: if baseClass.__name__ == 'AbstractWrapper': continue for name, appyType in baseClass.__dict__.iteritems(): - if not isinstance(appyType, Type) or \ - (isinstance(appyType, Ref) and appyType.isBack): + if not isinstance(appyType, gen.Type) or \ + (isinstance(appyType, gen.Ref) and appyType.isBack): continue # Back refs are initialised within fw refs appyType.init(name, baseClass, appName) diff --git a/gen/migrator.py b/gen/migrator.py index a3db7b1..29d8386 100644 --- a/gen/migrator.py +++ b/gen/migrator.py @@ -9,8 +9,7 @@ class Migrator: self.installer = installer def migrateTo_0_7_1(self): - '''Appy 0.7.1 has its own management of Ref fields and does not use - Archetypes references and the reference catalog anymore. So we must + '''Appy 0.7.1 has its own management of Ref fields. So we must update data structures that store Ref info on instances.''' ins = self.installer ins.info('Migrating to Appy 0.7.1...') diff --git a/gen/mixins/TestMixin.py b/gen/mixins/TestMixin.py index c46337e..cafc4a7 100644 --- a/gen/mixins/TestMixin.py +++ b/gen/mixins/TestMixin.py @@ -3,7 +3,7 @@ import os, os.path, sys # ------------------------------------------------------------------------------ class TestMixin: - '''This class is mixed in with any PloneTestCase.''' + '''This class is mixed in with any ZopeTestCase.''' def createUser(self, userId, roles): '''Creates a user with id p_userId with some p_roles.''' self.acl_users.addMember(userId, 'password', [], []) @@ -59,7 +59,7 @@ class TestMixin: def beforeTest(test): '''Is executed before every test.''' g = test.globs - g['tool'] = test.app.plone.get('portal_%s' % g['appName'].lower()).appy() + g['tool'] = test.app.config.appy() cfg = g['tool'].o.getProductConfig() g['appFolder'] = cfg.diskFolder moduleOrClassName = g['test'].name # Not used yet. diff --git a/gen/mixins/ToolMixin.py b/gen/mixins/ToolMixin.py index 5d6f73e..86cef2a 100644 --- a/gen/mixins/ToolMixin.py +++ b/gen/mixins/ToolMixin.py @@ -6,9 +6,9 @@ from appy.shared.data import languages import appy.gen from appy.gen import Type, Search, Selection from appy.gen.utils import SomeObjects, sequenceTypes, getClassName -from appy.gen.plone25.mixins import BaseMixin -from appy.gen.plone25.wrappers import AbstractWrapper -from appy.gen.plone25.descriptors import ClassDescriptor +from appy.gen.mixins import BaseMixin +from appy.gen.wrappers import AbstractWrapper +from appy.gen.descriptors import ClassDescriptor try: from AccessControl.ZopeSecurityPolicy import _noroles except ImportError: @@ -411,7 +411,7 @@ class ToolMixin(BaseMixin): def getCreateMeans(self, contentTypeOrAppyClass): '''Gets the different ways objects of p_contentTypeOrAppyClass (which - can be a Plone content type or a Appy class) can be created + can be a Zope content type or a Appy class) can be created (via a web form, by importing external data, etc). Result is a dict whose keys are strings (ie "form", "import"...) and whose values are additional data bout the particular mean.''' @@ -810,7 +810,7 @@ class ToolMixin(BaseMixin): 9: 'month_sep', 10: 'month_oct', 11: 'month_nov', 12: 'month_dec'} def getMonthName(self, monthNumber): '''Gets the translated month name of month numbered p_monthNumber.''' - return self.translate(self.monthsIds[int(monthNumber)], domain='plone') + return self.translate(self.monthsIds[int(monthNumber)]) # -------------------------------------------------------------------------- # Authentication-related methods @@ -824,7 +824,7 @@ class ToolMixin(BaseMixin): if jsEnabled and not cookiesEnabled: msg = self.translate(u'You must enable cookies before you can ' \ - 'log in.', domain='plone') + 'log in.') return self.goto(urlBack, msg.encode('utf-8')) # Perform the Zope-level authentication login = rq.get('__ac_name', '') @@ -835,11 +835,10 @@ class ToolMixin(BaseMixin): user = self.acl_users.validate(rq) if self.userIsAnon(): rq.RESPONSE.expireCookie('__ac', path='/') - msg = self.translate(u'Login failed', domain='plone') + msg = self.translate(u'Login failed') logMsg = 'Authentication failed (tried with login "%s")' % login else: - msg = self.translate(u'Welcome! You are now logged in.', - domain='plone') + msg = self.translate(u'Welcome! You are now logged in.') logMsg = 'User "%s" has been logged in.' % login msg = msg.encode('utf-8') self.log(logMsg) @@ -865,7 +864,7 @@ class ToolMixin(BaseMixin): session.invalidate() self.log('User "%s" has been logged out.' % userId) # Remove user from variable "loggedUsers" - from appy.gen.plone25.installer import loggedUsers + from appy.gen.installer import loggedUsers if loggedUsers.has_key(userId): del loggedUsers[userId] return self.goto(self.getApp().absolute_url()) diff --git a/gen/mixins/__init__.py b/gen/mixins/__init__.py index e1528b2..9dbee23 100644 --- a/gen/mixins/__init__.py +++ b/gen/mixins/__init__.py @@ -1,17 +1,14 @@ '''This package contains mixin classes that are mixed in with generated classes: - - mixins/BaseMixin is mixed in with Standard Archetypes classes; + - mixins/BaseMixin is mixed in with standard Zope classes; - mixins/ToolMixin is mixed in with the generated application Tool class.''' # ------------------------------------------------------------------------------ import os, os.path, sys, types, mimetypes, urllib, cgi from appy import Object -import appy.gen -from appy.gen import Type, String, Selection, Role, No, WorkflowAnonymous, \ - Transition, Permission +import appy.gen as gen from appy.gen.utils import * from appy.gen.layout import Table, defaultPageLayouts -from appy.gen.descriptors import WorkflowDescriptor -from appy.gen.plone25.descriptors import ClassDescriptor +from appy.gen.descriptors import WorkflowDescriptor, ClassDescriptor # ------------------------------------------------------------------------------ class BaseMixin: @@ -187,8 +184,7 @@ class BaseMixin: fields in the database.''' rq = self.REQUEST tool = self.getTool() - errorMessage = self.translate( - 'Please correct the indicated errors.', domain='plone') + errorMessage = self.translate('Please correct the indicated errors.') isNew = rq.get('is_new') == 'True' # If this object is created from an initiator, get info about him. initiator = None @@ -209,7 +205,7 @@ class BaseMixin: urlBack = tool.getSiteUrl() else: urlBack = self.getUrl() - self.say(self.translate('Changes canceled.', domain='plone')) + self.say(self.translate('Changes canceled.')) return self.goto(urlBack) # Object for storing validation errors @@ -245,7 +241,7 @@ class BaseMixin: obj, msg = self.createOrUpdate(isNew, values, initiator, initiatorField) # Redirect the user to the appropriate page - if not msg: msg = obj.translate('Changes saved.', domain='plone') + if not msg: msg = obj.translate('Changes saved.') # If the object has already been deleted (ie, it is a kind of transient # object like a one-shot form and has already been deleted in method # onEdit), redirect to the main site page. @@ -711,7 +707,7 @@ class BaseMixin: if not includeFake: includeIt = mayTrigger else: - includeIt = mayTrigger or isinstance(mayTrigger, No) + includeIt = mayTrigger or isinstance(mayTrigger, gen.No) if not includeNotShowable: includeIt = includeIt and transition.isShowable(wf, self) if not includeIt: continue @@ -864,7 +860,7 @@ class BaseMixin: # Get the initial workflow state initialState = self.State(name=False) # Create a Transition instance representing the initial transition. - initialTransition = Transition((initialState, initialState)) + initialTransition = gen.Transition((initialState, initialState)) initialTransition.trigger('_init_', self, wf, '') def getWorkflow(self, name=False, className=None): @@ -875,7 +871,7 @@ class BaseMixin: else: appyClass = self.getTool().getAppyClass(className) if hasattr(appyClass, 'workflow'): wf = appyClass.workflow - else: wf = WorkflowAnonymous + else: wf = gen.WorkflowAnonymous if not name: return wf return WorkflowDescriptor.getWorkflowName(wf) diff --git a/gen/model.py b/gen/model.py index a42045c..0fafbc6 100644 --- a/gen/model.py +++ b/gen/model.py @@ -1,14 +1,10 @@ '''This file contains basic classes that will be added into any user application for creating the basic structure of the application "Tool" which - is the set of web pages used for configuring the application. The "Tool" is - available to administrators under the standard Plone link "site setup". Plone - itself is shipped with several tools used for conguring the various parts of - Plone (content types, catalogs, workflows, etc.)''' + is the set of web pages used for configuring the application.''' # ------------------------------------------------------------------------------ import types -from appy.gen import * -Grp=Group # Avoid name clash between appy.gen.Group and class Group below +import appy.gen as gen # Prototypical instances of every type ----------------------------------------- class Protos: @@ -73,7 +69,7 @@ class ModelClass: value = appyType.getInputLayouts() elif isinstance(value, basestring): value = '"%s"' % value - elif isinstance(value, Ref): + elif isinstance(value, gen.Ref): if not value.isBack: continue value = klass._appy_getTypeBody(value, wrapperName) elif type(value) == type(ModelClass): @@ -82,11 +78,11 @@ class ModelClass: value = value.__name__ else: value = '%s.%s' % (moduleName, value.__name__) - elif isinstance(value, Selection): + elif isinstance(value, gen.Selection): value = 'Selection("%s")' % value.methodName - elif isinstance(value, Grp): + elif isinstance(value, gen.Group): value = 'Grp("%s")' % value.name - elif isinstance(value, Page): + elif isinstance(value, gen.Page): value = 'pages["%s"]' % value.name elif callable(value): value = '%s.%s' % (wrapperName, value.__name__) @@ -135,20 +131,22 @@ class User(ModelClass): _appy_attributes = ['title', 'name', 'firstName', 'login', 'password1', 'password2', 'roles'] # All methods defined below are fake. Real versions are in the wrapper. - title = String(show=False, indexed=True) + title = gen.String(show=False, indexed=True) gm = {'group': 'main', 'multiplicity': (1,1), 'width': 25} - name = String(**gm) - firstName = String(**gm) + name = gen.String(**gm) + firstName = gen.String(**gm) def showLogin(self): pass def validateLogin(self): pass - login = String(show=showLogin, validator=validateLogin, indexed=True, **gm) + login = gen.String(show=showLogin, validator=validateLogin, + indexed=True, **gm) def showPassword(self): pass def validatePassword(self): pass - password1 = String(format=String.PASSWORD, show=showPassword, - validator=validatePassword, **gm) - password2 = String(format=String.PASSWORD, show=showPassword, **gm) + password1 = gen.String(format=gen.String.PASSWORD, show=showPassword, + validator=validatePassword, **gm) + password2 = gen.String(format=gen.String.PASSWORD, show=showPassword, **gm) gm['multiplicity'] = (0, None) - roles = String(validator=Selection('getGrantableRoles'), indexed=True, **gm) + roles = gen.String(validator=gen.Selection('getGrantableRoles'), + indexed=True, **gm) # The Group class -------------------------------------------------------------- class Group(ModelClass): @@ -156,25 +154,25 @@ class Group(ModelClass): _appy_attributes = ['title', 'login', 'roles', 'users'] # All methods defined below are fake. Real versions are in the wrapper. m = {'group': 'main', 'width': 25, 'indexed': True} - title = String(multiplicity=(1,1), **m) + title = gen.String(multiplicity=(1,1), **m) def showLogin(self): pass def validateLogin(self): pass - login = String(show=showLogin, validator=validateLogin, - multiplicity=(1,1), **m) - roles = String(validator=Selection('getGrantableRoles'), - multiplicity=(0,None), **m) - users = Ref(User, multiplicity=(0,None), add=False, link=True, - back=Ref(attribute='groups', show=True), - showHeaders=True, shownInfo=('title', 'login')) + login = gen.String(show=showLogin, validator=validateLogin, + multiplicity=(1,1), **m) + roles = gen.String(validator=gen.Selection('getGrantableRoles'), + multiplicity=(0,None), **m) + users = gen.Ref(User, multiplicity=(0,None), add=False, link=True, + back=gen.Ref(attribute='groups', show=True), + showHeaders=True, shownInfo=('title', 'login')) # The Translation class -------------------------------------------------------- class Translation(ModelClass): _appy_attributes = ['po', 'title'] # All methods defined below are fake. Real versions are in the wrapper. def getPoFile(self): pass - po = Action(action=getPoFile, page=Page('actions', show='view'), - result='filetmp') - title = String(show=False, indexed=True) + po = gen.Action(action=getPoFile, page=gen.Page('actions', show='view'), + result='filetmp') + title = gen.String(show=False, indexed=True) def label(self): pass def show(self, name): pass @@ -195,30 +193,31 @@ class Tool(ModelClass): # Tool attributes def validPythonWithUno(self, value): pass # Real method in the wrapper - unoEnabledPython = String(group="connectionToOpenOffice", - validator=validPythonWithUno) - openOfficePort = Integer(default=2002, group="connectionToOpenOffice") - numberOfResultsPerPage = Integer(default=30, show=False) - listBoxesMaximumWidth = Integer(default=100, show=False) - appyVersion = String(show=False, layouts='f') + unoEnabledPython = gen.String(group="connectionToOpenOffice", + validator=validPythonWithUno) + openOfficePort = gen.Integer(default=2002, group="connectionToOpenOffice") + numberOfResultsPerPage = gen.Integer(default=30, show=False) + listBoxesMaximumWidth = gen.Integer(default=100, show=False) + appyVersion = gen.String(show=False, layouts='f') def refreshSecurity(self): pass # Real method in the wrapper - refreshSecurity = Action(action=refreshSecurity, confirm=True) + refreshSecurity = gen.Action(action=refreshSecurity, confirm=True) # Ref(User) will maybe be transformed into Ref(CustomUserClass). - users = Ref(User, multiplicity=(0,None), add=True, link=False, - back=Ref(attribute='toTool', show=False), - page=Page('users', show='view'), - queryable=True, queryFields=('title', 'login'), - showHeaders=True, shownInfo=('title', 'login', 'roles')) - groups = Ref(Group, multiplicity=(0,None), add=True, link=False, - back=Ref(attribute='toTool2', show=False), - page=Page('groups', show='view'), - queryable=True, queryFields=('title', 'login'), - showHeaders=True, shownInfo=('title', 'login', 'roles')) - translations = Ref(Translation, multiplicity=(0,None),add=False,link=False, - back=Ref(attribute='trToTool', show=False), show='view', - page=Page('translations', show='view')) - enableNotifications = Boolean(default=True, - page=Page('notifications', show=False)) + users = gen.Ref(User, multiplicity=(0,None), add=True, link=False, + back=gen.Ref(attribute='toTool', show=False), + page=gen.Page('users', show='view'), + queryable=True, queryFields=('title', 'login'), + showHeaders=True, shownInfo=('title', 'login', 'roles')) + groups = gen.Ref(Group, multiplicity=(0,None), add=True, link=False, + back=gen.Ref(attribute='toTool2', show=False), + page=gen.Page('groups', show='view'), + queryable=True, queryFields=('title', 'login'), + showHeaders=True, shownInfo=('title', 'login', 'roles')) + translations = gen.Ref(Translation, multiplicity=(0,None), add=False, + link=False, show='view', + back=gen.Ref(attribute='trToTool', show=False), + page=gen.Page('translations', show='view')) + enableNotifications = gen.Boolean(default=True, + page=gen.Page('notifications', show=False)) @classmethod def _appy_clean(klass): diff --git a/gen/notifier.py b/gen/notifier.py index f5e2990..cdc273f 100644 --- a/gen/notifier.py +++ b/gen/notifier.py @@ -41,8 +41,8 @@ def sendMail(obj, transition, transitionName, workflow): '''Sends mail about p_transition that has been triggered on p_obj that is controlled by p_workflow.''' wfName = WorkflowDescriptor.getWorkflowName(workflow.__class__) - ploneObj = obj.o - portal = ploneObj.portal_url.getPortalObject() + zopeObj = obj.o + tool = zopeObj.getTool() mailInfo = transition.notify(workflow, obj) if not mailInfo[0]: return # Send a mail to nobody. # mailInfo may be one of the following: @@ -54,15 +54,15 @@ def sendMail(obj, transition, transitionName, workflow): # address or one role) or sequences of strings. # Determine mail subject and body. if len(mailInfo) <= 2: - # The user didn't mention mail body and subject. We will use - # those defined from i18n labels. - wfHistory = ploneObj.getWorkflowHistory() + # The user didn't mention mail body and subject. We will use those + # defined from i18n labels. + wfHistory = zopeObj.getHistory() labelPrefix = '%s_%s' % (wfName, transitionName) tName = obj.translate(labelPrefix) - keys = {'siteUrl': portal.absolute_url(), - 'siteTitle': portal.Title(), - 'objectUrl': ploneObj.absolute_url(), - 'objectTitle': ploneObj.Title(), + keys = {'siteUrl': tool.getPath('/').absolute_url(), + 'siteTitle': tool.getAppName(), + 'objectUrl': zopeObj.absolute_url(), + 'objectTitle': zopeObj.Title(), 'transitionName': tName, 'transitionComment': wfHistory[0]['comments']} mailSubject = obj.translate(labelPrefix + '_mail_subject', keys) diff --git a/gen/odt/__init__.py b/gen/odt/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/gen/odt/generator.py b/gen/odt/generator.py deleted file mode 100644 index b4b4d89..0000000 --- a/gen/odt/generator.py +++ /dev/null @@ -1,54 +0,0 @@ -'''This file contains the main Generator class used for generating an - ODT file from an Appy application.''' - -# ------------------------------------------------------------------------------ -import os, os.path -from appy.gen import Page -from appy.gen.utils import produceNiceMessage -from appy.gen.generator import Generator as AbstractGenerator - -# ------------------------------------------------------------------------------ -class Generator(AbstractGenerator): - '''This generator generates ODT files from an Appy application.''' - - def __init__(self, *args, **kwargs): - AbstractGenerator.__init__(self, *args, **kwargs) - self.repls = {'generator': self} - - def finalize(self): - pass - - def getOdtFieldLabel(self, fieldName): - '''Given a p_fieldName, this method creates the label as it will appear - in the ODT file.''' - return '%s' % \ - (fieldName, produceNiceMessage(fieldName)) - - def generateClass(self, classDescr): - '''Is called each time an Appy class is found in the application.''' - repls = self.repls.copy() - repls['classDescr'] = classDescr - self.copyFile('basic.odt', repls, - destName='%sEdit.odt' % classDescr.klass.__name__, isPod=True) - - def fieldIsStaticallyInvisible(self, field): - '''This method determines if p_field is always invisible. It can be - verified for example if field.type.show is the boolean value False or - if the page where the field must be displayed has a boolean attribute - "show" having the boolean value False.''' - if (type(field.show) == bool) and not field.show: return True - if (type(field.page.show) == bool) and not field.page.show: return True - return False - - undumpable = ('Ref', 'Action', 'File', 'Computed') - def getRelevantAttributes(self, classDescr): - '''Some fields, like computed fields or actions, should not be dumped - into the ODT file. This method returns the list of "dumpable" - fields.''' - res = [] - for fieldName, field, klass in classDescr.getOrderedAppyAttributes(): - if (field.type not in self.undumpable) and \ - (not self.fieldIsStaticallyInvisible(field)): - res.append((fieldName, field)) - return res -# ------------------------------------------------------------------------------ diff --git a/gen/odt/templates/basic.odt b/gen/odt/templates/basic.odt deleted file mode 100644 index 7f3816a..0000000 Binary files a/gen/odt/templates/basic.odt and /dev/null differ diff --git a/gen/plone25/__init__.py b/gen/plone25/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/gen/templates/Class.py b/gen/templates/Class.py index 2ec361c..38dec98 100644 --- a/gen/templates/Class.py +++ b/gen/templates/Class.py @@ -4,8 +4,8 @@ from OFS.Folder import Folder from appy.gen.utils import createObject from AccessControl import ClassSecurityInfo import Products..config as cfg -from appy.gen.plone25.mixins import BaseMixin -from appy.gen.plone25.mixins.ToolMixin import ToolMixin +from appy.gen.mixins import BaseMixin +from appy.gen.mixins.ToolMixin import ToolMixin from wrappers import _Wrapper as Wrapper def manage_add(self, id, title='', REQUEST=None): diff --git a/gen/templates/Styles.css.dtml b/gen/templates/Styles.css.dtml deleted file mode 100644 index 70f4dd9..0000000 --- a/gen/templates/Styles.css.dtml +++ /dev/null @@ -1,90 +0,0 @@ -#importedElem { color: grey; font-style: italic; } -.appyPod { float:right; } -.appyFocus { color: #900101; } - -.appyChanges th { - font-style: italic; - background-color: transparent; - border: 0 none transparent; - padding: 0.1em 0.1em 0.1em 0.1em; -} - -.appyChanges td { - padding: 0.1em 0.2em 0.1em 0.2em !important; - border-top: 1px dashed #8CACBB !important; - border-right: 0 none transparent !important; - border-left: 0 none transparent !important; - border-bottom: 0 none transparent !important; -} - -/* Tooltip */ -a.tooltip span { - display:none; - padding:2px 3px; - margin-top: 25px; -} -a.rtip span { margin-left:3px; } -a.ltip span { margin-left:-150px } -a.tooltip:hover span { - display: inline; - position: absolute; - border: 1px solid grey; - background-color: white; - color: #dd; -} - -/* Table styles */ -fieldset { - line-height: 1em; - border: 2px solid #8CACBB; - margin: 0.5em 0em 0.5em 0em; - padding: 0 0.7em 0.5em; -} - -.noPadding { - padding-right: 0em !important; - padding-left: 0em !important; - padding-top: 0em !important; - padding-bottom: 0em !important; -} - -.appyButton { - background: &dtml-globalBackgroundColor; url(&dtml-portal_url;/linkOpaque.gif) 5px 1px no-repeat; - cursor: pointer; - font-size: &dtml-fontSmallSize;; - padding: 1px 1px 1px 12px; - text-transform: &dtml-textTransform;; - /* overflow: visible; IE produces ugly results with this */ -} - -.fakeButton { - background: #ffd5c0 url(&dtml-portal_url;/ui/fakeTransition.gif) 5px 1px no-repeat; - padding: 3px 4px 3px 12px; -} - -/* Portlet elements */ -.portletHeader { - text-transform: none; - padding: 1px 0.5em; -} -.portletSearch { - padding: 0 0 0 0.6em; - font-style: normal; - font-size: 95%; -} -.portletGroup { - font-variant: small-caps; - font-weight: bold; - font-style: normal; -} - -.portletGroupItem { padding-left: 0.8em; font-style: italic; } -.portletMenu { margin-bottom: 0.4em; } - -/* image-right, but without border */ -.image-right { - border:0px solid Black; - clear:both; - float:right; - margin:0.5em; -} diff --git a/gen/templates/__init__.py b/gen/templates/__init__.py index 4d20f53..3452a0f 100644 --- a/gen/templates/__init__.py +++ b/gen/templates/__init__.py @@ -1,7 +1,7 @@ # Test coverage-related stuff -------------------------------------------------- import sys -from appy.gen.plone25.mixins.TestMixin import TestMixin +from appy.gen.mixins.TestMixin import TestMixin covFolder = TestMixin.getCovFolder() # The previous method checks in sys.argv whether Zope was lauched for performing # coverage tests or not. @@ -26,7 +26,7 @@ def countTest(): # ------------------------------------------------------------------------------ import config -from appy.gen.plone25.installer import ZopeInstaller +from appy.gen.installer import ZopeInstaller # Zope-level installation of the generated product. ---------------------------- def initialize(context): diff --git a/gen/templates/config.py b/gen/templates/config.py index 4d56cc9..b640024 100644 --- a/gen/templates/config.py +++ b/gen/templates/config.py @@ -5,9 +5,7 @@ import wrappers # The following imports are here for allowing mixin classes to access those -# elements without being statically dependent on Plone/Zope packages. Indeed, -# every Archetype instance has a method "getProductConfig" that returns this -# module. +# elements without being statically dependent on Zope packages. from persistent.list import PersistentList from zExceptions import BadRequest from ZPublisher.HTTPRequest import BaseRequest diff --git a/gen/templates/configure.zcml b/gen/templates/configure.zcml deleted file mode 100644 index be6e398..0000000 --- a/gen/templates/configure.zcml +++ /dev/null @@ -1,6 +0,0 @@ - - - diff --git a/gen/templates/testAll.py b/gen/templates/testAll.py index 4edff9f..0e34d74 100644 --- a/gen/templates/testAll.py +++ b/gen/templates/testAll.py @@ -3,17 +3,13 @@ from unittest import TestSuite from Testing import ZopeTestCase from Testing.ZopeTestCase import ZopeDocTestSuite -from Products.PloneTestCase import PloneTestCase -from appy.gen.plone25.mixins.TestMixin import TestMixin, beforeTest, afterTest +from appy.gen.mixins.TestMixin import TestMixin, beforeTest, afterTest -# Initialize Zope & Plone test systems ----------------------------------------- -ZopeTestCase.installProduct('PloneLanguageTool') +# Initialize the Zope test system ---------------------------------------------- ZopeTestCase.installProduct('') -PloneTestCase.setupPloneSite(products=['PloneLanguageTool', - '']) -class Test(PloneTestCase.PloneTestCase, TestMixin): +class Test(ZopeTestCase.ZopeTestCase, TestMixin): '''Base test class for test cases.''' # Data needed for defining the tests ------------------------------------------- diff --git a/gen/templates/wrappers.py b/gen/templates/wrappers.py index 9149b7f..92f85b7 100644 --- a/gen/templates/wrappers.py +++ b/gen/templates/wrappers.py @@ -1,11 +1,11 @@ # ------------------------------------------------------------------------------ from appy.gen import * Grp = Group # Avoid name clashes with the Group class below and appy.gen.Group -from appy.gen.plone25.wrappers import AbstractWrapper -from appy.gen.plone25.wrappers.ToolWrapper import ToolWrapper as WTool -from appy.gen.plone25.wrappers.UserWrapper import UserWrapper as WUser -from appy.gen.plone25.wrappers.GroupWrapper import GroupWrapper as WGroup -from appy.gen.plone25.wrappers.TranslationWrapper import TranslationWrapper as WT +from appy.gen.wrappers import AbstractWrapper +from appy.gen.wrappers.ToolWrapper import ToolWrapper as WTool +from appy.gen.wrappers.UserWrapper import UserWrapper as WUser +from appy.gen.wrappers.GroupWrapper import GroupWrapper as WGroup +from appy.gen.wrappers.TranslationWrapper import TranslationWrapper as WT from Globals import InitializeClass from AccessControl import ClassSecurityInfo tfw = {"edit":"f","cell":"f","view":"f"} # Layout for Translation fields diff --git a/gen/ui/import.pt b/gen/ui/import.pt index 500956b..6f34d2c 100644 --- a/gen/ui/import.pt +++ b/gen/ui/import.pt @@ -1,23 +1,9 @@ - - -Disable standard Plone green tabs -
- -
-
- -Fill main slot of Plone main_template - - + + +
Form for importing several meetings at once.
+ tal:attributes="action python: tool.absolute_url()+'/do'" method="post"> @@ -120,6 +106,6 @@ tal:condition="python: importElems[1] and not allAreImported" tal:attributes="value python:tool.translate('import_many')"/>

- - + + diff --git a/gen/ui/page.pt b/gen/ui/page.pt index 88033e2..fa1afd9 100644 --- a/gen/ui/page.pt +++ b/gen/ui/page.pt @@ -70,11 +70,11 @@ - - - - - + + + + +   - History ||  + History ||  Show document creator @@ -263,32 +263,28 @@ + title="Previous" tal:attributes="src string:$appUrl/ui/previous.png"/> - + + title="Save" tal:attributes="src string:$appUrl/ui/save.png"/> + title="Cancel" tal:attributes="src string:$appUrl/ui/cancel.png"/> - @@ -303,14 +299,12 @@ + title="Next" tal:attributes="src string:$appUrl/ui/next.png"/> - + diff --git a/gen/ui/portlet.pt b/gen/ui/portlet.pt index 742aeb5..5784d18 100644 --- a/gen/ui/portlet.pt +++ b/gen/ui/portlet.pt @@ -117,7 +117,7 @@ tal:content="structure python: _(label)"> Delete the element diff --git a/gen/ui/widgets/action.pt b/gen/ui/widgets/action.pt index 53e9720..9967654 100644 --- a/gen/ui/widgets/action.pt +++ b/gen/ui/widgets/action.pt @@ -14,10 +14,7 @@ onClick python: 'askConfirm(\'form\', \'%s\', "%s")' % (formId, labelConfirm)"/> - The previous onClick is simply used to prevent Plone - from adding a CSS class that displays a popup when the user triggers the form multiple - times. + tal:attributes="value label"/> diff --git a/gen/ui/widgets/boolean.pt b/gen/ui/widgets/boolean.pt index fcd89bb..cc10d4c 100644 --- a/gen/ui/widgets/boolean.pt +++ b/gen/ui/widgets/boolean.pt @@ -29,11 +29,11 @@
   - + - + diff --git a/gen/ui/widgets/file.pt b/gen/ui/widgets/file.pt index d0372cb..de70889 100644 --- a/gen/ui/widgets/file.pt +++ b/gen/ui/widgets/file.pt @@ -24,14 +24,13 @@
- Keep the file untouched. + Keep the file unchanged. - +
Delete the file. @@ -39,8 +38,7 @@ tal:attributes="name string:${name}_delete; id string:${name}_delete; onclick string:document.getElementById('${name}_file').disabled=true;"/> - +
Replace with a new file. @@ -49,8 +47,7 @@ name string:${name}_delete; id string:${name}_upload; onclick string:document.getElementById('${name}_file').disabled=false"/> - +
The upload field. diff --git a/gen/ui/widgets/ref.pt b/gen/ui/widgets/ref.pt index 6f2a1cd..1d20dcd 100644 --- a/gen/ui/widgets/ref.pt +++ b/gen/ui/widgets/ref.pt @@ -43,14 +43,13 @@
Delete the element diff --git a/gen/utils.py b/gen/utils.py index 94872fd..5389cfd 100644 --- a/gen/utils.py +++ b/gen/utils.py @@ -247,30 +247,30 @@ CONVERSION_ERROR = 'An error occurred while executing command "%s". %s' class FileWrapper: '''When you get, from an appy object, the value of a File attribute, you get an instance of this class.''' - def __init__(self, atFile): + def __init__(self, zopeFile): '''This constructor is only used by Appy to create a nice File instance - from a Plone/Zope corresponding instance (p_atFile). If you need to + from a Zope corresponding instance (p_zopeFile). If you need to create a new file and assign it to a File attribute, use the attribute setter, do not create yourself an instance of this class.''' d = self.__dict__ - d['_atFile'] = atFile # Not for you! - d['name'] = atFile.filename - d['content'] = atFile.data - d['mimeType'] = atFile.content_type - d['size'] = atFile.size # In bytes + d['_zopeFile'] = zopeFile # Not for you! + d['name'] = zopeFile.filename + d['content'] = zopeFile.data + d['mimeType'] = zopeFile.content_type + d['size'] = zopeFile.size # In bytes def __setattr__(self, name, v): d = self.__dict__ if name == 'name': - self._atFile.filename = v + self._zopeFile.filename = v d['name'] = v elif name == 'content': - self._atFile.update_data(v, self.mimeType, len(v)) + self._zopeFile.update_data(v, self.mimeType, len(v)) d['content'] = v d['size'] = len(v) elif name == 'mimeType': - self._atFile.content_type = self.mimeType = v + self._zopeFile.content_type = self.mimeType = v else: raise 'Impossible to set attribute %s. "Settable" attributes ' \ 'are "name", "content" and "mimeType".' % name @@ -326,8 +326,7 @@ def getClassName(klass, appName=None): Zope class. For some classes, name p_appName is required: it is part of the class name.''' moduleName = klass.__module__ - if (moduleName == 'appy.gen.plone25.model') or \ - moduleName.endswith('.wrappers'): + if (moduleName == 'appy.gen.model') or moduleName.endswith('.wrappers'): # This is a model (generation time or run time) res = appName + klass.__name__ elif klass.__bases__ and (klass.__bases__[-1].__module__ == 'appy.gen'): diff --git a/gen/wrappers/GroupWrapper.py b/gen/wrappers/GroupWrapper.py index 32bd3f8..47c2ee7 100644 --- a/gen/wrappers/GroupWrapper.py +++ b/gen/wrappers/GroupWrapper.py @@ -1,5 +1,5 @@ # ------------------------------------------------------------------------------ -from appy.gen.plone25.wrappers import AbstractWrapper +from appy.gen.wrappers import AbstractWrapper # ------------------------------------------------------------------------------ class GroupWrapper(AbstractWrapper): diff --git a/gen/wrappers/ToolWrapper.py b/gen/wrappers/ToolWrapper.py index 949bab2..c240fe8 100644 --- a/gen/wrappers/ToolWrapper.py +++ b/gen/wrappers/ToolWrapper.py @@ -2,7 +2,7 @@ import os.path import appy from appy.shared.utils import executeCommand -from appy.gen.plone25.wrappers import AbstractWrapper +from appy.gen.wrappers import AbstractWrapper # ------------------------------------------------------------------------------ _PY = 'Please specify a file corresponding to a Python interpreter ' \ diff --git a/gen/wrappers/TranslationWrapper.py b/gen/wrappers/TranslationWrapper.py index 3a91233..d1ed616 100644 --- a/gen/wrappers/TranslationWrapper.py +++ b/gen/wrappers/TranslationWrapper.py @@ -1,6 +1,6 @@ # ------------------------------------------------------------------------------ import os.path -from appy.gen.plone25.wrappers import AbstractWrapper +from appy.gen.wrappers import AbstractWrapper from appy.gen.po import PoFile, PoMessage from appy.shared.utils import getOsTempFolder diff --git a/gen/wrappers/UserWrapper.py b/gen/wrappers/UserWrapper.py index 1d6c310..8c53709 100644 --- a/gen/wrappers/UserWrapper.py +++ b/gen/wrappers/UserWrapper.py @@ -1,5 +1,5 @@ # ------------------------------------------------------------------------------ -from appy.gen.plone25.wrappers import AbstractWrapper +from appy.gen.wrappers import AbstractWrapper # ------------------------------------------------------------------------------ class UserWrapper(AbstractWrapper):
ActionByDateComment
ActionByDateComment
- @@ -136,7 +136,7 @@ - diff --git a/gen/ui/result.pt b/gen/ui/result.pt index ebdae1f..318a3b7 100644 --- a/gen/ui/result.pt +++ b/gen/ui/result.pt @@ -125,13 +125,12 @@ - + - +