1 | from zope.interface import classProvides, implements |
---|
2 | from zope.app.annotation.interfaces import IAnnotations |
---|
3 | |
---|
4 | from collective.transmogrifier.interfaces import ISection, ISectionBlueprint |
---|
5 | |
---|
6 | from Products.CMFCore import utils |
---|
7 | |
---|
8 | from quintagroup.transmogrifier.logger import VALIDATIONKEY |
---|
9 | |
---|
10 | class CatalogSourceSection(object): |
---|
11 | classProvides(ISectionBlueprint) |
---|
12 | implements(ISection) |
---|
13 | |
---|
14 | def __init__(self, transmogrifier, name, options, previous): |
---|
15 | self.previous = previous |
---|
16 | self.context = transmogrifier.context |
---|
17 | |
---|
18 | # next is for communication with 'logger' section |
---|
19 | self.anno = IAnnotations(transmogrifier) |
---|
20 | self.storage = self.anno.setdefault(VALIDATIONKEY, []) |
---|
21 | |
---|
22 | self.pathkey = options.pop('path-key', '_path') |
---|
23 | self.entrieskey = options.pop('entries-key', '_entries') |
---|
24 | |
---|
25 | # remove 'blueprint' option - it cannot be a query |
---|
26 | options.pop('blueprint') |
---|
27 | |
---|
28 | self.query = {} |
---|
29 | for k, v in options.items(): |
---|
30 | for p in v.split(';'): |
---|
31 | params = p.split('=', 1) |
---|
32 | if len(params) == 1: |
---|
33 | self.query[k] = p.strip() |
---|
34 | else : |
---|
35 | q = self.query.setdefault(k, {}) |
---|
36 | q[params[0].strip()] = params[1].strip() |
---|
37 | |
---|
38 | self.catalog = utils.getToolByName(self.context, 'portal_catalog') |
---|
39 | |
---|
40 | def __iter__(self): |
---|
41 | for item in self.previous: |
---|
42 | yield item |
---|
43 | |
---|
44 | exported = [] |
---|
45 | |
---|
46 | results = list(self.catalog(**self.query)) |
---|
47 | results.sort(key=lambda x: x.getPath()) |
---|
48 | for brain in results: |
---|
49 | # discussion items are indexed and they must be replaced to |
---|
50 | # content objects to which they correspond |
---|
51 | # we need to skip them |
---|
52 | if brain.portal_type == 'Discussion Item': |
---|
53 | path = '/'.join(brain.getPath().split('/')[:-2]) |
---|
54 | cp, id_ = path.rsplit('/', 1) |
---|
55 | brain = self.catalog(path=cp, id=id_)[0] |
---|
56 | else: |
---|
57 | path = brain.getPath() |
---|
58 | |
---|
59 | # folderish objects are tried to export twice: |
---|
60 | # when their contained items are exported and when they are |
---|
61 | # returned in catalog search results |
---|
62 | if path in exported: |
---|
63 | continue |
---|
64 | exported.append(path) |
---|
65 | |
---|
66 | # export also all parents of current object |
---|
67 | containers = [] |
---|
68 | container_path = path.rsplit('/', 1)[0] |
---|
69 | while container_path: |
---|
70 | if container_path in exported: |
---|
71 | container_path = container_path.rsplit('/', 1)[0] |
---|
72 | continue |
---|
73 | contained = self.getContained(container_path) |
---|
74 | if contained: |
---|
75 | exported.append(container_path) |
---|
76 | containers.append({ |
---|
77 | self.pathkey: '/'.join(container_path.split('/')[2:]), |
---|
78 | self.entrieskey: contained, |
---|
79 | }) |
---|
80 | container_path = container_path.rsplit('/', 1)[0] |
---|
81 | containers.reverse() |
---|
82 | # order metter for us |
---|
83 | for i in containers: |
---|
84 | self.storage.append(i[self.pathkey]) |
---|
85 | yield i |
---|
86 | |
---|
87 | item = { |
---|
88 | self.pathkey: '/'.join(path.split('/')[2:]), |
---|
89 | } |
---|
90 | if brain.is_folderish: |
---|
91 | contained = self.getContained(path) |
---|
92 | if contained: |
---|
93 | item[self.entrieskey] = contained |
---|
94 | |
---|
95 | self.storage.append(item[self.pathkey]) |
---|
96 | yield item |
---|
97 | |
---|
98 | # cleanup |
---|
99 | if VALIDATIONKEY in self.anno: |
---|
100 | del self.anno[VALIDATIONKEY] |
---|
101 | |
---|
102 | def getContained(self, path): |
---|
103 | """ Return list of (object_id, portal_type) for objects that are returned by catalog |
---|
104 | and contained in folder with given 'path'. |
---|
105 | """ |
---|
106 | results = [] |
---|
107 | seen = [] |
---|
108 | raw_results = self.catalog(path=path, **self.query) |
---|
109 | for brain in raw_results: |
---|
110 | current = brain.getPath() |
---|
111 | relative = current[len(path):] |
---|
112 | relative = relative.strip('/') |
---|
113 | if not relative: |
---|
114 | # it's object with path that was given in catalog query |
---|
115 | continue |
---|
116 | elif '/' in relative: |
---|
117 | # object stored in subfolders, we need append to results their parent folder |
---|
118 | parent_path = '/'.join([path, relative.split('/', 1)[0]]) |
---|
119 | if parent_path not in seen: |
---|
120 | res = self.catalog(path=path) #, meta_type='Folder') |
---|
121 | for i in res: |
---|
122 | if i.getPath() == parent_path: |
---|
123 | results.append(i) |
---|
124 | seen.append(parent_path) |
---|
125 | break |
---|
126 | elif current not in seen: |
---|
127 | # object is directly stored in folder, that has path given in query |
---|
128 | seen.append(current) |
---|
129 | results.append(brain) |
---|
130 | contained = [(i.getId, str(i.portal_type)) for i in results] |
---|
131 | return tuple(contained) |
---|