[375] | 1 | from zope.interface import classProvides, implements |
---|
[436] | 2 | from zope.app.annotation.interfaces import IAnnotations |
---|
[375] | 3 | |
---|
| 4 | from collective.transmogrifier.interfaces import ISection, ISectionBlueprint |
---|
| 5 | |
---|
| 6 | from Products.CMFCore import utils |
---|
| 7 | |
---|
[436] | 8 | from quintagroup.transmogrifier.logger import VALIDATIONKEY |
---|
| 9 | |
---|
[375] | 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 | |
---|
[436] | 18 | # next is for communication with 'logger' section |
---|
| 19 | self.anno = IAnnotations(transmogrifier) |
---|
| 20 | self.storage = self.anno.setdefault(VALIDATIONKEY, []) |
---|
| 21 | |
---|
[375] | 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: |
---|
[436] | 49 | # discussion items are indexed and they must be replaced to |
---|
| 50 | # content objects to which they correspond |
---|
[375] | 51 | # we need to skip them |
---|
[436] | 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() |
---|
[375] | 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 | |
---|
[436] | 66 | # export also all parents of current object |
---|
| 67 | containers = [] |
---|
[375] | 68 | container_path = path.rsplit('/', 1)[0] |
---|
[436] | 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 | |
---|
[375] | 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 | |
---|
[436] | 95 | self.storage.append(item[self.pathkey]) |
---|
[375] | 96 | yield item |
---|
| 97 | |
---|
[436] | 98 | # cleanup |
---|
| 99 | if VALIDATIONKEY in self.anno: |
---|
| 100 | del self.anno[VALIDATIONKEY] |
---|
| 101 | |
---|
[375] | 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 = [] |
---|
[436] | 107 | seen = [] |
---|
[375] | 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('/') |
---|
[436] | 113 | if not relative: |
---|
| 114 | # it's object with path that was given in catalog query |
---|
[375] | 115 | continue |
---|
[436] | 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] |
---|
[375] | 131 | return tuple(contained) |
---|