source: products/quintagroup.transmogrifier/trunk/quintagroup/transmogrifier/portlets.py @ 1589

Last change on this file since 1589 was 1586, checked in by mylan, 14 years ago

Revert lost changes of export/import portlets:blacklist export/import and purge option on importing

File size: 11.1 KB
Line 
1import logging
2from xml.dom import minidom
3
4from zope.interface import classProvides, implements, providedBy
5from zope.component import getUtilitiesFor, queryMultiAdapter, getUtility, \
6    getMultiAdapter, adapts
7from zope.component.interfaces import IFactory
8from zope.app.container.interfaces import INameChooser
9from zope.schema._bootstrapinterfaces import ConstraintNotSatisfied
10from zope.schema.interfaces import ICollection
11
12
13from plone.portlets.interfaces import ILocalPortletAssignable, IPortletManager,\
14    IPortletAssignmentMapping, IPortletAssignment, ILocalPortletAssignmentManager
15from plone.portlets.constants import USER_CATEGORY, GROUP_CATEGORY, \
16    CONTENT_TYPE_CATEGORY, CONTEXT_CATEGORY
17from plone.app.portlets.interfaces import IPortletTypeInterface
18from plone.app.portlets.exportimport.interfaces import IPortletAssignmentExportImportHandler
19from plone.app.portlets.exportimport.portlets import PropertyPortletAssignmentExportImportHandler
20from plone.app.portlets.interfaces import IPortletTypeInterface
21
22from collective.transmogrifier.interfaces import ISection, ISectionBlueprint
23from collective.transmogrifier.utils import defaultMatcher
24
25class PortletsExporterSection(object):
26    classProvides(ISectionBlueprint)
27    implements(ISection)
28
29    def __init__(self, transmogrifier, name, options, previous):
30        self.previous = previous
31        self.context = transmogrifier.context
32
33        self.pathkey = defaultMatcher(options, 'path-key', name, 'path')
34        self.fileskey = options.get('files-key', '_files').strip()
35
36        self.doc = minidom.Document()
37
38    def __iter__(self):
39        self.portlet_schemata = dict([(iface, name,) for name, iface in 
40            getUtilitiesFor(IPortletTypeInterface)])
41        self.portlet_managers = list(getUtilitiesFor(IPortletManager))
42
43        for item in self.previous:
44            pathkey = self.pathkey(*item.keys())[0]
45
46            if not pathkey:
47                yield item; continue
48
49            path = item[pathkey]
50            obj = self.context.unrestrictedTraverse(path, None)
51            if obj is None:         # path doesn't exist
52                yield item; continue
53
54            if ILocalPortletAssignable.providedBy(obj):
55                data = None
56
57                root = self.doc.createElement('portlets')
58
59                for elem in self.exportAssignments(obj):
60                    root.appendChild(elem)
61                for elem in self.exportBlacklists(obj):
62                    root.appendChild(elem)
63                if root.hasChildNodes():
64                    self.doc.appendChild(root)
65                    data = self.doc.toprettyxml(indent='  ', encoding='utf-8')
66                    self.doc.unlink()
67
68                if data:
69                    files = item.setdefault(self.fileskey, {})
70                    item[self.fileskey]['portlets'] = {
71                        'name': '.portlets.xml',
72                        'data': data,
73                    }
74            yield item
75
76    def exportAssignments(self, obj):
77        assignments = []
78        for manager_name, manager in self.portlet_managers:
79            mapping = queryMultiAdapter((obj, manager), IPortletAssignmentMapping)
80            if mapping is None:
81                continue
82
83            mapping = mapping.__of__(obj)
84
85            for name, assignment in mapping.items():
86                type_ = None
87                for schema in providedBy(assignment).flattened():
88                    type_ = self.portlet_schemata.get(schema, None)
89                    if type_ is not None:
90                        break
91
92                if type_ is not None:
93                    child = self.doc.createElement('assignment')
94                    child.setAttribute('manager', manager_name)
95                    child.setAttribute('category', CONTEXT_CATEGORY)
96                    child.setAttribute('key', '/'.join(obj.getPhysicalPath()))
97                    child.setAttribute('type', type_)
98                    child.setAttribute('name', name)
99
100                    assignment = assignment.__of__(mapping)
101                    # use existing adapter for exporting a portlet assignment
102                    handler = IPortletAssignmentExportImportHandler(assignment)
103                    handler.export_assignment(schema, self.doc, child)
104
105                    assignments.append(child)
106
107        return assignments
108
109    def exportBlacklists(self, obj):
110        assignments = []
111        for manager_name, manager in self.portlet_managers:
112            assignable = queryMultiAdapter((obj, manager), ILocalPortletAssignmentManager)
113            if assignable is None:
114                continue
115            for category in (USER_CATEGORY, GROUP_CATEGORY, CONTENT_TYPE_CATEGORY, CONTEXT_CATEGORY,):
116                child = self.doc.createElement('blacklist')
117                child.setAttribute('manager', manager_name)
118                child.setAttribute('category', category)
119           
120                status = assignable.getBlacklistStatus(category)
121                if status == True:
122                    child.setAttribute('status', u'block')
123                elif status == False:
124                    child.setAttribute('status', u'show')
125                else:
126                    child.setAttribute('status', u'acquire')
127                   
128                assignments.append(child)
129
130        return assignments
131
132
133class PortletsImporterSection(object):
134    classProvides(ISectionBlueprint)
135    implements(ISection)
136
137    def __init__(self, transmogrifier, name, options, previous):
138        self.previous = previous
139        self.context = transmogrifier.context
140
141        self.pathkey = defaultMatcher(options, 'path-key', name, 'path')
142        self.fileskey = defaultMatcher(options, 'files-key', name, 'files')
143        self.purge = options.get('purge', 'false').strip().lower() == 'true' and True or False
144
145    def __iter__(self):
146
147        for item in self.previous:
148            pathkey = self.pathkey(*item.keys())[0]
149            fileskey = self.fileskey(*item.keys())[0]
150
151            if not (pathkey and fileskey):
152                yield item; continue
153            if 'portlets' not in item[fileskey]:
154                yield item; continue
155
156            path = item[pathkey]
157            obj = self.context.unrestrictedTraverse(path, None)
158            if obj is None:         # path doesn't exist
159                yield item; continue
160
161            # Purge assignments if 'purge' option set to true
162            if self.purge:
163                for name, portletManager in getUtilitiesFor(IPortletManager):
164                    assignable = queryMultiAdapter((obj, portletManager), IPortletAssignmentMapping)
165                    if assignable is not None:
166                        for key in list(assignable.keys()):
167                            del assignable[key]
168
169            if ILocalPortletAssignable.providedBy(obj):
170                data = None
171                data = item[fileskey]['portlets']['data']
172                doc = minidom.parseString(data)
173                root = doc.documentElement
174                for elem in root.childNodes:
175                    if elem.nodeName == 'assignment':
176                        self.importAssignment(obj, elem)
177                    elif elem.nodeName == 'blacklist':
178                        self.importBlacklist(obj, elem)
179
180            yield item
181
182    def importAssignment(self, obj, node):
183        """ Import an assignment from a node
184        """
185        # 1. Determine the assignment mapping and the name
186        manager_name = node.getAttribute('manager')
187        category = node.getAttribute('category')
188
189        manager = getUtility(IPortletManager, manager_name)
190        mapping = getMultiAdapter((obj, manager), IPortletAssignmentMapping)
191        if mapping is None:
192            return
193
194        # 2. Either find or create the assignment
195        assignment = None
196        name = node.getAttribute('name')
197        if name:
198            assignment = mapping.get(name, None)
199
200        type_ = node.getAttribute('type')
201
202        if assignment is None:
203            portlet_factory = getUtility(IFactory, name=type_)
204            assignment = portlet_factory()
205
206            if not name:
207                chooser = INameChooser(mapping)
208                name = chooser.chooseName(None, assignment)
209
210            mapping[name] = assignment
211
212        # aq-wrap it so that complex fields will work
213        assignment = assignment.__of__(obj)
214
215        # 3. Use an adapter to update the portlet settings
216        portlet_interface = getUtility(IPortletTypeInterface, name=type_)
217        assignment_handler = IPortletAssignmentExportImportHandler(assignment)
218        assignment_handler.import_assignment(portlet_interface, node)
219
220    def importBlacklist(self, obj, node):
221        """ Import a blacklist from a node
222        """
223        manager = node.getAttribute('manager')
224        category = node.getAttribute('category')
225        status = node.getAttribute('status')
226       
227        manager = getUtility(IPortletManager, name=manager)
228       
229        assignable = queryMultiAdapter((obj, manager), ILocalPortletAssignmentManager)
230       
231        if status.lower() == 'block':
232            assignable.setBlacklistStatus(category, True)
233        elif status.lower() == 'show':
234            assignable.setBlacklistStatus(category, False)
235        elif status.lower() == 'acquire':
236            assignable.setBlacklistStatus(category, None)
237
238
239logger = logging.getLogger('quintagroup.transmogrifier.portletsimporter')
240
241class PortletAssignmentExportImportHandler(PropertyPortletAssignmentExportImportHandler):
242    """ This adapter is needed because original fails to handle text from
243        pretty printed XML file.
244    """
245    adapts(IPortletAssignment)
246
247    def extract_text(self, node):
248        text = super(PortletAssignmentExportImportHandler, self).extract_text(node)
249        # strip text to remove newlines and space character from the beginning
250        # and the end
251        return text.strip()
252
253    def import_node(self, interface, child):
254        """Import a single <property /> node
255        """
256        property_name = child.getAttribute('name')
257
258        field = interface.get(property_name, None)
259        if field is None:
260            return
261
262        field = field.bind(self.assignment)
263        value = None
264
265        # If we have a collection, we need to look at the value_type.
266        # We look for <element>value</element> child nodes and get the
267        # value from there
268        if ICollection.providedBy(field):
269            value_type = field.value_type
270            value = []
271            for element in child.childNodes:
272                if element.nodeName != 'element':
273                    continue
274                element_value = self.extract_text(element)
275                value.append(self.from_unicode(value_type, element_value))
276            value = self.field_typecast(field, value)
277
278        # Otherwise, just get the value of the <property /> node
279        else:
280            value = self.extract_text(child)
281            value = self.from_unicode(field, value)
282
283        try:
284            field.validate(value)
285        except ConstraintNotSatisfied, e:
286            logger.warning('"%s" value doesn\'t satisfy constaints for "%s:%s" field' % \
287                (value, self.assignment.__name__, field.__name__))
288
289        field.set(self.assignment, value)
Note: See TracBrowser for help on using the repository browser.