appy.gen: Refactoring due to De-Plonization.
This commit is contained in:
parent
d934f49a99
commit
c5a8968bd3
|
@ -3,18 +3,15 @@
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
import sys, os.path
|
import sys, os.path
|
||||||
from optparse import OptionParser
|
from optparse import OptionParser
|
||||||
from appy.gen.generator import GeneratorError
|
from appy.gen.generator import GeneratorError, ZopeGenerator
|
||||||
from appy.shared.utils import LinesCounter
|
from appy.shared.utils import LinesCounter
|
||||||
import appy.version
|
import appy.version
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
ERROR_CODE = 1
|
ERROR_CODE = 1
|
||||||
VALID_PRODUCT_TYPES = ('zope', 'odt')
|
|
||||||
APP_NOT_FOUND = 'Application not found at %s.'
|
APP_NOT_FOUND = 'Application not found at %s.'
|
||||||
WRONG_NG_OF_ARGS = 'Wrong number of arguments.'
|
WRONG_NG_OF_ARGS = 'Wrong number of arguments.'
|
||||||
WRONG_OUTPUT_FOLDER = 'Output folder not found. Please create it first.'
|
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 ' \
|
C_OPTION = 'Removes from i18n files all labels that are not automatically ' \
|
||||||
'generated from your gen-application. It can be useful during ' \
|
'generated from your gen-application. It can be useful during ' \
|
||||||
'development, when you do lots of name changes (classes, ' \
|
'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.'
|
'set of translation files.'
|
||||||
|
|
||||||
class GeneratorScript:
|
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
|
"app" is the path to your Appy application, which must be a
|
||||||
Python package (= a folder containing a file named
|
Python package (= a folder containing a file named
|
||||||
|
@ -47,44 +44,29 @@ class GeneratorScript:
|
||||||
generated product, stored or symlinked in
|
generated product, stored or symlinked in
|
||||||
<yourZopeInstance>/Products.
|
<yourZopeInstance>/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.
|
"outputFolder" is the folder where the Zope product will be generated.
|
||||||
For example, if you develop your application in
|
For example, if you develop your application in
|
||||||
/home/gdy/MyProject/MyProject, you typically specify
|
/home/gdy/MyProject/MyProject, you typically specify
|
||||||
"/home/gdy/MyProject/zope" as outputFolder.
|
"/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):
|
def manageArgs(self, parser, options, args):
|
||||||
# Check number of args
|
# Check number of args
|
||||||
if len(args) != 3:
|
if len(args) != 2:
|
||||||
print WRONG_NG_OF_ARGS
|
print WRONG_NG_OF_ARGS
|
||||||
parser.print_help()
|
parser.print_help()
|
||||||
sys.exit(ERROR_CODE)
|
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
|
# Check existence of application
|
||||||
if not os.path.exists(args[0]):
|
if not os.path.exists(args[0]):
|
||||||
print APP_NOT_FOUND % args[0]
|
print APP_NOT_FOUND % args[0]
|
||||||
sys.exit(ERROR_CODE)
|
sys.exit(ERROR_CODE)
|
||||||
# Check existence of outputFolder basic type
|
# Check existence of outputFolder
|
||||||
if not os.path.exists(args[2]):
|
if not os.path.exists(args[1]):
|
||||||
print WRONG_OUTPUT_FOLDER
|
print WRONG_OUTPUT_FOLDER
|
||||||
sys.exit(ERROR_CODE)
|
sys.exit(ERROR_CODE)
|
||||||
# Convert all paths in absolute paths
|
# Convert all paths in absolute paths
|
||||||
for i in (0,2):
|
for i in (0,1):
|
||||||
args[i] = os.path.abspath(args[i])
|
args[i] = os.path.abspath(args[i])
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
optParser = OptionParser(usage=GeneratorScript.__doc__)
|
optParser = OptionParser(usage=GeneratorScript.__doc__)
|
||||||
optParser.add_option("-c", "--i18n-clean", action='store_true',
|
optParser.add_option("-c", "--i18n-clean", action='store_true',
|
||||||
|
@ -95,8 +77,8 @@ class GeneratorScript:
|
||||||
try:
|
try:
|
||||||
self.manageArgs(optParser, options, args)
|
self.manageArgs(optParser, options, args)
|
||||||
print 'Appy version:', appy.version.verbose
|
print 'Appy version:', appy.version.verbose
|
||||||
print 'Generating %s product in %s...' % (args[1], args[2])
|
print 'Generating Zope product in %s...' % args[1]
|
||||||
self.generateProduct(options, *args)
|
ZopeGenerator(args[0], args[1], options).run()
|
||||||
# Give the user some statistics about its code
|
# Give the user some statistics about its code
|
||||||
LinesCounter(args[0]).run()
|
LinesCounter(args[0]).run()
|
||||||
except GeneratorError, ge:
|
except GeneratorError, ge:
|
||||||
|
|
|
@ -1026,8 +1026,7 @@ class String(Type):
|
||||||
it will be given by the Appy validation machinery, so it must be
|
it will be given by the Appy validation machinery, so it must be
|
||||||
specified as parameter. The function returns True if the check is
|
specified as parameter. The function returns True if the check is
|
||||||
successful.'''
|
successful.'''
|
||||||
if not value: return True # Plone calls me erroneously for
|
if not value: return True
|
||||||
# non-mandatory fields.
|
|
||||||
# First, remove any non-digit char
|
# First, remove any non-digit char
|
||||||
v = ''
|
v = ''
|
||||||
for c in value:
|
for c in value:
|
||||||
|
@ -1058,8 +1057,7 @@ class String(Type):
|
||||||
'''Checks that p_value corresponds to a valid IBAN number. IBAN stands
|
'''Checks that p_value corresponds to a valid IBAN number. IBAN stands
|
||||||
for International Bank Account Number (ISO 13616). If the number is
|
for International Bank Account Number (ISO 13616). If the number is
|
||||||
valid, the method returns True.'''
|
valid, the method returns True.'''
|
||||||
if not value: return True # Plone calls me erroneously for
|
if not value: return True
|
||||||
# non-mandatory fields.
|
|
||||||
# First, remove any non-digit or non-letter char
|
# First, remove any non-digit or non-letter char
|
||||||
v = ''
|
v = ''
|
||||||
for c in value:
|
for c in value:
|
||||||
|
@ -1088,8 +1086,7 @@ class String(Type):
|
||||||
'''Checks that p_value corresponds to a valid BIC number. BIC stands
|
'''Checks that p_value corresponds to a valid BIC number. BIC stands
|
||||||
for Bank Identifier Code (ISO 9362). If the number is valid, the
|
for Bank Identifier Code (ISO 9362). If the number is valid, the
|
||||||
method returns True.'''
|
method returns True.'''
|
||||||
if not value: return True # Plone calls me erroneously for
|
if not value: return True
|
||||||
# non-mandatory fields.
|
|
||||||
# BIC number must be 8 or 11 chars
|
# BIC number must be 8 or 11 chars
|
||||||
if len(value) not in (8, 11): return False
|
if len(value) not in (8, 11): return False
|
||||||
# 4 first chars, representing bank name, must be letters
|
# 4 first chars, representing bank name, must be letters
|
||||||
|
@ -1176,15 +1173,7 @@ class String(Type):
|
||||||
else: return value
|
else: return value
|
||||||
if isinstance(value, basestring) and self.isMultiValued():
|
if isinstance(value, basestring) and self.isMultiValued():
|
||||||
value = [value]
|
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):
|
elif isinstance(value, tuple):
|
||||||
# When Appy storage was based on Archetype, multivalued string
|
|
||||||
# fields stored values as tuples of unicode strings.
|
|
||||||
value = list(value)
|
value = list(value)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
@ -1207,12 +1196,6 @@ class String(Type):
|
||||||
res = [t('%s_list_%s' % (self.labelId, v)) for v in value]
|
res = [t('%s_list_%s' % (self.labelId, v)) for v in value]
|
||||||
else:
|
else:
|
||||||
res = t('%s_list_%s' % (self.labelId, value))
|
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
|
# If value starts with a carriage return, add a space; else, it will
|
||||||
# be ignored.
|
# be ignored.
|
||||||
if isinstance(res, basestring) and \
|
if isinstance(res, basestring) and \
|
||||||
|
@ -1384,8 +1367,8 @@ class Boolean(Type):
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def getFormattedValue(self, obj, value):
|
def getFormattedValue(self, obj, value):
|
||||||
if value: res = obj.translate('yes', domain='plone')
|
if value: res = obj.translate('yes')
|
||||||
else: res = obj.translate('no', domain='plone')
|
else: res = obj.translate('no')
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def getStorableValue(self, value):
|
def getStorableValue(self, value):
|
||||||
|
@ -1517,7 +1500,7 @@ class File(Type):
|
||||||
|
|
||||||
def getFormattedValue(self, obj, value):
|
def getFormattedValue(self, obj, value):
|
||||||
if not value: return value
|
if not value: return value
|
||||||
return value._atFile
|
return value._zopeFile
|
||||||
|
|
||||||
def getRequestValue(self, request):
|
def getRequestValue(self, request):
|
||||||
return request.get('%s_file' % self.name)
|
return request.get('%s_file' % self.name)
|
||||||
|
@ -1591,7 +1574,7 @@ class File(Type):
|
||||||
elif isinstance(value, OFSImageFile):
|
elif isinstance(value, OFSImageFile):
|
||||||
setattr(obj, self.name, value)
|
setattr(obj, self.name, value)
|
||||||
elif isinstance(value, FileWrapper):
|
elif isinstance(value, FileWrapper):
|
||||||
setattr(obj, self.name, value._atFile)
|
setattr(obj, self.name, value._zopeFile)
|
||||||
elif isinstance(value, basestring):
|
elif isinstance(value, basestring):
|
||||||
setattr(obj, self.name, File.getFileObject(value, zope=True))
|
setattr(obj, self.name, File.getFileObject(value, zope=True))
|
||||||
elif type(value) in sequenceTypes:
|
elif type(value) in sequenceTypes:
|
||||||
|
@ -2189,7 +2172,7 @@ class Pod(Type):
|
||||||
def store(self, obj, value):
|
def store(self, obj, value):
|
||||||
'''Stores (=freezes) a document (in p_value) in the field.'''
|
'''Stores (=freezes) a document (in p_value) in the field.'''
|
||||||
if isinstance(value, FileWrapper):
|
if isinstance(value, FileWrapper):
|
||||||
value = value._atFile
|
value = value._zopeFile
|
||||||
setattr(obj, self.name, value)
|
setattr(obj, self.name, value)
|
||||||
|
|
||||||
class List(Type):
|
class List(Type):
|
||||||
|
@ -2283,19 +2266,18 @@ appyToZopePermissions = {
|
||||||
|
|
||||||
class Role:
|
class Role:
|
||||||
'''Represents a role.'''
|
'''Represents a role.'''
|
||||||
ploneRoles = ('Manager', 'Member', 'Owner', 'Reviewer', 'Anonymous',
|
zopeRoles = ('Manager', 'Owner', 'Anonymous', 'Authenticated')
|
||||||
'Authenticated')
|
zopeLocalRoles = ('Owner',)
|
||||||
ploneLocalRoles = ('Owner',)
|
zopeUngrantableRoles = ('Anonymous', 'Authenticated')
|
||||||
ploneUngrantableRoles = ('Anonymous', 'Authenticated')
|
|
||||||
def __init__(self, name, local=False, grantable=True):
|
def __init__(self, name, local=False, grantable=True):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.local = local # True if it can be used as local role only.
|
self.local = local # True if it can be used as local role only.
|
||||||
# It is a standard Plone role or an application-specific one?
|
# It is a standard Zope role or an application-specific one?
|
||||||
self.plone = name in self.ploneRoles
|
self.zope = name in self.zopeRoles
|
||||||
if self.plone and (name in self.ploneLocalRoles):
|
if self.zope and (name in self.zopeLocalRoles):
|
||||||
self.local = True
|
self.local = True
|
||||||
self.grantable = grantable
|
self.grantable = grantable
|
||||||
if self.plone and (name in self.ploneUngrantableRoles):
|
if self.zope and (name in self.zopeUngrantableRoles):
|
||||||
self.grantable = False
|
self.grantable = False
|
||||||
# An ungrantable role is one that is, like the Anonymous or
|
# An ungrantable role is one that is, like the Anonymous or
|
||||||
# Authenticated roles, automatically attributed to a user.
|
# Authenticated roles, automatically attributed to a user.
|
||||||
|
@ -2575,8 +2557,7 @@ class Transition:
|
||||||
# Return a message to the user if needed
|
# Return a message to the user if needed
|
||||||
if not doSay or (transitionName == '_init_'): return
|
if not doSay or (transitionName == '_init_'): return
|
||||||
if not msg:
|
if not msg:
|
||||||
msg = obj.translate(u'Your content\'s status has been modified.',
|
msg = obj.translate(u'Changes saved.')
|
||||||
domain='plone')
|
|
||||||
obj.say(msg)
|
obj.say(msg)
|
||||||
|
|
||||||
class Permission:
|
class Permission:
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
import types, copy
|
import types, copy
|
||||||
from appy.gen import State, Transition, Type
|
import appy.gen as gen
|
||||||
from po import PoMessage
|
from po import PoMessage
|
||||||
from model import ModelClass, toolFieldPrefixes
|
from model import ModelClass, toolFieldPrefixes
|
||||||
from utils import produceNiceMessage, getClassName
|
from utils import produceNiceMessage, getClassName
|
||||||
|
@ -27,8 +27,7 @@ class ClassDescriptor(Descriptor):
|
||||||
'''This class gives information about an Appy class.'''
|
'''This class gives information about an Appy class.'''
|
||||||
|
|
||||||
def __init__(self, klass, orderedAttributes, generator):
|
def __init__(self, klass, orderedAttributes, generator):
|
||||||
appy.gen.descriptors.ClassDescriptor.__init__(self, klass,
|
Descriptor.__init__(self, klass, orderedAttributes, generator)
|
||||||
orderedAttributes, generator)
|
|
||||||
self.methods = '' # Needed method definitions will be generated here
|
self.methods = '' # Needed method definitions will be generated here
|
||||||
# We remember here encountered pages and groups defined in the Appy
|
# We remember here encountered pages and groups defined in the Appy
|
||||||
# type. Indeed, after having parsed all application classes, we will
|
# type. Indeed, after having parsed all application classes, we will
|
||||||
|
@ -70,7 +69,7 @@ class ClassDescriptor(Descriptor):
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
attrValue = getattr(self.modelClass, attrName)
|
attrValue = getattr(self.modelClass, attrName)
|
||||||
hookClass = self.modelClass
|
hookClass = self.modelClass
|
||||||
if isinstance(attrValue, Type):
|
if isinstance(attrValue, gen.Type):
|
||||||
if not condition or eval(condition):
|
if not condition or eval(condition):
|
||||||
attrs.append( (attrName, attrValue, hookClass) )
|
attrs.append( (attrName, attrValue, hookClass) )
|
||||||
# Then, add attributes from parent classes
|
# Then, add attributes from parent classes
|
||||||
|
@ -142,7 +141,7 @@ class ClassDescriptor(Descriptor):
|
||||||
attrValue = getattr(self.klass, attrName)
|
attrValue = getattr(self.klass, attrName)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
attrValue = getattr(self.modelClass, attrName)
|
attrValue = getattr(self.modelClass, attrName)
|
||||||
if isinstance(attrValue, Type):
|
if isinstance(attrValue, gen.Type):
|
||||||
if configClass:
|
if configClass:
|
||||||
attrValue = copy.copy(attrValue)
|
attrValue = copy.copy(attrValue)
|
||||||
attrValue.optional = False
|
attrValue.optional = False
|
||||||
|
@ -184,13 +183,13 @@ class ClassDescriptor(Descriptor):
|
||||||
res = []
|
res = []
|
||||||
if self.klass.__dict__.has_key('creators') and self.klass.creators:
|
if self.klass.__dict__.has_key('creators') and self.klass.creators:
|
||||||
for creator in self.klass.creators:
|
for creator in self.klass.creators:
|
||||||
if isinstance(creator, Role):
|
if isinstance(creator, gen.Role):
|
||||||
if creator.local:
|
if creator.local:
|
||||||
raise 'Local role "%s" cannot be used as a creator.' % \
|
raise 'Local role "%s" cannot be used as a creator.' % \
|
||||||
creator.name
|
creator.name
|
||||||
res.append(creator)
|
res.append(creator)
|
||||||
else:
|
else:
|
||||||
res.append(Role(creator))
|
res.append(gen.Role(creator))
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def getCreateMean(self, type='Import'):
|
def getCreateMean(self, type='Import'):
|
||||||
|
@ -213,13 +212,17 @@ class ClassDescriptor(Descriptor):
|
||||||
res = []
|
res = []
|
||||||
if klass.__dict__.has_key('search'):
|
if klass.__dict__.has_key('search'):
|
||||||
searches = klass.__dict__['search']
|
searches = klass.__dict__['search']
|
||||||
if isinstance(searches, basestring): res.append(Search(searches))
|
if isinstance(searches, basestring):
|
||||||
elif isinstance(searches, Search): res.append(searches)
|
res.append(gen.Search(searches))
|
||||||
|
elif isinstance(searches, gen.Search):
|
||||||
|
res.append(searches)
|
||||||
else:
|
else:
|
||||||
# It must be a list of searches.
|
# It must be a list of searches.
|
||||||
for search in searches:
|
for search in searches:
|
||||||
if isinstance(search, basestring):res.append(Search(search))
|
if isinstance(search, basestring):
|
||||||
else: res.append(search)
|
res.append(gen.Search(search))
|
||||||
|
else:
|
||||||
|
res.append(search)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -268,11 +271,10 @@ class FieldDescriptor:
|
||||||
'''This class gathers information about a specific typed attribute defined
|
'''This class gathers information about a specific typed attribute defined
|
||||||
in a gen-class.'''
|
in a gen-class.'''
|
||||||
|
|
||||||
singleValuedTypes = ('Integer', 'Float', 'Boolean', 'Date', 'File')
|
|
||||||
# Although Appy allows to specify a multiplicity[0]>1 for those types, it is
|
# 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
|
# not currently. So we will always generate single-valued type definitions
|
||||||
# definitions for them.
|
# for them.
|
||||||
specialParams = ('title', 'description')
|
singleValuedTypes = ('Integer', 'Float', 'Boolean', 'Date', 'File')
|
||||||
|
|
||||||
def __init__(self, fieldName, appyType, classDescriptor):
|
def __init__(self, fieldName, appyType, classDescriptor):
|
||||||
self.appyType = appyType
|
self.appyType = appyType
|
||||||
|
@ -387,8 +389,7 @@ class FieldDescriptor:
|
||||||
if self.appyType.editDefault:
|
if self.appyType.editDefault:
|
||||||
self.generator.tool.addDefaultField(self)
|
self.generator.tool.addDefaultField(self)
|
||||||
# - put an index on this field?
|
# - put an index on this field?
|
||||||
if self.appyType.indexed and \
|
if self.appyType.indexed and (self.fieldName != 'title'):
|
||||||
(self.fieldName not in ('title', 'description')):
|
|
||||||
self.classDescr.addIndexMethod(self)
|
self.classDescr.addIndexMethod(self)
|
||||||
# i18n labels
|
# i18n labels
|
||||||
messages = self.generator.labels
|
messages = self.generator.labels
|
||||||
|
@ -477,7 +478,7 @@ class ToolClassDescriptor(ClassDescriptor):
|
||||||
self.addField(fieldName, fieldType)
|
self.addField(fieldName, fieldType)
|
||||||
fieldType.validator.append(fieldDescr.fieldName)
|
fieldType.validator.append(fieldDescr.fieldName)
|
||||||
fieldType.page.name = 'data'
|
fieldType.page.name = 'data'
|
||||||
fieldType.group = Group(fieldDescr.classDescr.klass.__name__)
|
fieldType.group = gen.Group(fieldDescr.classDescr.klass.__name__)
|
||||||
|
|
||||||
def addDefaultField(self, fieldDescr):
|
def addDefaultField(self, fieldDescr):
|
||||||
className = fieldDescr.classDescr.name
|
className = fieldDescr.classDescr.name
|
||||||
|
@ -485,22 +486,22 @@ class ToolClassDescriptor(ClassDescriptor):
|
||||||
fieldType = fieldDescr.appyType.clone()
|
fieldType = fieldDescr.appyType.clone()
|
||||||
self.addField(fieldName, fieldType)
|
self.addField(fieldName, fieldType)
|
||||||
fieldType.page.name = 'data'
|
fieldType.page.name = 'data'
|
||||||
fieldType.group = Group(fieldDescr.classDescr.klass.__name__)
|
fieldType.group = gen.Group(fieldDescr.classDescr.klass.__name__)
|
||||||
|
|
||||||
def addPodRelatedFields(self, fieldDescr):
|
def addPodRelatedFields(self, fieldDescr):
|
||||||
'''Adds the fields needed in the Tool for configuring a Pod field.'''
|
'''Adds the fields needed in the Tool for configuring a Pod field.'''
|
||||||
className = fieldDescr.classDescr.name
|
className = fieldDescr.classDescr.name
|
||||||
# On what page and group to display those fields ?
|
# On what page and group to display those fields ?
|
||||||
pg = {'page': 'documentGeneration',
|
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.
|
# Add the field that will store the pod template.
|
||||||
fieldName = 'podTemplateFor%s_%s' % (className, fieldDescr.fieldName)
|
fieldName = 'podTemplateFor%s_%s' % (className, fieldDescr.fieldName)
|
||||||
fieldType = File(**pg)
|
fieldType = gen.File(**pg)
|
||||||
self.addField(fieldName, fieldType)
|
self.addField(fieldName, fieldType)
|
||||||
# Add the field that will store the output format(s)
|
# Add the field that will store the output format(s)
|
||||||
fieldName = 'formatsFor%s_%s' % (className, fieldDescr.fieldName)
|
fieldName = 'formatsFor%s_%s' % (className, fieldDescr.fieldName)
|
||||||
fieldType = String(validator=Selection('getPodOutputFormats'),
|
fieldType = gen.String(validator=gen.Selection('getPodOutputFormats'),
|
||||||
multiplicity=(1,None), default=('odt',), **pg)
|
multiplicity=(1,None), default=('odt',), **pg)
|
||||||
self.addField(fieldName, fieldType)
|
self.addField(fieldName, fieldType)
|
||||||
|
|
||||||
def addQueryResultColumns(self, classDescr):
|
def addQueryResultColumns(self, classDescr):
|
||||||
|
@ -508,7 +509,7 @@ class ToolClassDescriptor(ClassDescriptor):
|
||||||
to select what default columns will be shown on query results.'''
|
to select what default columns will be shown on query results.'''
|
||||||
className = classDescr.name
|
className = classDescr.name
|
||||||
fieldName = 'resultColumnsFor%s' % className
|
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',
|
'_appy_getAllFields*%s' % className), page='userInterface',
|
||||||
group=classDescr.klass.__name__)
|
group=classDescr.klass.__name__)
|
||||||
self.addField(fieldName, fieldType)
|
self.addField(fieldName, fieldType)
|
||||||
|
@ -520,21 +521,21 @@ class ToolClassDescriptor(ClassDescriptor):
|
||||||
# Field that defines if advanced search is enabled for class
|
# Field that defines if advanced search is enabled for class
|
||||||
# p_classDescr or not.
|
# p_classDescr or not.
|
||||||
fieldName = 'enableAdvancedSearchFor%s' % className
|
fieldName = 'enableAdvancedSearchFor%s' % className
|
||||||
fieldType = Boolean(default=True, page='userInterface',
|
fieldType = gen.Boolean(default=True, page='userInterface',
|
||||||
group=classDescr.klass.__name__)
|
group=classDescr.klass.__name__)
|
||||||
self.addField(fieldName, fieldType)
|
self.addField(fieldName, fieldType)
|
||||||
# Field that defines how many columns are shown on the custom search
|
# Field that defines how many columns are shown on the custom search
|
||||||
# screen.
|
# screen.
|
||||||
fieldName = 'numberOfSearchColumnsFor%s' % className
|
fieldName = 'numberOfSearchColumnsFor%s' % className
|
||||||
fieldType = Integer(default=3, page='userInterface',
|
fieldType = gen.Integer(default=3, page='userInterface',
|
||||||
group=classDescr.klass.__name__)
|
group=classDescr.klass.__name__)
|
||||||
self.addField(fieldName, fieldType)
|
self.addField(fieldName, fieldType)
|
||||||
# Field that allows to select, among all indexed fields, what fields
|
# Field that allows to select, among all indexed fields, what fields
|
||||||
# must really be used in the search screen.
|
# must really be used in the search screen.
|
||||||
fieldName = 'searchFieldsFor%s' % className
|
fieldName = 'searchFieldsFor%s' % className
|
||||||
defaultValue = [a[0] for a in classDescr.getOrderedAppyAttributes(
|
defaultValue = [a[0] for a in classDescr.getOrderedAppyAttributes(
|
||||||
condition='attrValue.indexed')]
|
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,
|
'_appy_getSearchableFields*%s' % className), default=defaultValue,
|
||||||
page='userInterface', group=classDescr.klass.__name__)
|
page='userInterface', group=classDescr.klass.__name__)
|
||||||
self.addField(fieldName, fieldType)
|
self.addField(fieldName, fieldType)
|
||||||
|
@ -546,8 +547,8 @@ class ToolClassDescriptor(ClassDescriptor):
|
||||||
# Field that defines the path of the files to import.
|
# Field that defines the path of the files to import.
|
||||||
fieldName = 'importPathFor%s' % className
|
fieldName = 'importPathFor%s' % className
|
||||||
defValue = classDescr.getCreateMean('Import').path
|
defValue = classDescr.getCreateMean('Import').path
|
||||||
fieldType = String(page='data', multiplicity=(1,1), default=defValue,
|
fieldType = gen.String(page='data', multiplicity=(1,1),
|
||||||
group=classDescr.klass.__name__)
|
default=defValue,group=classDescr.klass.__name__)
|
||||||
self.addField(fieldName, fieldType)
|
self.addField(fieldName, fieldType)
|
||||||
|
|
||||||
def addWorkflowFields(self, classDescr):
|
def addWorkflowFields(self, classDescr):
|
||||||
|
@ -560,13 +561,13 @@ class ToolClassDescriptor(ClassDescriptor):
|
||||||
if classDescr.isRoot() or issubclass(classDescr.klass, ModelClass):
|
if classDescr.isRoot() or issubclass(classDescr.klass, ModelClass):
|
||||||
defaultValue = True
|
defaultValue = True
|
||||||
fieldName = 'showWorkflowFor%s' % className
|
fieldName = 'showWorkflowFor%s' % className
|
||||||
fieldType = Boolean(default=defaultValue, page='userInterface',
|
fieldType = gen.Boolean(default=defaultValue, page='userInterface',
|
||||||
group=groupName)
|
group=groupName)
|
||||||
self.addField(fieldName, fieldType)
|
self.addField(fieldName, fieldType)
|
||||||
# Adds the boolean field for showing or not the field "enter comments".
|
# Adds the boolean field for showing or not the field "enter comments".
|
||||||
fieldName = 'showWorkflowCommentFieldFor%s' % className
|
fieldName = 'showWorkflowCommentFieldFor%s' % className
|
||||||
fieldType = Boolean(default=defaultValue, page='userInterface',
|
fieldType = gen.Boolean(default=defaultValue, page='userInterface',
|
||||||
group=groupName)
|
group=groupName)
|
||||||
self.addField(fieldName, fieldType)
|
self.addField(fieldName, fieldType)
|
||||||
# Adds the boolean field for showing all states in current state or not.
|
# 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,
|
# 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:
|
if len(classDescr.getPhases()) > 1:
|
||||||
defaultValue = True
|
defaultValue = True
|
||||||
fieldName = 'showAllStatesInPhaseFor%s' % className
|
fieldName = 'showAllStatesInPhaseFor%s' % className
|
||||||
fieldType = Boolean(default=defaultValue, page='userInterface',
|
fieldType = gen.Boolean(default=defaultValue, page='userInterface',
|
||||||
group=groupName)
|
group=groupName)
|
||||||
self.addField(fieldName, fieldType)
|
self.addField(fieldName, fieldType)
|
||||||
|
|
||||||
class UserClassDescriptor(ClassDescriptor):
|
class UserClassDescriptor(ClassDescriptor):
|
||||||
'''Represents an Archetypes-compliant class that corresponds to the User
|
'''Appy-specific class for representing a user.'''
|
||||||
for the generated application.'''
|
|
||||||
def __init__(self, klass, generator):
|
def __init__(self, klass, generator):
|
||||||
ClassDescriptor.__init__(self,klass,klass._appy_attributes[:],generator)
|
ClassDescriptor.__init__(self,klass,klass._appy_attributes[:],generator)
|
||||||
self.modelClass = self.klass
|
self.modelClass = self.klass
|
||||||
|
@ -649,8 +649,8 @@ class TranslationClassDescriptor(ClassDescriptor):
|
||||||
def addLabelField(self, messageId, page):
|
def addLabelField(self, messageId, page):
|
||||||
'''Adds a Computed field that will display, in the source language, the
|
'''Adds a Computed field that will display, in the source language, the
|
||||||
content of the text to translate.'''
|
content of the text to translate.'''
|
||||||
field = Computed(method=self.modelClass.label, plainText=False,
|
field = gen.Computed(method=self.modelClass.label, plainText=False,
|
||||||
page=page, show=self.modelClass.show, layouts='f')
|
page=page, show=self.modelClass.show, layouts='f')
|
||||||
self.addField('%s_label' % messageId, field)
|
self.addField('%s_label' % messageId, field)
|
||||||
|
|
||||||
def addMessageField(self, messageId, page, i18nFiles):
|
def addMessageField(self, messageId, page, i18nFiles):
|
||||||
|
@ -683,7 +683,7 @@ class TranslationClassDescriptor(ClassDescriptor):
|
||||||
params['width'] = width
|
params['width'] = width
|
||||||
else:
|
else:
|
||||||
# This is a multi-line field, or a very-long-single-lined field
|
# 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
|
params['height'] = height
|
||||||
self.addField(messageId, String(**params))
|
self.addField(messageId, gen.String(**params))
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
import os, os.path, re, sys, parser, symbol, token, types
|
import os, os.path, re, sys, parser, symbol, token, types
|
||||||
import appy.pod, appy.pod.renderer
|
import appy.pod, appy.pod.renderer
|
||||||
from appy.shared.utils import FolderDeleter
|
from appy.shared.utils import FolderDeleter
|
||||||
#from appy.gen import *
|
import appy.gen as gen
|
||||||
from po import PoMessage, PoFile, PoParser
|
from po import PoMessage, PoFile, PoParser
|
||||||
from descriptors import *
|
from descriptors import *
|
||||||
from utils import produceNiceMessage, getClassName
|
from utils import produceNiceMessage, getClassName
|
||||||
|
@ -140,7 +140,7 @@ class Generator:
|
||||||
self.user = None
|
self.user = None
|
||||||
self.workflows = []
|
self.workflows = []
|
||||||
self.initialize()
|
self.initialize()
|
||||||
self.config = Config.getDefault()
|
self.config = gen.Config.getDefault()
|
||||||
self.modulesWithTests = set()
|
self.modulesWithTests = set()
|
||||||
self.totalNumberOfTests = 0
|
self.totalNumberOfTests = 0
|
||||||
|
|
||||||
|
@ -152,9 +152,9 @@ class Generator:
|
||||||
workflow.'''
|
workflow.'''
|
||||||
res = 'none'
|
res = 'none'
|
||||||
for attrValue in klass.__dict__.itervalues():
|
for attrValue in klass.__dict__.itervalues():
|
||||||
if isinstance(attrValue, Type):
|
if isinstance(attrValue, gen.Type):
|
||||||
res = 'class'
|
res = 'class'
|
||||||
elif isinstance(attrValue, State):
|
elif isinstance(attrValue, gen.State):
|
||||||
res = 'workflow'
|
res = 'workflow'
|
||||||
if not res:
|
if not res:
|
||||||
for baseClass in klass.__bases__:
|
for baseClass in klass.__bases__:
|
||||||
|
@ -219,13 +219,13 @@ class Generator:
|
||||||
attrs = astClasses[moduleElem.__name__].attributes
|
attrs = astClasses[moduleElem.__name__].attributes
|
||||||
if appyType == 'class':
|
if appyType == 'class':
|
||||||
# Determine the class type (standard, tool, user...)
|
# Determine the class type (standard, tool, user...)
|
||||||
if issubclass(moduleElem, Tool):
|
if issubclass(moduleElem, gen.Tool):
|
||||||
if not self.tool:
|
if not self.tool:
|
||||||
klass = self.descriptorClasses['tool']
|
klass = self.descriptorClasses['tool']
|
||||||
self.tool = klass(moduleElem, attrs, self)
|
self.tool = klass(moduleElem, attrs, self)
|
||||||
else:
|
else:
|
||||||
self.tool.update(moduleElem, attrs)
|
self.tool.update(moduleElem, attrs)
|
||||||
elif issubclass(moduleElem, User):
|
elif issubclass(moduleElem, gen.User):
|
||||||
if not self.user:
|
if not self.user:
|
||||||
klass = self.descriptorClasses['user']
|
klass = self.descriptorClasses['user']
|
||||||
self.user = klass(moduleElem, attrs, self)
|
self.user = klass(moduleElem, attrs, self)
|
||||||
|
@ -244,7 +244,7 @@ class Generator:
|
||||||
self.workflows.append(descriptor)
|
self.workflows.append(descriptor)
|
||||||
if self.containsTests(moduleElem):
|
if self.containsTests(moduleElem):
|
||||||
self.modulesWithTests.add(moduleObj.__name__)
|
self.modulesWithTests.add(moduleObj.__name__)
|
||||||
elif isinstance(moduleElem, Config):
|
elif isinstance(moduleElem, gen.Config):
|
||||||
self.config = moduleElem
|
self.config = moduleElem
|
||||||
|
|
||||||
# Walk potential sub-modules
|
# Walk potential sub-modules
|
||||||
|
@ -461,7 +461,6 @@ class ZopeGenerator(Generator):
|
||||||
self.generateTool()
|
self.generateTool()
|
||||||
self.generateInit()
|
self.generateInit()
|
||||||
self.generateTests()
|
self.generateTests()
|
||||||
self.generateConfigureZcml()
|
|
||||||
# Create version.txt
|
# Create version.txt
|
||||||
f = open(os.path.join(self.outputFolder, 'version.txt'), 'w')
|
f = open(os.path.join(self.outputFolder, 'version.txt'), 'w')
|
||||||
f.write(self.version)
|
f.write(self.version)
|
||||||
|
@ -536,13 +535,13 @@ class ZopeGenerator(Generator):
|
||||||
self.generateWrappers()
|
self.generateWrappers()
|
||||||
self.generateConfig()
|
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
|
'''Produces a list of all the roles used within all workflows and
|
||||||
classes defined in this application.
|
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;
|
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
|
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"
|
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 wfDescr in self.workflows:
|
||||||
for attr in dir(wfDescr.klass):
|
for attr in dir(wfDescr.klass):
|
||||||
attrValue = getattr(wfDescr.klass, attr)
|
attrValue = getattr(wfDescr.klass, attr)
|
||||||
if isinstance(attrValue, State) or \
|
if isinstance(attrValue, gen.State) or \
|
||||||
isinstance(attrValue, Transition):
|
isinstance(attrValue, gen.Transition):
|
||||||
for role in attrValue.getUsedRoles():
|
for role in attrValue.getUsedRoles():
|
||||||
if role.name not in allRoles:
|
if role.name not in allRoles:
|
||||||
allRoles[role.name] = role
|
allRoles[role.name] = role
|
||||||
|
@ -569,7 +568,7 @@ class ZopeGenerator(Generator):
|
||||||
allRoles[role.name] = role
|
allRoles[role.name] = role
|
||||||
res = allRoles.values()
|
res = allRoles.values()
|
||||||
# Filter the result according to parameters
|
# Filter the result according to parameters
|
||||||
for p in ('plone', 'local', 'grantable'):
|
for p in ('zope', 'local', 'grantable'):
|
||||||
if eval(p) != None:
|
if eval(p) != None:
|
||||||
res = [r for r in res if eval('r.%s == %s' % (p, p))]
|
res = [r for r in res if eval('r.%s == %s' % (p, p))]
|
||||||
return res
|
return res
|
||||||
|
@ -613,17 +612,6 @@ class ZopeGenerator(Generator):
|
||||||
res = configClasses
|
res = configClasses
|
||||||
return res
|
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 += '<five:deprecatedManageAddDelete class=".%s.%s"/>\n' % \
|
|
||||||
(klass.name, klass.name)
|
|
||||||
repls['deprecated'] = depr
|
|
||||||
self.copyFile('configure.zcml', repls)
|
|
||||||
|
|
||||||
def generateConfig(self):
|
def generateConfig(self):
|
||||||
repls = self.repls.copy()
|
repls = self.repls.copy()
|
||||||
# Get some lists of classes
|
# Get some lists of classes
|
||||||
|
@ -677,9 +665,9 @@ class ZopeGenerator(Generator):
|
||||||
attributes.append('"%s":[%s]' % (classDescr.name, ','.join(qNames)))
|
attributes.append('"%s":[%s]' % (classDescr.name, ','.join(qNames)))
|
||||||
repls['attributes'] = ',\n '.join(attributes)
|
repls['attributes'] = ',\n '.join(attributes)
|
||||||
# Compute list of used roles for registering them if needed
|
# 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])
|
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])
|
repls['gRoles'] = ','.join(['"%s"' % r.name for r in globalRoles])
|
||||||
grantableRoles = self.getAllUsedRoles(local=False, grantable=True)
|
grantableRoles = self.getAllUsedRoles(local=False, grantable=True)
|
||||||
repls['grRoles'] = ','.join(['"%s"' % r.name for r in grantableRoles])
|
repls['grRoles'] = ','.join(['"%s"' % r.name for r in grantableRoles])
|
||||||
|
@ -780,7 +768,7 @@ class ZopeGenerator(Generator):
|
||||||
self.copyFile('testAll.py', repls, destFolder='tests')
|
self.copyFile('testAll.py', repls, destFolder='tests')
|
||||||
|
|
||||||
def generateTool(self):
|
def generateTool(self):
|
||||||
'''Generates the Plone tool that corresponds to this application.'''
|
'''Generates the tool that corresponds to this application.'''
|
||||||
Msg = PoMessage
|
Msg = PoMessage
|
||||||
# Create Tool-related i18n-related messages
|
# Create Tool-related i18n-related messages
|
||||||
msg = Msg(self.tool.name, '', Msg.CONFIG % self.applicationName)
|
msg = Msg(self.tool.name, '', Msg.CONFIG % self.applicationName)
|
||||||
|
@ -874,7 +862,7 @@ class ZopeGenerator(Generator):
|
||||||
poMsg.produceNiceDefault()
|
poMsg.produceNiceDefault()
|
||||||
if poMsg not in self.labels:
|
if poMsg not in self.labels:
|
||||||
self.labels.append(poMsg)
|
self.labels.append(poMsg)
|
||||||
# Generate the resulting Archetypes class.
|
# Generate the resulting Zope class.
|
||||||
self.copyFile('Class.py', repls, destName=fileName)
|
self.copyFile('Class.py', repls, destName=fileName)
|
||||||
|
|
||||||
def generateWorkflow(self, wfDescr):
|
def generateWorkflow(self, wfDescr):
|
||||||
|
@ -886,14 +874,14 @@ class ZopeGenerator(Generator):
|
||||||
wfName = WorkflowDescriptor.getWorkflowName(wfDescr.klass)
|
wfName = WorkflowDescriptor.getWorkflowName(wfDescr.klass)
|
||||||
# Add i18n messages for states
|
# Add i18n messages for states
|
||||||
for name in dir(wfDescr.klass):
|
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 = PoMessage('%s_%s' % (wfName, name), '', name)
|
||||||
poMsg.produceNiceDefault()
|
poMsg.produceNiceDefault()
|
||||||
self.labels.append(poMsg)
|
self.labels.append(poMsg)
|
||||||
# Add i18n messages for transitions
|
# Add i18n messages for transitions
|
||||||
for name in dir(wfDescr.klass):
|
for name in dir(wfDescr.klass):
|
||||||
transition = getattr(wfDescr.klass, name)
|
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 = PoMessage('%s_%s' % (wfName, name), '', name)
|
||||||
poMsg.produceNiceDefault()
|
poMsg.produceNiceDefault()
|
||||||
self.labels.append(poMsg)
|
self.labels.append(poMsg)
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
import os, os.path, time
|
import os, os.path, time
|
||||||
import appy
|
import appy
|
||||||
import appy.version
|
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.po import PoParser
|
||||||
from appy.gen.utils import updateRolesForPermission, createObject
|
from appy.gen.utils import updateRolesForPermission, createObject
|
||||||
from appy.shared.data import languages
|
from appy.shared.data import languages
|
||||||
|
@ -110,7 +110,7 @@ class ZopeInstaller:
|
||||||
for name in files:
|
for name in files:
|
||||||
baseName, ext = os.path.splitext(name)
|
baseName, ext = os.path.splitext(name)
|
||||||
f = file(j(root, name))
|
f = file(j(root, name))
|
||||||
if ext in File.imageExts:
|
if ext in gen.File.imageExts:
|
||||||
zopeFolder.manage_addImage(name, f)
|
zopeFolder.manage_addImage(name, f)
|
||||||
elif ext == '.pt':
|
elif ext == '.pt':
|
||||||
manage_addPageTemplate(zopeFolder, baseName, '', f.read())
|
manage_addPageTemplate(zopeFolder, baseName, '', f.read())
|
||||||
|
@ -302,13 +302,11 @@ class ZopeInstaller:
|
||||||
# "po" file on disk.
|
# "po" file on disk.
|
||||||
appFolder = self.config.diskFolder
|
appFolder = self.config.diskFolder
|
||||||
appName = self.config.PROJECTNAME
|
appName = self.config.PROJECTNAME
|
||||||
dn = os.path.dirname
|
i18nFolder = os.path.join(appFolder, 'tr')
|
||||||
jn = os.path.join
|
|
||||||
i18nFolder = jn(jn(jn(dn(dn(dn(appFolder))),'Products'),appName),'i18n')
|
|
||||||
for translation in appyTool.translations:
|
for translation in appyTool.translations:
|
||||||
# Get the "po" file
|
# Get the "po" file
|
||||||
poName = '%s-%s.po' % (appName, translation.id)
|
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:
|
for message in poFile.messages:
|
||||||
setattr(translation, message.id, message.getMessage())
|
setattr(translation, message.id, message.getMessage())
|
||||||
appyTool.log('Translation "%s" updated from "%s".' % \
|
appyTool.log('Translation "%s" updated from "%s".' % \
|
||||||
|
@ -359,7 +357,7 @@ class ZopeInstaller:
|
||||||
wrapperClass = klass.wrapperClass
|
wrapperClass = klass.wrapperClass
|
||||||
if not hasattr(wrapperClass, 'title'):
|
if not hasattr(wrapperClass, 'title'):
|
||||||
# Special field "type" is mandatory for every class.
|
# 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')
|
title.init('title', None, 'appy')
|
||||||
setattr(wrapperClass, 'title', title)
|
setattr(wrapperClass, 'title', title)
|
||||||
names = self.config.attributes[wrapperClass.__name__[:-8]]
|
names = self.config.attributes[wrapperClass.__name__[:-8]]
|
||||||
|
@ -368,8 +366,8 @@ class ZopeInstaller:
|
||||||
for baseClass in klass.wrapperClass.__bases__:
|
for baseClass in klass.wrapperClass.__bases__:
|
||||||
if baseClass.__name__ == 'AbstractWrapper': continue
|
if baseClass.__name__ == 'AbstractWrapper': continue
|
||||||
for name, appyType in baseClass.__dict__.iteritems():
|
for name, appyType in baseClass.__dict__.iteritems():
|
||||||
if not isinstance(appyType, Type) or \
|
if not isinstance(appyType, gen.Type) or \
|
||||||
(isinstance(appyType, Ref) and appyType.isBack):
|
(isinstance(appyType, gen.Ref) and appyType.isBack):
|
||||||
continue # Back refs are initialised within fw refs
|
continue # Back refs are initialised within fw refs
|
||||||
appyType.init(name, baseClass, appName)
|
appyType.init(name, baseClass, appName)
|
||||||
|
|
||||||
|
|
|
@ -9,8 +9,7 @@ class Migrator:
|
||||||
self.installer = installer
|
self.installer = installer
|
||||||
|
|
||||||
def migrateTo_0_7_1(self):
|
def migrateTo_0_7_1(self):
|
||||||
'''Appy 0.7.1 has its own management of Ref fields and does not use
|
'''Appy 0.7.1 has its own management of Ref fields. So we must
|
||||||
Archetypes references and the reference catalog anymore. So we must
|
|
||||||
update data structures that store Ref info on instances.'''
|
update data structures that store Ref info on instances.'''
|
||||||
ins = self.installer
|
ins = self.installer
|
||||||
ins.info('Migrating to Appy 0.7.1...')
|
ins.info('Migrating to Appy 0.7.1...')
|
||||||
|
|
|
@ -3,7 +3,7 @@ import os, os.path, sys
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
class TestMixin:
|
class TestMixin:
|
||||||
'''This class is mixed in with any PloneTestCase.'''
|
'''This class is mixed in with any ZopeTestCase.'''
|
||||||
def createUser(self, userId, roles):
|
def createUser(self, userId, roles):
|
||||||
'''Creates a user with id p_userId with some p_roles.'''
|
'''Creates a user with id p_userId with some p_roles.'''
|
||||||
self.acl_users.addMember(userId, 'password', [], [])
|
self.acl_users.addMember(userId, 'password', [], [])
|
||||||
|
@ -59,7 +59,7 @@ class TestMixin:
|
||||||
def beforeTest(test):
|
def beforeTest(test):
|
||||||
'''Is executed before every test.'''
|
'''Is executed before every test.'''
|
||||||
g = test.globs
|
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()
|
cfg = g['tool'].o.getProductConfig()
|
||||||
g['appFolder'] = cfg.diskFolder
|
g['appFolder'] = cfg.diskFolder
|
||||||
moduleOrClassName = g['test'].name # Not used yet.
|
moduleOrClassName = g['test'].name # Not used yet.
|
||||||
|
|
|
@ -6,9 +6,9 @@ from appy.shared.data import languages
|
||||||
import appy.gen
|
import appy.gen
|
||||||
from appy.gen import Type, Search, Selection
|
from appy.gen import Type, Search, Selection
|
||||||
from appy.gen.utils import SomeObjects, sequenceTypes, getClassName
|
from appy.gen.utils import SomeObjects, sequenceTypes, getClassName
|
||||||
from appy.gen.plone25.mixins import BaseMixin
|
from appy.gen.mixins import BaseMixin
|
||||||
from appy.gen.plone25.wrappers import AbstractWrapper
|
from appy.gen.wrappers import AbstractWrapper
|
||||||
from appy.gen.plone25.descriptors import ClassDescriptor
|
from appy.gen.descriptors import ClassDescriptor
|
||||||
try:
|
try:
|
||||||
from AccessControl.ZopeSecurityPolicy import _noroles
|
from AccessControl.ZopeSecurityPolicy import _noroles
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
@ -411,7 +411,7 @@ class ToolMixin(BaseMixin):
|
||||||
|
|
||||||
def getCreateMeans(self, contentTypeOrAppyClass):
|
def getCreateMeans(self, contentTypeOrAppyClass):
|
||||||
'''Gets the different ways objects of p_contentTypeOrAppyClass (which
|
'''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
|
(via a web form, by importing external data, etc). Result is a
|
||||||
dict whose keys are strings (ie "form", "import"...) and whose
|
dict whose keys are strings (ie "form", "import"...) and whose
|
||||||
values are additional data bout the particular mean.'''
|
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'}
|
9: 'month_sep', 10: 'month_oct', 11: 'month_nov', 12: 'month_dec'}
|
||||||
def getMonthName(self, monthNumber):
|
def getMonthName(self, monthNumber):
|
||||||
'''Gets the translated month name of month numbered p_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
|
# Authentication-related methods
|
||||||
|
@ -824,7 +824,7 @@ class ToolMixin(BaseMixin):
|
||||||
|
|
||||||
if jsEnabled and not cookiesEnabled:
|
if jsEnabled and not cookiesEnabled:
|
||||||
msg = self.translate(u'You must enable cookies before you can ' \
|
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'))
|
return self.goto(urlBack, msg.encode('utf-8'))
|
||||||
# Perform the Zope-level authentication
|
# Perform the Zope-level authentication
|
||||||
login = rq.get('__ac_name', '')
|
login = rq.get('__ac_name', '')
|
||||||
|
@ -835,11 +835,10 @@ class ToolMixin(BaseMixin):
|
||||||
user = self.acl_users.validate(rq)
|
user = self.acl_users.validate(rq)
|
||||||
if self.userIsAnon():
|
if self.userIsAnon():
|
||||||
rq.RESPONSE.expireCookie('__ac', path='/')
|
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
|
logMsg = 'Authentication failed (tried with login "%s")' % login
|
||||||
else:
|
else:
|
||||||
msg = self.translate(u'Welcome! You are now logged in.',
|
msg = self.translate(u'Welcome! You are now logged in.')
|
||||||
domain='plone')
|
|
||||||
logMsg = 'User "%s" has been logged in.' % login
|
logMsg = 'User "%s" has been logged in.' % login
|
||||||
msg = msg.encode('utf-8')
|
msg = msg.encode('utf-8')
|
||||||
self.log(logMsg)
|
self.log(logMsg)
|
||||||
|
@ -865,7 +864,7 @@ class ToolMixin(BaseMixin):
|
||||||
session.invalidate()
|
session.invalidate()
|
||||||
self.log('User "%s" has been logged out.' % userId)
|
self.log('User "%s" has been logged out.' % userId)
|
||||||
# Remove user from variable "loggedUsers"
|
# 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]
|
if loggedUsers.has_key(userId): del loggedUsers[userId]
|
||||||
return self.goto(self.getApp().absolute_url())
|
return self.goto(self.getApp().absolute_url())
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,14 @@
|
||||||
'''This package contains mixin classes that are mixed in with generated classes:
|
'''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.'''
|
- mixins/ToolMixin is mixed in with the generated application Tool class.'''
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
import os, os.path, sys, types, mimetypes, urllib, cgi
|
import os, os.path, sys, types, mimetypes, urllib, cgi
|
||||||
from appy import Object
|
from appy import Object
|
||||||
import appy.gen
|
import appy.gen as gen
|
||||||
from appy.gen import Type, String, Selection, Role, No, WorkflowAnonymous, \
|
|
||||||
Transition, Permission
|
|
||||||
from appy.gen.utils import *
|
from appy.gen.utils import *
|
||||||
from appy.gen.layout import Table, defaultPageLayouts
|
from appy.gen.layout import Table, defaultPageLayouts
|
||||||
from appy.gen.descriptors import WorkflowDescriptor
|
from appy.gen.descriptors import WorkflowDescriptor, ClassDescriptor
|
||||||
from appy.gen.plone25.descriptors import ClassDescriptor
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
class BaseMixin:
|
class BaseMixin:
|
||||||
|
@ -187,8 +184,7 @@ class BaseMixin:
|
||||||
fields in the database.'''
|
fields in the database.'''
|
||||||
rq = self.REQUEST
|
rq = self.REQUEST
|
||||||
tool = self.getTool()
|
tool = self.getTool()
|
||||||
errorMessage = self.translate(
|
errorMessage = self.translate('Please correct the indicated errors.')
|
||||||
'Please correct the indicated errors.', domain='plone')
|
|
||||||
isNew = rq.get('is_new') == 'True'
|
isNew = rq.get('is_new') == 'True'
|
||||||
# If this object is created from an initiator, get info about him.
|
# If this object is created from an initiator, get info about him.
|
||||||
initiator = None
|
initiator = None
|
||||||
|
@ -209,7 +205,7 @@ class BaseMixin:
|
||||||
urlBack = tool.getSiteUrl()
|
urlBack = tool.getSiteUrl()
|
||||||
else:
|
else:
|
||||||
urlBack = self.getUrl()
|
urlBack = self.getUrl()
|
||||||
self.say(self.translate('Changes canceled.', domain='plone'))
|
self.say(self.translate('Changes canceled.'))
|
||||||
return self.goto(urlBack)
|
return self.goto(urlBack)
|
||||||
|
|
||||||
# Object for storing validation errors
|
# Object for storing validation errors
|
||||||
|
@ -245,7 +241,7 @@ class BaseMixin:
|
||||||
obj, msg = self.createOrUpdate(isNew, values, initiator, initiatorField)
|
obj, msg = self.createOrUpdate(isNew, values, initiator, initiatorField)
|
||||||
|
|
||||||
# Redirect the user to the appropriate page
|
# 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
|
# 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
|
# object like a one-shot form and has already been deleted in method
|
||||||
# onEdit), redirect to the main site page.
|
# onEdit), redirect to the main site page.
|
||||||
|
@ -711,7 +707,7 @@ class BaseMixin:
|
||||||
if not includeFake:
|
if not includeFake:
|
||||||
includeIt = mayTrigger
|
includeIt = mayTrigger
|
||||||
else:
|
else:
|
||||||
includeIt = mayTrigger or isinstance(mayTrigger, No)
|
includeIt = mayTrigger or isinstance(mayTrigger, gen.No)
|
||||||
if not includeNotShowable:
|
if not includeNotShowable:
|
||||||
includeIt = includeIt and transition.isShowable(wf, self)
|
includeIt = includeIt and transition.isShowable(wf, self)
|
||||||
if not includeIt: continue
|
if not includeIt: continue
|
||||||
|
@ -864,7 +860,7 @@ class BaseMixin:
|
||||||
# Get the initial workflow state
|
# Get the initial workflow state
|
||||||
initialState = self.State(name=False)
|
initialState = self.State(name=False)
|
||||||
# Create a Transition instance representing the initial transition.
|
# Create a Transition instance representing the initial transition.
|
||||||
initialTransition = Transition((initialState, initialState))
|
initialTransition = gen.Transition((initialState, initialState))
|
||||||
initialTransition.trigger('_init_', self, wf, '')
|
initialTransition.trigger('_init_', self, wf, '')
|
||||||
|
|
||||||
def getWorkflow(self, name=False, className=None):
|
def getWorkflow(self, name=False, className=None):
|
||||||
|
@ -875,7 +871,7 @@ class BaseMixin:
|
||||||
else:
|
else:
|
||||||
appyClass = self.getTool().getAppyClass(className)
|
appyClass = self.getTool().getAppyClass(className)
|
||||||
if hasattr(appyClass, 'workflow'): wf = appyClass.workflow
|
if hasattr(appyClass, 'workflow'): wf = appyClass.workflow
|
||||||
else: wf = WorkflowAnonymous
|
else: wf = gen.WorkflowAnonymous
|
||||||
if not name: return wf
|
if not name: return wf
|
||||||
return WorkflowDescriptor.getWorkflowName(wf)
|
return WorkflowDescriptor.getWorkflowName(wf)
|
||||||
|
|
||||||
|
|
101
gen/model.py
101
gen/model.py
|
@ -1,14 +1,10 @@
|
||||||
'''This file contains basic classes that will be added into any user
|
'''This file contains basic classes that will be added into any user
|
||||||
application for creating the basic structure of the application "Tool" which
|
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
|
is the set of web pages used for configuring the application.'''
|
||||||
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.)'''
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
import types
|
import types
|
||||||
from appy.gen import *
|
import appy.gen as gen
|
||||||
Grp=Group # Avoid name clash between appy.gen.Group and class Group below
|
|
||||||
|
|
||||||
# Prototypical instances of every type -----------------------------------------
|
# Prototypical instances of every type -----------------------------------------
|
||||||
class Protos:
|
class Protos:
|
||||||
|
@ -73,7 +69,7 @@ class ModelClass:
|
||||||
value = appyType.getInputLayouts()
|
value = appyType.getInputLayouts()
|
||||||
elif isinstance(value, basestring):
|
elif isinstance(value, basestring):
|
||||||
value = '"%s"' % value
|
value = '"%s"' % value
|
||||||
elif isinstance(value, Ref):
|
elif isinstance(value, gen.Ref):
|
||||||
if not value.isBack: continue
|
if not value.isBack: continue
|
||||||
value = klass._appy_getTypeBody(value, wrapperName)
|
value = klass._appy_getTypeBody(value, wrapperName)
|
||||||
elif type(value) == type(ModelClass):
|
elif type(value) == type(ModelClass):
|
||||||
|
@ -82,11 +78,11 @@ class ModelClass:
|
||||||
value = value.__name__
|
value = value.__name__
|
||||||
else:
|
else:
|
||||||
value = '%s.%s' % (moduleName, value.__name__)
|
value = '%s.%s' % (moduleName, value.__name__)
|
||||||
elif isinstance(value, Selection):
|
elif isinstance(value, gen.Selection):
|
||||||
value = 'Selection("%s")' % value.methodName
|
value = 'Selection("%s")' % value.methodName
|
||||||
elif isinstance(value, Grp):
|
elif isinstance(value, gen.Group):
|
||||||
value = 'Grp("%s")' % value.name
|
value = 'Grp("%s")' % value.name
|
||||||
elif isinstance(value, Page):
|
elif isinstance(value, gen.Page):
|
||||||
value = 'pages["%s"]' % value.name
|
value = 'pages["%s"]' % value.name
|
||||||
elif callable(value):
|
elif callable(value):
|
||||||
value = '%s.%s' % (wrapperName, value.__name__)
|
value = '%s.%s' % (wrapperName, value.__name__)
|
||||||
|
@ -135,20 +131,22 @@ class User(ModelClass):
|
||||||
_appy_attributes = ['title', 'name', 'firstName', 'login', 'password1',
|
_appy_attributes = ['title', 'name', 'firstName', 'login', 'password1',
|
||||||
'password2', 'roles']
|
'password2', 'roles']
|
||||||
# All methods defined below are fake. Real versions are in the wrapper.
|
# 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}
|
gm = {'group': 'main', 'multiplicity': (1,1), 'width': 25}
|
||||||
name = String(**gm)
|
name = gen.String(**gm)
|
||||||
firstName = String(**gm)
|
firstName = gen.String(**gm)
|
||||||
def showLogin(self): pass
|
def showLogin(self): pass
|
||||||
def validateLogin(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 showPassword(self): pass
|
||||||
def validatePassword(self): pass
|
def validatePassword(self): pass
|
||||||
password1 = String(format=String.PASSWORD, show=showPassword,
|
password1 = gen.String(format=gen.String.PASSWORD, show=showPassword,
|
||||||
validator=validatePassword, **gm)
|
validator=validatePassword, **gm)
|
||||||
password2 = String(format=String.PASSWORD, show=showPassword, **gm)
|
password2 = gen.String(format=gen.String.PASSWORD, show=showPassword, **gm)
|
||||||
gm['multiplicity'] = (0, None)
|
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 --------------------------------------------------------------
|
# The Group class --------------------------------------------------------------
|
||||||
class Group(ModelClass):
|
class Group(ModelClass):
|
||||||
|
@ -156,25 +154,25 @@ class Group(ModelClass):
|
||||||
_appy_attributes = ['title', 'login', 'roles', 'users']
|
_appy_attributes = ['title', 'login', 'roles', 'users']
|
||||||
# All methods defined below are fake. Real versions are in the wrapper.
|
# All methods defined below are fake. Real versions are in the wrapper.
|
||||||
m = {'group': 'main', 'width': 25, 'indexed': True}
|
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 showLogin(self): pass
|
||||||
def validateLogin(self): pass
|
def validateLogin(self): pass
|
||||||
login = String(show=showLogin, validator=validateLogin,
|
login = gen.String(show=showLogin, validator=validateLogin,
|
||||||
multiplicity=(1,1), **m)
|
multiplicity=(1,1), **m)
|
||||||
roles = String(validator=Selection('getGrantableRoles'),
|
roles = gen.String(validator=gen.Selection('getGrantableRoles'),
|
||||||
multiplicity=(0,None), **m)
|
multiplicity=(0,None), **m)
|
||||||
users = Ref(User, multiplicity=(0,None), add=False, link=True,
|
users = gen.Ref(User, multiplicity=(0,None), add=False, link=True,
|
||||||
back=Ref(attribute='groups', show=True),
|
back=gen.Ref(attribute='groups', show=True),
|
||||||
showHeaders=True, shownInfo=('title', 'login'))
|
showHeaders=True, shownInfo=('title', 'login'))
|
||||||
|
|
||||||
# The Translation class --------------------------------------------------------
|
# The Translation class --------------------------------------------------------
|
||||||
class Translation(ModelClass):
|
class Translation(ModelClass):
|
||||||
_appy_attributes = ['po', 'title']
|
_appy_attributes = ['po', 'title']
|
||||||
# All methods defined below are fake. Real versions are in the wrapper.
|
# All methods defined below are fake. Real versions are in the wrapper.
|
||||||
def getPoFile(self): pass
|
def getPoFile(self): pass
|
||||||
po = Action(action=getPoFile, page=Page('actions', show='view'),
|
po = gen.Action(action=getPoFile, page=gen.Page('actions', show='view'),
|
||||||
result='filetmp')
|
result='filetmp')
|
||||||
title = String(show=False, indexed=True)
|
title = gen.String(show=False, indexed=True)
|
||||||
def label(self): pass
|
def label(self): pass
|
||||||
def show(self, name): pass
|
def show(self, name): pass
|
||||||
|
|
||||||
|
@ -195,30 +193,31 @@ class Tool(ModelClass):
|
||||||
|
|
||||||
# Tool attributes
|
# Tool attributes
|
||||||
def validPythonWithUno(self, value): pass # Real method in the wrapper
|
def validPythonWithUno(self, value): pass # Real method in the wrapper
|
||||||
unoEnabledPython = String(group="connectionToOpenOffice",
|
unoEnabledPython = gen.String(group="connectionToOpenOffice",
|
||||||
validator=validPythonWithUno)
|
validator=validPythonWithUno)
|
||||||
openOfficePort = Integer(default=2002, group="connectionToOpenOffice")
|
openOfficePort = gen.Integer(default=2002, group="connectionToOpenOffice")
|
||||||
numberOfResultsPerPage = Integer(default=30, show=False)
|
numberOfResultsPerPage = gen.Integer(default=30, show=False)
|
||||||
listBoxesMaximumWidth = Integer(default=100, show=False)
|
listBoxesMaximumWidth = gen.Integer(default=100, show=False)
|
||||||
appyVersion = String(show=False, layouts='f')
|
appyVersion = gen.String(show=False, layouts='f')
|
||||||
def refreshSecurity(self): pass # Real method in the wrapper
|
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).
|
# Ref(User) will maybe be transformed into Ref(CustomUserClass).
|
||||||
users = Ref(User, multiplicity=(0,None), add=True, link=False,
|
users = gen.Ref(User, multiplicity=(0,None), add=True, link=False,
|
||||||
back=Ref(attribute='toTool', show=False),
|
back=gen.Ref(attribute='toTool', show=False),
|
||||||
page=Page('users', show='view'),
|
page=gen.Page('users', show='view'),
|
||||||
queryable=True, queryFields=('title', 'login'),
|
queryable=True, queryFields=('title', 'login'),
|
||||||
showHeaders=True, shownInfo=('title', 'login', 'roles'))
|
showHeaders=True, shownInfo=('title', 'login', 'roles'))
|
||||||
groups = Ref(Group, multiplicity=(0,None), add=True, link=False,
|
groups = gen.Ref(Group, multiplicity=(0,None), add=True, link=False,
|
||||||
back=Ref(attribute='toTool2', show=False),
|
back=gen.Ref(attribute='toTool2', show=False),
|
||||||
page=Page('groups', show='view'),
|
page=gen.Page('groups', show='view'),
|
||||||
queryable=True, queryFields=('title', 'login'),
|
queryable=True, queryFields=('title', 'login'),
|
||||||
showHeaders=True, shownInfo=('title', 'login', 'roles'))
|
showHeaders=True, shownInfo=('title', 'login', 'roles'))
|
||||||
translations = Ref(Translation, multiplicity=(0,None),add=False,link=False,
|
translations = gen.Ref(Translation, multiplicity=(0,None), add=False,
|
||||||
back=Ref(attribute='trToTool', show=False), show='view',
|
link=False, show='view',
|
||||||
page=Page('translations', show='view'))
|
back=gen.Ref(attribute='trToTool', show=False),
|
||||||
enableNotifications = Boolean(default=True,
|
page=gen.Page('translations', show='view'))
|
||||||
page=Page('notifications', show=False))
|
enableNotifications = gen.Boolean(default=True,
|
||||||
|
page=gen.Page('notifications', show=False))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _appy_clean(klass):
|
def _appy_clean(klass):
|
||||||
|
|
|
@ -41,8 +41,8 @@ def sendMail(obj, transition, transitionName, workflow):
|
||||||
'''Sends mail about p_transition that has been triggered on p_obj that is
|
'''Sends mail about p_transition that has been triggered on p_obj that is
|
||||||
controlled by p_workflow.'''
|
controlled by p_workflow.'''
|
||||||
wfName = WorkflowDescriptor.getWorkflowName(workflow.__class__)
|
wfName = WorkflowDescriptor.getWorkflowName(workflow.__class__)
|
||||||
ploneObj = obj.o
|
zopeObj = obj.o
|
||||||
portal = ploneObj.portal_url.getPortalObject()
|
tool = zopeObj.getTool()
|
||||||
mailInfo = transition.notify(workflow, obj)
|
mailInfo = transition.notify(workflow, obj)
|
||||||
if not mailInfo[0]: return # Send a mail to nobody.
|
if not mailInfo[0]: return # Send a mail to nobody.
|
||||||
# mailInfo may be one of the following:
|
# 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.
|
# address or one role) or sequences of strings.
|
||||||
# Determine mail subject and body.
|
# Determine mail subject and body.
|
||||||
if len(mailInfo) <= 2:
|
if len(mailInfo) <= 2:
|
||||||
# The user didn't mention mail body and subject. We will use
|
# The user didn't mention mail body and subject. We will use those
|
||||||
# those defined from i18n labels.
|
# defined from i18n labels.
|
||||||
wfHistory = ploneObj.getWorkflowHistory()
|
wfHistory = zopeObj.getHistory()
|
||||||
labelPrefix = '%s_%s' % (wfName, transitionName)
|
labelPrefix = '%s_%s' % (wfName, transitionName)
|
||||||
tName = obj.translate(labelPrefix)
|
tName = obj.translate(labelPrefix)
|
||||||
keys = {'siteUrl': portal.absolute_url(),
|
keys = {'siteUrl': tool.getPath('/').absolute_url(),
|
||||||
'siteTitle': portal.Title(),
|
'siteTitle': tool.getAppName(),
|
||||||
'objectUrl': ploneObj.absolute_url(),
|
'objectUrl': zopeObj.absolute_url(),
|
||||||
'objectTitle': ploneObj.Title(),
|
'objectTitle': zopeObj.Title(),
|
||||||
'transitionName': tName,
|
'transitionName': tName,
|
||||||
'transitionComment': wfHistory[0]['comments']}
|
'transitionComment': wfHistory[0]['comments']}
|
||||||
mailSubject = obj.translate(labelPrefix + '_mail_subject', keys)
|
mailSubject = obj.translate(labelPrefix + '_mail_subject', keys)
|
||||||
|
|
|
@ -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 '<text:p><text:bookmark text:name="%s"/>%s</text:p>' % \
|
|
||||||
(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
|
|
||||||
# ------------------------------------------------------------------------------
|
|
Binary file not shown.
|
@ -4,8 +4,8 @@ from OFS.Folder import Folder
|
||||||
from appy.gen.utils import createObject
|
from appy.gen.utils import createObject
|
||||||
from AccessControl import ClassSecurityInfo
|
from AccessControl import ClassSecurityInfo
|
||||||
import Products.<!applicationName!>.config as cfg
|
import Products.<!applicationName!>.config as cfg
|
||||||
from appy.gen.plone25.mixins import BaseMixin
|
from appy.gen.mixins import BaseMixin
|
||||||
from appy.gen.plone25.mixins.ToolMixin import ToolMixin
|
from appy.gen.mixins.ToolMixin import ToolMixin
|
||||||
from wrappers import <!genClassName!>_Wrapper as Wrapper
|
from wrappers import <!genClassName!>_Wrapper as Wrapper
|
||||||
|
|
||||||
def manage_add<!genClassName!>(self, id, title='', REQUEST=None):
|
def manage_add<!genClassName!>(self, id, title='', REQUEST=None):
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -1,7 +1,7 @@
|
||||||
<!codeHeader!>
|
<!codeHeader!>
|
||||||
# Test coverage-related stuff --------------------------------------------------
|
# Test coverage-related stuff --------------------------------------------------
|
||||||
import sys
|
import sys
|
||||||
from appy.gen.plone25.mixins.TestMixin import TestMixin
|
from appy.gen.mixins.TestMixin import TestMixin
|
||||||
covFolder = TestMixin.getCovFolder()
|
covFolder = TestMixin.getCovFolder()
|
||||||
# The previous method checks in sys.argv whether Zope was lauched for performing
|
# The previous method checks in sys.argv whether Zope was lauched for performing
|
||||||
# coverage tests or not.
|
# coverage tests or not.
|
||||||
|
@ -26,7 +26,7 @@ def countTest():
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
import config
|
import config
|
||||||
from appy.gen.plone25.installer import ZopeInstaller
|
from appy.gen.installer import ZopeInstaller
|
||||||
|
|
||||||
# Zope-level installation of the generated product. ----------------------------
|
# Zope-level installation of the generated product. ----------------------------
|
||||||
def initialize(context):
|
def initialize(context):
|
||||||
|
|
|
@ -5,9 +5,7 @@ import wrappers
|
||||||
<!imports!>
|
<!imports!>
|
||||||
|
|
||||||
# The following imports are here for allowing mixin classes to access those
|
# The following imports are here for allowing mixin classes to access those
|
||||||
# elements without being statically dependent on Plone/Zope packages. Indeed,
|
# elements without being statically dependent on Zope packages.
|
||||||
# every Archetype instance has a method "getProductConfig" that returns this
|
|
||||||
# module.
|
|
||||||
from persistent.list import PersistentList
|
from persistent.list import PersistentList
|
||||||
from zExceptions import BadRequest
|
from zExceptions import BadRequest
|
||||||
from ZPublisher.HTTPRequest import BaseRequest
|
from ZPublisher.HTTPRequest import BaseRequest
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
<configure xmlns="http://namespaces.zope.org/zope"
|
|
||||||
xmlns:browser="http://namespaces.zope.org/browser"
|
|
||||||
xmlns:five="http://namespaces.zope.org/five"
|
|
||||||
i18n_domain="<!applicationName!>">
|
|
||||||
<!deprecated!>
|
|
||||||
</configure>
|
|
|
@ -3,17 +3,13 @@
|
||||||
from unittest import TestSuite
|
from unittest import TestSuite
|
||||||
from Testing import ZopeTestCase
|
from Testing import ZopeTestCase
|
||||||
from Testing.ZopeTestCase import ZopeDocTestSuite
|
from Testing.ZopeTestCase import ZopeDocTestSuite
|
||||||
from Products.PloneTestCase import PloneTestCase
|
from appy.gen.mixins.TestMixin import TestMixin, beforeTest, afterTest
|
||||||
from appy.gen.plone25.mixins.TestMixin import TestMixin, beforeTest, afterTest
|
|
||||||
<!imports!>
|
<!imports!>
|
||||||
|
|
||||||
# Initialize Zope & Plone test systems -----------------------------------------
|
# Initialize the Zope test system ----------------------------------------------
|
||||||
ZopeTestCase.installProduct('PloneLanguageTool')
|
|
||||||
ZopeTestCase.installProduct('<!applicationName!>')
|
ZopeTestCase.installProduct('<!applicationName!>')
|
||||||
PloneTestCase.setupPloneSite(products=['PloneLanguageTool',
|
|
||||||
'<!applicationName!>'])
|
|
||||||
|
|
||||||
class Test(PloneTestCase.PloneTestCase, TestMixin):
|
class Test(ZopeTestCase.ZopeTestCase, TestMixin):
|
||||||
'''Base test class for <!applicationName!> test cases.'''
|
'''Base test class for <!applicationName!> test cases.'''
|
||||||
|
|
||||||
# Data needed for defining the tests -------------------------------------------
|
# Data needed for defining the tests -------------------------------------------
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
from appy.gen import *
|
from appy.gen import *
|
||||||
Grp = Group # Avoid name clashes with the Group class below and appy.gen.Group
|
Grp = Group # Avoid name clashes with the Group class below and appy.gen.Group
|
||||||
from appy.gen.plone25.wrappers import AbstractWrapper
|
from appy.gen.wrappers import AbstractWrapper
|
||||||
from appy.gen.plone25.wrappers.ToolWrapper import ToolWrapper as WTool
|
from appy.gen.wrappers.ToolWrapper import ToolWrapper as WTool
|
||||||
from appy.gen.plone25.wrappers.UserWrapper import UserWrapper as WUser
|
from appy.gen.wrappers.UserWrapper import UserWrapper as WUser
|
||||||
from appy.gen.plone25.wrappers.GroupWrapper import GroupWrapper as WGroup
|
from appy.gen.wrappers.GroupWrapper import GroupWrapper as WGroup
|
||||||
from appy.gen.plone25.wrappers.TranslationWrapper import TranslationWrapper as WT
|
from appy.gen.wrappers.TranslationWrapper import TranslationWrapper as WT
|
||||||
from Globals import InitializeClass
|
from Globals import InitializeClass
|
||||||
from AccessControl import ClassSecurityInfo
|
from AccessControl import ClassSecurityInfo
|
||||||
tfw = {"edit":"f","cell":"f","view":"f"} # Layout for Translation fields
|
tfw = {"edit":"f","cell":"f","view":"f"} # Layout for Translation fields
|
||||||
|
|
|
@ -1,23 +1,9 @@
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
|
<tal:main define="tool context/config">
|
||||||
lang="en" xmlns:tal="http://xml.zope.org/namespaces/tal"
|
<html metal:use-macro="context/ui/template/macros/main">
|
||||||
xmlns:metal="http://xml.zope.org/namespaces/metal"
|
<metal:fill fill-slot="content"
|
||||||
xmlns:i18n="http://xml.zope.org/namespaces/i18n"
|
tal:define="className request/className;
|
||||||
metal:use-macro="here/main_template/macros/master">
|
importElems python: tool.getImportElements(className);
|
||||||
|
global allAreImported python:True">
|
||||||
<tal:comment replace="nothing">Disable standard Plone green tabs</tal:comment>
|
|
||||||
<div metal:fill-slot="top_slot">
|
|
||||||
<metal:block metal:use-macro="here/global_defines/macros/defines" />
|
|
||||||
<div tal:define="dummy python:request.set('disable_border', 1)" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<tal:comment replace="nothing">Fill main slot of Plone main_template</tal:comment>
|
|
||||||
<body>
|
|
||||||
<metal:fill fill-slot="main"
|
|
||||||
tal:define="appFolder context/getParentNode;
|
|
||||||
className request/className;
|
|
||||||
tool python: portal.get('portal_%s' % appFolder.id.lower());
|
|
||||||
importElems python: tool.getImportElements(className);
|
|
||||||
global allAreImported python:True">
|
|
||||||
|
|
||||||
<div metal:use-macro="context/ui/page/macros/prologue"/>
|
<div metal:use-macro="context/ui/page/macros/prologue"/>
|
||||||
<script language="javascript">
|
<script language="javascript">
|
||||||
|
@ -68,7 +54,7 @@
|
||||||
</script>
|
</script>
|
||||||
<tal:comment replace="nothing">Form for importing several meetings at once.</tal:comment>
|
<tal:comment replace="nothing">Form for importing several meetings at once.</tal:comment>
|
||||||
<form name="importElements"
|
<form name="importElements"
|
||||||
tal:attributes="action python: appFolder.absolute_url()+'/do'" method="post">
|
tal:attributes="action python: tool.absolute_url()+'/do'" method="post">
|
||||||
<input type="hidden" name="action" value="ImportObjects"/>
|
<input type="hidden" name="action" value="ImportObjects"/>
|
||||||
<input type="hidden" name="className" tal:attributes="value className"/>
|
<input type="hidden" name="className" tal:attributes="value className"/>
|
||||||
<input type="hidden" name="importPath" value=""/>
|
<input type="hidden" name="importPath" value=""/>
|
||||||
|
@ -120,6 +106,6 @@
|
||||||
tal:condition="python: importElems[1] and not allAreImported"
|
tal:condition="python: importElems[1] and not allAreImported"
|
||||||
tal:attributes="value python:tool.translate('import_many')"/>
|
tal:attributes="value python:tool.translate('import_many')"/>
|
||||||
</p>
|
</p>
|
||||||
</metal:fill>
|
</metal:fill>
|
||||||
</body>
|
|
||||||
</html>
|
</html>
|
||||||
|
</tal:main>
|
||||||
|
|
|
@ -70,11 +70,11 @@
|
||||||
<tal:history condition="objs">
|
<tal:history condition="objs">
|
||||||
<metal:nav use-macro="context/ui/navigate/macros/appyNavigate"/>
|
<metal:nav use-macro="context/ui/navigate/macros/appyNavigate"/>
|
||||||
<table width="100%" class="history">
|
<table width="100%" class="history">
|
||||||
<tr i18n:domain="plone">
|
<tr>
|
||||||
<th align="left" i18n:translate="listingheader_action">Action</th>
|
<th align="left">Action</th>
|
||||||
<th align="left" i18n:translate="listingheader_performed_by">By</th>
|
<th align="left">By</th>
|
||||||
<th align="left" i18n:translate="listingheader_date_and_time">Date</th>
|
<th align="left">Date</th>
|
||||||
<th align="left" i18n:translate="listingheader_comment">Comment</th>
|
<th align="left">Comment</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tal:event repeat="event objs">
|
<tal:event repeat="event objs">
|
||||||
<tr tal:define="odd repeat/event/odd;
|
<tr tal:define="odd repeat/event/odd;
|
||||||
|
@ -202,7 +202,7 @@
|
||||||
<img align="left" style="cursor:pointer" onClick="toggleCookie('appyHistory')"
|
<img align="left" style="cursor:pointer" onClick="toggleCookie('appyHistory')"
|
||||||
tal:attributes="src python:test(historyExpanded, 'ui/collapse.gif', 'ui/expand.gif');"
|
tal:attributes="src python:test(historyExpanded, 'ui/collapse.gif', 'ui/expand.gif');"
|
||||||
id="appyHistory_img"/>
|
id="appyHistory_img"/>
|
||||||
<span i18n:translate="label_history" i18n:domain="plone" class="historyLabel">History</span> ||
|
<span>History</span> ||
|
||||||
</tal:accessHistory>
|
</tal:accessHistory>
|
||||||
|
|
||||||
<tal:comment replace="nothing">Show document creator</tal:comment>
|
<tal:comment replace="nothing">Show document creator</tal:comment>
|
||||||
|
@ -263,32 +263,28 @@
|
||||||
<tal:previous condition="python: previousPage and pageInfo['showPrevious']">
|
<tal:previous condition="python: previousPage and pageInfo['showPrevious']">
|
||||||
<tal:button condition="isEdit">
|
<tal:button condition="isEdit">
|
||||||
<input type="image" class="imageInput" style="cursor:pointer" name="buttonPrevious"
|
<input type="image" class="imageInput" style="cursor:pointer" name="buttonPrevious"
|
||||||
title="label_previous" i18n:attributes="title" i18n:domain="plone"
|
title="Previous" tal:attributes="src string:$appUrl/ui/previous.png"/>
|
||||||
tal:attributes="src string:$appUrl/ui/previous.png"/>
|
|
||||||
<input type="hidden" name="previousPage" tal:attributes="value previousPage"/>
|
<input type="hidden" name="previousPage" tal:attributes="value previousPage"/>
|
||||||
</tal:button>
|
</tal:button>
|
||||||
<tal:link condition="not: isEdit">
|
<tal:link condition="not: isEdit">
|
||||||
<a tal:attributes="href python: contextObj.getUrl(page=previousPage)">
|
<a tal:attributes="href python: contextObj.getUrl(page=previousPage)">
|
||||||
<img tal:attributes="src string:$appUrl/ui/previous.png"
|
<img tal:attributes="src string:$appUrl/ui/previous.png" title="Previous"/>
|
||||||
title="label_previous" i18n:attributes="title" i18n:domain="plone"/>
|
|
||||||
</a>
|
</a>
|
||||||
</tal:link>
|
</tal:link>
|
||||||
</tal:previous>
|
</tal:previous>
|
||||||
|
|
||||||
<tal:save condition="python: isEdit and pageInfo['showSave']">
|
<tal:save condition="python: isEdit and pageInfo['showSave']">
|
||||||
<input type="image" class="imageInput" style="cursor:pointer" name="buttonOk"
|
<input type="image" class="imageInput" style="cursor:pointer" name="buttonOk"
|
||||||
title="label_save" i18n:attributes="title" i18n:domain="plone"
|
title="Save" tal:attributes="src string:$appUrl/ui/save.png"/>
|
||||||
tal:attributes="src string:$appUrl/ui/save.png"/>
|
|
||||||
</tal:save>
|
</tal:save>
|
||||||
|
|
||||||
<tal:cancel condition="python: isEdit and pageInfo['showCancel']">
|
<tal:cancel condition="python: isEdit and pageInfo['showCancel']">
|
||||||
<input type="image" class="imageInput" style="cursor:pointer" name="buttonCancel"
|
<input type="image" class="imageInput" style="cursor:pointer" name="buttonCancel"
|
||||||
title="label_cancel" i18n:attributes="title" i18n:domain="plone"
|
title="Cancel" tal:attributes="src string:$appUrl/ui/cancel.png"/>
|
||||||
tal:attributes="src string:$appUrl/ui/cancel.png"/>
|
|
||||||
</tal:cancel>
|
</tal:cancel>
|
||||||
|
|
||||||
<tal:edit condition="python: not isEdit and pageInfo['showOnEdit']">
|
<tal:edit condition="python: not isEdit and pageInfo['showOnEdit']">
|
||||||
<img title="Edit" i18n:domain="plone" i18n:attributes="title" style="cursor:pointer"
|
<img title="Edit" style="cursor:pointer"
|
||||||
tal:attributes="onClick python: 'href: window.location=\'%s\'' % contextObj.getUrl(mode='edit', page=page);
|
tal:attributes="onClick python: 'href: window.location=\'%s\'' % contextObj.getUrl(mode='edit', page=page);
|
||||||
src string: $appUrl/ui/editBig.png"
|
src string: $appUrl/ui/editBig.png"
|
||||||
tal:condition="python: contextObj.allows('Modify portal content')"/>
|
tal:condition="python: contextObj.allows('Modify portal content')"/>
|
||||||
|
@ -303,14 +299,12 @@
|
||||||
<tal:next condition="python: nextPage and pageInfo['showNext']">
|
<tal:next condition="python: nextPage and pageInfo['showNext']">
|
||||||
<tal:button condition="isEdit">
|
<tal:button condition="isEdit">
|
||||||
<input type="image" class="imageInput" style="cursor:pointer" name="buttonNext"
|
<input type="image" class="imageInput" style="cursor:pointer" name="buttonNext"
|
||||||
title="label_next" i18n:attributes="title" i18n:domain="plone"
|
title="Next" tal:attributes="src string:$appUrl/ui/next.png"/>
|
||||||
tal:attributes="src string:$appUrl/ui/next.png"/>
|
|
||||||
<input type="hidden" name="nextPage" tal:attributes="value nextPage"/>
|
<input type="hidden" name="nextPage" tal:attributes="value nextPage"/>
|
||||||
</tal:button>
|
</tal:button>
|
||||||
<tal:link condition="not: isEdit">
|
<tal:link condition="not: isEdit">
|
||||||
<a tal:attributes="href python: contextObj.getUrl(page=nextPage)">
|
<a tal:attributes="href python: contextObj.getUrl(page=nextPage)">
|
||||||
<img tal:attributes="src string:$appUrl/ui/next.png"
|
<img tal:attributes="src string:$appUrl/ui/next.png" title="Next"/>
|
||||||
title="label_next" i18n:attributes="title" i18n:domain="plone"/>
|
|
||||||
</a>
|
</a>
|
||||||
</tal:link>
|
</tal:link>
|
||||||
</tal:next>
|
</tal:next>
|
||||||
|
|
|
@ -117,7 +117,7 @@
|
||||||
tal:content="structure python: _(label)"></a>
|
tal:content="structure python: _(label)"></a>
|
||||||
</td>
|
</td>
|
||||||
<td align="right">
|
<td align="right">
|
||||||
<img title="Edit" i18n:domain="plone" i18n:attributes="title" style="cursor:pointer"
|
<img title="Edit" style="cursor:pointer"
|
||||||
tal:attributes="onClick python: 'href: window.location=\'%s\'' % contextObj.getUrl(mode='edit', page=pageName);
|
tal:attributes="onClick python: 'href: window.location=\'%s\'' % contextObj.getUrl(mode='edit', page=pageName);
|
||||||
src string: $appUrl/ui/edit.gif"
|
src string: $appUrl/ui/edit.gif"
|
||||||
tal:condition="python: contextObj.allows('Modify portal content') and phase['pagesInfo'][pageName]['showOnEdit']"/>
|
tal:condition="python: contextObj.allows('Modify portal content') and phase['pagesInfo'][pageName]['showOnEdit']"/>
|
||||||
|
@ -136,7 +136,7 @@
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td align="right">
|
<td align="right">
|
||||||
<img title="Edit" i18n:domain="plone" i18n:attributes="title" style="cursor:pointer"
|
<img title="Edit" style="cursor:pointer"
|
||||||
tal:attributes="onClick python: 'href: window.location=\'%s\'' % contextObj.getUrl(mode='edit', page=aPage);
|
tal:attributes="onClick python: 'href: window.location=\'%s\'' % contextObj.getUrl(mode='edit', page=aPage);
|
||||||
src string: $appUrl/ui/edit.gif"
|
src string: $appUrl/ui/edit.gif"
|
||||||
tal:condition="python: user.has_permission('Modify portal content', contextObj) and phase['pagesInfo'][aPage]['showOnEdit']"/>
|
tal:condition="python: user.has_permission('Modify portal content', contextObj) and phase['pagesInfo'][aPage]['showOnEdit']"/>
|
||||||
|
|
|
@ -125,13 +125,12 @@
|
||||||
<a tal:define="navInfo python:'search.%s.%s.%d.%d' % (className, searchName, repeat['obj'].number()+startNumber, totalNumber);"
|
<a tal:define="navInfo python:'search.%s.%s.%d.%d' % (className, searchName, repeat['obj'].number()+startNumber, totalNumber);"
|
||||||
tal:attributes="href python: obj.getUrl(mode='edit', page='main', nav=navInfo)"
|
tal:attributes="href python: obj.getUrl(mode='edit', page='main', nav=navInfo)"
|
||||||
tal:condition="python: obj.allows('Modify portal content')">
|
tal:condition="python: obj.allows('Modify portal content')">
|
||||||
<img title="Edit" i18n:domain="plone" i18n:attributes="title"
|
<img title="Edit" tal:attributes="src string: $appUrl/ui/edit.gif"/>
|
||||||
tal:attributes="src string: $appUrl/ui/edit.gif"/>
|
|
||||||
</a></td>
|
</a></td>
|
||||||
<tal:comment replace="nothing">Delete the element</tal:comment>
|
<tal:comment replace="nothing">Delete the element</tal:comment>
|
||||||
<td>
|
<td>
|
||||||
<img tal:condition="python: obj.allows('Delete objects') and obj.mayDelete()"
|
<img tal:condition="python: obj.allows('Delete objects') and obj.mayDelete()"
|
||||||
title="Delete" i18n:domain="plone" i18n:attributes="title" style="cursor:pointer"
|
title="Delete" style="cursor:pointer"
|
||||||
tal:attributes="src string: $appUrl/ui/delete.png;
|
tal:attributes="src string: $appUrl/ui/delete.png;
|
||||||
onClick python:'onDeleteObject(\'%s\')' % obj.UID()"/>
|
onClick python:'onDeleteObject(\'%s\')' % obj.UID()"/>
|
||||||
</td>
|
</td>
|
||||||
|
|
|
@ -14,10 +14,7 @@
|
||||||
onClick python: 'askConfirm(\'form\', \'%s\', "%s")' % (formId, labelConfirm)"/>
|
onClick python: 'askConfirm(\'form\', \'%s\', "%s")' % (formId, labelConfirm)"/>
|
||||||
</tal:confirm>
|
</tal:confirm>
|
||||||
<input type="submit" name="do" tal:condition="not: widget/confirm"
|
<input type="submit" name="do" tal:condition="not: widget/confirm"
|
||||||
tal:attributes="value label" onClick="javascript:;"/>
|
tal:attributes="value label"/>
|
||||||
<tal:comment replace="nothing">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:comment>
|
|
||||||
</form>
|
</form>
|
||||||
</metal:view>
|
</metal:view>
|
||||||
|
|
||||||
|
|
|
@ -29,11 +29,11 @@
|
||||||
<label tal:attributes="for widgetName" tal:content="python: tool.translate(widget['labelId'])"></label><br>
|
<label tal:attributes="for widgetName" tal:content="python: tool.translate(widget['labelId'])"></label><br>
|
||||||
<tal:yes define="valueId python:'%s_yes' % name">
|
<tal:yes define="valueId python:'%s_yes' % name">
|
||||||
<input type="radio" value="True" tal:attributes="name typedWidget; id valueId"/>
|
<input type="radio" value="True" tal:attributes="name typedWidget; id valueId"/>
|
||||||
<label tal:attributes="for valueId" i18n:translate="yes" i18n:domain="plone"></label>
|
<label tal:attributes="for valueId">Yes</label>
|
||||||
</tal:yes>
|
</tal:yes>
|
||||||
<tal:no define="valueId python:'%s_no' % name">
|
<tal:no define="valueId python:'%s_no' % name">
|
||||||
<input type="radio" value="False" tal:attributes="name typedWidget; id valueId"/>
|
<input type="radio" value="False" tal:attributes="name typedWidget; id valueId"/>
|
||||||
<label tal:attributes="for valueId" i18n:translate="no" i18n:domain="plone"></label>
|
<label tal:attributes="for valueId">No</label>
|
||||||
</tal:no>
|
</tal:no>
|
||||||
<tal:whatever define="valueId python:'%s_whatever' % name">
|
<tal:whatever define="valueId python:'%s_whatever' % name">
|
||||||
<input type="radio" value="" tal:attributes="name typedWidget; id valueId" checked="checked"/>
|
<input type="radio" value="" tal:attributes="name typedWidget; id valueId" checked="checked"/>
|
||||||
|
|
|
@ -24,14 +24,13 @@
|
||||||
<metal:call use-macro="app/ui/widgets/file/macros/view"/><br/>
|
<metal:call use-macro="app/ui/widgets/file/macros/view"/><br/>
|
||||||
</tal:showFile>
|
</tal:showFile>
|
||||||
<tal:editButtons condition="not: empty">
|
<tal:editButtons condition="not: empty">
|
||||||
<tal:comment replace="nothing">Keep the file untouched.</tal:comment>
|
<tal:comment replace="nothing">Keep the file unchanged.</tal:comment>
|
||||||
<input type="radio" value="nochange"
|
<input type="radio" value="nochange"
|
||||||
tal:attributes="checked python:test(info['size']!=0, 'checked', None);
|
tal:attributes="checked python:test(info['size']!=0, 'checked', None);
|
||||||
name string:${name}_delete;
|
name string:${name}_delete;
|
||||||
id string:${name}_nochange;
|
id string:${name}_nochange;
|
||||||
onclick string:document.getElementById('${name}_file').disabled=true;"/>
|
onclick string:document.getElementById('${name}_file').disabled=true;"/>
|
||||||
<label tal:attributes="for string:${name}_nochange"
|
<label tal:attributes="for string:${name}_nochange">Keep the file unchanged</label>
|
||||||
i18n:translate="nochange_file" i18n:domain="plone">Keep the file unchanged</label>
|
|
||||||
<br/>
|
<br/>
|
||||||
<tal:comment replace="nothing">Delete the file.</tal:comment>
|
<tal:comment replace="nothing">Delete the file.</tal:comment>
|
||||||
<tal:delete condition="not: widget/required">
|
<tal:delete condition="not: widget/required">
|
||||||
|
@ -39,8 +38,7 @@
|
||||||
tal:attributes="name string:${name}_delete;
|
tal:attributes="name string:${name}_delete;
|
||||||
id string:${name}_delete;
|
id string:${name}_delete;
|
||||||
onclick string:document.getElementById('${name}_file').disabled=true;"/>
|
onclick string:document.getElementById('${name}_file').disabled=true;"/>
|
||||||
<label tal:attributes="for string:${name}_delete"
|
<label tal:attributes="for string:${name}_delete">Delete the file</label>
|
||||||
i18n:translate="delete_file" i18n:domain="plone">Delete the file</label>
|
|
||||||
<br/>
|
<br/>
|
||||||
</tal:delete>
|
</tal:delete>
|
||||||
<tal:comment replace="nothing">Replace with a new file.</tal:comment>
|
<tal:comment replace="nothing">Replace with a new file.</tal:comment>
|
||||||
|
@ -49,8 +47,7 @@
|
||||||
name string:${name}_delete;
|
name string:${name}_delete;
|
||||||
id string:${name}_upload;
|
id string:${name}_upload;
|
||||||
onclick string:document.getElementById('${name}_file').disabled=false"/>
|
onclick string:document.getElementById('${name}_file').disabled=false"/>
|
||||||
<label tal:attributes="for string:${name}_upload;"
|
<label tal:attributes="for string:${name}_upload;">Replace it with a new file</label>
|
||||||
i18n:translate="upload_file" i18n:domain="plone">Replace it with a new file</label>
|
|
||||||
<br/>
|
<br/>
|
||||||
</tal:editButtons>
|
</tal:editButtons>
|
||||||
<tal:comment replace="nothing">The upload field.</tal:comment>
|
<tal:comment replace="nothing">The upload field.</tal:comment>
|
||||||
|
|
|
@ -43,14 +43,13 @@
|
||||||
<td tal:condition="python: obj.allows('Modify portal content') and not appyType['noForm']">
|
<td tal:condition="python: obj.allows('Modify portal content') and not appyType['noForm']">
|
||||||
<a tal:define="navInfo python:'ref.%s.%s:%s.%d.%d' % (contextObj.UID(), fieldName, appyType['pageName'], repeat['obj'].number()+startNumber, totalNumber);"
|
<a tal:define="navInfo python:'ref.%s.%s:%s.%d.%d' % (contextObj.UID(), fieldName, appyType['pageName'], repeat['obj'].number()+startNumber, totalNumber);"
|
||||||
tal:attributes="href python: obj.getUrl(mode='edit', page='main', nav=navInfo)">
|
tal:attributes="href python: obj.getUrl(mode='edit', page='main', nav=navInfo)">
|
||||||
<img title="label_edit" i18n:domain="plone" i18n:attributes="title"
|
<img title="Edit" tal:attributes="src string: $appUrl/ui/edit.gif"/>
|
||||||
tal:attributes="src string: $appUrl/ui/edit.gif"/>
|
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<tal:comment replace="nothing">Delete the element</tal:comment>
|
<tal:comment replace="nothing">Delete the element</tal:comment>
|
||||||
<td>
|
<td>
|
||||||
<img tal:condition="python: not appyType['isBack'] and obj.allows('Delete objects') and obj.mayDelete()"
|
<img tal:condition="python: not appyType['isBack'] and obj.allows('Delete objects') and obj.mayDelete()"
|
||||||
title="Delete" i18n:domain="plone" i18n:attributes="title" style="cursor:pointer"
|
title="Delete" style="cursor:pointer"
|
||||||
tal:attributes="src string: $appUrl/ui/delete.png;
|
tal:attributes="src string: $appUrl/ui/delete.png;
|
||||||
onClick python:'onDeleteObject(\'%s\')' % obj.UID()"/>
|
onClick python:'onDeleteObject(\'%s\')' % obj.UID()"/>
|
||||||
</td>
|
</td>
|
||||||
|
|
23
gen/utils.py
23
gen/utils.py
|
@ -247,30 +247,30 @@ CONVERSION_ERROR = 'An error occurred while executing command "%s". %s'
|
||||||
class FileWrapper:
|
class FileWrapper:
|
||||||
'''When you get, from an appy object, the value of a File attribute, you
|
'''When you get, from an appy object, the value of a File attribute, you
|
||||||
get an instance of this class.'''
|
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
|
'''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
|
create a new file and assign it to a File attribute, use the
|
||||||
attribute setter, do not create yourself an instance of this
|
attribute setter, do not create yourself an instance of this
|
||||||
class.'''
|
class.'''
|
||||||
d = self.__dict__
|
d = self.__dict__
|
||||||
d['_atFile'] = atFile # Not for you!
|
d['_zopeFile'] = zopeFile # Not for you!
|
||||||
d['name'] = atFile.filename
|
d['name'] = zopeFile.filename
|
||||||
d['content'] = atFile.data
|
d['content'] = zopeFile.data
|
||||||
d['mimeType'] = atFile.content_type
|
d['mimeType'] = zopeFile.content_type
|
||||||
d['size'] = atFile.size # In bytes
|
d['size'] = zopeFile.size # In bytes
|
||||||
|
|
||||||
def __setattr__(self, name, v):
|
def __setattr__(self, name, v):
|
||||||
d = self.__dict__
|
d = self.__dict__
|
||||||
if name == 'name':
|
if name == 'name':
|
||||||
self._atFile.filename = v
|
self._zopeFile.filename = v
|
||||||
d['name'] = v
|
d['name'] = v
|
||||||
elif name == 'content':
|
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['content'] = v
|
||||||
d['size'] = len(v)
|
d['size'] = len(v)
|
||||||
elif name == 'mimeType':
|
elif name == 'mimeType':
|
||||||
self._atFile.content_type = self.mimeType = v
|
self._zopeFile.content_type = self.mimeType = v
|
||||||
else:
|
else:
|
||||||
raise 'Impossible to set attribute %s. "Settable" attributes ' \
|
raise 'Impossible to set attribute %s. "Settable" attributes ' \
|
||||||
'are "name", "content" and "mimeType".' % name
|
'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
|
Zope class. For some classes, name p_appName is required: it is
|
||||||
part of the class name.'''
|
part of the class name.'''
|
||||||
moduleName = klass.__module__
|
moduleName = klass.__module__
|
||||||
if (moduleName == 'appy.gen.plone25.model') or \
|
if (moduleName == 'appy.gen.model') or moduleName.endswith('.wrappers'):
|
||||||
moduleName.endswith('.wrappers'):
|
|
||||||
# This is a model (generation time or run time)
|
# This is a model (generation time or run time)
|
||||||
res = appName + klass.__name__
|
res = appName + klass.__name__
|
||||||
elif klass.__bases__ and (klass.__bases__[-1].__module__ == 'appy.gen'):
|
elif klass.__bases__ and (klass.__bases__[-1].__module__ == 'appy.gen'):
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
from appy.gen.plone25.wrappers import AbstractWrapper
|
from appy.gen.wrappers import AbstractWrapper
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
class GroupWrapper(AbstractWrapper):
|
class GroupWrapper(AbstractWrapper):
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
import os.path
|
import os.path
|
||||||
import appy
|
import appy
|
||||||
from appy.shared.utils import executeCommand
|
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 ' \
|
_PY = 'Please specify a file corresponding to a Python interpreter ' \
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
import os.path
|
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.gen.po import PoFile, PoMessage
|
||||||
from appy.shared.utils import getOsTempFolder
|
from appy.shared.utils import getOsTempFolder
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
from appy.gen.plone25.wrappers import AbstractWrapper
|
from appy.gen.wrappers import AbstractWrapper
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
class UserWrapper(AbstractWrapper):
|
class UserWrapper(AbstractWrapper):
|
||||||
|
|
Loading…
Reference in a new issue