Updated new.py for installing Plone 4 without buildout and added an alternative way to insert POD expressions in POD templates via fields (track-changed text still works).

This commit is contained in:
Gaetan Delannay 2010-10-27 12:06:21 +02:00
parent feca97bda3
commit 50c8a139fc
9 changed files with 1841 additions and 1825 deletions

View file

@ -1,9 +1,9 @@
'''This script allows to create a brand new read-to-use Plone/Zone instance. '''This script allows to create a brand new ready-to-use Plone/Zone instance.
As prerequisite, you must have installed Plone through the Unifier installer As prerequisite, you must have installed Plone through the Unifier installer
available at http://plone.org.''' available at http://plone.org.'''
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
import os, os.path, sys, shutil import os, os.path, sys, shutil, re
from optparse import OptionParser from optparse import OptionParser
from appy.shared.utils import cleanFolder, copyFolder from appy.shared.utils import cleanFolder, copyFolder
@ -22,12 +22,34 @@ MKZOPE_NOT_FOUND = 'Script mkzopeinstance.py not found in "%s and ' \
WRONG_INSTANCE_PATH = '"%s" must be an existing folder for creating the ' \ WRONG_INSTANCE_PATH = '"%s" must be an existing folder for creating the ' \
'instance in it.' 'instance in it.'
zopeCtl = '''#!/bin/sh
PYTHON="%s"
INSTANCE_HOME="%s"
CONFIG_FILE="$INSTANCE_HOME/etc/zope.conf"
PYTHONPATH="$INSTANCE_HOME/lib/python"
ZDCTL="%s/Zope2/Startup/zopectl.py"
export INSTANCE_HOME
export PYTHON
export PYTHONPATH
exec "$PYTHON" "$ZDCTL" -C "$CONFIG_FILE" "$@"
'''
runZope = '''#! /bin/sh
PYTHON="%s"
INSTANCE_HOME="%s"
CONFIG_FILE="$INSTANCE_HOME/etc/zope.conf"
PYTHONPATH="$INSTANCE_HOME/lib/python"
ZOPE_RUN="%s/Zope2/Startup/run.py"
export INSTANCE_HOME
export PYTHON
export PYTHONPATH
exec "$PYTHON" "$ZOPE_RUN" -C "$CONFIG_FILE" "$@"
'''
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
class NewScript: class NewScript:
'''usage: %prog ploneVersion plonePath instancePath '''usage: %prog ploneVersion plonePath instancePath
"ploneVersion" can be plone25, plone30, or plone3x "ploneVersion" can be plone25, plone30, plone3x or plone4
(plone3x can be Plone 3.2.x, Plone 3.3.5...) (plone3x represents 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 and 3.0 are typically installed in Plone 2.5 and 3.0 are typically installed in
@ -35,48 +57,7 @@ class NewScript:
installed in in /usr/local/Plone. 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', 'plone30', 'plone3x') ploneVersions = ('plone25', 'plone30', 'plone3x', 'plone4')
def createInstance(self, linksForProducts):
'''Calls the Zope script that allows to create a Zope instance and copy
into it all the Plone packages and products.'''
# Find the Python interpreter running Zope
for elem in os.listdir(self.plonePath):
pythonPath = None
elemPath = os.path.join(self.plonePath, elem)
if elem.startswith('Python-') and os.path.isdir(elemPath):
pythonPath = elemPath + '/bin/python'
if not os.path.exists(pythonPath):
raise NewError(PYTHON_EXE_NOT_FOUND % pythonPath)
break
if not pythonPath:
raise NewError(PYTHON_NOT_FOUND % self.plonePath)
# Find the Zope script mkzopeinstance.py
makeInstancePath = None
for dirname, dirs, files in os.walk(self.plonePath):
# Do not browse the buildout-cache
for fileName in files:
if (fileName == 'mkzopeinstance.py') and \
('/buildout-cache/' not in dirname):
makeInstancePath = os.path.join(dirname, fileName)
if not makeInstancePath:
raise NewError(MKZOPE_NOT_FOUND % self.plonePath)
# Execute mkzopeinstance.py with the right Python interpreter
cmd = '%s %s -d %s' % (pythonPath, makeInstancePath, self.instancePath)
print cmd
os.system(cmd)
# Now, make the instance Plone-ready
action = 'Copying'
if linksForProducts:
action = 'Symlinking'
print '%s Plone stuff in the Zope instance...' % action
if self.ploneVersion in ('plone25', 'plone30'):
self.installPlone25or30Stuff(linksForProducts)
elif self.ploneVersion == 'plone3x':
self.installPlone3Stuff()
# Clean the copied folders
cleanFolder(os.path.join(self.instancePath, 'Products'))
cleanFolder(os.path.join(self.instancePath, 'lib/python'))
def installPlone25or30Stuff(self, 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
@ -105,56 +86,9 @@ class NewScript:
# Copy thre product into the instance # Copy thre product into the instance
copyFolder(folderName, destFolder) copyFolder(folderName, destFolder)
uglyChunks = ('pkg_resources', '.declare_namespace(') filesToPatch = ('meta.zcml', 'configure.zcml', 'overrides.zcml')
def findPythonPackageInEgg(self, currentFolder): patchRex = re.compile('<includePlugins.*?/>', re.S)
'''Finds the Python package that is deeply hidden into the egg.''' def patchPlone3x(self):
# Find the file __init__.py
isFinalPackage = False
for elem in os.listdir(currentFolder):
elemPath = os.path.join(currentFolder, elem)
if elem == '__init__.py':
f = file(elemPath)
content = f.read()
f.close()
# Is it a awful egg init ?
for chunk in self.uglyChunks:
if content.find(chunk) == -1:
isFinalPackage = True # It is not an ugly egg init.
break
if not isFinalPackage:
# Maybe we are wrong: our way to identify egg-viciated __init__
# files is approximative. If we believe it is not the final package,
# but we find other Python files in the folder, we must admit that
# we've nevertheless found the final Python package.
otherPythonFiles = False
for elem in os.listdir(currentFolder):
if elem.endswith('.py') and (elem != '__init__.py'):
otherPythonFiles = True
break
if otherPythonFiles:
# Ok, this is the final Python package
return currentFolder
# Find the subfolder and find the Python package into it.
for elem in os.listdir(currentFolder):
elemPath = os.path.join(currentFolder, elem)
if os.path.isdir(elemPath):
return self.findPythonPackageInEgg(elemPath)
else:
return currentFolder
def getSubFolder(self, folder):
'''In p_folder, we now that there is only one subfolder. This method
returns the subfolder's absolute path.'''
for elem in os.listdir(folder):
elemPath = os.path.join(folder, elem)
if (elem != 'EGG-INFO') and os.path.isdir(elemPath):
return elemPath
return None
viciatedFiles = {'meta.zcml': 'includePlugins',
'configure.zcml': 'includePlugins',
'overrides.zcml': 'includePluginsOverrides'}
def patchPlone(self, productsFolder, libFolder):
'''Auto-proclaimed ugly code in z3c forces us to patch some files '''Auto-proclaimed ugly code in z3c forces us to patch some files
in Products.CMFPlone because these guys make the assumption that in Products.CMFPlone because these guys make the assumption that
"plone.xxx" packages are within eggs when they've implemented their "plone.xxx" packages are within eggs when they've implemented their
@ -163,102 +97,156 @@ class NewScript:
CMFPlone files. It does not seem to affect Plone behaviour. Indeed, CMFPlone files. It does not seem to affect Plone behaviour. Indeed,
these directives seem to be useful only when adding sad (ie, non these directives seem to be useful only when adding sad (ie, non
Appy) Plone plug-ins.''' Appy) Plone plug-ins.'''
ploneFolder = os.path.join(productsFolder, 'CMFPlone') j = os.path.join
# Patch viciated files ploneFolder = os.path.join(self.productsFolder, 'CMFPlone')
for fileName, uglyDirective in self.viciatedFiles.iteritems(): # Patch files
for fileName in self.filesToPatch:
filePath = os.path.join(ploneFolder, fileName) filePath = os.path.join(ploneFolder, fileName)
f = file(filePath) f = file(filePath)
fileContent = f.read() fileContent = f.read()
f.close() f.close()
if fileContent.find(uglyDirective) != -1:
toReplace = '<%s package="plone" file="%s" />' % \
(uglyDirective, fileName)
fileContent = fileContent.replace(toReplace, '')
f = file(filePath, 'w') f = file(filePath, 'w')
f.write(fileContent) f.write(self.patchRex.sub('<!--Del. includePlugins-->',fileContent))
f.close() f.close()
def installPlone3Stuff(self): filesToPatch2 = ('profiles/default/skins.xml')
'''Here, we will copy all Plone3-related stuff in the Zope instance def patchPlone4(self):
we've created, to get a full Plone-ready Zope instance.''' '''Patches Plone 4 that can't live without buildout as-is.'''
# All Plone 3 eggs are in buildout-cache/eggs. We will extract from self.patchPlone3x() # We still need this for Plone 4 as well.
# those silly overstructured folder hierarchies the standard Python # bin/zopectl
# packages that lie in it, and copy them in the instance. Within these content = zopeCtl % (self.pythonPath, self.instancePath, self.zopePath)
# eggs, we need to distinguish: f = file('%s/bin/zopectl' % self.instancePath, 'w')
# - standard Python packages that will be copied in f.write(content)
# <zopeInstance>/lib/python (ie, like Appy applications) f.close()
# - Zope products that will be copied in # bin/runzope
# <zopeInstance>/Products (ie, like Appy generated Zope products) content = runZope % (self.pythonPath, self.instancePath, self.zopePath)
f = file('%s/bin/runzope' % self.instancePath, 'w')
f.write(content)
f.close()
j = os.path.join
themeFolder = '%s/plonetheme' % self.libFolder
for theme in os.listdir(themeFolder):
# Create a simlink to every theme in self.productsFolder
tFolder = j(themeFolder, theme)
if not os.path.isdir(tFolder): continue
os.system('ln -s %s %s/%s' % (tFolder, self.productsFolder, theme))
# Patch skins.xml
fileName = '%s/profiles/default/skins.xml' % tFolder
f = file(fileName)
content = f.read()
f.close()
f = file(fileName, 'w')
f.write(content.replace('plonetheme.%s:' % theme, '%s/' % theme))
f.close()
# As eggs have been deleted, Plone can't tell which version of Zope and
# Plone are there. So we patch the code that tries to get Plone and Zope
# versions.
codeFile = "%s/pkg_resources.py" % self.libFolder
f = file(codeFile)
content = f.read()
f.close()
content = content.replace("raise DistributionNotFound(req)",
"dist = Distribution(project_name=req.project_name, " \
"version='1.1.1', platform='linux2', location='%s')" % \
self.instancePath)
f = file(codeFile, 'w')
f.write(content)
f.close()
def copyEggs(self):
'''Copy content of eggs into the Zope instance.'''
j = os.path.join j = os.path.join
eggsFolder = j(self.plonePath, 'buildout-cache/eggs') eggsFolder = j(self.plonePath, 'buildout-cache/eggs')
productsFolder = j(self.instancePath, 'Products') self.ploneThemes = []
libFolder = j(self.instancePath, 'lib/python')
for name in os.listdir(eggsFolder): for name in os.listdir(eggsFolder):
eggMainFolder = j(eggsFolder, name) if name == 'EGG-INFO': continue
if name.startswith('Products.'): absName = j(eggsFolder, name)
# A Zope product. Copy its content in Products. # Copy every file or sub-folder into self.libFolder or
innerFolder= self.getSubFolder(self.getSubFolder(eggMainFolder)) # self.productsFolder.
destFolder = j(productsFolder, os.path.basename(innerFolder)) for fileName in os.listdir(absName):
copyFolder(innerFolder, destFolder) absFileName = j(absName, fileName)
if fileName == 'Products' and not name.startswith('Zope2-'):
# Copy every sub-folder into self.productsFolder
for folderName in os.listdir(absFileName):
absFolder = j(absFileName, folderName)
if not os.path.isdir(absFolder): continue
copyFolder(absFolder, j(self.productsFolder,folderName))
elif os.path.isdir(absFileName):
copyFolder(absFileName, j(self.libFolder, fileName))
else: else:
# A standard Python package. Copy its content in lib/python. shutil.copy(absFileName, self.libFolder)
# Go into the subFolder that is not EGG-INFO.
eggFolder = self.getSubFolder(eggMainFolder) def createInstance(self, linksForProducts):
if not eggFolder: '''Calls the Zope script that allows to create a Zope instance and copy
# This egg is malformed and contains basic Python files. into it all the Plone packages and products.'''
# Copy those files directly in libFolder. j = os.path.join
for fileName in os.listdir(eggMainFolder): # Find the Python interpreter running Zope
if fileName.endswith('.py'): for elem in os.listdir(self.plonePath):
fullFileName= j(eggMainFolder, fileName) pythonPath = None
shutil.copy(fullFileName, libFolder) elemPath = j(self.plonePath, elem)
continue if elem.startswith('Python-') and os.path.isdir(elemPath):
eggFolderName = os.path.basename(eggFolder) pythonPath = elemPath + '/bin/python'
if eggFolderName == 'Products': if not os.path.exists(pythonPath):
# Goddamned. This should go in productsFolder and not in raise NewError(PYTHON_EXE_NOT_FOUND % pythonPath)
# libFolder. break
innerFolder = self.getSubFolder(eggFolder) if not pythonPath:
destFolder = j(productsFolder,os.path.basename(innerFolder)) raise NewError(PYTHON_NOT_FOUND % self.plonePath)
copyFolder(innerFolder, destFolder) self.pythonPath = pythonPath
# Find the Zope script mkzopeinstance.py and Zope itself
makeInstancePath = None
self.zopePath = None
for dirname, dirs, files in os.walk(self.plonePath):
# Find Zope
for folderName in dirs:
if folderName.startswith('Zope2-'):
self.zopePath = j(dirname, folderName)
# Find mkzopeinstance
for fileName in files:
if fileName == 'mkzopeinstance.py':
if self.ploneVersion == 'plone4':
makeInstancePath = j(dirname, fileName)
else: else:
packageFolder = self.findPythonPackageInEgg(eggFolder) if ('/buildout-cache/' not in dirname):
# Create the destination folder(s) in the instance, makeInstancePath = j(dirname, fileName)
# within libFolder if not makeInstancePath:
destFolders = [] raise NewError(MKZOPE_NOT_FOUND % self.plonePath)
if packageFolder != eggFolder: # Execute mkzopeinstance.py with the right Python interpreter.
destFolders = [eggFolderName] # For Plone4, we will call it later.
remFolders = packageFolder[len(eggFolder):] cmd = '%s %s -d %s' % (pythonPath, makeInstancePath, self.instancePath)
remFolders = remFolders.strip(os.sep) if self.ploneVersion != 'plone4':
if remFolders.find(os.sep) != -1: print cmd
# There are more subfolders os.system(cmd)
destFolders += remFolders.split(os.sep)[:-1] # Now, make the instance Plone-ready
if destFolders: action = 'Copying'
# We must create the subfolders (if not yet created) if linksForProducts:
# before copying the Python package. action = 'Symlinking'
baseFolder = libFolder print '%s Plone stuff in the Zope instance...' % action
for subFolder in destFolders: if self.ploneVersion in ('plone25', 'plone30'):
subFolderPath = j(baseFolder,subFolder) self.installPlone25or30Stuff(linksForProducts)
if not os.path.exists(subFolderPath): elif self.ploneVersion in ('plone3x', 'plone4'):
os.mkdir(subFolderPath) self.copyEggs()
# Create an empty __init__.py in it. if self.ploneVersion == 'plone3x':
init = j(subFolderPath,'__init__.py') self.patchPlone3x()
f = file(init, 'w') elif self.ploneVersion == 'plone4':
f.write('# Makes me a Python package.') # Create the Zope instance
f.close() os.environ['PYTHONPATH'] = '%s:%s' % \
baseFolder = subFolderPath (j(self.instancePath,'Products'),
destFolder = os.sep.join(destFolders) j(self.instancePath, 'lib/python'))
destFolder = j(libFolder, destFolder) print cmd
if not os.path.exists(destFolder): os.system(cmd)
os.makedirs(destFolder) self.patchPlone4()
else: # Remove .bat files under Linux
destFolder = libFolder if os.name == 'posix':
destFolder = j(destFolder, os.path.basename(packageFolder)) cleanFolder(j(self.instancePath, 'bin'), exts=('.bat',))
copyFolder(packageFolder, destFolder)
self.patchPlone(productsFolder, libFolder)
def manageArgs(self, args): def manageArgs(self, args):
'''Ensures that the script was called with the right parameters.''' '''Ensures that the script was called with the right parameters.'''
if len(args) != 3: raise NewError(WRONG_NB_OF_ARGS) if len(args) != 3: raise NewError(WRONG_NB_OF_ARGS)
self.ploneVersion, self.plonePath, self.instancePath = args self.ploneVersion, self.plonePath, self.instancePath = args
# Add some more folder definitions
j = os.path.join
self.productsFolder = j(self.instancePath, 'Products')
self.libFolder = j(self.instancePath, 'lib/python')
# Check Plone version # Check Plone version
if self.ploneVersion not in self.ploneVersions: if self.ploneVersion not in self.ploneVersions:
raise NewError(WRONG_PLONE_VERSION % str(self.ploneVersions)) raise NewError(WRONG_PLONE_VERSION % str(self.ploneVersions))

View file

@ -189,6 +189,7 @@
</table> </table>
<tal:comment replace="nothing">Show forward reference(s)</tal:comment> <tal:comment replace="nothing">Show forward reference(s)</tal:comment>
<table tal:attributes="class python:test(innerRef, '', 'listing nosort'); <table tal:attributes="class python:test(innerRef, '', 'listing nosort');
width python:test(innerRef, '100%', appyType['layouts']['view']['width']);" width python:test(innerRef, '100%', appyType['layouts']['view']['width']);"
align="right" tal:condition="python: not appyType['isBack'] and objs" cellpadding="0" cellspacing="0"> align="right" tal:condition="python: not appyType['isBack'] and objs" cellpadding="0" cellspacing="0">

View file

@ -100,8 +100,8 @@ class PodEnvironment(OdfEnvironment):
# For the currently read expression, is there style-related information # For the currently read expression, is there style-related information
# associated with it? # associated with it?
self.exprHasStyle = False self.exprHasStyle = False
self.gotNamespaces = False # Namespace definitions were not already # Namespace definitions are not already encountered.
# encountered self.gotNamespaces = False
# Store inserts # Store inserts
self.inserts = inserts self.inserts = inserts
# Currently walked "if" actions # Currently walked "if" actions
@ -208,7 +208,8 @@ class PodParser(OdfParser):
e.state = e.IGNORING e.state = e.IGNORING
elif elem == ('%s:annotation' % officeNs): elif elem == ('%s:annotation' % officeNs):
e.state = e.READING_STATEMENT e.state = e.READING_STATEMENT
elif elem == ('%s:change-start' % textNs): elif (elem == ('%s:change-start' % textNs)) or \
(elem == ('%s:conditional-text' % textNs)):
e.state = e.READING_EXPRESSION e.state = e.READING_EXPRESSION
e.exprHasStyle = False e.exprHasStyle = False
else: else:
@ -280,7 +281,8 @@ class PodParser(OdfParser):
e.currentStatement.append(statementLine) e.currentStatement.append(statementLine)
e.currentContent = '' e.currentContent = ''
elif e.state == e.READING_EXPRESSION: elif e.state == e.READING_EXPRESSION:
if elem == ('%s:change-end' % textNs): if (elem == ('%s:change-end' % textNs)) or \
(elem == ('%s:conditional-text' % textNs)):
expression = e.currentContent.strip() expression = e.currentContent.strip()
e.currentContent = '' e.currentContent = ''
# Manage expression # Manage expression

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,2 @@
old = 'OLD'
new = 'NEW'

Binary file not shown.

Binary file not shown.

View file

@ -68,7 +68,7 @@ binaryRex = re.compile(r'[\000-\006\177-\277]')
class Resource: class Resource:
'''Every instance of this class represents some web resource accessible '''Every instance of this class represents some web resource accessible
through WebDAV.''' through HTTP.'''
def __init__(self, url, username=None, password=None, measure=False): def __init__(self, url, username=None, password=None, measure=False):
self.username = username self.username = username
@ -94,9 +94,7 @@ class Resource:
else: raise 'Wrong URL: %s' % str(url) else: raise 'Wrong URL: %s' % str(url)
def __repr__(self): def __repr__(self):
port = ':' + str(self.port) return '<Dav resource at %s>' % self.url
if self.port == 80: port = ''
return '<Dav resource at %s%s/%s>' % (self.url, port, self.uri)
def updateHeaders(self, headers): def updateHeaders(self, headers):
# Add credentials if present # Add credentials if present
@ -203,12 +201,22 @@ class Resource:
if not uri: uri = self.uri if not uri: uri = self.uri
return self.sendRequest('GET', uri, headers=headers) return self.sendRequest('GET', uri, headers=headers)
def post(self, data, uri=None, headers={}): def post(self, data=None, uri=None, headers={}, type='form'):
'''Perform a HTTP POST on the server.''' '''Perform a HTTP POST on the server. If p_type is:
- "form", p_data is a dict representing form data that will be
form-encoded;
- "soap", p_data is a XML request that will be wrapped in a SOAP
message.'''
if not uri: uri = self.uri if not uri: uri = self.uri
# Prepare the data to send
if type == 'form':
# Format the form data and prepare headers # Format the form data and prepare headers
body = DataEncoder(data).encode() body = DataEncoder(data).encode()
headers['Content-Type'] = 'application/x-www-form-urlencoded' headers['Content-Type'] = 'application/x-www-form-urlencoded'
elif type =='soap':
body = data
headers['SOAPAction'] = self.url
headers['Content-Type'] = 'text/xml'
headers['Content-Length'] = str(len(body)) headers['Content-Length'] = str(len(body))
return self.sendRequest('POST', uri, headers=headers, body=body) return self.sendRequest('POST', uri, headers=headers, body=body)
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------

View file

@ -51,7 +51,10 @@ def cleanFolder(folder, exts=extsToClean, verbose=False):
def copyFolder(source, dest, cleanDest=False): def copyFolder(source, dest, cleanDest=False):
'''Copies the content of folder p_source to folder p_dest. p_dest is '''Copies the content of folder p_source to folder p_dest. p_dest is
created, with intermediary subfolders if required. If p_cleanDest is created, with intermediary subfolders if required. If p_cleanDest is
True, it removes completely p_dest if it existed.''' True, it removes completely p_dest if it existed. Else, content of
p_source will be added to possibly existing content in p_dest, excepted
if file names corresponds. In this case, file in p_source will overwrite
file in p_dest.'''
dest = os.path.abspath(dest) dest = os.path.abspath(dest)
# Delete the dest folder if required # Delete the dest folder if required
if os.path.exists(dest) and cleanDest: if os.path.exists(dest) and cleanDest: