| 1 |
# (C) Copyright 2004 Nuxeo SARL <http://nuxeo.com> |
|---|
| 2 |
# Author: Emmanuel Pietriga <ep@nuxeo.com> |
|---|
| 3 |
# |
|---|
| 4 |
# This program is free software; you can redistribute it and/or modify |
|---|
| 5 |
# it under the terms of the GNU General Public License version 2 as published |
|---|
| 6 |
# by the Free Software Foundation. |
|---|
| 7 |
# |
|---|
| 8 |
# This program is distributed in the hope that it will be useful, |
|---|
| 9 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 10 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 11 |
# GNU General Public License for more details. |
|---|
| 12 |
# |
|---|
| 13 |
# You should have received a copy of the GNU General Public License |
|---|
| 14 |
# along with this program; if not, write to the Free Software |
|---|
| 15 |
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA |
|---|
| 16 |
# 02111-1307, USA. |
|---|
| 17 |
# |
|---|
| 18 |
# $Id$ |
|---|
| 19 |
|
|---|
| 20 |
from Globals import InitializeClass |
|---|
| 21 |
|
|---|
| 22 |
from Products.CMFCore.permissions import ManagePortal |
|---|
| 23 |
from Products.CPSIO.BaseExporter import BaseExporter |
|---|
| 24 |
from Products.CPSIO.IOBase import IOBase |
|---|
| 25 |
|
|---|
| 26 |
from AccessControl import ClassSecurityInfo |
|---|
| 27 |
|
|---|
| 28 |
from elementtree.ElementTree import ElementTree, Element, SubElement |
|---|
| 29 |
|
|---|
| 30 |
import os |
|---|
| 31 |
from types import ListType, TupleType |
|---|
| 32 |
|
|---|
| 33 |
from zLOG import LOG, DEBUG, INFO, WARNING, ERROR |
|---|
| 34 |
|
|---|
| 35 |
MAIN_NAMESPACE_URI = 'http://www.nuxeo.com/2004/06/' |
|---|
| 36 |
|
|---|
| 37 |
class Exporter(BaseExporter): |
|---|
| 38 |
|
|---|
| 39 |
options_template = 'plone2exporter_form' |
|---|
| 40 |
|
|---|
| 41 |
security = ClassSecurityInfo() |
|---|
| 42 |
security.declareObjectPublic() |
|---|
| 43 |
|
|---|
| 44 |
__roles__ = None |
|---|
| 45 |
__allow_access_to_unprotected_subobjects__ = 1 |
|---|
| 46 |
|
|---|
| 47 |
def __init__(self, portal): |
|---|
| 48 |
self.portal = portal |
|---|
| 49 |
self.ns_uri = MAIN_NAMESPACE_URI + 'cps3#' |
|---|
| 50 |
|
|---|
| 51 |
security.declareProtected(ManagePortal, 'export') |
|---|
| 52 |
def exportFile(self): |
|---|
| 53 |
"""Main Export""" |
|---|
| 54 |
|
|---|
| 55 |
self.log("Exporting to file " + self.file_path) |
|---|
| 56 |
LOG("Exporting to file ", DEBUG, self.file_path) |
|---|
| 57 |
root = self.buildTree() |
|---|
| 58 |
if 'export_portal_types' in self.options: |
|---|
| 59 |
file_name = PortalTypeExporter(self.portal, self.dir_name).exportFile() |
|---|
| 60 |
el = SubElement(root, "{%s}cpsportaltypes" % self.ns_uri) |
|---|
| 61 |
el.set('ref', file_name) |
|---|
| 62 |
if 'export_workflows' in self.options: |
|---|
| 63 |
file_name = WorkflowExporter(self.portal, self.dir_name).exportFile() |
|---|
| 64 |
el = SubElement(root, "{%s}cpsworkflows" % self.ns_uri) |
|---|
| 65 |
el.set('ref', file_name) |
|---|
| 66 |
if ('export_hierarchy' in self.options or |
|---|
| 67 |
'export_documents' in self.options): |
|---|
| 68 |
ehms = [item for item in self.options if item.startswith('export_hierarchy_')] |
|---|
| 69 |
if ehms: |
|---|
| 70 |
ehm = ehms[0][17:] |
|---|
| 71 |
else: |
|---|
| 72 |
ehm = None |
|---|
| 73 |
file_name = HierarchyExporter(self.portal, self.dir_name, ehm).exportFile() |
|---|
| 74 |
el = SubElement(root, "{%s}cpshierarchy" % self.ns_uri) |
|---|
| 75 |
el.set('ref', file_name) |
|---|
| 76 |
doc = ElementTree(root) |
|---|
| 77 |
doc.write(self.file_path, encoding="iso-8859-15") |
|---|
| 78 |
|
|---|
| 79 |
self.archiveExport() |
|---|
| 80 |
|
|---|
| 81 |
def buildTree(self): |
|---|
| 82 |
root = Element("{%s}cpsdefinitions" % self.ns_uri) |
|---|
| 83 |
return root |
|---|
| 84 |
|
|---|
| 85 |
|
|---|
| 86 |
InitializeClass(Exporter) |
|---|
| 87 |
|
|---|
| 88 |
# |
|---|
| 89 |
# CMF/Plone 2 Portal Type Exporter |
|---|
| 90 |
# |
|---|
| 91 |
class PortalTypeExporter(IOBase): |
|---|
| 92 |
|
|---|
| 93 |
def __init__(self, portal, dir_name): |
|---|
| 94 |
self.portal = portal |
|---|
| 95 |
self.pt_tool = self.portal.portal_types |
|---|
| 96 |
self.file_name = 'portal_types' |
|---|
| 97 |
self.file_path = os.path.join(CLIENT_HOME, dir_name, self.file_name) |
|---|
| 98 |
self.ns_uri = MAIN_NAMESPACE_URI + 'cpsportaltypes#' |
|---|
| 99 |
|
|---|
| 100 |
def exportFile(self): |
|---|
| 101 |
"""Export portal types""" |
|---|
| 102 |
|
|---|
| 103 |
self.log("Exporting portal types to file " + self.file_path) |
|---|
| 104 |
LOG("Exporting portal types to file", DEBUG, self.file_path) |
|---|
| 105 |
|
|---|
| 106 |
root = self.buildTree() |
|---|
| 107 |
doc = ElementTree(root) |
|---|
| 108 |
doc.write(self.file_path, encoding="iso-8859-15") |
|---|
| 109 |
return self.file_name |
|---|
| 110 |
|
|---|
| 111 |
def buildTree(self): |
|---|
| 112 |
"""Build the elementtree representing all FTIs""" |
|---|
| 113 |
|
|---|
| 114 |
root = Element("{%s}portalTypes" % self.ns_uri) |
|---|
| 115 |
|
|---|
| 116 |
for pt_id, pt in self.pt_tool.objectItems(): |
|---|
| 117 |
el = self.exportCMFFTI(pt_id, pt, root) |
|---|
| 118 |
|
|---|
| 119 |
return root |
|---|
| 120 |
|
|---|
| 121 |
def exportCMFFTI(self, pt_id, fti, parent): |
|---|
| 122 |
"""Build an elementtree representation of a Factory-based Type |
|---|
| 123 |
Information object""" |
|---|
| 124 |
|
|---|
| 125 |
el = SubElement(parent, "{%s}cmffti" % self.ns_uri) |
|---|
| 126 |
el.set('id', pt_id) |
|---|
| 127 |
# process properties |
|---|
| 128 |
for prop in self._getFTIProperties(fti): |
|---|
| 129 |
# export some properties as attributes |
|---|
| 130 |
if prop[0] in ('content_icon', 'content_meta_type', 'product', |
|---|
| 131 |
'factory', 'immediate_view', 'global_allow', |
|---|
| 132 |
'filter_content_types', 'allow_discussion'): |
|---|
| 133 |
if prop[1] is not None: |
|---|
| 134 |
el.set(prop[0], str(prop[1])) |
|---|
| 135 |
else: |
|---|
| 136 |
el.set(prop[0], '') |
|---|
| 137 |
# export all other as elements - this includes the ones |
|---|
| 138 |
# we do not know anything about |
|---|
| 139 |
else: |
|---|
| 140 |
el2 = SubElement(el, "{%s}%s" % (self.ns_uri, prop[0])) |
|---|
| 141 |
if isinstance(prop[1], ListType) or isinstance(prop[1], TupleType): |
|---|
| 142 |
el2.text = ','.join([token for token in prop[1]]) |
|---|
| 143 |
else: |
|---|
| 144 |
el2.text = str(prop[1]) |
|---|
| 145 |
el2.set('type', type(prop[1]).__name__) |
|---|
| 146 |
|
|---|
| 147 |
# process actions |
|---|
| 148 |
self.exportActions(fti, el) |
|---|
| 149 |
return el |
|---|
| 150 |
|
|---|
| 151 |
def exportActions(self, fti, parent): |
|---|
| 152 |
"""Process actions associated with an FTI""" |
|---|
| 153 |
|
|---|
| 154 |
el = SubElement(parent, "{%s}actions" % self.ns_uri) |
|---|
| 155 |
for action in self._getActions(fti): |
|---|
| 156 |
el2 = SubElement(el, "{%s}action" % self.ns_uri) |
|---|
| 157 |
el2.set('id', action.getId()) |
|---|
| 158 |
el2.set('name', action.Title()) |
|---|
| 159 |
el2.set('category', action.getCategory()) |
|---|
| 160 |
if action.getVisibility(): |
|---|
| 161 |
el2.set('visibility', '1') |
|---|
| 162 |
else: |
|---|
| 163 |
el2.set('visibility', '0') |
|---|
| 164 |
el3 = SubElement(el2, "{%s}expression" % self.ns_uri) |
|---|
| 165 |
el3.text = action.getActionExpression() |
|---|
| 166 |
if action.getCondition(): |
|---|
| 167 |
el3 = SubElement(el2, "{%s}condition" % self.ns_uri) |
|---|
| 168 |
el3.text = action.getCondition() |
|---|
| 169 |
perms = action.getPermissions() |
|---|
| 170 |
if len(perms) and perms[0]: |
|---|
| 171 |
# no permissions <=> perms = ('',) |
|---|
| 172 |
el3 = SubElement(el2, "{%s}permission" % self.ns_uri) |
|---|
| 173 |
el3.text = ','.join([token for token in action.getPermissions()]) |
|---|
| 174 |
|
|---|
| 175 |
def _getFTIProperties(self, fti): |
|---|
| 176 |
"""Get all properties of a given FTI""" |
|---|
| 177 |
|
|---|
| 178 |
return fti.propertyItems() |
|---|
| 179 |
|
|---|
| 180 |
def _getActions(self, fti): |
|---|
| 181 |
"""Get action definitions (python ds)""" |
|---|
| 182 |
|
|---|
| 183 |
return fti.listActions() |
|---|
| 184 |
|
|---|
| 185 |
# |
|---|
| 186 |
# CMF/Plone 2 Workflow Exporter |
|---|
| 187 |
# |
|---|
| 188 |
class WorkflowExporter(IOBase): |
|---|
| 189 |
|
|---|
| 190 |
def __init__(self, portal, dir_name): |
|---|
| 191 |
self.portal = portal |
|---|
| 192 |
self.wtool = self.portal.portal_workflow |
|---|
| 193 |
self.pt_tool = self.portal.portal_types |
|---|
| 194 |
self.file_name = 'workflows' |
|---|
| 195 |
self.file_path = os.path.join(CLIENT_HOME, dir_name, self.file_name) |
|---|
| 196 |
self.ns_uri = MAIN_NAMESPACE_URI + 'cpsworkflows#' |
|---|
| 197 |
|
|---|
| 198 |
def exportFile(self): |
|---|
| 199 |
"""Export workflows""" |
|---|
| 200 |
|
|---|
| 201 |
self.log("Exporting workflows to file " + self.file_path) |
|---|
| 202 |
LOG("Exporting workflows to file", DEBUG, self.file_path) |
|---|
| 203 |
|
|---|
| 204 |
root = self.buildTree() |
|---|
| 205 |
doc = ElementTree(root) |
|---|
| 206 |
doc.write(self.file_path, encoding="iso-8859-15") |
|---|
| 207 |
return self.file_name |
|---|
| 208 |
|
|---|
| 209 |
def buildTree(self): |
|---|
| 210 |
"""Build the elementtree representing all workflows""" |
|---|
| 211 |
|
|---|
| 212 |
root = Element("{%s}workflows" % self.ns_uri) |
|---|
| 213 |
|
|---|
| 214 |
# first, get chains at the portal_work level |
|---|
| 215 |
el = SubElement(root, "{%s}globalChains" % self.ns_uri) |
|---|
| 216 |
for portal_type in self.pt_tool.objectIds(): |
|---|
| 217 |
chain = list(self.wtool.getChainFor(portal_type)) |
|---|
| 218 |
if chain: |
|---|
| 219 |
el2 = SubElement(el, "{%s}globalChain" % self.ns_uri) |
|---|
| 220 |
el2.set('chain', ','.join(chain)) |
|---|
| 221 |
el2.set('portal_type', portal_type) |
|---|
| 222 |
|
|---|
| 223 |
el = SubElement(root, "{%s}workflowDefinitions" % self.ns_uri) |
|---|
| 224 |
for wf_id, wf in self.wtool.objectItems(): |
|---|
| 225 |
self.buildWorkflow(wf_id, wf, el) |
|---|
| 226 |
# provide trigger type mapping in case constant values change |
|---|
| 227 |
# in the code |
|---|
| 228 |
el = SubElement(root, "{%s}triggerTypes" % self.ns_uri) |
|---|
| 229 |
from Products.DCWorkflow import Transitions |
|---|
| 230 |
for trigger_type in [attr for attr in Transitions.__dict__.keys() |
|---|
| 231 |
if attr.startswith('TRIGGER_')]: |
|---|
| 232 |
el2 = SubElement(el, "{%s}triggerType" % self.ns_uri) |
|---|
| 233 |
el2.set('name', trigger_type) |
|---|
| 234 |
el2.set('ref', str(getattr(Transitions, trigger_type))) |
|---|
| 235 |
# there are no transition behaviors in CMF/Plone, but this |
|---|
| 236 |
# is a required element of the relax-ng schema |
|---|
| 237 |
el = SubElement(root, "{%s}transitionBehaviors" % self.ns_uri) |
|---|
| 238 |
return root |
|---|
| 239 |
|
|---|
| 240 |
def buildWorkflow(self, wf_id, workflow, parent): |
|---|
| 241 |
"""Build an elementtree representation of a workflow""" |
|---|
| 242 |
|
|---|
| 243 |
el = SubElement(parent, "{%s}workflow" % self.ns_uri) |
|---|
| 244 |
el.set('id', wf_id) |
|---|
| 245 |
el.set('title', workflow.title) |
|---|
| 246 |
if workflow.state_var: |
|---|
| 247 |
el.set('state_variable', workflow.state_var) |
|---|
| 248 |
if workflow.permissions: |
|---|
| 249 |
el.set('permissions', ','.join(list(workflow.permissions))) |
|---|
| 250 |
|
|---|
| 251 |
self.buildStates(workflow.states.objectValues(), el) |
|---|
| 252 |
self.buildTransitions(workflow.transitions.objectValues(), el) |
|---|
| 253 |
self.buildScripts(workflow.scripts.objectValues(), el) |
|---|
| 254 |
self.buildVariables(workflow.variables.objectValues(), el) |
|---|
| 255 |
|
|---|
| 256 |
def buildStates(self, states, parent): |
|---|
| 257 |
"""Build an elementtree representation of a workflow's states""" |
|---|
| 258 |
|
|---|
| 259 |
el = SubElement(parent, "{%s}states" % self.ns_uri) |
|---|
| 260 |
for state in states: |
|---|
| 261 |
el2 = SubElement(el, "{%s}state" % self.ns_uri) |
|---|
| 262 |
el2.set('id', state.id) |
|---|
| 263 |
el2.set('title', state.title) |
|---|
| 264 |
el2.set('description', state.description) |
|---|
| 265 |
if state.transitions: |
|---|
| 266 |
el2.set('allowedTransitions', ','.join(list(state.transitions))) |
|---|
| 267 |
if state.permissions: |
|---|
| 268 |
el3 = SubElement(el2, "{%s}permissions" % self.ns_uri) |
|---|
| 269 |
for permission in state.permissions: |
|---|
| 270 |
el4 = SubElement(el3, "{%s}permission" % self.ns_uri) |
|---|
| 271 |
el4.set('title', permission) |
|---|
| 272 |
perm_info = state.getPermissionInfo(permission) |
|---|
| 273 |
el4.set('acquired', str(perm_info.get('acquired', 1))) |
|---|
| 274 |
roles = perm_info.get('roles', []) |
|---|
| 275 |
if roles: |
|---|
| 276 |
el4.set('roles', ','.join(roles)) |
|---|
| 277 |
if state.var_values: |
|---|
| 278 |
el3 = SubElement(el2, "{%s}variableValues" % self.ns_uri) |
|---|
| 279 |
for var_name, var_value in state.var_values.items(): |
|---|
| 280 |
el4 = SubElement(el3, "{%s}variableValue" % self.ns_uri) |
|---|
| 281 |
el4.set('name', str(var_name)) |
|---|
| 282 |
el4.set('value', str(var_value)) |
|---|
| 283 |
el4.set('type', type(var_value).__name__) |
|---|
| 284 |
|
|---|
| 285 |
def buildTransitions(self, transitions, parent): |
|---|
| 286 |
"""Build an elementtree representation of a workflow's transitions""" |
|---|
| 287 |
|
|---|
| 288 |
el = SubElement(parent, "{%s}transitions" % self.ns_uri) |
|---|
| 289 |
|
|---|
| 290 |
for transition in transitions: |
|---|
| 291 |
el2 = SubElement(el, "{%s}transition" % self.ns_uri) |
|---|
| 292 |
el2.set('id',transition.getId()) |
|---|
| 293 |
# DCWorkflow Transition data |
|---|
| 294 |
el2.set('title', transition.title) |
|---|
| 295 |
el2.set('description', transition.description) |
|---|
| 296 |
el2.set('new_state_id', transition.new_state_id) |
|---|
| 297 |
# trigger_type is an int |
|---|
| 298 |
el2.set('trigger_type', str(transition.trigger_type)) |
|---|
| 299 |
if transition.actbox_name: |
|---|
| 300 |
el2.set('actbox_name', transition.actbox_name) |
|---|
| 301 |
if transition.actbox_url: |
|---|
| 302 |
el2.set('actbox_url', transition.actbox_url) |
|---|
| 303 |
if transition.actbox_category: |
|---|
| 304 |
el2.set('actbox_category', transition.actbox_category) |
|---|
| 305 |
if transition.script_name: |
|---|
| 306 |
el2.set('script_name', transition.script_name) |
|---|
| 307 |
if transition.after_script_name: |
|---|
| 308 |
el2.set('after_script_name', transition.after_script_name) |
|---|
| 309 |
guard = transition.getGuard() |
|---|
| 310 |
g_permissions = guard.getPermissionsText() |
|---|
| 311 |
g_roles = guard.getRolesText() |
|---|
| 312 |
g_expression = guard.getExprText() |
|---|
| 313 |
if g_permissions or g_roles or g_expression: |
|---|
| 314 |
el3 = SubElement(el2, "{%s}guard" % self.ns_uri) |
|---|
| 315 |
if g_permissions: |
|---|
| 316 |
el3.set('permissions', g_permissions) |
|---|
| 317 |
if g_roles: |
|---|
| 318 |
el3.set('roles', g_roles) |
|---|
| 319 |
if g_expression: |
|---|
| 320 |
el3.set('expr', g_expression) |
|---|
| 321 |
|
|---|
| 322 |
def buildScripts(self, scripts, parent): |
|---|
| 323 |
"""Build an elementtree representation of a workflow's scripts""" |
|---|
| 324 |
|
|---|
| 325 |
el = SubElement(parent, "{%s}scripts" % self.ns_uri) |
|---|
| 326 |
for script in scripts: |
|---|
| 327 |
el2 = SubElement(el, "{%s}script" % self.ns_uri) |
|---|
| 328 |
el2.set('id', script.id) |
|---|
| 329 |
el2.set('title', script.title) |
|---|
| 330 |
# get script code, remove lines starting with ##bind (not necessary) |
|---|
| 331 |
code = script.read() |
|---|
| 332 |
code_lines = code.splitlines(1) |
|---|
| 333 |
code_lines = [line for line in code_lines if not line.startswith('##bind')] |
|---|
| 334 |
code = ''.join(code_lines) |
|---|
| 335 |
el3 = SubElement(el2, "{%s}code" % self.ns_uri) |
|---|
| 336 |
el3.set('xml:space', 'preserve') |
|---|
| 337 |
el3.text = code |
|---|
| 338 |
if getattr(script, '_proxy_roles', None): |
|---|
| 339 |
el2.set('proxy_roles', ','.join(list(script._proxy_roles))) |
|---|
| 340 |
if getattr(script, '_owner', None): |
|---|
| 341 |
el2.set('owner', str(script._owner)) |
|---|
| 342 |
|
|---|
| 343 |
def buildVariables(self, variables, parent): |
|---|
| 344 |
"""Build an elementtree representation of a workflow's variables""" |
|---|
| 345 |
|
|---|
| 346 |
el = SubElement(parent, "{%s}variables" % self.ns_uri) |
|---|
| 347 |
for variable in variables: |
|---|
| 348 |
el2 = SubElement(el, "{%s}variable" % self.ns_uri) |
|---|
| 349 |
el2.set('id', variable.id) |
|---|
| 350 |
el2.set('description', variable.description) |
|---|
| 351 |
if variable.for_catalog: |
|---|
| 352 |
el2.set('availableToCatalog', '1') |
|---|
| 353 |
else: |
|---|
| 354 |
el2.set('availableToCatalog', '0') |
|---|
| 355 |
if variable.for_status: |
|---|
| 356 |
el2.set('storeInWorkflowStatus', '1') |
|---|
| 357 |
else: |
|---|
| 358 |
el2.set('storeInWorkflowStatus', '0') |
|---|
| 359 |
if variable.default_value: |
|---|
| 360 |
el3 = SubElement(el2, "{%s}defaultValue" % self.ns_uri) |
|---|
| 361 |
el3.text = variable.default_value |
|---|
| 362 |
det = variable.getDefaultExprText() |
|---|
| 363 |
if det: |
|---|
| 364 |
el3 = SubElement(el2, "{%s}defaultExpression" % self.ns_uri) |
|---|
| 365 |
el3.text = det |
|---|
| 366 |
if variable.update_always: |
|---|
| 367 |
el2.set('always_update', '1') |
|---|
| 368 |
else: |
|---|
| 369 |
el2.set('always_update', '0') |
|---|
| 370 |
guard = variable.getInfoGuard() |
|---|
| 371 |
g_permissions = guard.getPermissionsText() |
|---|
| 372 |
g_roles = guard.getRolesText() |
|---|
| 373 |
g_expression = guard.getExprText() |
|---|
| 374 |
if g_permissions or g_roles or g_expression: |
|---|
| 375 |
el3 = SubElement(el2, "{%s}guard" % self.ns_uri) |
|---|
| 376 |
if g_permissions: |
|---|
| 377 |
el3.set('permissions', g_permissions) |
|---|
| 378 |
if g_roles: |
|---|
| 379 |
el3.set('roles', g_roles) |
|---|
| 380 |
if g_expression: |
|---|
| 381 |
el3.set('expr', g_expression) |
|---|
| 382 |
|
|---|
| 383 |
# |
|---|
| 384 |
# CMF/Plone 2 Hierarchy Exporter |
|---|
| 385 |
# |
|---|
| 386 |
class HierarchyExporter(IOBase): |
|---|
| 387 |
|
|---|
| 388 |
def __init__(self, portal, dir_name, export_hierarchy_method): |
|---|
| 389 |
self.portal = portal |
|---|
| 390 |
self.mtool = self.portal.portal_membership |
|---|
| 391 |
self.file_name = 'hierarchy' |
|---|
| 392 |
self.file_path = os.path.join(CLIENT_HOME, dir_name, self.file_name) |
|---|
| 393 |
self.ns_uri = MAIN_NAMESPACE_URI + 'cpshierarchy#' |
|---|
| 394 |
# should be wssc, ws or sc |
|---|
| 395 |
self.export_hierarchy_method = export_hierarchy_method |
|---|
| 396 |
|
|---|
| 397 |
def exportFile(self): |
|---|
| 398 |
"""Export hierarchy""" |
|---|
| 399 |
|
|---|
| 400 |
self.log("Exporting hierarchy to file " + self.file_path) |
|---|
| 401 |
LOG("Exporting hierarchy to file", DEBUG, self.file_path) |
|---|
| 402 |
|
|---|
| 403 |
root = self.buildTree() |
|---|
| 404 |
doc = ElementTree(root) |
|---|
| 405 |
doc.write(self.file_path, encoding="utf-8") |
|---|
| 406 |
return self.file_name |
|---|
| 407 |
|
|---|
| 408 |
def buildTree(self): |
|---|
| 409 |
"""Build the elementtree representing hierarchy""" |
|---|
| 410 |
|
|---|
| 411 |
root = Element("{%s}hierarchy" % self.ns_uri) |
|---|
| 412 |
|
|---|
| 413 |
if self.export_hierarchy_method in ('wssc', 'ws'): |
|---|
| 414 |
ws_el = self.createFolder('workspaces', 'Workspace', root, |
|---|
| 415 |
{'title': 'Workspaces'}, |
|---|
| 416 |
{'Title': 'Workspaces'}) |
|---|
| 417 |
|
|---|
| 418 |
if self.export_hierarchy_method in ('wssc', 'sc'): |
|---|
| 419 |
sc_el = self.createFolder('sections', 'Section', root, |
|---|
| 420 |
{'title': 'Sections'}, |
|---|
| 421 |
{'Title': 'Sections'}) |
|---|
| 422 |
|
|---|
| 423 |
# get all CMF/Plone hierarchy elements which are direct children |
|---|
| 424 |
# of the portal object (typically Members/ and other folders) |
|---|
| 425 |
# and do a recursive descent of the tree |
|---|
| 426 |
for object in self.portal.objectValues(): |
|---|
| 427 |
if object.meta_type in ('Plone Folder', 'Large Plone Folder', |
|---|
| 428 |
'Folder'): |
|---|
| 429 |
if self.export_hierarchy_method in ('wssc', 'ws'): |
|---|
| 430 |
self.buildFolder(object, ws_el, 'Workspace') |
|---|
| 431 |
if (self.export_hierarchy_method in ('wssc', 'sc') |
|---|
| 432 |
and object.id != 'Members'): |
|---|
| 433 |
# do not duplicate Members sub-hierarchy as sections |
|---|
| 434 |
# (does not really make sense, or does it?) |
|---|
| 435 |
self.buildFolder(object, sc_el, 'Section') |
|---|
| 436 |
|
|---|
| 437 |
return root |
|---|
| 438 |
|
|---|
| 439 |
def createFolder(self, folder_id, portal_type, parent, props, dm): |
|---|
| 440 |
"""Create a folder from scratch (not actually present |
|---|
| 441 |
in the portal's hierarchy)""" |
|---|
| 442 |
|
|---|
| 443 |
el = SubElement(parent, "{%s}folder" % self.ns_uri) |
|---|
| 444 |
# folder properties |
|---|
| 445 |
el.set('id', folder_id) |
|---|
| 446 |
el.set('portal_type', portal_type) |
|---|
| 447 |
el2 = SubElement(el, "{%s}datamodel" % self.ns_uri) |
|---|
| 448 |
for field_id, field_val in dm.items(): |
|---|
| 449 |
el3 = SubElement(el2, "{%s}field" % self.ns_uri) |
|---|
| 450 |
el3.text = str(field_val) |
|---|
| 451 |
el3.set('name', field_id) |
|---|
| 452 |
el3.set('type', type(field_val).__name__) |
|---|
| 453 |
el2 = SubElement(el, "{%s}properties" % self.ns_uri) |
|---|
| 454 |
for property_id, property_val in props.items(): |
|---|
| 455 |
el3 = SubElement(el2, "{%s}%s" % (self.ns_uri, property_id)) |
|---|
| 456 |
el3.text = str(property_val) |
|---|
| 457 |
el3.set('type', type(property_val).__name__) |
|---|
| 458 |
return el |
|---|
| 459 |
|
|---|
| 460 |
def buildFolder(self, folder, parent, portal_type): |
|---|
| 461 |
"""Build an elementtree representation of a folder""" |
|---|
| 462 |
|
|---|
| 463 |
if self.export_hierarchy_method in ('wssc', 'ws'): |
|---|
| 464 |
el = SubElement(parent, "{%s}folder" % self.ns_uri) |
|---|
| 465 |
# folder properties |
|---|
| 466 |
el.set('id', folder.id) |
|---|
| 467 |
el.set('portal_type', portal_type) |
|---|
| 468 |
# required by schema, even if empty |
|---|
| 469 |
el2a = SubElement(el, "{%s}datamodel" % self.ns_uri) |
|---|
| 470 |
el2b = SubElement(el, "{%s}properties" % self.ns_uri) |
|---|
| 471 |
for property_id, property_val in folder.propertyItems(): |
|---|
| 472 |
if property_val: |
|---|
| 473 |
el3 = SubElement(el2b, "{%s}%s" % (self.ns_uri, property_id)) |
|---|
| 474 |
el3.text = str(property_val) |
|---|
| 475 |
el3.set('type', type(property_val).__name__) |
|---|
| 476 |
if property_id == 'title': |
|---|
| 477 |
# 'Title' seems to be the only property that |
|---|
| 478 |
# can be retrieved and mapped to a CPS datamodel field |
|---|
| 479 |
el3 = SubElement(el2a, "{%s}field" % self.ns_uri) |
|---|
| 480 |
el3.text = str(property_val) |
|---|
| 481 |
el3.set('name', 'Title') |
|---|
| 482 |
el3.set('type', 'str') |
|---|
| 483 |
|
|---|
| 484 |
# process its children |
|---|
| 485 |
for object in folder.objectValues(): |
|---|
| 486 |
if object.meta_type in ('Plone Folder', 'Large Plone Folder', |
|---|
| 487 |
'Folder'): |
|---|
| 488 |
self.buildFolder(object, el, portal_type) |
|---|