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

Last change on this file since 1566 was 1566, checked in by koval, 14 years ago

added handling of not satisfied constaints in the portlet assignment importer

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