source: products/quintagroup.transmogrifier/branches/plone-2.1/quintagroup.transmogrifier/quintagroup/transmogrifier/catalogsource.py @ 3065

Last change on this file since 3065 was 3065, checked in by kroman0, 12 years ago

vmaksymiv: Fixed catalogsource path-based exports

File size: 6.6 KB
Line 
1import copy
2import logging
3
4from zope.interface import classProvides, implements
5from zope.app.annotation.interfaces import IAnnotations
6
7from collective.transmogrifier.interfaces import ISection, ISectionBlueprint
8
9from Products.CMFCore import utils
10
11from quintagroup.transmogrifier.logger import VALIDATIONKEY
12
13logger = logging.getLogger("CatalogSourceSection")
14
15class CatalogSourceSection(object):
16    classProvides(ISectionBlueprint)
17    implements(ISection)
18
19    def __init__(self, transmogrifier, name, options, previous):
20        self.previous = previous
21        self.context = transmogrifier.context
22
23        # next is for communication with 'logger' section
24        self.anno = IAnnotations(transmogrifier)
25        self.storage = self.anno.setdefault(VALIDATIONKEY, [])
26
27        self.pathkey = options.pop('path-key', '_path')
28        self.entrieskey = options.pop('entries-key', '_entries')
29
30        # handle exclude-contained parameter
31        if "exclude-contained" in options.keys():
32            self.exclude_contained = options.pop('exclude-contained')
33            self.exclude_contained = self.exclude_contained == "true"
34        else:
35            self.exclude_contained = False
36
37        # remove 'blueprint' option - it cannot be a query
38        options.pop('blueprint')
39
40        self.query = {}
41        for k, v in options.items():
42            for p in v.split(';'):
43                params = p.split('=', 1)
44                if len(params) == 1:
45                    self.query[k] = p.strip()
46                else :
47                    q = self.query.setdefault(k, {})
48                    q[params[0].strip()] = params[1].strip()
49
50        self.catalog = utils.getToolByName(self.context, 'portal_catalog')
51
52    def __iter__(self):
53        for item in self.previous:
54            yield item
55
56        exported = []
57        exported_parents = []
58
59        results = list(self.catalog(**self.query))
60        results.sort(key=lambda x: x.getPath())
61
62        for brain in results:
63            # discussion items are indexed and they must be replaced to
64            # content objects to which they correspond
65            # we need to skip them
66            if brain.portal_type == 'Discussion Item':
67                path =  '/'.join(brain.getPath().split('/')[:-2])
68                cp, id_ = path.rsplit('/', 1)
69                brain = self.catalog(path=cp, id=id_)[0]
70            else:
71                path = brain.getPath()
72
73            # folderish objects are tried to export twice:
74            # when their contained items are exported and when they are
75            # returned in catalog search results
76            if path in exported:
77                continue
78            exported.append(path)
79
80            # export also all parents of current object
81            containers = []
82            container_path = path.rsplit('/', 1)[0]
83            while container_path:
84
85                if container_path in exported:
86                    container_path = container_path.rsplit('/', 1)[0]
87                    continue
88
89                exported_parents.append(container_path)
90
91                contained = self.getContained(container_path, results, exported_parents)
92
93                if contained:
94                    exported.append(container_path)
95                    containers.append({
96                        self.pathkey: '/'.join(container_path.split('/')[2:]),
97                        self.entrieskey: contained,
98                    })
99                container_path = container_path.rsplit('/', 1)[0]
100
101            containers.reverse()
102            # order metter for us
103            for i in containers:
104                self.storage.append(i[self.pathkey])
105                yield i
106
107            item = {
108                self.pathkey: '/'.join(path.split('/')[2:]),
109            }
110            if brain.is_folderish:
111                contained = self.getContained(path, results, exported_parents)
112                if contained:
113                    item[self.entrieskey] = contained
114
115            self.storage.append(item[self.pathkey])
116            yield item
117
118        # cleanup
119        if VALIDATIONKEY in self.anno:
120            del self.anno[VALIDATIONKEY]
121
122    def getContained(self, path, orignal_results, parents):
123        """ Return list of (object_id, portal_type) for objects that are returned by catalog
124            and contained in folder with given 'path'.
125        """
126        results = []
127        seen = []
128
129
130        # Remove the orignal path element from the query if there was one
131        query = copy.deepcopy(self.query)
132        if "path" in query:
133            del query["path"]
134
135        raw_results = self.catalog(path=path, **query)
136
137        for brain in raw_results:
138            current = brain.getPath()
139            relative = current[len(path):]
140            relative = relative.strip('/')
141            if not relative:
142                # it's object with path that was given in catalog query
143                continue
144            elif '/' in relative:
145                # object stored in subfolders, we need append to results their parent folder
146                parent_path = '/'.join([path, relative.split('/', 1)[0]])
147                if parent_path not in seen:
148                    res = self.catalog(path=path) #, meta_type='Folder')
149                    for i in res:
150                        if i.getPath() == parent_path:
151                            results.append(i)
152                            seen.append(parent_path)
153                            break
154            elif current not in seen:
155                # object is directly stored in folder, that has path given in query
156                seen.append(current)
157                results.append(brain)
158
159        def filter(r):
160
161            # Parent objects must be allowed always
162            for parent in parents:
163                if r.getPath() == parent:
164                    return True
165
166            if r["UID"] in allowed_uids:
167                return True
168            else:
169                logger.info("Excluded contained item as it did not match the orignal catalog query:" + str(r.getPath()))
170
171        if self.exclude_contained and orignal_results is not None:
172            # Filter contained results against our query, so that
173            # we do not export results from parent objects which did not match
174            # Build list of allowed object UIDs -
175            allowed_uids = [ r["UID"] for r in orignal_results ]
176
177            # All parents must be allowed always
178            filtered_results = [ r for r in results if filter(r) == True  ]
179        else:
180            # Don't filter child items
181            filtered_results = results
182
183        contained = [(i.getId, str(i.portal_type)) for i in filtered_results ]
184
185        return tuple(contained)
Note: See TracBrowser for help on using the repository browser.