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

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

#246: Add exporting black-lists for portlet step

File size: 9.9 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                child.setAttribute('location', '/'.join(obj.getPhysicalPath()))
117           
118                status = assignable.getBlacklistStatus(category)
119                if status == True:
120                    child.setAttribute('status', u'block')
121                elif status == False:
122                    child.setAttribute('status', u'show')
123                else:
124                    child.setAttribute('status', u'acquire')
125                   
126                assignments.append(child)
127
128        return assignments
129
130
131class PortletsImporterSection(object):
132    classProvides(ISectionBlueprint)
133    implements(ISection)
134
135    def __init__(self, transmogrifier, name, options, previous):
136        self.previous = previous
137        self.context = transmogrifier.context
138
139        self.pathkey = defaultMatcher(options, 'path-key', name, 'path')
140        self.fileskey = defaultMatcher(options, 'files-key', name, 'files')
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            if ILocalPortletAssignable.providedBy(obj):
159                data = None
160                data = item[fileskey]['portlets']['data']
161                doc = minidom.parseString(data)
162                root = doc.documentElement
163                for elem in root.childNodes:
164                    if elem.nodeName == 'assignment':
165                        self.importAssignment(obj, elem)
166                    #elif elem.nodeName == 'blacklist':
167                        #self.importBlacklist(obj, elem)
168
169            yield item
170
171    def importAssignment(self, obj, node):
172        """ Import an assignment from a node
173        """
174        # 1. Determine the assignment mapping and the name
175        manager_name = node.getAttribute('manager')
176        category = node.getAttribute('category')
177
178        manager = getUtility(IPortletManager, manager_name)
179        mapping = getMultiAdapter((obj, manager), IPortletAssignmentMapping)
180
181        # 2. Either find or create the assignment
182        assignment = None
183        name = node.getAttribute('name')
184        if name:
185            assignment = mapping.get(name, None)
186
187        type_ = node.getAttribute('type')
188
189        if assignment is None:
190            portlet_factory = getUtility(IFactory, name=type_)
191            assignment = portlet_factory()
192
193            if not name:
194                chooser = INameChooser(mapping)
195                name = chooser.chooseName(None, assignment)
196
197            mapping[name] = assignment
198
199        # aq-wrap it so that complex fields will work
200        assignment = assignment.__of__(obj)
201
202        # 3. Use an adapter to update the portlet settings
203        portlet_interface = getUtility(IPortletTypeInterface, name=type_)
204        assignment_handler = IPortletAssignmentExportImportHandler(assignment)
205        assignment_handler.import_assignment(portlet_interface, node)
206
207logger = logging.getLogger('quintagroup.transmogrifier.portletsimporter')
208
209class PortletAssignmentExportImportHandler(PropertyPortletAssignmentExportImportHandler):
210    """ This adapter is needed because original fails to handle text from
211        pretty printed XML file.
212    """
213    adapts(IPortletAssignment)
214
215    def extract_text(self, node):
216        text = super(PortletAssignmentExportImportHandler, self).extract_text(node)
217        # strip text to remove newlines and space character from the beginning
218        # and the end
219        return text.strip()
220
221    def import_node(self, interface, child):
222        """Import a single <property /> node
223        """
224        property_name = child.getAttribute('name')
225
226        field = interface.get(property_name, None)
227        if field is None:
228            return
229
230        field = field.bind(self.assignment)
231        value = None
232
233        # If we have a collection, we need to look at the value_type.
234        # We look for <element>value</element> child nodes and get the
235        # value from there
236        if ICollection.providedBy(field):
237            value_type = field.value_type
238            value = []
239            for element in child.childNodes:
240                if element.nodeName != 'element':
241                    continue
242                element_value = self.extract_text(element)
243                value.append(self.from_unicode(value_type, element_value))
244            value = self.field_typecast(field, value)
245
246        # Otherwise, just get the value of the <property /> node
247        else:
248            value = self.extract_text(child)
249            value = self.from_unicode(field, value)
250
251        try:
252            field.validate(value)
253        except ConstraintNotSatisfied, e:
254            logger.warning('"%s" value doesn\'t satisfy constaints for "%s:%s" field' % \
255                (value, self.assignment.__name__, field.__name__))
256
257        field.set(self.assignment, value)
Note: See TracBrowser for help on using the repository browser.