diff --git a/gen/plone25/descriptors.py b/gen/plone25/descriptors.py index d77a4c3..c8583de 100644 --- a/gen/plone25/descriptors.py +++ b/gen/plone25/descriptors.py @@ -11,7 +11,8 @@ from utils import stringify import appy.gen import appy.gen.descriptors from appy.gen.po import PoMessage -from appy.gen import Date, String, State, Transition, Type, Search, Selection +from appy.gen import Date, String, State, Transition, Type, Search, \ + Selection, Import from appy.gen.utils import GroupDescr, PageDescr, produceNiceMessage, \ sequenceTypes TABS = 4 # Number of blanks in a Python indentation. @@ -486,6 +487,20 @@ class ArchetypesClassDescriptor(ClassDescriptor): res = self.isFolder(theClass.__bases__[0]) return res + def getCreateMean(self, type='Import'): + '''Returns the mean for this class that corresponds to p_type, or + None if the class does not support this create mean.''' + if not self.klass.__dict__.has_key('create'): return None + else: + means = self.klass.create + if not means: return None + if not isinstance(means, tuple) and not isinstance(means, list): + means = [means] + for mean in means: + exec 'found = isinstance(mean, %s)' % type + if found: return mean + return None + @staticmethod def getSearches(klass): '''Returns the list of searches that are defined on this class.''' diff --git a/gen/plone25/generator.py b/gen/plone25/generator.py index ede67e5..1e0f49a 100644 --- a/gen/plone25/generator.py +++ b/gen/plone25/generator.py @@ -615,6 +615,9 @@ class Generator(AbstractGenerator): Flavour._appy_addQueryResultColumns(classDescr) # Add the search-related fields. Flavour._appy_addSearchRelatedFields(classDescr) + importMean = classDescr.getCreateMean('Import') + if importMean: + Flavour._appy_addImportRelatedFields(classDescr) Flavour._appy_addWorkflowFields(self.flavourDescr) Flavour._appy_addWorkflowFields(self.podTemplateDescr) # Generate the flavour class and related i18n messages diff --git a/gen/plone25/mixins/FlavourMixin.py b/gen/plone25/mixins/FlavourMixin.py index c06e4b1..5a67ad6 100644 --- a/gen/plone25/mixins/FlavourMixin.py +++ b/gen/plone25/mixins/FlavourMixin.py @@ -1,4 +1,5 @@ # ------------------------------------------------------------------------------ +import os, os.path import appy.gen from appy.gen import Type from appy.gen.plone25.mixins import AbstractMixin @@ -148,4 +149,26 @@ class FlavourMixin(AbstractMixin): dAttr = self._appy_getTypeAsDict(attrName, attr, appyClass) res.append((attrName, dAttr)) return res + + def getImportElements(self, contentType): + '''Returns the list of elements that can be imported from p_path for + p_contentType.''' + tool = self.getParentNode() + appyClass = tool.getAppyClass(contentType) + importParams = tool.getCreateMeans(appyClass)['import'] + onElement = importParams['onElement'].__get__('') + sortMethod = importParams['sort'] + if sortMethod: sortMethod = sortMethod.__get__('') + elems = [] + importPath = getattr(self, 'importPathFor%s' % contentType) + for elem in os.listdir(importPath): + elemFullPath = os.path.join(importParams['path'], elem) + elemInfo = onElement(elemFullPath) + if elemInfo: + elemInfo.insert(0, elemFullPath) # To the result, I add the full + # path of the elem, which will not be shown. + elems.append(elemInfo) + if sortMethod: + elems = sortMethod(elems) + return [importParams['headers'], elems] # ------------------------------------------------------------------------------ diff --git a/gen/plone25/mixins/ToolMixin.py b/gen/plone25/mixins/ToolMixin.py index eb6eaf8..3827826 100644 --- a/gen/plone25/mixins/ToolMixin.py +++ b/gen/plone25/mixins/ToolMixin.py @@ -364,26 +364,6 @@ class ToolMixin(AbstractMixin): res[means.id] = means.__dict__ return res - def getImportElements(self, contentType): - '''Returns the list of elements that can be imported from p_path for - p_contentType.''' - appyClass = self.getAppyClass(contentType) - importParams = self.getCreateMeans(appyClass)['import'] - onElement = importParams['onElement'].__get__('') - sortMethod = importParams['sort'] - if sortMethod: sortMethod = sortMethod.__get__('') - elems = [] - for elem in os.listdir(importParams['path']): - elemFullPath = os.path.join(importParams['path'], elem) - elemInfo = onElement(elemFullPath) - if elemInfo: - elemInfo.insert(0, elemFullPath) # To the result, I add the full - # path of the elem, which will not be shown. - elems.append(elemInfo) - if sortMethod: - elems = sortMethod(elems) - return [importParams['headers'], elems] - def onImportObjects(self): '''This method is called when the user wants to create objects from external data.''' diff --git a/gen/plone25/model.py b/gen/plone25/model.py index f7fdf8a..546ba1f 100644 --- a/gen/plone25/model.py +++ b/gen/plone25/model.py @@ -206,6 +206,18 @@ class Flavour(ModelClass): page='userInterface', group=classDescr.klass.__name__) klass._appy_addField(fieldName, fieldType, classDescr) + @classmethod + def _appy_addImportRelatedFields(klass, classDescr): + '''Adds, for class p_classDescr, attributes related to the import + functionality for class p_classDescr.''' + className = classDescr.name + # Field that defines the path of the files to import. + fieldName = 'importPathFor%s' % className + defValue = classDescr.getCreateMean('Import').path + fieldType = String(page='data', multiplicity=(1,1), default=defValue, + group=classDescr.klass.__name__) + klass._appy_addField(fieldName, fieldType, classDescr) + @classmethod def _appy_addWorkflowFields(klass, classDescr): '''Adds, for a given p_classDescr, the workflow-related fields.''' diff --git a/gen/plone25/skin/import.pt b/gen/plone25/skin/import.pt index 7bfffdf..fd7ce29 100644 --- a/gen/plone25/skin/import.pt +++ b/gen/plone25/skin/import.pt @@ -16,7 +16,8 @@ tal:define="appFolder context/getParentNode; contentType request/type_name; tool python: portal.get('portal_%s' % appFolder.id.lower()); - importElems python: tool.getImportElements(contentType); + flavour python: tool.getFlavour(contentType); + importElems python: flavour.getImportElements(contentType); global allAreImported python:True">
diff --git a/gen/plone25/wrappers/__init__.py b/gen/plone25/wrappers/__init__.py index eb7fa4e..54d54d5 100644 --- a/gen/plone25/wrappers/__init__.py +++ b/gen/plone25/wrappers/__init__.py @@ -295,6 +295,42 @@ class AbstractWrapper: if res: return res._len # It is a LazyMap instance else: return 0 + def compute(self, klass, context=None, expression=None, noSecurity=False, + **fields): + '''This method, like m_search and m_count above, performs a query on + objects of p_klass. But in this case, instead of returning a list of + matching objects (like m_search) or counting elements (like p_count), + it evaluates, on every matching object, a Python p_expression (which + may be an expression or a statement), and returns, if needed, a + result. The result may be initialized through parameter p_context. + p_expression is evaluated with 2 variables in its context: "obj" + which is the currently walked object, instance of p_klass, and "ctx", + which is the context as initialized (or not) by p_context. p_context + may be used as + (1) a variable that is updated on every call to produce a result; + (2) an input variable; + (3) both. + + The method returns p_context, modified or not by evaluation of + p_expression on every matching object. + + When you need to perform an action or computation on a lot of + objects, use this method instead of doing things like + + "for obj in self.search(MyClass,...)" + ''' + flavour = self.flavour + contentType = flavour.o.getPortalType(klass) + search = Search('customSearch', **fields) + # Initialize the context variable "ctx" + ctx = context + for brain in self.tool.o.executeQuery(contentType, flavour.number, \ + search=search, brainsOnly=True, noSecurity=noSecurity): + # Get the Appy object from the brain + obj = brain.getObject().appy() + exec expression + return ctx + def reindex(self): '''Asks a direct object reindexing. In most cases you don't have to reindex objects "manually" with this method. When an object is