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

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

#246: Added 'purge' option for exporting of portlet step

File size: 11.0 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            mapping = mapping.__of__(obj)
81
82            for name, assignment in mapping.items():
83                type_ = None
84                for schema in providedBy(assignment).flattened():
85                    type_ = self.portlet_schemata.get(schema, None)
86                    if type_ is not None:
87                        break
88
89                if type_ is not None:
90                    child = self.doc.createElement('assignment')
91                    child.setAttribute('manager', manager_name)
92                    child.setAttribute('category', CONTEXT_CATEGORY)
93                    child.setAttribute('key', '/'.join(obj.getPhysicalPath()))
94                    child.setAttribute('type', type_)
95                    child.setAttribute('name', name)
96
97                    assignment = assignment.__of__(mapping)
98                    # use existing adapter for exporting a portlet assignment
99                    handler = IPortletAssignmentExportImportHandler(assignment)
100                    handler.export_assignment(schema, self.doc, child)
101
102                    assignments.append(child)
103
104        return assignments
105
106    def exportBlacklists(self, obj):
107        assignments = []
108        for manager_name, manager in self.portlet_managers:
109            assignable = queryMultiAdapter((obj, manager), ILocalPortletAssignmentManager)
110            if assignable is None:
111                continue
112            for category in (USER_CATEGORY, GROUP_CATEGORY, CONTENT_TYPE_CATEGORY, CONTEXT_CATEGORY,):
113                child = self.doc.createElement('blacklist')
114                child.setAttribute('manager', manager_name)
115                child.setAttribute('category', category)
116           
117                status = assignable.getBlacklistStatus(category)
118                if status == True:
119                    child.setAttribute('status', u'block')
120                elif status == False:
121                    child.setAttribute('status', u'show')
122                else:
123                    child.setAttribute('status', u'acquire')
124                   
125                assignments.append(child)
126
127        return assignments
128
129
130class PortletsImporterSection(object):
131    classProvides(ISectionBlueprint)
132    implements(ISection)
133
134    def __init__(self, transmogrifier, name, options, previous):
135        self.previous = previous
136        self.context = transmogrifier.context
137
138        self.pathkey = defaultMatcher(options, 'path-key', name, 'path')
139        self.fileskey = defaultMatcher(options, 'files-key', name, 'files')
140        self.purge = options.get('purge', 'false').strip().lower() == 'true' and True or False
141
142    def __iter__(self):
143
144        for item in self.previous:
145            pathkey = self.pathkey(*item.keys())[0]
146            fileskey = self.fileskey(*item.keys())[0]
147
148            if not (pathkey and fileskey):
149                yield item; continue
150            if 'portlets' not in item[fileskey]:
151                yield item; continue
152
153            path = item[pathkey]
154            obj = self.context.unrestrictedTraverse(path, None)
155            if obj is None:         # path doesn't exist
156                yield item; continue
157
158            # Purge assignments if 'purge' option set to true
159            if self.purge:
160                for name, portletManager in getUtilitiesFor(IPortletManager):
161                    assignable = queryMultiAdapter((obj, portletManager), IPortletAssignmentMapping)
162                    if assignable is not None:
163                        for key in list(assignable.keys()):
164                            del assignable[key]
165
166            if ILocalPortletAssignable.providedBy(obj):
167                data = None
168                data = item[fileskey]['portlets']['data']
169                doc = minidom.parseString(data)
170                root = doc.documentElement
171                for elem in root.childNodes:
172                    if elem.nodeName == 'assignment':
173                        self.importAssignment(obj, elem)
174                    elif elem.nodeName == 'blacklist':
175                        self.importBlacklist(obj, elem)
176
177            yield item
178
179    def importAssignment(self, obj, node):
180        """ Import an assignment from a node
181        """
182        # 1. Determine the assignment mapping and the name
183        manager_name = node.getAttribute('manager')
184        category = node.getAttribute('category')
185
186        manager = getUtility(IPortletManager, manager_name)
187        mapping = getMultiAdapter((obj, manager), IPortletAssignmentMapping)
188
189        # 2. Either find or create the assignment
190        assignment = None
191        name = node.getAttribute('name')
192        if name:
193            assignment = mapping.get(name, None)
194
195        type_ = node.getAttribute('type')
196
197        if assignment is None:
198            portlet_factory = getUtility(IFactory, name=type_)
199            assignment = portlet_factory()
200
201            if not name:
202                chooser = INameChooser(mapping)
203                name = chooser.chooseName(None, assignment)
204
205            mapping[name] = assignment
206
207        # aq-wrap it so that complex fields will work
208        assignment = assignment.__of__(obj)
209
210        # 3. Use an adapter to update the portlet settings
211        portlet_interface = getUtility(IPortletTypeInterface, name=type_)
212        assignment_handler = IPortletAssignmentExportImportHandler(assignment)
213        assignment_handler.import_assignment(portlet_interface, node)
214
215    def importBlacklist(self, obj, node):
216        """ Import a blacklist from a node
217        """
218        manager = node.getAttribute('manager')
219        category = node.getAttribute('category')
220        status = node.getAttribute('status')
221       
222        manager = getUtility(IPortletManager, name=manager)
223       
224        assignable = queryMultiAdapter((obj, manager), ILocalPortletAssignmentManager)
225       
226        if status.lower() == 'block':
227            assignable.setBlacklistStatus(category, True)
228        elif status.lower() == 'show':
229            assignable.setBlacklistStatus(category, False)
230        elif status.lower() == 'acquire':
231            assignable.setBlacklistStatus(category, None)
232
233
234logger = logging.getLogger('quintagroup.transmogrifier.portletsimporter')
235
236class PortletAssignmentExportImportHandler(PropertyPortletAssignmentExportImportHandler):
237    """ This adapter is needed because original fails to handle text from
238        pretty printed XML file.
239    """
240    adapts(IPortletAssignment)
241
242    def extract_text(self, node):
243        text = super(PortletAssignmentExportImportHandler, self).extract_text(node)
244        # strip text to remove newlines and space character from the beginning
245        # and the end
246        return text.strip()
247
248    def import_node(self, interface, child):
249        """Import a single <property /> node
250        """
251        property_name = child.getAttribute('name')
252
253        field = interface.get(property_name, None)
254        if field is None:
255            return
256
257        field = field.bind(self.assignment)
258        value = None
259
260        # If we have a collection, we need to look at the value_type.
261        # We look for <element>value</element> child nodes and get the
262        # value from there
263        if ICollection.providedBy(field):
264            value_type = field.value_type
265            value = []
266            for element in child.childNodes:
267                if element.nodeName != 'element':
268                    continue
269                element_value = self.extract_text(element)
270                value.append(self.from_unicode(value_type, element_value))
271            value = self.field_typecast(field, value)
272
273        # Otherwise, just get the value of the <property /> node
274        else:
275            value = self.extract_text(child)
276            value = self.from_unicode(field, value)
277
278        try:
279            field.validate(value)
280        except ConstraintNotSatisfied, e:
281            logger.warning('"%s" value doesn\'t satisfy constaints for "%s:%s" field' % \
282                (value, self.assignment.__name__, field.__name__))
283
284        field.set(self.assignment, value)
Note: See TracBrowser for help on using the repository browser.