| 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) |
|---|