new.py now can create instances for Plone 2.5.5, Plone 3.0 to Plone 3.3.5. specificWritePermission and specificReadPermission can hold named (string) permissions instead of simple boolean values (which is still allowed). frontPage can call a custom macro. When launching generate.py with -c option, labels prefixed with custom_ are kept.
This commit is contained in:
parent
bfd2357f69
commit
dbcadc506d
102
bin/new.py
102
bin/new.py
|
@ -27,14 +27,16 @@ WRONG_INSTANCE_PATH = '"%s" must be an existing folder for creating the ' \
|
||||||
class NewScript:
|
class NewScript:
|
||||||
'''usage: %prog ploneVersion plonePath instancePath
|
'''usage: %prog ploneVersion plonePath instancePath
|
||||||
|
|
||||||
"ploneVersion" can be plone25 or plone3
|
"ploneVersion" can be plone25, plone30, or plone3x
|
||||||
|
(plone3x can be Plone 3.2.x, Plone 3.3.5...)
|
||||||
|
|
||||||
"plonePath" is the (absolute) path to you plone installation.
|
"plonePath" is the (absolute) path to you plone installation.
|
||||||
Plone 2.5 is typically installed in /opt/Plone-2.5.5,
|
Plone 2.5 and 3.0 are typically installed in
|
||||||
while Plone 3 is typically installed in /usr/local/Plone.
|
/opt/Plone-x.x.x, while Plone 3 > 3.0 is typically
|
||||||
|
installed in in /usr/local/Plone.
|
||||||
"instancePath" is the (absolute) path where you want to create your
|
"instancePath" is the (absolute) path where you want to create your
|
||||||
instance (should not already exist).'''
|
instance (should not already exist).'''
|
||||||
ploneVersions = ('plone25', 'plone3')
|
ploneVersions = ('plone25', 'plone30', 'plone3x')
|
||||||
|
|
||||||
def createInstance(self, linksForProducts):
|
def createInstance(self, linksForProducts):
|
||||||
'''Calls the Zope script that allows to create a Zope instance and copy
|
'''Calls the Zope script that allows to create a Zope instance and copy
|
||||||
|
@ -65,35 +67,44 @@ class NewScript:
|
||||||
print cmd
|
print cmd
|
||||||
os.system(cmd)
|
os.system(cmd)
|
||||||
# Now, make the instance Plone-ready
|
# Now, make the instance Plone-ready
|
||||||
productsFolder = os.path.join(self.instancePath, 'Products')
|
action = 'Copying'
|
||||||
libFolder = os.path.join(self.instancePath, 'lib/python')
|
if linksForProducts:
|
||||||
print 'Copying Plone stuff in the Zope instance...'
|
action = 'Symlinking'
|
||||||
if self.ploneVersion == 'plone25':
|
print '%s Plone stuff in the Zope instance...' % action
|
||||||
self.installPlone25Stuff(productsFolder,libFolder,linksForProducts)
|
if self.ploneVersion in ('plone25', 'plone30'):
|
||||||
elif self.ploneVersion == 'plone3':
|
self.installPlone25or30Stuff(linksForProducts)
|
||||||
self.installPlone3Stuff(productsFolder, libFolder)
|
elif self.ploneVersion == 'plone3x':
|
||||||
|
self.installPlone3Stuff()
|
||||||
# Clean the copied folders
|
# Clean the copied folders
|
||||||
cleanFolder(productsFolder)
|
cleanFolder(os.path.join(self.instancePath, 'Products'))
|
||||||
cleanFolder(libFolder)
|
cleanFolder(os.path.join(self.instancePath, 'lib/python'))
|
||||||
|
|
||||||
def installPlone25Stuff(self, productsFolder, libFolder, linksForProducts):
|
def installPlone25or30Stuff(self, linksForProducts):
|
||||||
'''Here, we will copy all Plone2-related stuff in the Zope instance
|
'''Here, we will copy all Plone2-related stuff in the Zope instance
|
||||||
we've created, to get a full Plone-ready Zope instance. If
|
we've created, to get a full Plone-ready Zope instance. If
|
||||||
p_linksForProducts is True, we do not perform a real copy: we will
|
p_linksForProducts is True, we do not perform a real copy: we will
|
||||||
create symlinks to products lying within Plone installer files.'''
|
create symlinks to products lying within Plone installer files.'''
|
||||||
installerProducts = os.path.join(self.plonePath, 'zeocluster/Products')
|
j = os.path.join
|
||||||
for name in os.listdir(installerProducts):
|
if self.ploneVersion == 'plone25':
|
||||||
folderName = os.path.join(installerProducts, name)
|
sourceFolders = ('zeocluster/Products',)
|
||||||
if os.path.isdir(folderName):
|
else:
|
||||||
destFolder = os.path.join(productsFolder, name)
|
sourceFolders = ('zinstance/Products', 'zinstance/lib/python')
|
||||||
# This is a Plone product. Copy it to the instance.
|
for sourceFolder in sourceFolders:
|
||||||
if (self.ploneVersion == 'plone25') and linksForProducts:
|
sourceBase = j(self.plonePath, sourceFolder)
|
||||||
# Create a symlink to this product in the instance
|
destBase = j(self.instancePath,
|
||||||
cmd = 'ln -s %s %s' % (folderName, destFolder)
|
sourceFolder[sourceFolder.find('/')+1:])
|
||||||
os.system(cmd)
|
for name in os.listdir(sourceBase):
|
||||||
else:
|
folderName = j(sourceBase, name)
|
||||||
# Copy thre product into the instance
|
if os.path.isdir(folderName):
|
||||||
shutil.copytree(folderName, destFolder)
|
destFolder = j(destBase, name)
|
||||||
|
# This is a Plone product. Copy it to the instance.
|
||||||
|
if linksForProducts:
|
||||||
|
# Create a symlink to this product in the instance
|
||||||
|
cmd = 'ln -s %s %s' % (folderName, destFolder)
|
||||||
|
os.system(cmd)
|
||||||
|
else:
|
||||||
|
# Copy thre product into the instance
|
||||||
|
shutil.copytree(folderName, destFolder)
|
||||||
|
|
||||||
uglyChunks = ('pkg_resources', '.declare_namespace(')
|
uglyChunks = ('pkg_resources', '.declare_namespace(')
|
||||||
def findPythonPackageInEgg(self, currentFolder):
|
def findPythonPackageInEgg(self, currentFolder):
|
||||||
|
@ -168,7 +179,7 @@ class NewScript:
|
||||||
f.write(fileContent)
|
f.write(fileContent)
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
def installPlone3Stuff(self, productsFolder, libFolder):
|
def installPlone3Stuff(self):
|
||||||
'''Here, we will copy all Plone3-related stuff in the Zope instance
|
'''Here, we will copy all Plone3-related stuff in the Zope instance
|
||||||
we've created, to get a full Plone-ready Zope instance.'''
|
we've created, to get a full Plone-ready Zope instance.'''
|
||||||
# All Plone 3 eggs are in buildout-cache/eggs. We will extract from
|
# All Plone 3 eggs are in buildout-cache/eggs. We will extract from
|
||||||
|
@ -179,14 +190,16 @@ class NewScript:
|
||||||
# <zopeInstance>/lib/python (ie, like Appy applications)
|
# <zopeInstance>/lib/python (ie, like Appy applications)
|
||||||
# - Zope products that will be copied in
|
# - Zope products that will be copied in
|
||||||
# <zopeInstance>/Products (ie, like Appy generated Zope products)
|
# <zopeInstance>/Products (ie, like Appy generated Zope products)
|
||||||
eggsFolder = os.path.join(self.plonePath, 'buildout-cache/eggs')
|
j = os.path.join
|
||||||
|
eggsFolder = j(self.plonePath, 'buildout-cache/eggs')
|
||||||
|
productsFolder = j(self.instancePath, 'Products')
|
||||||
|
libFolder = j(self.instancePath, 'lib/python')
|
||||||
for name in os.listdir(eggsFolder):
|
for name in os.listdir(eggsFolder):
|
||||||
eggMainFolder = os.path.join(eggsFolder, name)
|
eggMainFolder = j(eggsFolder, name)
|
||||||
if name.startswith('Products.'):
|
if name.startswith('Products.'):
|
||||||
# A Zope product. Copy its content in Products.
|
# A Zope product. Copy its content in Products.
|
||||||
innerFolder= self.getSubFolder(self.getSubFolder(eggMainFolder))
|
innerFolder= self.getSubFolder(self.getSubFolder(eggMainFolder))
|
||||||
destFolder = os.path.join(productsFolder,
|
destFolder = j(productsFolder, os.path.basename(innerFolder))
|
||||||
os.path.basename(innerFolder))
|
|
||||||
shutil.copytree(innerFolder, destFolder)
|
shutil.copytree(innerFolder, destFolder)
|
||||||
else:
|
else:
|
||||||
# A standard Python package. Copy its content in lib/python.
|
# A standard Python package. Copy its content in lib/python.
|
||||||
|
@ -197,7 +210,7 @@ class NewScript:
|
||||||
# Copy those files directly in libFolder.
|
# Copy those files directly in libFolder.
|
||||||
for fileName in os.listdir(eggMainFolder):
|
for fileName in os.listdir(eggMainFolder):
|
||||||
if fileName.endswith('.py'):
|
if fileName.endswith('.py'):
|
||||||
fullFileName= os.path.join(eggMainFolder, fileName)
|
fullFileName= j(eggMainFolder, fileName)
|
||||||
shutil.copy(fullFileName, libFolder)
|
shutil.copy(fullFileName, libFolder)
|
||||||
continue
|
continue
|
||||||
eggFolderName = os.path.basename(eggFolder)
|
eggFolderName = os.path.basename(eggFolder)
|
||||||
|
@ -205,8 +218,7 @@ class NewScript:
|
||||||
# Goddamned. This should go in productsFolder and not in
|
# Goddamned. This should go in productsFolder and not in
|
||||||
# libFolder.
|
# libFolder.
|
||||||
innerFolder = self.getSubFolder(eggFolder)
|
innerFolder = self.getSubFolder(eggFolder)
|
||||||
destFolder = os.path.join(productsFolder,
|
destFolder = j(productsFolder,os.path.basename(innerFolder))
|
||||||
os.path.basename(innerFolder))
|
|
||||||
shutil.copytree(innerFolder, destFolder)
|
shutil.copytree(innerFolder, destFolder)
|
||||||
else:
|
else:
|
||||||
packageFolder = self.findPythonPackageInEgg(eggFolder)
|
packageFolder = self.findPythonPackageInEgg(eggFolder)
|
||||||
|
@ -225,23 +237,22 @@ class NewScript:
|
||||||
# before copying the Python package.
|
# before copying the Python package.
|
||||||
baseFolder = libFolder
|
baseFolder = libFolder
|
||||||
for subFolder in destFolders:
|
for subFolder in destFolders:
|
||||||
subFolderPath=os.path.join(baseFolder,subFolder)
|
subFolderPath = j(baseFolder,subFolder)
|
||||||
if not os.path.exists(subFolderPath):
|
if not os.path.exists(subFolderPath):
|
||||||
os.mkdir(subFolderPath)
|
os.mkdir(subFolderPath)
|
||||||
# Create an empty __init__.py in it.
|
# Create an empty __init__.py in it.
|
||||||
init = os.path.join(subFolderPath,'__init__.py')
|
init = j(subFolderPath,'__init__.py')
|
||||||
f = file(init, 'w')
|
f = file(init, 'w')
|
||||||
f.write('# Makes me a Python package.')
|
f.write('# Makes me a Python package.')
|
||||||
f.close()
|
f.close()
|
||||||
baseFolder = subFolderPath
|
baseFolder = subFolderPath
|
||||||
destFolder = os.sep.join(destFolders)
|
destFolder = os.sep.join(destFolders)
|
||||||
destFolder = os.path.join(libFolder, destFolder)
|
destFolder = j(libFolder, destFolder)
|
||||||
if not os.path.exists(destFolder):
|
if not os.path.exists(destFolder):
|
||||||
os.makedirs(destFolder)
|
os.makedirs(destFolder)
|
||||||
else:
|
else:
|
||||||
destFolder = libFolder
|
destFolder = libFolder
|
||||||
destFolder = os.path.join(
|
destFolder = j(destFolder, os.path.basename(packageFolder))
|
||||||
destFolder, os.path.basename(packageFolder))
|
|
||||||
shutil.copytree(packageFolder, destFolder)
|
shutil.copytree(packageFolder, destFolder)
|
||||||
self.patchPlone(productsFolder, libFolder)
|
self.patchPlone(productsFolder, libFolder)
|
||||||
|
|
||||||
|
@ -264,11 +275,12 @@ class NewScript:
|
||||||
def run(self):
|
def run(self):
|
||||||
optParser = OptionParser(usage=NewScript.__doc__)
|
optParser = OptionParser(usage=NewScript.__doc__)
|
||||||
optParser.add_option("-l", "--links", action="store_true",
|
optParser.add_option("-l", "--links", action="store_true",
|
||||||
help="[Linux, plone25 only] Within the created instance, symlinks "\
|
help="[Linux, plone25 or plone30 only] Within the created " \
|
||||||
"to Products lying within the Plone installer files are " \
|
"instance, symlinks to Products lying within the Plone " \
|
||||||
"created instead of copying them into the instance. This " \
|
"installer files are created instead of copying them into " \
|
||||||
"avoids duplicating the Products source code and is " \
|
"the instance. This avoids duplicating the Products source " \
|
||||||
"interesting if you create a lot of Zope instances.")
|
"code and is interesting if you create a lot of Zope " \
|
||||||
|
"instances.")
|
||||||
(options, args) = optParser.parse_args()
|
(options, args) = optParser.parse_args()
|
||||||
linksForProducts = options.links
|
linksForProducts = options.links
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -306,9 +306,20 @@ class Type:
|
||||||
self.searchable = searchable
|
self.searchable = searchable
|
||||||
# Normally, permissions to read or write every attribute in a type are
|
# Normally, permissions to read or write every attribute in a type are
|
||||||
# granted if the user has the global permission to read or
|
# granted if the user has the global permission to read or
|
||||||
# create/edit instances of the whole type. If you want a given attribute
|
# edit instances of the whole type. If you want a given attribute
|
||||||
# to be protected by specific permissions, set one or the 2 next boolean
|
# to be protected by specific permissions, set one or the 2 next boolean
|
||||||
# values to "True".
|
# values to "True". In this case, you will create a new "field-only"
|
||||||
|
# read and/or write permission. If you need to protect several fields
|
||||||
|
# with the same read/write permission, you can avoid defining one
|
||||||
|
# specific permission for every field by specifying a "named"
|
||||||
|
# permission (string) instead of assigning "True" to the following
|
||||||
|
# arg(s). A named permission will be global to your whole Zope site, so
|
||||||
|
# take care to the naming convention. Typically, a named permission is
|
||||||
|
# of the form: "<yourAppName>: Write|Read xxx". If, for example, I want
|
||||||
|
# to define, for my application "MedicalFolder" a specific permission
|
||||||
|
# for a bunch of fields that can only be modified by a doctor, I can
|
||||||
|
# define a permission "MedicalFolder: Write medical information" and
|
||||||
|
# assign it to the "specificWritePermission" of every impacted field.
|
||||||
self.specificReadPermission = specificReadPermission
|
self.specificReadPermission = specificReadPermission
|
||||||
self.specificWritePermission = specificWritePermission
|
self.specificWritePermission = specificWritePermission
|
||||||
# Widget width and height
|
# Widget width and height
|
||||||
|
@ -383,12 +394,18 @@ class Type:
|
||||||
self.descrId = self.labelId + '_descr'
|
self.descrId = self.labelId + '_descr'
|
||||||
self.helpId = self.labelId + '_help'
|
self.helpId = self.labelId + '_help'
|
||||||
# Determine read and write permissions for this field
|
# Determine read and write permissions for this field
|
||||||
if self.specificReadPermission:
|
rp = self.specificReadPermission
|
||||||
|
if rp and not isinstance(rp, basestring):
|
||||||
self.readPermission = '%s: Read %s %s' % (appName, prefix, name)
|
self.readPermission = '%s: Read %s %s' % (appName, prefix, name)
|
||||||
|
elif rp and isinstance(rp, basestring):
|
||||||
|
self.readPermission = rp
|
||||||
else:
|
else:
|
||||||
self.readPermission = 'View'
|
self.readPermission = 'View'
|
||||||
if self.specificWritePermission:
|
wp = self.specificWritePermission
|
||||||
|
if wp and not isinstance(wp, basestring):
|
||||||
self.writePermission = '%s: Write %s %s' % (appName, prefix, name)
|
self.writePermission = '%s: Write %s %s' % (appName, prefix, name)
|
||||||
|
elif wp and isinstance(wp, basestring):
|
||||||
|
self.writePermission = wp
|
||||||
else:
|
else:
|
||||||
self.writePermission = 'Modify portal content'
|
self.writePermission = 'Modify portal content'
|
||||||
if isinstance(self, Ref):
|
if isinstance(self, Ref):
|
||||||
|
@ -508,7 +525,12 @@ class Type:
|
||||||
value = getattr(obj, self.name, None)
|
value = getattr(obj, self.name, None)
|
||||||
if (value == None):
|
if (value == None):
|
||||||
# If there is no value, get the default value if any
|
# If there is no value, get the default value if any
|
||||||
if not self.editDefault: return self.default
|
if not self.editDefault:
|
||||||
|
# Return self.default, of self.default() if it is a method
|
||||||
|
if type(self.default) == types.FunctionType:
|
||||||
|
return self.default(obj.appy())
|
||||||
|
else:
|
||||||
|
return self.default
|
||||||
# If value is editable, get the default value from the flavour
|
# If value is editable, get the default value from the flavour
|
||||||
portalTypeName = obj._appy_getPortalType(obj.REQUEST)
|
portalTypeName = obj._appy_getPortalType(obj.REQUEST)
|
||||||
tool = obj.getTool()
|
tool = obj.getTool()
|
||||||
|
@ -577,14 +599,14 @@ class Type:
|
||||||
return obj.translate('%s_valid' % self.labelId)
|
return obj.translate('%s_valid' % self.labelId)
|
||||||
elif type(self.validator) == validatorTypes[1]:
|
elif type(self.validator) == validatorTypes[1]:
|
||||||
# It is a regular expression
|
# It is a regular expression
|
||||||
if not validator.match(value):
|
if not self.validator.match(value):
|
||||||
# If the regular expression is among the default ones, we
|
# If the regular expression is among the default ones, we
|
||||||
# generate a specific error message.
|
# generate a specific error message.
|
||||||
if validator == String.EMAIL:
|
if self.validator == String.EMAIL:
|
||||||
return obj.translate('bad_email')
|
return obj.translate('bad_email')
|
||||||
elif validator == String.URL:
|
elif self.validator == String.URL:
|
||||||
return obj.translate('bad_url')
|
return obj.translate('bad_url')
|
||||||
elif validator == String.ALPHANUMERIC:
|
elif self.validator == String.ALPHANUMERIC:
|
||||||
return obj.translate('bad_alphanumeric')
|
return obj.translate('bad_alphanumeric')
|
||||||
else:
|
else:
|
||||||
return obj.translate('%s_valid' % self.labelId)
|
return obj.translate('%s_valid' % self.labelId)
|
||||||
|
@ -771,19 +793,20 @@ class String(Type):
|
||||||
# CSS property: "none" (default), "uppercase", "capitalize" or
|
# CSS property: "none" (default), "uppercase", "capitalize" or
|
||||||
# "lowercase".
|
# "lowercase".
|
||||||
self.transform = transform
|
self.transform = transform
|
||||||
# Default width and height vary according to String format
|
|
||||||
if width == None:
|
|
||||||
if format == String.TEXT: width = 60
|
|
||||||
else: width = 30
|
|
||||||
if height == None:
|
|
||||||
if format == String.TEXT: height = 5
|
|
||||||
else: height = 1
|
|
||||||
Type.__init__(self, validator, multiplicity, index, default, optional,
|
Type.__init__(self, validator, multiplicity, index, default, optional,
|
||||||
editDefault, show, page, group, layouts, move, indexed,
|
editDefault, show, page, group, layouts, move, indexed,
|
||||||
searchable, specificReadPermission,
|
searchable, specificReadPermission,
|
||||||
specificWritePermission, width, height, colspan, master,
|
specificWritePermission, width, height, colspan, master,
|
||||||
masterValue, focus, historized)
|
masterValue, focus, historized)
|
||||||
self.isSelect = self.isSelection()
|
self.isSelect = self.isSelection()
|
||||||
|
# Default width and height vary according to String format
|
||||||
|
if width == None:
|
||||||
|
if format == String.TEXT: self.width = 60
|
||||||
|
else: self.width = 30
|
||||||
|
if height == None:
|
||||||
|
if format == String.TEXT: self.height = 5
|
||||||
|
elif self.isSelect: self.height = 4
|
||||||
|
else: self.height = 1
|
||||||
self.filterable = self.indexed and (self.format == String.LINE) and \
|
self.filterable = self.indexed and (self.format == String.LINE) and \
|
||||||
not self.isSelect
|
not self.isSelect
|
||||||
|
|
||||||
|
@ -1139,6 +1162,7 @@ class Ref(Type):
|
||||||
self.validable = self.link
|
self.validable = self.link
|
||||||
|
|
||||||
def getDefaultLayouts(self): return {'view': 'l-f', 'edit': 'lrv-f'}
|
def getDefaultLayouts(self): return {'view': 'l-f', 'edit': 'lrv-f'}
|
||||||
|
|
||||||
def isShowable(self, obj, layoutType):
|
def isShowable(self, obj, layoutType):
|
||||||
if (layoutType == 'edit') and self.add: return False
|
if (layoutType == 'edit') and self.add: return False
|
||||||
if self.isBack:
|
if self.isBack:
|
||||||
|
@ -1438,9 +1462,14 @@ class Permission:
|
||||||
defining a workflow, for example, you need to use instances of
|
defining a workflow, for example, you need to use instances of
|
||||||
"ReadPermission" and "WritePermission", the 2 children classes of this
|
"ReadPermission" and "WritePermission", the 2 children classes of this
|
||||||
class. For example, if you need to refer to write permission of
|
class. For example, if you need to refer to write permission of
|
||||||
attribute "t1" of class A, write: "WritePermission("A.t1") or
|
attribute "t1" of class A, write: WritePermission("A.t1") or
|
||||||
WritePermission("x.y.A.t1") if class A is not in the same module as
|
WritePermission("x.y.A.t1") if class A is not in the same module as
|
||||||
where you instantiate the class.'''
|
where you instantiate the class.
|
||||||
|
|
||||||
|
Note that this holds only if you use attributes "specificReadPermission"
|
||||||
|
and "specificWritePermission" as booleans. When defining named
|
||||||
|
(string) permissions, for referring to it you simply use those strings,
|
||||||
|
you do not create instances of ReadPermission or WritePermission.'''
|
||||||
def __init__(self, fieldDescriptor):
|
def __init__(self, fieldDescriptor):
|
||||||
self.fieldDescriptor = fieldDescriptor
|
self.fieldDescriptor = fieldDescriptor
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,9 @@
|
||||||
# w - The widgets of the current page/class
|
# w - The widgets of the current page/class
|
||||||
# n - The navigation panel (inter-objects navigation)
|
# n - The navigation panel (inter-objects navigation)
|
||||||
# b - The range of buttons (intra-object navigation, save, edit, delete...)
|
# b - The range of buttons (intra-object navigation, save, edit, delete...)
|
||||||
# m - The global status message sometimes shown.
|
# m - The global status message sometimes shown. If you specify this in a
|
||||||
|
# layout, ensure that you have hidden the global_statusmessage zone as
|
||||||
|
# proposed by Plone. Else, the message will appear twice.
|
||||||
|
|
||||||
# Layout elements for a field --------------------------------------------------
|
# Layout elements for a field --------------------------------------------------
|
||||||
# l - "label" The field label
|
# l - "label" The field label
|
||||||
|
|
|
@ -156,11 +156,8 @@ class Generator(AbstractGenerator):
|
||||||
self.generateWorkflows()
|
self.generateWorkflows()
|
||||||
self.generateWrappers()
|
self.generateWrappers()
|
||||||
self.generateTests()
|
self.generateTests()
|
||||||
if self.config.frontPage == True:
|
if self.config.frontPage:
|
||||||
self.labels.append(msg('front_page_text', '', msg.FRONT_PAGE_TEXT))
|
self.generateFrontPage()
|
||||||
self.copyFile('frontPage.pt', self.repls,
|
|
||||||
destFolder=self.skinsFolder,
|
|
||||||
destName='%sFrontPage.pt' % self.applicationName)
|
|
||||||
self.copyFile('configure.zcml', self.repls)
|
self.copyFile('configure.zcml', self.repls)
|
||||||
self.copyFile('import_steps.xml', self.repls,
|
self.copyFile('import_steps.xml', self.repls,
|
||||||
destFolder='profiles/default')
|
destFolder='profiles/default')
|
||||||
|
@ -169,8 +166,6 @@ class Generator(AbstractGenerator):
|
||||||
self.copyFile('Portlet.pt', self.repls,
|
self.copyFile('Portlet.pt', self.repls,
|
||||||
destName='%s.pt' % self.portletName, destFolder=self.skinsFolder)
|
destName='%s.pt' % self.portletName, destFolder=self.skinsFolder)
|
||||||
self.copyFile('tool.gif', {})
|
self.copyFile('tool.gif', {})
|
||||||
self.copyFile(
|
|
||||||
'global_statusmessage.pt', {}, destFolder=self.skinsFolder)
|
|
||||||
self.copyFile('Styles.css.dtml',self.repls, destFolder=self.skinsFolder,
|
self.copyFile('Styles.css.dtml',self.repls, destFolder=self.skinsFolder,
|
||||||
destName = '%s.css.dtml' % self.applicationName)
|
destName = '%s.css.dtml' % self.applicationName)
|
||||||
self.copyFile('IEFixes.css.dtml',self.repls,destFolder=self.skinsFolder)
|
self.copyFile('IEFixes.css.dtml',self.repls,destFolder=self.skinsFolder)
|
||||||
|
@ -418,7 +413,7 @@ class Generator(AbstractGenerator):
|
||||||
repls['appClasses'] = "[%s]" % ','.join(appClasses)
|
repls['appClasses'] = "[%s]" % ','.join(appClasses)
|
||||||
repls['minimalistPlone'] = self.config.minimalistPlone
|
repls['minimalistPlone'] = self.config.minimalistPlone
|
||||||
repls['showPortlet'] = self.config.showPortlet
|
repls['showPortlet'] = self.config.showPortlet
|
||||||
repls['appFrontPage'] = self.config.frontPage == True
|
repls['appFrontPage'] = bool(self.config.frontPage)
|
||||||
repls['workflows'] = workflows
|
repls['workflows'] = workflows
|
||||||
self.copyFile('Install.py', repls, destFolder='Extensions')
|
self.copyFile('Install.py', repls, destFolder='Extensions')
|
||||||
|
|
||||||
|
@ -576,6 +571,27 @@ class Generator(AbstractGenerator):
|
||||||
repls['modulesWithTests'] = ','.join(modules)
|
repls['modulesWithTests'] = ','.join(modules)
|
||||||
self.copyFile('testAll.py', repls, destFolder='tests')
|
self.copyFile('testAll.py', repls, destFolder='tests')
|
||||||
|
|
||||||
|
def generateFrontPage(self):
|
||||||
|
fp = self.config.frontPage
|
||||||
|
repls = self.repls.copy()
|
||||||
|
if fp == True:
|
||||||
|
# We need a front page, but no specific one has been given.
|
||||||
|
# So we will create a basic one that will simply display
|
||||||
|
# some translated text.
|
||||||
|
self.labels.append(msg('front_page_text', '', msg.FRONT_PAGE_TEXT))
|
||||||
|
repls['pageContent'] = '<span tal:replace="structure python: ' \
|
||||||
|
'tool.translateWithMapping(\'front_page_text\')"/>'
|
||||||
|
else:
|
||||||
|
# The user has specified a macro to show. So in the generated front
|
||||||
|
# page, we will call this macro. The user will need to add itself
|
||||||
|
# a .pt file containing this macro in the skins folder of the
|
||||||
|
# generated Plone product.
|
||||||
|
page, macro = fp.split('/')
|
||||||
|
repls['pageContent'] = '<metal:call use-macro=' \
|
||||||
|
'"context/%s/macros/%s"/>' % (page, macro)
|
||||||
|
self.copyFile('frontPage.pt', repls, destFolder=self.skinsFolder,
|
||||||
|
destName='%sFrontPage.pt' % self.applicationName)
|
||||||
|
|
||||||
def generateTool(self):
|
def generateTool(self):
|
||||||
'''Generates the Plone tool that corresponds to this application.'''
|
'''Generates the Plone tool that corresponds to this application.'''
|
||||||
# Generate the tool class in itself and related i18n messages
|
# Generate the tool class in itself and related i18n messages
|
||||||
|
|
|
@ -74,8 +74,16 @@ class ToolMixin(AbstractMixin):
|
||||||
'''Returns the list of root classes for this application.'''
|
'''Returns the list of root classes for this application.'''
|
||||||
return self.getProductConfig().rootClasses
|
return self.getProductConfig().rootClasses
|
||||||
|
|
||||||
def showPortlet(self):
|
def showPortlet(self, context):
|
||||||
return not self.portal_membership.isAnonymousUser()
|
if self.portal_membership.isAnonymousUser(): return False
|
||||||
|
if context.id == 'skyn': context = context.getParentNode()
|
||||||
|
res = True
|
||||||
|
if not self.getRootClasses():
|
||||||
|
res = False
|
||||||
|
# If there is no root class, show the portlet only if we are within
|
||||||
|
# the configuration.
|
||||||
|
if (self.id in context.absolute_url()): res = True
|
||||||
|
return res
|
||||||
|
|
||||||
def getObject(self, uid, appy=False):
|
def getObject(self, uid, appy=False):
|
||||||
'''Allows to retrieve an object from its p_uid.'''
|
'''Allows to retrieve an object from its p_uid.'''
|
||||||
|
@ -630,7 +638,7 @@ class ToolMixin(AbstractMixin):
|
||||||
contentType, flavourNumber = d1.split(':')
|
contentType, flavourNumber = d1.split(':')
|
||||||
flavourNumber = int(flavourNumber)
|
flavourNumber = int(flavourNumber)
|
||||||
searchName = keySuffix = d2
|
searchName = keySuffix = d2
|
||||||
batchSize = self.getNumberOfResultsPerPage()
|
batchSize = self.appy().numberOfResultsPerPage
|
||||||
if not searchName: keySuffix = contentType
|
if not searchName: keySuffix = contentType
|
||||||
s = self.REQUEST.SESSION
|
s = self.REQUEST.SESSION
|
||||||
searchKey = 'search_%s_%s' % (flavourNumber, keySuffix)
|
searchKey = 'search_%s_%s' % (flavourNumber, keySuffix)
|
||||||
|
|
|
@ -285,23 +285,23 @@ class AbstractMixin:
|
||||||
'''Returns the method named p_methodName.'''
|
'''Returns the method named p_methodName.'''
|
||||||
return getattr(self, methodName, None)
|
return getattr(self, methodName, None)
|
||||||
|
|
||||||
def getFormattedValue(self, name, useParamValue=False, value=None,
|
def getFieldValue(self, name, useParamValue=False, value=None,
|
||||||
forMasterId=False):
|
formatted=True):
|
||||||
'''Returns the value of field named p_name for this object (p_self).
|
'''Returns the value of field named p_name for this object (p_self).
|
||||||
|
|
||||||
If p_useParamValue is True, the method uses p_value instead of the
|
If p_useParamValue is True, the method uses p_value instead of the
|
||||||
real field value (useful for rendering a value from the object
|
real field value (useful for rendering a value from the object
|
||||||
history, for example).
|
history, for example).
|
||||||
|
|
||||||
If p_forMasterId is True, it returns the value as will be needed to
|
If p_formatted is False, it will return the true database
|
||||||
produce an identifier used within HTML pages for master/slave
|
(or default) value. Else, it will produce a nice, string and
|
||||||
relationships.'''
|
potentially translated value.'''
|
||||||
appyType = self.getAppyType(name)
|
appyType = self.getAppyType(name)
|
||||||
# Which value will we use ?
|
# Which value will we use ?
|
||||||
if not useParamValue:
|
if not useParamValue:
|
||||||
value = appyType.getValue(self)
|
value = appyType.getValue(self)
|
||||||
# Return the value as is if it is None or forMasterId
|
# Return the value as is if it is None or forMasterId
|
||||||
if forMasterId: return value
|
if not formatted: return value
|
||||||
# Return the formatted value else
|
# Return the formatted value else
|
||||||
return appyType.getFormattedValue(self, value)
|
return appyType.getFormattedValue(self, value)
|
||||||
|
|
||||||
|
@ -741,7 +741,7 @@ class AbstractMixin:
|
||||||
res = brains
|
res = brains
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def fieldValueSelected(self, fieldName, vocabValue):
|
def fieldValueSelected(self, fieldName, vocabValue, dbValue):
|
||||||
'''When displaying a selection box (ie a String with a validator being a
|
'''When displaying a selection box (ie a String with a validator being a
|
||||||
list), must the _vocabValue appear as selected?'''
|
list), must the _vocabValue appear as selected?'''
|
||||||
rq = self.REQUEST
|
rq = self.REQUEST
|
||||||
|
@ -749,14 +749,14 @@ class AbstractMixin:
|
||||||
if rq.has_key(fieldName):
|
if rq.has_key(fieldName):
|
||||||
compValue = rq.get(fieldName)
|
compValue = rq.get(fieldName)
|
||||||
else:
|
else:
|
||||||
compValue = self.getAppyType(fieldName).getValue(self)
|
compValue = dbValue
|
||||||
# Compare the value
|
# Compare the value
|
||||||
if type(compValue) in sequenceTypes:
|
if type(compValue) in sequenceTypes:
|
||||||
if vocabValue in compValue: return True
|
if vocabValue in compValue: return True
|
||||||
else:
|
else:
|
||||||
if vocabValue == compValue: return True
|
if vocabValue == compValue: return True
|
||||||
|
|
||||||
def checkboxChecked(self, fieldName):
|
def checkboxChecked(self, fieldName, dbValue):
|
||||||
'''When displaying a checkbox, must it be checked or not?'''
|
'''When displaying a checkbox, must it be checked or not?'''
|
||||||
rq = self.REQUEST
|
rq = self.REQUEST
|
||||||
# Get the value we must compare (from request or from database)
|
# Get the value we must compare (from request or from database)
|
||||||
|
@ -764,11 +764,11 @@ class AbstractMixin:
|
||||||
compValue = rq.get(fieldName)
|
compValue = rq.get(fieldName)
|
||||||
compValue = compValue in ('True', 1, '1')
|
compValue = compValue in ('True', 1, '1')
|
||||||
else:
|
else:
|
||||||
compValue = self.getAppyType(fieldName).getValue(self)
|
compValue = dbValue
|
||||||
# Compare the value
|
# Compare the value
|
||||||
return compValue
|
return compValue
|
||||||
|
|
||||||
def dateValueSelected(self, fieldName, fieldPart, dateValue):
|
def dateValueSelected(self, fieldName, fieldPart, dateValue, dbValue):
|
||||||
'''When displaying a date field, must the particular p_dateValue be
|
'''When displaying a date field, must the particular p_dateValue be
|
||||||
selected in the field corresponding to the date part?'''
|
selected in the field corresponding to the date part?'''
|
||||||
# Get the value we must compare (from request or from database)
|
# Get the value we must compare (from request or from database)
|
||||||
|
@ -779,7 +779,7 @@ class AbstractMixin:
|
||||||
if compValue.isdigit():
|
if compValue.isdigit():
|
||||||
compValue = int(compValue)
|
compValue = int(compValue)
|
||||||
else:
|
else:
|
||||||
compValue = self.getAppyType(fieldName).getValue(self)
|
compValue = dbValue
|
||||||
if compValue:
|
if compValue:
|
||||||
compValue = getattr(compValue, fieldPart)()
|
compValue = getattr(compValue, fieldPart)()
|
||||||
# Compare the value
|
# Compare the value
|
||||||
|
|
|
@ -455,7 +455,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
<tr tal:repeat="change event/changes/items" valign="top">
|
<tr tal:repeat="change event/changes/items" valign="top">
|
||||||
<td tal:content="python: tool.translate(change[1][1])"></td>
|
<td tal:content="python: tool.translate(change[1][1])"></td>
|
||||||
<td tal:define="appyValue python: contextObj.getFormattedValue(change[0], useParamValue=True, value=change[1][0]);
|
<td tal:define="appyValue python: contextObj.getFieldValue(change[0], useParamValue=True, value=change[1][0]);
|
||||||
appyType python:contextObj.getAppyType(change[0], asDict=True);
|
appyType python:contextObj.getAppyType(change[0], asDict=True);
|
||||||
severalValues python: (appyType['multiplicity'][1] > 1) or (appyType['multiplicity'][1] == None)">
|
severalValues python: (appyType['multiplicity'][1] > 1) or (appyType['multiplicity'][1] == None)">
|
||||||
<span tal:condition="not: severalValues" tal:replace="appyValue"></span>
|
<span tal:condition="not: severalValues" tal:replace="appyValue"></span>
|
||||||
|
|
|
@ -13,10 +13,10 @@
|
||||||
<table cellpadding="0" cellspacing="0" width="100%">
|
<table cellpadding="0" cellspacing="0" width="100%">
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<a tal:condition="python: len(flavours)==1"
|
<a tal:condition="python: len(flavours)==1 and rootClasses"
|
||||||
tal:attributes="href python:'%s?type_name=%s&flavourNumber=1' % (queryUrl, ','.join(rootClasses))"
|
tal:attributes="href python:'%s?type_name=%s&flavourNumber=1' % (queryUrl, ','.join(rootClasses))"
|
||||||
tal:content="python: tool.translate(appName)"></a>
|
tal:content="python: tool.translate(appName)"></a>
|
||||||
<span tal:condition="python: len(flavours)>1"
|
<span tal:condition="python: len(flavours)>1 or not rootClasses"
|
||||||
tal:replace="python: tool.translate(appName)"/>
|
tal:replace="python: tool.translate(appName)"/>
|
||||||
</td>
|
</td>
|
||||||
<td align="right">
|
<td align="right">
|
||||||
|
@ -159,7 +159,7 @@
|
||||||
<tr>
|
<tr>
|
||||||
<td tal:define="label python:'%s_phase_%s' % (contextObj.meta_type, phase['name']);
|
<td tal:define="label python:'%s_phase_%s' % (contextObj.meta_type, phase['name']);
|
||||||
displayLink python: (phase['phaseStatus'] != 'Future') and ('/portal_factory' not in contextObj.absolute_url()) and (len(phase['pages']) == 1)"
|
displayLink python: (phase['phaseStatus'] != 'Future') and ('/portal_factory' not in contextObj.absolute_url()) and (len(phase['pages']) == 1)"
|
||||||
tal:attributes="class python: 'appyPhase step' + phase['phaseStatus']">
|
tal:attributes="class python: (len(phases) > 1) and ('appyPhase step%s' % phase['phaseStatus']) or 'appyPhase'">
|
||||||
<span class="portletGroup" tal:condition="python: len(phases) > 1">
|
<span class="portletGroup" tal:condition="python: len(phases) > 1">
|
||||||
<a tal:attributes="href python: '%s?page=%s' % (contextObj.getUrl(), phase['pages'][0]);"
|
<a tal:attributes="href python: '%s?page=%s' % (contextObj.getUrl(), phase['pages'][0]);"
|
||||||
tal:condition="displayLink"
|
tal:condition="displayLink"
|
||||||
|
|
|
@ -6,12 +6,12 @@
|
||||||
<input type="checkbox"
|
<input type="checkbox"
|
||||||
tal:attributes="name python: name + '_visible';
|
tal:attributes="name python: name + '_visible';
|
||||||
id name;
|
id name;
|
||||||
checked python:contextObj.checkboxChecked(name);
|
checked python:contextObj.checkboxChecked(name, rawValue);
|
||||||
onClick python:'toggleCheckbox(\'%s\', \'%s_hidden\');;updateSlaves(getMasterValue(this), \'%s\')' % (name, name, widget['id']);
|
onClick python:'toggleCheckbox(\'%s\', \'%s_hidden\');;updateSlaves(getMasterValue(this), \'%s\')' % (name, name, widget['id']);
|
||||||
class python: 'noborder ' + widget['master_css']"/>
|
class python: 'noborder ' + widget['master_css']"/>
|
||||||
<input tal:attributes="name name;
|
<input tal:attributes="name name;
|
||||||
id string:${name}_hidden;
|
id string:${name}_hidden;
|
||||||
value python: test(contextObj.checkboxChecked(name), 'True', 'False')"
|
value python: test(contextObj.checkboxChecked(name, rawValue), 'True', 'False')"
|
||||||
type="hidden" />
|
type="hidden" />
|
||||||
</metal:edit>
|
</metal:edit>
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
<tal:days repeat="day days">
|
<tal:days repeat="day days">
|
||||||
<option tal:define="zDay python: str(day).zfill(2)"
|
<option tal:define="zDay python: str(day).zfill(2)"
|
||||||
tal:attributes="value zDay;
|
tal:attributes="value zDay;
|
||||||
selected python:contextObj.dateValueSelected(name, 'day', day)"
|
selected python:contextObj.dateValueSelected(name, 'day', day, rawValue)"
|
||||||
tal:content="zDay"></option>
|
tal:content="zDay"></option>
|
||||||
</tal:days>
|
</tal:days>
|
||||||
</select>
|
</select>
|
||||||
|
@ -28,7 +28,7 @@
|
||||||
<tal:months repeat="month months">
|
<tal:months repeat="month months">
|
||||||
<option tal:define="zMonth python: str(month).zfill(2)"
|
<option tal:define="zMonth python: str(month).zfill(2)"
|
||||||
tal:attributes="value zMonth;
|
tal:attributes="value zMonth;
|
||||||
selected python:contextObj.dateValueSelected(name, 'month', month)"
|
selected python:contextObj.dateValueSelected(name, 'month', month, rawValue)"
|
||||||
tal:content="zMonth"></option>
|
tal:content="zMonth"></option>
|
||||||
</tal:months>
|
</tal:months>
|
||||||
</select>
|
</select>
|
||||||
|
@ -39,7 +39,7 @@
|
||||||
<option value="">-</option>
|
<option value="">-</option>
|
||||||
<option tal:repeat="year years"
|
<option tal:repeat="year years"
|
||||||
tal:attributes="value year;
|
tal:attributes="value year;
|
||||||
selected python:contextObj.dateValueSelected(name, 'year', year)"
|
selected python:contextObj.dateValueSelected(name, 'year', year, rawValue)"
|
||||||
tal:content="year"></option>
|
tal:content="year"></option>
|
||||||
</select>
|
</select>
|
||||||
<tal:comment replace="nothing">The icon for displaying the date chooser</tal:comment>
|
<tal:comment replace="nothing">The icon for displaying the date chooser</tal:comment>
|
||||||
|
@ -53,7 +53,7 @@
|
||||||
<tal:hours repeat="hour hours">
|
<tal:hours repeat="hour hours">
|
||||||
<option tal:define="zHour python: str(hour).zfill(2)"
|
<option tal:define="zHour python: str(hour).zfill(2)"
|
||||||
tal:attributes="value zHour;
|
tal:attributes="value zHour;
|
||||||
selected python:contextObj.dateValueSelected(name, 'hour', hour)"
|
selected python:contextObj.dateValueSelected(name, 'hour', hour, rawValue)"
|
||||||
tal:content="zHour"></option>
|
tal:content="zHour"></option>
|
||||||
</tal:hours>
|
</tal:hours>
|
||||||
</select> :
|
</select> :
|
||||||
|
@ -64,7 +64,7 @@
|
||||||
<tal:minutes repeat="minute minutes">
|
<tal:minutes repeat="minute minutes">
|
||||||
<option tal:define="zMinute python: str(minute).zfill(2)"
|
<option tal:define="zMinute python: str(minute).zfill(2)"
|
||||||
tal:attributes="value zMinute;
|
tal:attributes="value zMinute;
|
||||||
selected python:contextObj.dateValueSelected(name, 'minute', minute)"
|
selected python:contextObj.dateValueSelected(name, 'minute', minute, rawValue)"
|
||||||
tal:content="zMinute"></option>
|
tal:content="zMinute"></option>
|
||||||
</tal:minutes>
|
</tal:minutes>
|
||||||
</select>
|
</select>
|
||||||
|
|
|
@ -54,7 +54,8 @@
|
||||||
tal:define="contextMacro python: portal.skyn.widgets;
|
tal:define="contextMacro python: portal.skyn.widgets;
|
||||||
layout python: widget['layouts'][layoutType];
|
layout python: widget['layouts'][layoutType];
|
||||||
name widget/name;
|
name widget/name;
|
||||||
value python: contextObj.getFormattedValue(name);
|
value python: contextObj.getFieldValue(name);
|
||||||
|
rawValue python: contextObj.getFieldValue(name, formatted=False);
|
||||||
requestValue python: request.get(name, None);
|
requestValue python: request.get(name, None);
|
||||||
inRequest python: request.has_key(name);
|
inRequest python: request.has_key(name);
|
||||||
errors errors | python: ();
|
errors errors | python: ();
|
||||||
|
|
|
@ -4,8 +4,7 @@
|
||||||
maxMult python: widget['multiplicity'][1];
|
maxMult python: widget['multiplicity'][1];
|
||||||
severalValues python: (maxMult == None) or (maxMult > 1)">
|
severalValues python: (maxMult == None) or (maxMult > 1)">
|
||||||
<span tal:condition="python: fmt in (0, 3)"
|
<span tal:condition="python: fmt in (0, 3)"
|
||||||
tal:attributes="class widget/master_css;
|
tal:attributes="class widget/master_css; id rawValue">
|
||||||
id python: contextObj.getFormattedValue(name, forMasterId=True)">
|
|
||||||
<ul class="appyList" tal:condition="python: value and severalValues">
|
<ul class="appyList" tal:condition="python: value and severalValues">
|
||||||
<li class="appyBullet" tal:repeat="sv value"><i tal:content="structure sv"></i></li>
|
<li class="appyBullet" tal:repeat="sv value"><i tal:content="structure sv"></i></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -29,15 +28,17 @@
|
||||||
isOneLine python: fmt in (0,3)">
|
isOneLine python: fmt in (0,3)">
|
||||||
|
|
||||||
<tal:choice condition="isSelect">
|
<tal:choice condition="isSelect">
|
||||||
<select tal:define="possibleValues python:contextObj.getPossibleValues(name, withTranslations=True, withBlankValue=True)"
|
<select tal:define="possibleValues python:contextObj.getPossibleValues(name, withTranslations=True, withBlankValue=True);
|
||||||
|
multiValued python: (widget['multiplicity'][1] != 1) and True or False"
|
||||||
tal:attributes="name name;
|
tal:attributes="name name;
|
||||||
id name;
|
id name;
|
||||||
multiple python: test(widget['multiplicity'][1] != 1, 'multiple', '');
|
multiple python: multiValued and 'multiple' or '';
|
||||||
onchange python: test(isMaster, 'javascript:updateSlaves(getMasterValue(this), \'%s\')' % widget['id'], '');
|
onchange python: isMaster and ('javascript:updateSlaves(getMasterValue(this), \'%s\')' % widget['id']) or '';
|
||||||
class widget/master_css">
|
class widget/master_css;
|
||||||
|
size python: multiValued and widget['height'] or 1">
|
||||||
<option tal:repeat="possibleValue possibleValues"
|
<option tal:repeat="possibleValue possibleValues"
|
||||||
tal:attributes="value python: possibleValue[0];
|
tal:attributes="value python: possibleValue[0];
|
||||||
selected python:contextObj.fieldValueSelected(name, possibleValue[0])"
|
selected python:contextObj.fieldValueSelected(name, possibleValue[0], rawValue)"
|
||||||
tal:content="python:tool.truncateValue(possibleValue[1], widget)"></option>
|
tal:content="python:tool.truncateValue(possibleValue[1], widget)"></option>
|
||||||
</select>
|
</select>
|
||||||
</tal:choice>
|
</tal:choice>
|
||||||
|
@ -83,7 +84,7 @@
|
||||||
validator defines a list of values, with a "AND/OR" checkbox.</tal:comment>
|
validator defines a list of values, with a "AND/OR" checkbox.</tal:comment>
|
||||||
<tal:selectSearch condition="widget/isSelect">
|
<tal:selectSearch condition="widget/isSelect">
|
||||||
<tal:comment replace="nothing">The "and" / "or" radio buttons</tal:comment>
|
<tal:comment replace="nothing">The "and" / "or" radio buttons</tal:comment>
|
||||||
<tal:operator define="operName python: 'o_%s' % fieldName;
|
<tal:operator define="operName python: 'o_%s' % name;
|
||||||
orName python: '%s_or' % operName;
|
orName python: '%s_or' % operName;
|
||||||
andName python: '%s_and' % operName;"
|
andName python: '%s_and' % operName;"
|
||||||
condition="python: widget['multiplicity'][1]!=1">
|
condition="python: widget['multiplicity'][1]!=1">
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<div metal:define-macro="portlet"
|
<div metal:define-macro="portlet"
|
||||||
tal:define="tool python: context.<!toolInstanceName!>;
|
tal:define="tool python: context.<!toolInstanceName!>;
|
||||||
flavour python: tool.getFlavour(tool);"
|
flavour python: tool.getFlavour(tool);"
|
||||||
tal:condition="tool/showPortlet">
|
tal:condition="python: tool.showPortlet(context)">
|
||||||
<metal:block metal:use-macro="here/global_defines/macros/defines" />
|
<metal:block metal:use-macro="here/global_defines/macros/defines" />
|
||||||
<metal:prologue use-macro="here/skyn/page/macros/prologue"/>
|
<metal:prologue use-macro="here/skyn/page/macros/prologue"/>
|
||||||
<dl tal:define="rootClasses tool/getRootClasses;
|
<dl tal:define="rootClasses tool/getRootClasses;
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
/* <dtml-with base_properties> (do not remove this :) */
|
/* <dtml-with base_properties> (do not remove this :) */
|
||||||
/* <dtml-call "REQUEST.set('portal_url', portal_url())"> (not this either :) */
|
/* <dtml-call "REQUEST.set('portal_url', portal_url())"> (not this either :) */
|
||||||
|
|
||||||
|
textarea { width: 99%; }
|
||||||
|
|
||||||
#portal-breadcrumbs { display: none; }
|
#portal-breadcrumbs { display: none; }
|
||||||
#importedElem { color: grey; font-style: italic; }
|
#importedElem { color: grey; font-style: italic; }
|
||||||
label { font-weight: bold; font-style: italic; }
|
label { font-weight: bold; font-style: italic; }
|
||||||
|
|
|
@ -1,15 +1,11 @@
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"
|
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"
|
||||||
metal:use-macro="here/main_template/macros/master" i18n:domain="<!applicationName!>">
|
metal:use-macro="here/main_template/macros/master" i18n:domain="<!applicationName!>">
|
||||||
|
|
||||||
<!-- Disable standard Plone green tabs -->
|
<metal:disable fill-slot="top_slot"
|
||||||
<div metal:fill-slot="top_slot">
|
tal:define="dummy python:request.set('disable_border',1)" />
|
||||||
<metal:block metal:use-macro="here/global_defines/macros/defines" />
|
|
||||||
<div tal:define="dummy python:request.set('disable_border', 1)" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div metal:fill-slot="main">
|
<div metal:fill-slot="main" tal:define="tool python: context.<!toolInstanceName!>">
|
||||||
<span tal:replace="structure python: context.<!toolInstanceName!>.translateWithMapping('front_page_text')"/>
|
<!pageContent!>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en" i18n:domain="plone">
|
|
||||||
<body>
|
|
||||||
<tal:message metal:define-macro="portal_message"></tal:message>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
10
gen/po.py
10
gen/po.py
|
@ -248,10 +248,10 @@ class PoFile:
|
||||||
keepExistingOrder=True):
|
keepExistingOrder=True):
|
||||||
'''Updates the existing messages with p_newMessages.
|
'''Updates the existing messages with p_newMessages.
|
||||||
If p_removeNotNewMessages is True, all messages in self.messages
|
If p_removeNotNewMessages is True, all messages in self.messages
|
||||||
that are not in newMessages will be removed. If p_keepExistingOrder
|
that are not in newMessages will be removed, excepted if they start
|
||||||
is False, self.messages will be sorted according to p_newMessages.
|
with "custom_". If p_keepExistingOrder is False, self.messages will
|
||||||
Else, newMessages that are not yet in self.messages will be appended
|
be sorted according to p_newMessages. Else, newMessages that are not
|
||||||
to the end of self.messages.'''
|
yet in self.messages will be appended to the end of self.messages.'''
|
||||||
# First, remove not new messages if necessary
|
# First, remove not new messages if necessary
|
||||||
newIds = [m.id for m in newMessages]
|
newIds = [m.id for m in newMessages]
|
||||||
removedIds = []
|
removedIds = []
|
||||||
|
@ -259,7 +259,7 @@ class PoFile:
|
||||||
i = len(self.messages)-1
|
i = len(self.messages)-1
|
||||||
while i >= 0:
|
while i >= 0:
|
||||||
oldId = self.messages[i].id
|
oldId = self.messages[i].id
|
||||||
if oldId not in newIds:
|
if not oldId.startswith('custom_') and (oldId not in newIds):
|
||||||
del self.messages[i]
|
del self.messages[i]
|
||||||
del self.messagesDict[oldId]
|
del self.messagesDict[oldId]
|
||||||
removedIds.append(oldId)
|
removedIds.append(oldId)
|
||||||
|
|
Loading…
Reference in a new issue