Commit 9ad7275e authored by Jérome Perrin's avatar Jérome Perrin

ERP5Type/patches: prepare for removal of Products.DCWorkflowGraph

Supports the case where Products.DCWorkflowGraph is not present.
Even though we are removing Products.DCWorkflowGraph from the
software release, we don't remove this monkey patch yet, because
this monkey patch also fixed a severe security issue. We keep the
patch for the cases where a recent ERP5 runs on an old SlapOS where
the product is still there.

This change just moves the existing code in a try/except ImportError
block
parent 674d2230
...@@ -28,167 +28,174 @@ ...@@ -28,167 +28,174 @@
# #
############################################################################## ##############################################################################
from AccessControl import ClassSecurityInfo try:
from Products.ERP5Type.Globals import InitializeClass import Products.DCWorkflowGraph
from Products.ERP5Type import Permissions except ImportError:
pass
# Products.DCWorkflowGraph.config does not check the return value of
# getenv('PATH'). This fails if PATH is not defined which is the case when
# running ZEO with SlapOS for example. But, Products.DCWorkflowGraph.__init__
# imports Products.DCWorkflowGraph.config as a side-effect of importing
# getGraph, so the only solution is to create a Module which will hide the
# one from DCWorkflowGraph
from types import ModuleType
dc_workflow_config_module = ModuleType('Products.DCWorkflowGraph.config')
import sys
sys.modules['Products.DCWorkflowGraph.config'] = dc_workflow_config_module
# where is 'pot'?, add your path here
import os
DOT_EXE = 'dot'
bin_search_path = []
if os.name == 'nt':
DOT_EXE = 'dot.exe'
# patch from Joachim Bauch bauch@struktur.de
# on Windows, the path to the ATT Graphviz installation
# is read from the registry.
try:
import win32api, win32con
# make sure that "key" is defined in our except block
key = None
try:
key = win32api.RegOpenKeyEx(win32con.HKEY_LOCAL_MACHINE, r'SOFTWARE\ATT\Graphviz')
value, type = win32api.RegQueryValueEx(key, 'InstallPath')
bin_search_path = [os.path.join(str(value), 'bin')]
except:
if key: win32api.RegCloseKey(key)
# key doesn't exist
pass
except ImportError:
# win32 may be not installed...
pass
else: else:
# for posix systems # BBB keep Products.DCWorkflowGraph patch for a while as it solves a security issue
DOT_EXE = 'dot' from AccessControl import ClassSecurityInfo
path = os.getenv("PATH") from Products.ERP5Type.Globals import InitializeClass
if path is not None: from Products.ERP5Type import Permissions
bin_search_path = path.split(":")
# Products.DCWorkflowGraph.config does not check the return value of
dc_workflow_config_module.bin_search_path = bin_search_path # getenv('PATH'). This fails if PATH is not defined which is the case when
dc_workflow_config_module.DOT_EXE = DOT_EXE # running ZEO with SlapOS for example. But, Products.DCWorkflowGraph.__init__
# imports Products.DCWorkflowGraph.config as a side-effect of importing
# getGraph, so the only solution is to create a Module which will hide the
def getObjectTitle(obj, REQUEST=None): # one from DCWorkflowGraph
""" from types import ModuleType
Get a state/transition title to be displayed in the graph. dc_workflow_config_module = ModuleType('Products.DCWorkflowGraph.config')
Monkey-patched to support translation similar to what import sys
Products.ERP5Type.Accessor.WorkflowState.TranslatedGetter does sys.modules['Products.DCWorkflowGraph.config'] = dc_workflow_config_module
"""
if REQUEST is not None: # where is 'pot'?, add your path here
only_ids = REQUEST.get('only_ids', False) import os
translate = REQUEST.get('translate', False)
else: DOT_EXE = 'dot'
only_ids = False bin_search_path = []
translate = False
if os.name == 'nt':
_id = obj.getId() DOT_EXE = 'dot.exe'
title = obj.title
if not title or only_ids: # patch from Joachim Bauch bauch@struktur.de
title = _id # on Windows, the path to the ATT Graphviz installation
# is read from the registry.
try:
import win32api, win32con
# make sure that "key" is defined in our except block
key = None
try:
key = win32api.RegOpenKeyEx(win32con.HKEY_LOCAL_MACHINE, r'SOFTWARE\ATT\Graphviz')
value, type = win32api.RegQueryValueEx(key, 'InstallPath')
bin_search_path = [os.path.join(str(value), 'bin')]
except:
if key: win32api.RegCloseKey(key)
# key doesn't exist
pass
except ImportError:
# win32 may be not installed...
pass
else: else:
if translate: # for posix systems
# Translate the title in all supported Localizer languages DOT_EXE = 'dot'
wf_id = obj.getWorkflow().id path = os.getenv("PATH")
localizer = obj.Localizer if path is not None:
original_title = title bin_search_path = path.split(":")
for lang in localizer.get_supported_languages():
msg_id = '%s [state in %s]' % (title, wf_id) dc_workflow_config_module.bin_search_path = bin_search_path
translated_title = localizer.erp5_ui.gettext( dc_workflow_config_module.DOT_EXE = DOT_EXE
msg_id,
lang=lang,
# Fallback on non-workflow state translation def getObjectTitle(obj, REQUEST=None):
default=localizer.erp5_ui.gettext(original_title, """
lang=lang, Get a state/transition title to be displayed in the graph.
default=None))
Monkey-patched to support translation similar to what
if (translated_title is not None and Products.ERP5Type.Accessor.WorkflowState.TranslatedGetter does
translated_title != original_title): """
if REQUEST is not None:
title += "\\n%s" % translated_title only_ids = REQUEST.get('only_ids', False)
translate = REQUEST.get('translate', False)
title += "\\n(%s)"% _id else:
only_ids = False
return title translate = False
from Products.DCWorkflowGraph import DCWorkflowGraph _id = obj.getId()
DCWorkflowGraph.getObjectTitle = getObjectTitle title = obj.title
if not title or only_ids:
from Products.DCWorkflowGraph.config import bin_search_path, DOT_EXE title = _id
from zLOG import LOG, WARNING else:
import subprocess if translate:
# Translate the title in all supported Localizer languages
def getGraph(self, wf_id="", format="png", REQUEST=None): wf_id = obj.getWorkflow().id
"""show a workflow as a graph, copy from: localizer = obj.Localizer
"OpenFlowEditor":http://www.openflow.it/wwwopenflow/Download/OpenFlowEditor_0_4.tgz original_title = title
for lang in localizer.get_supported_languages():
Monkey-patched to specify font name and size as 'dot' uses Times font by msg_id = '%s [state in %s]' % (title, wf_id)
default which does not support Japanese: translated_title = localizer.erp5_ui.gettext(
msg_id,
http://www.graphviz.org/doc/fontfaq.txt lang=lang,
# Fallback on non-workflow state translation
Another solution would be to modify fontconfig configuration so that Times default=localizer.erp5_ui.gettext(original_title,
match Japanese font or to use Unifont which supports many code points. lang=lang,
""" default=None))
try:
pot = self.getPOT(wf_id, REQUEST) if (translated_title is not None and
except TypeError: translated_title != original_title):
# DCWorkflowGraph < 0.4
pot = self.getPOT(wf_id) title += "\\n%s" % translated_title
try:
encoding = self.portal_properties.site_properties.getProperty( title += "\\n(%s)"% _id
'default_charset', 'utf-8')
except AttributeError: return title
# no portal_properties or site_properties, fallback to:
encoding = self.management_page_charset.lower() from Products.DCWorkflowGraph import DCWorkflowGraph
result = pot.encode(encoding) DCWorkflowGraph.getObjectTitle = getObjectTitle
if REQUEST is None: from Products.DCWorkflowGraph.config import bin_search_path, DOT_EXE
REQUEST = self.REQUEST from zLOG import LOG, WARNING
setHeader = REQUEST.RESPONSE.setHeader import subprocess
if format != 'dot': def getGraph(self, wf_id="", format="png", REQUEST=None):
p = subprocess.Popen((DCWorkflowGraph.bin_search(DOT_EXE), """show a workflow as a graph, copy from:
'-Nfontname=IPAexGothic', '-Nfontsize=10', "OpenFlowEditor":http://www.openflow.it/wwwopenflow/Download/OpenFlowEditor_0_4.tgz
'-Efontname=IPAexGothic', '-Efontsize=10',
'-T%s' % format), Monkey-patched to fix command injection and specify font name and size as 'dot'
stdin=subprocess.PIPE, stdout=subprocess.PIPE) uses Times font by default which does not support Japanese:
result = p.communicate(result)[0]
http://www.graphviz.org/doc/fontfaq.txt
setHeader('Content-Type', 'image/%s' % format)
else: Another solution would be to modify fontconfig configuration so that Times
filename = wf_id or self.getId() match Japanese font or to use Unifont which supports many code points - but we
setHeader('Content-Type', 'text/x-graphviz') don't care, this is obsolete code.
setHeader('Content-Disposition', 'attachment; filename=%s.dot' % filename) """
try:
if not result: pot = self.getPOT(wf_id, REQUEST)
LOG("ERP5Type.patches.DCWorkflowGraph", WARNING, except TypeError:
"Empty %s graph file" % format) # DCWorkflowGraph < 0.4
pot = self.getPOT(wf_id)
return result try:
encoding = self.portal_properties.site_properties.getProperty(
DCWorkflowGraph.getGraph = getGraph 'default_charset', 'utf-8')
except AttributeError:
from Products.DCWorkflow.DCWorkflow import DCWorkflowDefinition # no portal_properties or site_properties, fallback to:
DCWorkflowDefinition.getGraph = getGraph encoding = self.management_page_charset.lower()
DCWorkflowDefinition.getPOT = DCWorkflowGraph.getPOT result = pot.encode(encoding)
security = ClassSecurityInfo() if REQUEST is None:
security.declareProtected(Permissions.ManagePortal, 'getPOT') REQUEST = self.REQUEST
security.declareProtected(Permissions.ManagePortal, 'getGraph') setHeader = REQUEST.RESPONSE.setHeader
DCWorkflowDefinition.security = security
InitializeClass(DCWorkflowDefinition) if format != 'dot':
p = subprocess.Popen((DCWorkflowGraph.bin_search(DOT_EXE),
'-Nfontname=IPAexGothic', '-Nfontsize=10',
'-Efontname=IPAexGothic', '-Efontsize=10',
'-T%s' % format),
stdin=subprocess.PIPE, stdout=subprocess.PIPE)
result = p.communicate(result)[0]
setHeader('Content-Type', 'image/%s' % format)
else:
filename = wf_id or self.getId()
setHeader('Content-Type', 'text/x-graphviz')
setHeader('Content-Disposition', 'attachment; filename=%s.dot' % filename)
if not result:
LOG("ERP5Type.patches.DCWorkflowGraph", WARNING,
"Empty %s graph file" % format)
return result
DCWorkflowGraph.getGraph = getGraph
from Products.DCWorkflow.DCWorkflow import DCWorkflowDefinition
DCWorkflowDefinition.getGraph = getGraph
DCWorkflowDefinition.getPOT = DCWorkflowGraph.getPOT
security = ClassSecurityInfo()
security.declareProtected(Permissions.ManagePortal, 'getPOT')
security.declareProtected(Permissions.ManagePortal, 'getGraph')
DCWorkflowDefinition.security = security
InitializeClass(DCWorkflowDefinition)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment