From f7143a2afdae767bce7c67f91ca2c4ecf3b88475 Mon Sep 17 00:00:00 2001 From: Gaetan Delannay Date: Fri, 18 Sep 2009 14:42:31 +0200 Subject: [PATCH] Added a CSS parser; corrected bug that prevents Appy to create a root folder in a Plone site; allowed methods from an appy tool or flavour to be available in ZPTs. --- gen/plone25/generator.py | 22 +++++++++++++----- gen/plone25/installer.py | 3 +++ gen/plone25/templates/appyWrappers.py | 2 ++ gen/plone25/wrappers/__init__.py | 2 +- shared/xml_parser.py | 32 ++++++++++++++++++++++++--- 5 files changed, 51 insertions(+), 10 deletions(-) diff --git a/gen/plone25/generator.py b/gen/plone25/generator.py index 20c3019..cc7f44a 100755 --- a/gen/plone25/generator.py +++ b/gen/plone25/generator.py @@ -149,8 +149,9 @@ class Generator(AbstractGenerator): destName = '%s.css.dtml' % self.applicationName) self.copyFile('do.py', self.repls, destFolder=self.skinsFolder, destName='%s_do.py' % self.applicationName) - self.copyFile('colophon.pt', self.repls, destFolder=self.skinsFolder) - self.copyFile('footer.pt', self.repls, destFolder=self.skinsFolder) + if self.config.minimalistPlone: + self.copyFile('colophon.pt', self.repls,destFolder=self.skinsFolder) + self.copyFile('footer.pt', self.repls, destFolder=self.skinsFolder) # Create version.txt f = open(os.path.join(self.outputFolder, 'version.txt'), 'w') f.write(self.version) @@ -502,6 +503,7 @@ class Generator(AbstractGenerator): parentWrapper = '%s_Wrapper' % k.name wrapperDef = 'class %s_Wrapper(%s, %s):\n' % \ (c.name, parentWrapper, parentClass) + wrapperDef += ' security = ClassSecurityInfo()\n' titleFound = False for attrName in c.orderedAttributes: if attrName == 'title': @@ -519,12 +521,20 @@ class Generator(AbstractGenerator): # Implicitly, the title will be added by Archetypes. So I need # to define a property for it. wrapperDef += self.generateWrapperProperty('title', String()) - # For custom tool, flavour and pod template, add a call to a method - # that allows to custom element to update the basic element. if isinstance(c, CustomToolClassDescriptor) or \ isinstance(c, CustomFlavourClassDescriptor): - wrapperDef += " if hasattr(%s, 'update'): %s.update(%s.__bases__[1])" % \ - (parentClass, parentClass, parentWrapper) + # For custom tool and flavour, add a call to a method that + # allows to customize elements from the base class. + wrapperDef += " if hasattr(%s, 'update'):\n " \ + "%s.update(%s.__bases__[1])\n" % ( + parentClass, parentClass, parentWrapper) + # For custom tool and flavour, add security declaration that + # will allow to call their methods from ZPTs. + wrapperDef += " for elem in dir(%s):\n " \ + "if not elem.startswith('_'): security.declarePublic" \ + "(elem)\n" % (parentClass) + # Register the class in Zope. + wrapperDef += 'InitializeClass(%s_Wrapper)\n' % c.name wrappers.append(wrapperDef) repls = self.repls.copy() repls['imports'] = '\n'.join(imports) diff --git a/gen/plone25/installer.py b/gen/plone25/installer.py index ae47ea8..d63e731 100644 --- a/gen/plone25/installer.py +++ b/gen/plone25/installer.py @@ -105,6 +105,9 @@ class PloneInstaller: if not hasattr(site.aq_base, self.productName): # Temporarily allow me to create Appy large plone folders getattr(site.portal_types, self.appyFolderType).global_allow = 1 + # Allow to create Appy large folders in the plone site + getattr(site.portal_types, + 'Plone Site').allowed_content_types += (self.appyFolderType,) site.invokeFactory(self.appyFolderType, self.productName, title=self.productName) getattr(site.portal_types, self.appyFolderType).global_allow = 0 diff --git a/gen/plone25/templates/appyWrappers.py b/gen/plone25/templates/appyWrappers.py index 56be4de..274b556 100755 --- a/gen/plone25/templates/appyWrappers.py +++ b/gen/plone25/templates/appyWrappers.py @@ -4,6 +4,8 @@ from appy.gen.plone25.wrappers import AbstractWrapper, FileWrapper from appy.gen.plone25.wrappers.ToolWrapper import ToolWrapper from appy.gen.plone25.wrappers.FlavourWrapper import FlavourWrapper from appy.gen.plone25.wrappers.PodTemplateWrapper import PodTemplateWrapper +from Globals import InitializeClass +from AccessControl import ClassSecurityInfo class PodTemplate(PodTemplateWrapper): diff --git a/gen/plone25/wrappers/__init__.py b/gen/plone25/wrappers/__init__.py index 8ed3369..48d2e4f 100644 --- a/gen/plone25/wrappers/__init__.py +++ b/gen/plone25/wrappers/__init__.py @@ -1,5 +1,5 @@ '''This package contains base classes for wrappers that hide to the Appy - developer the real classes used by the undelrying web framework.''' + developer the real classes used by the underlying web framework.''' # ------------------------------------------------------------------------------ import time, os.path, mimetypes diff --git a/shared/xml_parser.py b/shared/xml_parser.py index 90edef7..b78aa00 100755 --- a/shared/xml_parser.py +++ b/shared/xml_parser.py @@ -150,7 +150,7 @@ class XmlUnmarshaller(XmlParser): If "object" is specified, it means that the tag contains sub-tags, each one corresponding to the value of an attribute for this object. if "tuple" is specified, it will be converted to a list.''' - def __init__(self, klass=None): + def __init__(self, klass=None, tagTypes={}): XmlParser.__init__(self) self.klass = klass # If a klass is given here, instead of creating # a root UnmarshalledObject instance, we will create an instance of this @@ -160,6 +160,13 @@ class XmlUnmarshaller(XmlParser): # careful: we will not call the constructor of this class. We will # simply create an instance of UnmarshalledObject and dynamically change # the class of the created instance to this class. + self.tagTypes = tagTypes + # We expect that the parsed XML file will follow some conventions + # (ie, a tag that corresponds to a list has attribute type="list" or a + # tag that corresponds to an object has attribute type="object".). If + # it is not the case of p_xmlContent, you can provide the missing type + # information in p_tagTypes. Here is an example of p_tagTypes: + # {"information": "list", "days": "list", "person": "object"}. def startDocument(self): self.res = None # The resulting web of Python objects @@ -179,6 +186,8 @@ class XmlUnmarshaller(XmlParser): elemType = 'unicode' # Default value if attrs.has_key('type'): elemType = attrs['type'] + elif self.tagTypes.has_key(elem): + elemType = self.tagTypes[elem] if elemType in self.containerTags: # I must create a new container object. if elemType == 'object': newObject = UnmarshalledObject() @@ -216,7 +225,18 @@ class XmlUnmarshaller(XmlParser): currentContainer.content += value else: # Current container is an object - setattr(currentContainer, name, value) + if hasattr(currentContainer, name): + # We have already encountered a sub-object with this name. + # Having several sub-objects with the same name, we will + # create a list. + attrValue = getattr(currentContainer, name) + if not isinstance(attrValue, list): + attrValue = [attrValue, value] + else: + attrValue.append(value) + else: + attrValue = value + setattr(currentContainer, name, attrValue) def characters(self, content): e = XmlParser.characters(self, content) @@ -246,9 +266,15 @@ class XmlUnmarshaller(XmlParser): else: e.containerStack.pop() - # Alias 'unmarshall' -> 'parse' + # Alias: "unmarshall" -> "parse" unmarshall = XmlParser.parse +class CssParser(XmlUnmarshaller): + cssTags = {'rss': 'object', 'channel': 'object', 'item': 'object'} + def startDocument(self): + XmlUnmarshaller.startDocument(self) + self.tagTypes.update(self.cssTags) + # ------------------------------------------------------------------------------ class XmlMarshaller: '''This class allows to produce a XML version of a Python object, which