Commit 49d02258 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 651deb9a
......@@ -28,167 +28,174 @@
#
##############################################################################
from AccessControl import ClassSecurityInfo
from Products.ERP5Type.Globals import InitializeClass
from Products.ERP5Type import Permissions
# 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
try:
import Products.DCWorkflowGraph
except ImportError:
pass
else:
# for posix systems
DOT_EXE = 'dot'
path = os.getenv("PATH")
if path is not None:
bin_search_path = path.split(":")
dc_workflow_config_module.bin_search_path = bin_search_path
dc_workflow_config_module.DOT_EXE = DOT_EXE
def getObjectTitle(obj, REQUEST=None):
"""
Get a state/transition title to be displayed in the graph.
Monkey-patched to support translation similar to what
Products.ERP5Type.Accessor.WorkflowState.TranslatedGetter does
"""
if REQUEST is not None:
only_ids = REQUEST.get('only_ids', False)
translate = REQUEST.get('translate', False)
else:
only_ids = False
translate = False
_id = obj.getId()
title = obj.title
if not title or only_ids:
title = _id
# BBB keep Products.DCWorkflowGraph patch for a while as it solves a security issue
from AccessControl import ClassSecurityInfo
from Products.ERP5Type.Globals import InitializeClass
from Products.ERP5Type import Permissions
# 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:
if translate:
# Translate the title in all supported Localizer languages
wf_id = obj.getWorkflow().id
localizer = obj.Localizer
original_title = title
for lang in localizer.get_supported_languages():
msg_id = '%s [state in %s]' % (title, wf_id)
translated_title = localizer.erp5_ui.gettext(
msg_id,
lang=lang,
# Fallback on non-workflow state translation
default=localizer.erp5_ui.gettext(original_title,
lang=lang,
default=None))
if (translated_title is not None and
translated_title != original_title):
title += "\\n%s" % translated_title
title += "\\n(%s)"% _id
return title
from Products.DCWorkflowGraph import DCWorkflowGraph
DCWorkflowGraph.getObjectTitle = getObjectTitle
from Products.DCWorkflowGraph.config import bin_search_path, DOT_EXE
from zLOG import LOG, WARNING
import subprocess
def getGraph(self, wf_id="", format="png", REQUEST=None):
"""show a workflow as a graph, copy from:
"OpenFlowEditor":http://www.openflow.it/wwwopenflow/Download/OpenFlowEditor_0_4.tgz
Monkey-patched to specify font name and size as 'dot' uses Times font by
default which does not support Japanese:
http://www.graphviz.org/doc/fontfaq.txt
Another solution would be to modify fontconfig configuration so that Times
match Japanese font or to use Unifont which supports many code points.
"""
try:
pot = self.getPOT(wf_id, REQUEST)
except TypeError:
# DCWorkflowGraph < 0.4
pot = self.getPOT(wf_id)
try:
encoding = self.portal_properties.site_properties.getProperty(
'default_charset', 'utf-8')
except AttributeError:
# no portal_properties or site_properties, fallback to:
encoding = self.management_page_charset.lower()
result = pot.encode(encoding)
if REQUEST is None:
REQUEST = self.REQUEST
setHeader = REQUEST.RESPONSE.setHeader
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(pot)[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)
# for posix systems
DOT_EXE = 'dot'
path = os.getenv("PATH")
if path is not None:
bin_search_path = path.split(":")
dc_workflow_config_module.bin_search_path = bin_search_path
dc_workflow_config_module.DOT_EXE = DOT_EXE
def getObjectTitle(obj, REQUEST=None):
"""
Get a state/transition title to be displayed in the graph.
Monkey-patched to support translation similar to what
Products.ERP5Type.Accessor.WorkflowState.TranslatedGetter does
"""
if REQUEST is not None:
only_ids = REQUEST.get('only_ids', False)
translate = REQUEST.get('translate', False)
else:
only_ids = False
translate = False
_id = obj.getId()
title = obj.title
if not title or only_ids:
title = _id
else:
if translate:
# Translate the title in all supported Localizer languages
wf_id = obj.getWorkflow().id
localizer = obj.Localizer
original_title = title
for lang in localizer.get_supported_languages():
msg_id = '%s [state in %s]' % (title, wf_id)
translated_title = localizer.erp5_ui.gettext(
msg_id,
lang=lang,
# Fallback on non-workflow state translation
default=localizer.erp5_ui.gettext(original_title,
lang=lang,
default=None))
if (translated_title is not None and
translated_title != original_title):
title += "\\n%s" % translated_title
title += "\\n(%s)"% _id
return title
from Products.DCWorkflowGraph import DCWorkflowGraph
DCWorkflowGraph.getObjectTitle = getObjectTitle
from Products.DCWorkflowGraph.config import bin_search_path, DOT_EXE
from zLOG import LOG, WARNING
import subprocess
def getGraph(self, wf_id="", format="png", REQUEST=None):
"""show a workflow as a graph, copy from:
"OpenFlowEditor":http://www.openflow.it/wwwopenflow/Download/OpenFlowEditor_0_4.tgz
Monkey-patched to fix command injection and specify font name and size as 'dot'
uses Times font by default which does not support Japanese:
http://www.graphviz.org/doc/fontfaq.txt
Another solution would be to modify fontconfig configuration so that Times
match Japanese font or to use Unifont which supports many code points - but we
don't care, this is obsolete code.
"""
try:
pot = self.getPOT(wf_id, REQUEST)
except TypeError:
# DCWorkflowGraph < 0.4
pot = self.getPOT(wf_id)
try:
encoding = self.portal_properties.site_properties.getProperty(
'default_charset', 'utf-8')
except AttributeError:
# no portal_properties or site_properties, fallback to:
encoding = self.management_page_charset.lower()
result = pot.encode(encoding)
if REQUEST is None:
REQUEST = self.REQUEST
setHeader = REQUEST.RESPONSE.setHeader
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