[Erp5-report] r20676 - in /erp5/trunk/products/ERP5Wizard: ./ Constraint/ Core/ Document/ I...
nobody at svn.erp5.org
nobody at svn.erp5.org
Fri Apr 18 16:15:20 CEST 2008
Author: ivan
Date: Fri Apr 18 16:15:20 2008
New Revision: 20676
URL: http://svn.erp5.org?rev=20676&view=rev
Log:
Initial import.
Added:
erp5/trunk/products/ERP5Wizard/
erp5/trunk/products/ERP5Wizard/Constraint/
erp5/trunk/products/ERP5Wizard/Constraint/__init__.py
erp5/trunk/products/ERP5Wizard/Core/
erp5/trunk/products/ERP5Wizard/Core/__init__.py
erp5/trunk/products/ERP5Wizard/Document/
erp5/trunk/products/ERP5Wizard/Document/__init__.py
erp5/trunk/products/ERP5Wizard/Interface/
erp5/trunk/products/ERP5Wizard/Interface/__init__.py
erp5/trunk/products/ERP5Wizard/Permissions.py
erp5/trunk/products/ERP5Wizard/PropertySheet/
erp5/trunk/products/ERP5Wizard/PropertySheet/ExpressPreference.py
erp5/trunk/products/ERP5Wizard/Tool/
erp5/trunk/products/ERP5Wizard/Tool/WizardTool.py
erp5/trunk/products/ERP5Wizard/Tool/__init__.py
erp5/trunk/products/ERP5Wizard/VERSION.txt (with props)
erp5/trunk/products/ERP5Wizard/__init__.py
erp5/trunk/products/ERP5Wizard/dtml/
erp5/trunk/products/ERP5Wizard/dtml/explainWizardTool.dtml
erp5/trunk/products/ERP5Wizard/skins/
erp5/trunk/products/ERP5Wizard/tests/
erp5/trunk/products/ERP5Wizard/tests/__init__.py
erp5/trunk/products/ERP5Wizard/tests/testGeneratorCall.py
erp5/trunk/products/ERP5Wizard/tool.png (with props)
Added: erp5/trunk/products/ERP5Wizard/Constraint/__init__.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Wizard/Constraint/__init__.py?rev=20676&view=auto
==============================================================================
(empty)
Added: erp5/trunk/products/ERP5Wizard/Core/__init__.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Wizard/Core/__init__.py?rev=20676&view=auto
==============================================================================
(empty)
Added: erp5/trunk/products/ERP5Wizard/Document/__init__.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Wizard/Document/__init__.py?rev=20676&view=auto
==============================================================================
(empty)
Added: erp5/trunk/products/ERP5Wizard/Interface/__init__.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Wizard/Interface/__init__.py?rev=20676&view=auto
==============================================================================
(empty)
Added: erp5/trunk/products/ERP5Wizard/Permissions.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Wizard/Permissions.py?rev=20676&view=auto
==============================================================================
(empty)
Added: erp5/trunk/products/ERP5Wizard/PropertySheet/ExpressPreference.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Wizard/PropertySheet/ExpressPreference.py?rev=20676&view=auto
==============================================================================
--- erp5/trunk/products/ERP5Wizard/PropertySheet/ExpressPreference.py (added)
+++ erp5/trunk/products/ERP5Wizard/PropertySheet/ExpressPreference.py Fri Apr 18 16:15:20 2008
@@ -1,0 +1,100 @@
+##############################################################################
+#
+# Copyright (c) 2007 Nexedi SARL and Contributors. All Rights Reserved.
+# Yusei TAHARA <yusei at nexedi.com>
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsability of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# garantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# This program is Free Software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+##############################################################################
+
+class ExpressPreference:
+ """
+ User Preferences for erp5 express
+
+ Contains all preferences (see portal_preferences) relative to erp5 express.
+ """
+
+ _properties = (
+ { 'id' : 'preferred_witch_tool_server_url',
+ 'description' : 'The URL of a server which provides Witch Tool',
+ 'type' : 'string',
+ 'preference' : 1,
+ 'read_permission' : 'Manage portal',
+ 'write_permission' : 'Manage portal',
+ 'mode' : 'w' },
+ { 'id' : 'preferred_witch_tool_server_root',
+ 'description' : 'The root of a server which provides Witch Tool',
+ 'type' : 'string',
+ 'preference' : 1,
+ 'read_permission' : 'Manage portal',
+ 'write_permission' : 'Manage portal',
+ 'mode' : 'w' },
+ { 'id' : 'preferred_express_subscription_status',
+ 'description' : 'ERP5 Express subscription status',
+ 'type' : 'string',
+ 'preference' : 1,
+ 'read_permission' : 'Manage portal',
+ 'write_permission' : 'Manage portal',
+ 'mode' : 'w' },
+ { 'id' : 'preferred_express_configuration_status',
+ 'description' : 'ERP5 Express configuration status',
+ 'type' : 'string',
+ 'preference' : 1,
+ 'read_permission' : 'Manage portal',
+ 'write_permission' : 'Manage portal',
+ 'mode' : 'w' },
+ { 'id' : 'preferred_express_user_id',
+ 'description' : 'ERP5 Express subscription user id',
+ 'type' : 'string',
+ 'preference' : 1,
+ 'read_permission' : 'Modify portal content',
+ 'write_permission' : 'Modify portal content',
+ 'mode' : 'w' },
+ { 'id' : 'preferred_express_password',
+ 'description' : 'ERP5 Express subscription password',
+ 'type' : 'string',
+ 'preference' : 1,
+ 'read_permission' : 'Modify portal content',
+ 'write_permission' : 'Modify portal content',
+ 'mode' : 'w' },
+ { 'id' : 'preferred_express_after_setup_script_id',
+ 'description' : 'ERP5 Express after setup script id',
+ 'type' : 'string',
+ 'preference' : 1,
+ 'read_permission' : 'Modify portal content',
+ 'write_permission' : 'Modify portal content',
+ 'mode' : 'w' },
+ { 'id' : 'preferred_express_erp5_uid',
+ 'description' : 'ERP5 Express unique ID',
+ 'type' : 'string',
+ 'preference' : 1,
+ 'read_permission' : 'Modify portal content',
+ 'write_permission' : 'Modify portal content',
+ 'mode' : 'w' },
+ { 'id' : 'preferred_express_client_uid',
+ 'description' : 'ERP5 Express client unique ID',
+ 'type' : 'string',
+ 'preference' : 1,
+ 'read_permission' : 'Modify portal content',
+ 'write_permission' : 'Modify portal content',
+ 'mode' : 'w' },
+ )
Added: erp5/trunk/products/ERP5Wizard/Tool/WizardTool.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Wizard/Tool/WizardTool.py?rev=20676&view=auto
==============================================================================
--- erp5/trunk/products/ERP5Wizard/Tool/WizardTool.py (added)
+++ erp5/trunk/products/ERP5Wizard/Tool/WizardTool.py Fri Apr 18 16:15:20 2008
@@ -1,0 +1,625 @@
+##############################################################################
+#
+# Copyright (c) 2006 Nexedi SARL and Contributors. All Rights Reserved.
+# Romain Courteaud <romain at nexedi.com>
+# Ivan Tyagov <ivan at nexedi.com>
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsability of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# garantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# This program is Free Software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+##############################################################################
+
+from AccessControl import ClassSecurityInfo
+from Globals import InitializeClass, DTMLFile
+from Products.ERP5Type.Tool.BaseTool import BaseTool
+from Products.ERP5Type import Permissions
+from Products.ERP5Wizard import _dtmldir
+from Products.CMFCore.utils import getToolByName
+from zLOG import LOG, INFO, WARNING, ERROR, DEBUG
+from cStringIO import StringIO
+from UserDict import UserDict
+import xmlrpclib, socket, sys, traceback, urllib, urllib2, base64, cgi
+from AccessControl.SecurityManagement import newSecurityManager, noSecurityManager
+import zLOG
+
+def _setSuperSecurityManager(self):
+ """ Change to super user account. """
+ user = self.getWrappedOwner()
+ newSecurityManager(self.REQUEST, user)
+
+class GeneratorCall(UserDict):
+ """ Class use to generate/interpret XML-RPC call for the wizard. """
+
+ _binary_keys = ("data", "filedata",)
+ _string_keys = ("previous", "next", "command", "server_buffer",)
+
+ def __init__(self, *args, **kw):
+ UserDict.__init__(self, *args, **kw)
+ self.convert_data = {}
+ for key in (self._binary_keys + self._string_keys):
+ self.setdefault(key, None)
+
+ def load(self, xmlrpccall):
+ """ Convert the xmlrpccall into the object. """
+ self.convert_data = xmlrpclib.loads(xmlrpccall)[0][0]
+ for binary_key in self._binary_keys:
+ if self.convert_data[binary_key] is not None:
+ if isinstance(self.convert_data[binary_key], list):
+ self[binary_key] = []
+ for item in self.convert_data[binary_key]:
+ self[binary_key].append(self._decodeData(item[16:-18]))
+ else:
+ self[binary_key] = self._decodeData(self.convert_data[binary_key][16:-18])
+ ## load string keys
+ for string_key in self._string_keys:
+ self[string_key] = self.convert_data[string_key]
+
+ def dump(self):
+ """ Dump object to a xmlrpccall. """
+ for binary_key in self._binary_keys:
+ if isinstance(self[binary_key], list):
+ ## we have list of values
+ self.convert_data[binary_key] = []
+ for item in self[binary_key]:
+ self.convert_data[binary_key].append(self._encodeData(item))
+ else:
+ if self[binary_key] is not None:
+ self.convert_data[binary_key] = self._encodeData(self[binary_key])
+ else:
+ self.convert_data[binary_key] = None
+ for string_key in self._string_keys:
+ self.convert_data[string_key] = self[string_key]
+ return xmlrpclib.dumps((self.convert_data,), 'GeneratorAnswer', allow_none=1)
+
+ def _decodeData(self, data):
+ """ Decode data. """
+ binary_decoder = xmlrpclib.Binary()
+ binary_decoder.decode(data)
+ return binary_decoder.data
+
+ def _encodeData(self, data):
+ """ Encode data to transmitable text. """
+ fp = StringIO()
+ try:
+ xmlrpclib.Binary(data=data).encode(fp)
+ return fp.getvalue()
+ finally:
+ fp.close()
+
+def getPicklableRequest(REQUEST):
+ """ Return 'pickable' request """
+ picklable_request = {}
+ for key, value in REQUEST.items():
+ picklable_request[key] = str(value)
+ return picklable_request
+
+def _generateErrorXML(error_message):
+ """ Generate HTML for displaying an error. """
+ log_message = traceback.format_exc()
+ return '<table><tr><td class="error">%s</td></tr></table>' % error_message
+
+## temporary storage or arguments passed from client to server
+#_v_server_buffer = {}
+
+## server to local preferences id translation table
+_server_to_preference_ids_map = {'client_id': 'preferred_express_client_uid',
+ 'current_bc_index': 'preferred_express_erp5_uid',
+ 'password': 'preferred_express_password',
+ 'user_id': 'preferred_express_user_id',}
+
+
+class WizardTool(BaseTool):
+ """ WizardTool is able to generate custom business templates. """
+
+ id = 'portal_wizard'
+ meta_type = 'ERP5 Wizard Tool'
+ portal_type = 'Wizard Tool'
+ isPortalContent = 1
+ isRADContent = 1
+ property_sheets = ()
+ security = ClassSecurityInfo()
+ security.declareProtected(Permissions.ManagePortal, 'manage_overview')
+ manage_overview = DTMLFile('explainWizardTool', _dtmldir )
+
+ # Stop traversing a concatenated path after the proxy method.
+ def __before_publishing_traverse__(self, self2, request):
+ path = request['TraversalRequestNameStack']
+ if path and path[-1] == 'proxy':
+ subpath = path[:-1]
+ subpath.reverse()
+ request.set('traverse_subpath', subpath)
+ path[:-1] = []
+
+ def _getProxyURL(self, subpath='', query=''):
+ # Helper method to construct an URL appropriate for proxying a request.
+ # This makes sure that URLs generated by absolute_url at a remote site
+ # will be always towards the proxy method again.
+ #
+ # Note that the code assumes that VirtualHostBase is visible. The setting
+ # of a front-end server must allow this.
+ #
+ # This should generate an URL like this:
+ #
+ # http://remotehost:9080/VirtualHostBase/http/localhost:8080/VirtualHostRoot/_vh_erp5/_vh_portal_wizard/_vh_proxy/erp5/person_module/2
+ part_list = []
+
+ server_url = self.getServerUrl().rstrip('/')
+ part_list.append(server_url)
+
+ part_list.append('VirtualHostBase')
+
+ portal_url = self.getPortalObject().absolute_url()
+ scheme, rest = urllib.splittype(portal_url)
+ addr, path = urllib.splithost(rest)
+ host, port = urllib.splitnport(addr, scheme == 'http' and 80 or 443)
+ part_list.append(scheme)
+ part_list.append('%s:%s' % (host, port))
+
+ part_list.append('VirtualHostRoot')
+
+ method_path = self.absolute_url_path() + '/proxy'
+ part_list.extend(('_vh_' + p for p in method_path.split('/') if p))
+
+ server_root = self.getServerRoot().strip('/')
+
+ if isinstance(subpath, (list, tuple)):
+ subpath = '/'.join(subpath)
+
+ if not subpath.startswith(server_root):
+ part_list.append(server_root)
+
+ part_list.append(subpath)
+
+ url = '/'.join((p for p in part_list if p))
+ if query:
+ url = url + '?' + query
+ return url
+
+ def _getSubsribedUserAndPassword(self):
+ """Retrieve the username and password for the subscription from
+ the system."""
+ user = self.getExpressConfigurationPreference('preferred_express_user_id', '')
+ pw = self.getExpressConfigurationPreference('preferred_express_password', '')
+ return (user, pw)
+
+ # This is a custom opener director for not handling redirections
+ # and errors automatically. This is necessary because the proxy
+ # should pass all results to a client as they are.
+ simple_opener_director = urllib2.OpenerDirector()
+ for name in ('ProxyHandler', 'UnknownHandler', 'HTTPHandler',
+ 'FTPHandler', 'FileHandler', 'HTTPSHandler'):
+ handler = getattr(urllib2, name, None)
+ if handler is not None:
+ simple_opener_director.add_handler(handler())
+
+ security.declareProtected(Permissions.View, 'proxy')
+ def proxy(self, **kw):
+ """Proxy a request to a server."""
+ if self.REQUEST['REQUEST_METHOD'] != 'GET':
+ # XXX this depends on the internal of HTTPRequest.
+ pos = self.REQUEST.stdin.tell()
+ self.REQUEST.stdin.seek(0)
+ # XXX if filesize is too big, this might cause a problem.
+ data = self.REQUEST.stdin.read()
+ self.REQUEST.stdin.seek(pos)
+ else:
+ data = None
+
+ content_type = self.REQUEST.get_header('content-type')
+
+ # XXX if ":method" trick is used, then remove it from subpath.
+ if self.REQUEST.traverse_subpath:
+ if data is not None:
+ user_input = data
+ else:
+ user_input = self.REQUEST.QUERY_STRING
+ if user_input:
+ mark = ':method'
+ content_type_value = None
+ content_type_dict = None
+ if content_type:
+ content_type_value, content_type_dict = cgi.parse_header(content_type)
+ if content_type_value=='multipart/form-data':
+ fp = StringIO(user_input)
+ user_input_dict = cgi.parse_multipart(fp, content_type_dict)
+ else:
+ user_input_dict = cgi.parse_qs(user_input)
+
+ for i in user_input_dict:
+ if i.endswith(mark):
+ method_name = i[:-len(mark)]
+ method_path = method_name.split('/')
+ if self.REQUEST.traverse_subpath[-len(method_path):]==method_path:
+ del self.REQUEST.traverse_subpath[-len(method_path):]
+ break
+
+ url = self._getProxyURL(self.REQUEST.traverse_subpath,
+ self.REQUEST['QUERY_STRING'])
+
+ # XXX this will send the password unconditionally!
+ # I hope https will be good enough.
+ header_dict = {}
+ user_and_password = self._getSubsribedUserAndPassword()
+ if (len(user_and_password)==2 and
+ user_and_password[0] and user_and_password[1]):
+ auth = 'Basic %s' % base64.encodestring('%s:%s' % user_and_password).strip()
+ header_dict['Authorization'] = auth
+
+ if content_type:
+ header_dict['Content-Type'] = content_type
+
+ request = urllib2.Request(url, data, header_dict)
+ f = self.simple_opener_director.open(request)
+
+ try:
+ data = f.read()
+ metadata = f.info()
+ response = self.REQUEST.RESPONSE
+ response.setStatus(f.code, f.msg)
+ response.setHeader('content-type', metadata.getheader('content-type'))
+
+ # FIXME this list should be confirmed with the RFC 2616.
+ for k in ('location', 'uri', 'cache-control', 'last-modified',
+ 'etag', 'if-matched', 'if-none-match',
+ 'if-range', 'content-language', 'content-range'
+ 'content-location', 'content-md5', 'expires',
+ 'content-encoding', 'vary', 'pragma', 'content-disposition',
+ 'content-length', 'age'):
+ if k in metadata:
+ response.setHeader(k, metadata.getheader(k))
+
+ return data
+ finally:
+ f.close()
+
+ def _getRemoteWitchTool(self, server_url):
+ """ Return remote generator tool interface. """
+ server = xmlrpclib.ServerProxy(server_url, allow_none=1)
+ witch_tool = server.portal_witch
+ return witch_tool
+
+ def _callRemoteMethod(self, distant_method, server_url=None):
+ """ Call remote method on server and get result. """
+ result_call = GeneratorCall()
+ if server_url is None:
+ # calculate it
+ server_url = self.getServerUrl() + self.getServerRoot()
+ witch_tool = self._getRemoteWitchTool(server_url)
+ parameter_dict = self.REQUEST.form
+ ## add client arguments
+ self._getServerInfo(parameter_dict)
+ ## call remote method
+ try:
+ method = getattr(witch_tool, distant_method)
+ html = method(parameter_dict)
+ except socket.error, message:
+ html = _generateErrorXML("""Cannot contact the server: %s.
+ Please check your network settings.""" %server_url)
+ zLOG.LOG('Wizard Tool socket error', zLOG.ERROR, message)
+ result_call.update({"command":"show",
+ "data": html,
+ "next": None,
+ "previous": None})
+ except xmlrpclib.ProtocolError, message:
+ html = _generateErrorXML("""The server %s refused to reply.
+ Please contact erp5-dev at erp5.org""" % server_url)
+ zLOG.LOG('Wizard Tool xmlrpc protocol error', zLOG.ERROR, message)
+ result_call.update({"command":"show",
+ "data": html,
+ "next": None,
+ "previous": None})
+ except xmlrpclib.Fault, message:
+ html = _generateErrorXML("Error/bug inside the server: %s." % server_url)
+ zLOG.LOG('Wizard Tool xmlrpc fault', zLOG.ERROR, message)
+ result_call.update({"command":"show",
+ "data": html,
+ "next": None,
+ "previous": None})
+ else:
+ result_call.load(html)
+ command = result_call["command"]
+ html = result_call["data"]
+ return result_call
+
+ def _setServerInfo(self, **kw):
+ """ Save to local Zope client address info. """
+ #global _v_server_buffer
+ global _server_to_preference_ids_map
+ for item, value in kw.items():
+ if item in _server_to_preference_ids_map.keys():
+ ## save persistently (as preference)
+ self.setExpressConfigurationPreference(_server_to_preference_ids_map[item],
+ value)
+
+ def _getServerInfo(self, parameter_dict):
+ """ Return local saved server info settings. """
+ global _server_to_preference_ids_map
+ for key, value in _server_to_preference_ids_map.items():
+ parameter_dict[key] = self.getExpressConfigurationPreference(value, None)
+ #for key, value in _v_server_buffer.items():
+ # parameter_dict[key] = value
+ ## add local ERP5 instance url
+ parameter_dict['erp5_url'] = self.getPortalObject().absolute_url()
+
+ def _importBT5FileData(self, bt5_filename, bt5_filedata):
+ """ Import bt5 file content. """
+ bt5_io = StringIO(bt5_filedata)
+ portal_templates = getToolByName(self.getPortalObject(), 'portal_templates')
+ try:
+ business_template = portal_templates.importFile(import_file=bt5_io, batch_mode=1)
+ except:
+ ## importing of generated bt5 failed
+ business_template = None
+ LOG("Wizard", ERROR, "[FAIL] Import of Nexedi Configurator bt5 file(%s)" %bt5_filename)
+ raise
+ bt5_io.close()
+ ## install bt5
+ portal_workflow = getToolByName(self.getPortalObject(), 'portal_workflow')
+ business_template.install()
+
+ security.declareProtected(Permissions.ModifyPortalContent, 'installBT5FilesFromServer')
+ def installBT5FilesFromServer(self,
+ server_response,
+ execute_after_setup_script = True,
+ install_standard_bt5 = True,
+ install_customer_bt5 = True,
+ use_super_manager = True):
+ """ Install or update BT5 files which we get from remote server. """
+ if use_super_manager:
+ # set current security manager to owner of site
+ _setSuperSecurityManager(self.getPortalObject())
+ bt5_files = server_response.get("filedata", [])
+ bt5_filenames = server_response["server_buffer"].get("filenames", [])
+ portal_templates = getToolByName(self.getPortalObject(), 'portal_templates')
+ counter = 0
+ LOG("Wizard", INFO,
+ "Starting installation for %s" %' '.join(bt5_filenames))
+ #execute_after_setup_script = install_standard_bt5 = install_customer_bt5 = False # dev mode
+ for bt5_id in bt5_filenames:
+ if bt5_id.startswith('http://'):
+ ## direct download of bt5 files available
+ if install_standard_bt5:
+ #bt = portal_templates.download(bt5_id)
+ #bt.install()
+ LOG("Wizard", INFO,
+ "[OK] standard bt5 installation (HTTP) from %s" %bt5_id)
+ else:
+ ## remote system supplied file content
+ if install_customer_bt5:
+ bt5_filedata = bt5_files[counter]
+ self._importBT5FileData(bt5_id, bt5_filedata)
+ LOG("Wizard", INFO,
+ "[OK] customized bt5 installation (XML-RPC) %s, %s bytes" %(bt5_id,len(bt5_filedata)))
+ ## ..
+ counter += 1
+ ## can we execute after setup script that will finish installation on client side?
+ bt5_after_setup_script_id = server_response["server_buffer"].get("after_setup_script_id", None)
+ if bt5_after_setup_script_id is None and \
+ self.getExpressConfigurationPreference('preferred_express_configuration_status', False):
+ ## we already have stored after setup script id
+ bt5_after_setup_script_id = self.getExpressConfigurationPreference('preferred_express_after_setup_script_id', None)
+
+ if execute_after_setup_script and bt5_after_setup_script_id is not None:
+ ## Execute script provided (if) in customer specific business template.
+ bt5_customer_template_id = server_response["server_buffer"]['filenames'][-1]
+ bt5_customer_template_id = bt5_customer_template_id.replace('.bt5', '')
+ after_script = getattr(self, bt5_after_setup_script_id, None)
+ if after_script is not None:
+ after_script_result = after_script(customer_template_id = bt5_customer_template_id)
+ LOG("Wizard", INFO,"[OK] execution of afer setup script %s (for bt5 %s)\n%s"
+ %(after_script.getId(), bt5_customer_template_id, after_script_result))
+ ## mark this ERP5 instance as configured
+ self.setExpressConfigurationPreference('preferred_express_configuration_status',
+ 1)
+ self.setExpressConfigurationPreference('preferred_express_after_setup_script_id',
+ bt5_after_setup_script_id)
+ LOG("Wizard", INFO,
+ "Completed installation for %s" %' '.join(bt5_filenames))
+ if use_super_manager:
+ noSecurityManager()
+
+ ######################################################
+ ## Navigation ##
+ ######################################################
+ security.declareProtected(Permissions.ModifyPortalContent, 'init')
+ def init(self, REQUEST=None, **kw):
+ """ Unconditionaly reset client_id and start new configuration process. """
+ #global _v_server_buffer
+ #reset_credentials = int(REQUEST.get('reset_credentials', 1))
+ #if reset_credentials!=0:
+ # ## reset username/password sent to remote server
+ # _v_server_buffer = {}
+ #user_id = REQUEST.get('field_my_ac_name', '')
+ #password = REQUEST.get('field_my_ac_password', '')
+ return self.next(REQUEST, **kw)
+
+ security.declareProtected(Permissions.ModifyPortalContent, 'login')
+ def login(self, REQUEST):
+ """ Login client and show next form. """
+ client_id = None
+ user_id = REQUEST.get('field_my_ac_name', '')
+ password = REQUEST.get('field_my_ac_password', '')
+ came_from_method = REQUEST.get('field_my_came_from_method', '')
+ ## call remote server
+ response = self._callRemoteMethod("getIdentification")
+ command = response["command"]
+ if command == "show":
+ ## server wants some more info - i.e possible
+ ## selection of working business configuration
+ if response.get('server_buffer', None) is not None:
+ client_id = response['server_buffer'].get('client_id', None)
+ self._setServerInfo(user_id = user_id,
+ password = password,
+ client_id = client_id)
+ return self.WizardTool_dialogForm(form_html = response["data"])
+ elif command == "next":
+ self._setServerInfo(user_id=user_id, \
+ password=password, \
+ client_id=response['server_buffer'].get('client_id', None), \
+ current_bc_index=response['server_buffer'].get('current_bc_index', None))
+ return self.next(REQUEST=REQUEST)
+ elif command == "login":
+ ## invalid user/password
+ self.REQUEST.RESPONSE.redirect( \
+ 'portal_wizard/%s?field_my_ac_name=%s&portal_status_message=%s' \
+ %(came_from_method, user_id, response['server_buffer']['message']))
+ return
+
+ security.declareProtected(Permissions.ModifyPortalContent, 'next')
+ def next(self, REQUEST):
+ """ Validate settings and return a new form to the user. """
+ response = self._callRemoteMethod("next")
+ if isinstance(response['server_buffer'], dict):
+ ## Remote server may request us to save some data.
+ self._setServerInfo(**response['server_buffer'])
+ ## Parse server response
+ command = response["command"]
+ html = response["data"]
+ if command == "show":
+ return self.WizardTool_dialogForm(previous = response['previous'], \
+ form_html = html, \
+ next = response['next'])
+ elif command == "update":
+ return self.next(REQUEST=REQUEST)
+ elif command == "login":
+ REQUEST.set('portal_status_message', html)
+ return self.view(REQUEST=REQUEST)
+ elif command == "install":
+ return self.startInstallation(REQUEST=REQUEST)
+
+ security.declareProtected(Permissions.ModifyPortalContent, 'previous')
+ def previous(self, REQUEST):
+ """ Display the previous form. """
+ response = self._callRemoteMethod('previous')
+ command = response["command"]
+ html = response['data']
+ if command == "show":
+ return self.WizardTool_dialogForm(previous = response['previous'], \
+ form_html = html, \
+ next = response['next'])
+ elif command == "login":
+ REQUEST.set('portal_status_message', html)
+ return self.view(REQUEST=REQUEST)
+
+ security.declarePublic(Permissions.AccessContentsInformation, 'getInstallationStatusReportFromClient')
+ def getInstallationStatusReportFromClient(self, active_process_id=None, REQUEST=None):
+ """ Query local ERP5 instance for installation status.
+ If installation is over the installation activities and reindexing
+ activities should not exists.
+ """
+ portal_activities = getToolByName(self.getPortalObject(), 'portal_activities')
+ if 0 == len(portal_activities.getMessageList()) and \
+ 0 == portal_activities.countMessageWithTag('initialERP5Setup'):
+ html = self.WizardTool_successfulConfiguration()
+ else:
+ html = """<h3> Installation is running.
+ Please be patient and do not move from current page until you get
+ confirmation that installation is over in about 5 minutes.</h3>"""
+ # TODO: add a progress bar (needs to install business templates
+ # separatly, with a specific tag)
+ # TODO: display some presentation, marketing images during install (like
+ # in a mandriva linux installer)
+ return html
+
+ security.declarePublic(Permissions.AccessContentsInformation, 'getInstallationStatusReportFromServer')
+ def getInstallationStatusReportFromServer(self, active_process_id=None, REQUEST=None):
+ """ Query remote server (usually only once for some installation status report """
+ response = self._callRemoteMethod("getInstallationStatusReport")
+ html = response["data"]
+ return html
+
+ security.declareProtected(Permissions.ModifyPortalContent, 'startInstallation')
+ def startInstallation(self, REQUEST):
+ """ Start installation process as an activity which will query generation server and
+ download/install bt5 template files and meanwhile offer user a nice GUI to observe
+ what's happening. """
+ active_process = self.portal_activities.newActiveProcess()
+ REQUEST.set('active_process_id', active_process.getId())
+ self.activate(active_process=active_process, tag = 'initialERP5Setup').initialERP5Setup()
+ return self.Wizard_viewInstallationStatus(REQUEST)
+
+ security.declareProtected(Permissions.ModifyPortalContent, 'initialERP5Setup')
+ def initialERP5Setup(self):
+ """ Get from remote generation server customized bt5 template files
+ and then install them. """
+ # TODO: the installation have to be splitted into 1 activity per business
+ # template install.
+ self.REQUEST.form['wizard_request_type'] = 'initial_setup'
+ # calculate server_url, because after bt5 installation reindexing is started
+ # which will make it impossible to get preferences items
+ server_url = self.getServerUrl() + self.getServerRoot()
+ server_response = self._callRemoteMethod('getBT5FilesForBusinessConfiguration', server_url)
+ ## save erp5_uid which will make it possible to distingush different business conf for client
+ current_bc_index = server_response['server_buffer']['current_bc_index']
+ self._setServerInfo(current_bc_index = current_bc_index)
+ self.installBT5FilesFromServer(server_response, True)
+ server_response = self._callRemoteMethod('finalizeInstallation', server_url)
+ LOG("Wizard", INFO,
+ "Successfuly installed generated business configuration from %s" %self.getServerUrl())
+
+ security.declareProtected(Permissions.ModifyPortalContent, 'repair')
+ def repair(self):
+ """ Repair broken ERP5 instance. This will install all business templates
+ for ERP5 instance as specified in its business configuration. """
+ self.REQUEST.form['wizard_request_type'] = 'repair'
+ server_response = self._callRemoteMethod('getBT5FilesForBusinessConfiguration')
+ if server_response['command'] == "install":
+ active_process = self.portal_activities.newActiveProcess()
+ self.activate(active_process=active_process).installBT5FilesFromServer(server_response, True)
+ html = server_response['data']
+ LOG("Wizard", INFO,
+ "Start repair process for ERP5 instance from %s" %self.getServerUrl())
+ return self.WizardTool_dialogForm(form_html = html)
+
+ security.declareProtected(Permissions.ModifyPortalContent, 'update')
+ def update(self):
+ """ Update ERP5's instance standard business templates. """
+ self.REQUEST.form['wizard_request_type'] = 'update'
+ server_response = self._callRemoteMethod('getBT5FilesForBusinessConfiguration')
+ if server_response['command'] == "install":
+ active_process = self.portal_activities.newActiveProcess()
+ self.activate(active_process=active_process).installBT5FilesFromServer(server_response,
+ execute_after_setup_script = False)
+ html = server_response['data']
+ LOG("Wizard", INFO,
+ "Start update process for ERP5 instance from %s" %self.getServerUrl())
+ return self.WizardTool_dialogForm(form_html = html)
+
+ security.declareProtected(Permissions.View, 'getServerUrl')
+ def getServerUrl(self):
+ return self.getExpressConfigurationPreference('preferred_witch_tool_server_url', '')
+
+ security.declareProtected(Permissions.View, 'getServerRoot')
+ def getServerRoot(self):
+ return self.getExpressConfigurationPreference('preferred_witch_tool_server_root', '')
+
+ security.declareProtected(Permissions.View, 'getExpressConfigurationPreference')
+ def getExpressConfigurationPreference(self, preference_id, default = None):
+ """ Get Express configuration preference """
+ portal_preferences = getToolByName(self, 'portal_preferences')
+ return portal_preferences.getPreference(preference_id, default)
+
+ security.declareProtected(Permissions.ModifyPortalContent, 'setExpressConfigurationPreference')
+ def setExpressConfigurationPreference(self, preference_id, value):
+ """ Set Express configuration preference """
+ portal_preferences = getToolByName(self, 'portal_preferences')
+ if portal_preferences.getActivePreference() is not None:
+ portal_preferences.setPreference(preference_id, value)
Added: erp5/trunk/products/ERP5Wizard/Tool/__init__.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Wizard/Tool/__init__.py?rev=20676&view=auto
==============================================================================
(empty)
Added: erp5/trunk/products/ERP5Wizard/VERSION.txt
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Wizard/VERSION.txt?rev=20676&view=auto
==============================================================================
--- erp5/trunk/products/ERP5Wizard/VERSION.txt (added)
+++ erp5/trunk/products/ERP5Wizard/VERSION.txt Fri Apr 18 16:15:20 2008
@@ -1,0 +1,1 @@
+ERP5Wizard 5.0
Propchange: erp5/trunk/products/ERP5Wizard/VERSION.txt
------------------------------------------------------------------------------
svn:eol-style = native
Added: erp5/trunk/products/ERP5Wizard/__init__.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Wizard/__init__.py?rev=20676&view=auto
==============================================================================
--- erp5/trunk/products/ERP5Wizard/__init__.py (added)
+++ erp5/trunk/products/ERP5Wizard/__init__.py Fri Apr 18 16:15:20 2008
@@ -1,0 +1,53 @@
+##############################################################################
+#
+# Copyright (c) 2006 Nexedi SARL and Contributors. All Rights Reserved.
+# Romain Courteaud <romain at nexedi.com>
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsability of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# garantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# This program is Free Software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+##############################################################################
+"""
+ ERP5Wizard is a ERP5Configurator client.
+"""
+
+from Products.ERP5Type.Utils import initializeProduct, updateGlobals
+import sys, Permissions
+this_module = sys.modules[ __name__ ]
+document_classes = updateGlobals(this_module, globals(), permissions_module=Permissions)
+
+# Finish installation
+def initialize(context):
+ import Document
+ from Tool import WizardTool
+ # Define object classes and tools
+ object_classes = ()
+ portal_tools = (WizardTool.WizardTool,)
+ content_classes = ()
+ content_constructors = ()
+ # Do initialization step
+ initializeProduct(context, this_module, globals(),
+ document_module=Document,
+ document_classes=document_classes,
+ object_classes=object_classes,
+ portal_tools=portal_tools,
+ content_constructors=content_constructors,
+ content_classes=content_classes)
Added: erp5/trunk/products/ERP5Wizard/dtml/explainWizardTool.dtml
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Wizard/dtml/explainWizardTool.dtml?rev=20676&view=auto
==============================================================================
--- erp5/trunk/products/ERP5Wizard/dtml/explainWizardTool.dtml (added)
+++ erp5/trunk/products/ERP5Wizard/dtml/explainWizardTool.dtml Fri Apr 18 16:15:20 2008
@@ -1,0 +1,15 @@
+<dtml-var manage_page_header>
+<dtml-var manage_tabs>
+
+<h3>Explain Wizard Tool</h3>
+<p>
+Wizard Tool provides a wizard-style user interface to
+allow the user to configure a site easily.
+</p>
+
+<p>
+You need to have a remote configurator site to exchange information
+and ask configuration services.
+</p>
+
+<dtml-var manage_page_footer>
Added: erp5/trunk/products/ERP5Wizard/tests/__init__.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Wizard/tests/__init__.py?rev=20676&view=auto
==============================================================================
(empty)
Added: erp5/trunk/products/ERP5Wizard/tests/testGeneratorCall.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Wizard/tests/testGeneratorCall.py?rev=20676&view=auto
==============================================================================
--- erp5/trunk/products/ERP5Wizard/tests/testGeneratorCall.py (added)
+++ erp5/trunk/products/ERP5Wizard/tests/testGeneratorCall.py Fri Apr 18 16:15:20 2008
@@ -1,0 +1,70 @@
+##############################################################################
+#
+# Copyright (c) 2007 Nexedi SA and Contributors. All Rights Reserved.
+# Jerome Perrin <jerome at nexedi.com>
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsability of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# garantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# This program is Free Software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+##############################################################################
+
+import unittest
+import xmlrpclib
+
+from Products.ERP5Wizard.Tool.WizardTool import GeneratorCall
+
+
+class TestGeneratorCall(unittest.TestCase):
+ """Tests Generator Call
+ """
+
+ def test_dump(self):
+ call = GeneratorCall()
+ dumped = call.dump()
+ self.failUnless(isinstance(dumped, str))
+ load = xmlrpclib.loads(dumped)
+ self.failUnless(isinstance(load, tuple))
+ self.assertEquals(len(load), 2)
+ self.assertEquals(load[1], 'GeneratorAnswer')
+
+ self.failUnless(isinstance(load[0], tuple))
+ self.assertEquals(len(load[0]), 1)
+ server_response_dict = load[0][0]
+ self.failUnless(isinstance(server_response_dict, dict))
+
+
+ def test_dump_load(self):
+ call = GeneratorCall(data='Foo')
+ self.assertEquals(call['data'], 'Foo')
+ dumped = call.dump()
+ self.failUnless(isinstance(dumped, str))
+
+ # reread it in a new call
+ read = GeneratorCall()
+ read.load(dumped)
+ self.assertEquals(read['data'], 'Foo')
+
+
+def test_suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(TestGeneratorCall))
+ return suite
+
Added: erp5/trunk/products/ERP5Wizard/tool.png
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Wizard/tool.png?rev=20676&view=auto
==============================================================================
Binary file - no diff available.
Propchange: erp5/trunk/products/ERP5Wizard/tool.png
------------------------------------------------------------------------------
svn:mime-type = image/png
More information about the Erp5-report
mailing list