From dc26ced4a15734979e1426c625ebe69bd35fd7d6 Mon Sep 17 00:00:00 2001 From: Rafael Monnerat <rafael@nexedi.com> Date: Wed, 22 Dec 2010 18:07:52 +0000 Subject: [PATCH] ERP5 Configurator Product initial commit. ERP5 Configurator allows developer creates configuration wizards for configure an ERP5 Instance. git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@41680 20353a03-c40f-0410-a6d1-a30d3c3de9de --- .../ERP5Configurator/Constraint/__init__.py | 0 .../Document/AccountConfiguratorItem.py | 76 +++ .../AccountingPeriodConfiguratorItem.py | 68 +++ .../Document/BusinessConfiguration.py | 476 ++++++++++++++++++ .../CatalogKeywordKeyConfiguratorItem.py | 65 +++ .../CategoriesSpreadsheetConfiguratorItem.py | 155 ++++++ .../Document/CategoryConfiguratorItem.py | 64 +++ .../Document/ConfigurationSave.py | 61 +++ .../Document/CurrencyConfiguratorItem.py | 73 +++ .../Document/CustomerBT5ConfiguratorItem.py | 64 +++ .../ExportCustomerBT5ConfiguratorItem.py | 66 +++ .../Document/OrganisationConfiguratorItem.py | 78 +++ .../Document/PermissionConfiguratorItem.py | 83 +++ .../Document/PersonConfiguratorItem.py | 96 ++++ .../Document/PortalTypeConfiguratorItem.py | 79 +++ ...talTypeRolesSpreadsheetConfiguratorItem.py | 103 ++++ .../Document/PreferenceConfiguratorItem.py | 118 +++++ .../Document/RoleConfiguratorItem.py | 98 ++++ .../Document/RuleConfiguratorItem.py | 71 +++ .../Document/ServiceConfiguratorItem.py | 66 +++ .../Document/SitePropertyConfiguratorItem.py | 64 +++ .../Document/StandardBT5ConfiguratorItem.py | 107 ++++ .../SystemPreferenceConfiguratorItem.py | 120 +++++ .../WorkflowSecurityConfiguratorItem.py | 123 +++++ product/ERP5Configurator/Document/__init__.py | 0 .../ConfigurationTemplate_readOOoCalcFile.py | 126 +++++ product/ERP5Configurator/Permissions.py | 0 .../PropertySheet/BusinessConfiguration.py | 42 ++ .../CategoriesSpreadsheetConfiguratorItem.py | 47 ++ .../PropertySheet/ConfigurationItem.py | 37 ++ ...talTypeRolesSpreadsheetConfiguratorItem.py | 46 ++ .../PropertySheet/ServiceConfiguratorItem.py | 37 ++ .../SitePropertyConfiguratorItem.py | 36 ++ .../StandardBT5ConfiguratorItem.py | 37 ++ .../PropertySheet/__init__.py | 1 + .../ERP5Configurator/Tool/ConfiguratorTool.py | 476 ++++++++++++++++++ product/ERP5Configurator/Tool/__init__.py | 0 product/ERP5Configurator/VERSION.txt | 1 + product/ERP5Configurator/__init__.py | 60 +++ product/ERP5Configurator/help/README | 1 + .../ERP5Configurator/interfaces/__init__.py | 0 .../interfaces/configurator_item.py | 46 ++ product/ERP5Configurator/mixin/__init__.py | 0 .../mixin/configurator_item.py | 50 ++ product/ERP5Configurator/skins/__init__.py | 0 product/ERP5Configurator/tests/__init__.py | 1 + product/ERP5Configurator/tool.png | Bin 0 -> 287 bytes 47 files changed, 3418 insertions(+) create mode 100644 product/ERP5Configurator/Constraint/__init__.py create mode 100644 product/ERP5Configurator/Document/AccountConfiguratorItem.py create mode 100644 product/ERP5Configurator/Document/AccountingPeriodConfiguratorItem.py create mode 100644 product/ERP5Configurator/Document/BusinessConfiguration.py create mode 100644 product/ERP5Configurator/Document/CatalogKeywordKeyConfiguratorItem.py create mode 100644 product/ERP5Configurator/Document/CategoriesSpreadsheetConfiguratorItem.py create mode 100644 product/ERP5Configurator/Document/CategoryConfiguratorItem.py create mode 100644 product/ERP5Configurator/Document/ConfigurationSave.py create mode 100644 product/ERP5Configurator/Document/CurrencyConfiguratorItem.py create mode 100644 product/ERP5Configurator/Document/CustomerBT5ConfiguratorItem.py create mode 100644 product/ERP5Configurator/Document/ExportCustomerBT5ConfiguratorItem.py create mode 100644 product/ERP5Configurator/Document/OrganisationConfiguratorItem.py create mode 100644 product/ERP5Configurator/Document/PermissionConfiguratorItem.py create mode 100644 product/ERP5Configurator/Document/PersonConfiguratorItem.py create mode 100644 product/ERP5Configurator/Document/PortalTypeConfiguratorItem.py create mode 100644 product/ERP5Configurator/Document/PortalTypeRolesSpreadsheetConfiguratorItem.py create mode 100644 product/ERP5Configurator/Document/PreferenceConfiguratorItem.py create mode 100644 product/ERP5Configurator/Document/RoleConfiguratorItem.py create mode 100644 product/ERP5Configurator/Document/RuleConfiguratorItem.py create mode 100644 product/ERP5Configurator/Document/ServiceConfiguratorItem.py create mode 100644 product/ERP5Configurator/Document/SitePropertyConfiguratorItem.py create mode 100644 product/ERP5Configurator/Document/StandardBT5ConfiguratorItem.py create mode 100644 product/ERP5Configurator/Document/SystemPreferenceConfiguratorItem.py create mode 100644 product/ERP5Configurator/Document/WorkflowSecurityConfiguratorItem.py create mode 100644 product/ERP5Configurator/Document/__init__.py create mode 100644 product/ERP5Configurator/Extensions/ConfigurationTemplate_readOOoCalcFile.py create mode 100644 product/ERP5Configurator/Permissions.py create mode 100644 product/ERP5Configurator/PropertySheet/BusinessConfiguration.py create mode 100644 product/ERP5Configurator/PropertySheet/CategoriesSpreadsheetConfiguratorItem.py create mode 100644 product/ERP5Configurator/PropertySheet/ConfigurationItem.py create mode 100644 product/ERP5Configurator/PropertySheet/PortalTypeRolesSpreadsheetConfiguratorItem.py create mode 100644 product/ERP5Configurator/PropertySheet/ServiceConfiguratorItem.py create mode 100644 product/ERP5Configurator/PropertySheet/SitePropertyConfiguratorItem.py create mode 100644 product/ERP5Configurator/PropertySheet/StandardBT5ConfiguratorItem.py create mode 100644 product/ERP5Configurator/PropertySheet/__init__.py create mode 100644 product/ERP5Configurator/Tool/ConfiguratorTool.py create mode 100644 product/ERP5Configurator/Tool/__init__.py create mode 100644 product/ERP5Configurator/VERSION.txt create mode 100644 product/ERP5Configurator/__init__.py create mode 100644 product/ERP5Configurator/help/README create mode 100644 product/ERP5Configurator/interfaces/__init__.py create mode 100644 product/ERP5Configurator/interfaces/configurator_item.py create mode 100644 product/ERP5Configurator/mixin/__init__.py create mode 100644 product/ERP5Configurator/mixin/configurator_item.py create mode 100644 product/ERP5Configurator/skins/__init__.py create mode 100644 product/ERP5Configurator/tests/__init__.py create mode 100644 product/ERP5Configurator/tool.png diff --git a/product/ERP5Configurator/Constraint/__init__.py b/product/ERP5Configurator/Constraint/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/product/ERP5Configurator/Document/AccountConfiguratorItem.py b/product/ERP5Configurator/Document/AccountConfiguratorItem.py new file mode 100644 index 0000000000..430813dfc8 --- /dev/null +++ b/product/ERP5Configurator/Document/AccountConfiguratorItem.py @@ -0,0 +1,76 @@ +############################################################################## +# +# Copyright (c) 2006 Nexedi SARL and Contributors. All Rights Reserved. +# Ivan Tyagov <ivan@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 Products.ERP5Type import Permissions, PropertySheet +from Products.ERP5Type.XMLObject import XMLObject +from Products.ERP5Configurator.mixin.configurator_item import ConfiguratorItemMixin + +class AccountConfiguratorItem(ConfiguratorItemMixin, XMLObject): + """ Setup an Accounting Account. """ + + meta_type = 'ERP5 Account Configurator Item' + portal_type = 'Account Configurator Item' + add_permission = Permissions.AddPortalContent + isPortalContent = 1 + isRADContent = 1 + + # Declarative security + security = ClassSecurityInfo() + security.declareObjectProtected(Permissions.AccessContentsInformation) + + # Declarative properties + property_sheets = ( PropertySheet.Base + , PropertySheet.XMLObject + , PropertySheet.CategoryCore + , PropertySheet.DublinCore + , PropertySheet.Account ) + + def build(self, business_configuration): + portal = self.getPortalObject() + account_module = portal.account_module + + extra_kw = {} + account_id = getattr(self, 'account_id', None) + if account_id: + # XXX FIXME This cause conflict when use configuration + # more then once. + #extra_kw['id'] = account_id + pass + account = account_module.newContent( + portal_type='Account', + title=self.getTitle(), + account_type=self.getAccountType(), + gap=self.getGap(), + financial_section=self.getFinancialSection(), + credit_account=self.isCreditAccount(), + description=self.getDescription(), + **extra_kw) + + ## add to customer template + self.install(account, business_configuration) diff --git a/product/ERP5Configurator/Document/AccountingPeriodConfiguratorItem.py b/product/ERP5Configurator/Document/AccountingPeriodConfiguratorItem.py new file mode 100644 index 0000000000..4c682d0dea --- /dev/null +++ b/product/ERP5Configurator/Document/AccountingPeriodConfiguratorItem.py @@ -0,0 +1,68 @@ +############################################################################## +# +# Copyright (c) 2008 Nexedi SA and Contributors. All Rights Reserved. +# Jerome Perrin <jerome@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 Products.ERP5Type import Permissions, PropertySheet +from Products.ERP5Type.XMLObject import XMLObject +from Products.ERP5Configurator.mixin.configurator_item import ConfiguratorItemMixin + +class AccountingPeriodConfiguratorItem(ConfiguratorItemMixin, XMLObject): + """ Setup an Accounting Period. """ + + meta_type = 'ERP5 Accounting Period Configurator Item' + portal_type = 'Accounting Period Configurator Item' + add_permission = Permissions.AddPortalContent + isPortalContent = 1 + isRADContent = 1 + + # Declarative security + security = ClassSecurityInfo() + security.declareObjectProtected(Permissions.AccessContentsInformation) + + # Declarative properties + property_sheets = ( PropertySheet.Base + , PropertySheet.XMLObject + , PropertySheet.CategoryCore + , PropertySheet.DublinCore + , PropertySheet.Task ) + + def build(self, business_configuration): + portal = self.getPortalObject() + organisation_id = business_configuration.\ + getGlobalConfigurationAttr('organisation_id') + organisation = portal.organisation_module._getOb(organisation_id) + + period = organisation.newContent( + portal_type='Accounting Period', + start_date=self.getStartDate(), + stop_date=self.getStopDate(), + short_title=self.getShortTitle(), + title=self.getTitle()) + + # no need to 'install' in the business template, because it's contain as + # subobject of an organisation we already added. diff --git a/product/ERP5Configurator/Document/BusinessConfiguration.py b/product/ERP5Configurator/Document/BusinessConfiguration.py new file mode 100644 index 0000000000..e14b4c204d --- /dev/null +++ b/product/ERP5Configurator/Document/BusinessConfiguration.py @@ -0,0 +1,476 @@ +############################################################################## +# +# Copyright (c) 2006 Nexedi SARL and Contributors. All Rights Reserved. +# Romain Courteaud <romain@nexedi.com> +# Ivan Tyagov <ivan@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 time +from AccessControl import ClassSecurityInfo +from Globals import PersistentMapping +from Acquisition import aq_base +from Products.ERP5Type import Permissions, PropertySheet +from zLOG import LOG, INFO +from cStringIO import StringIO + +from Products.ERP5Configurator.Tool.ConfiguratorTool import _validateFormToRequest +from Products.ERP5.Document.Url import Url +from Products.ERP5.Document.Item import Item + +## Workflow states definitions +INITIAL_STATE_TITLE = 'Start' +DOWNLOAD_STATE_TITLE = 'Download' +END_STATE_TITLE = 'End' + +class BusinessConfiguration(Item, Url): + """ + BusinessConfiguration store the values enter by the wizard. + """ + + meta_type = 'ERP5 Business Configuration' + portal_type = 'Business Configuration' + add_permission = Permissions.AddPortalContent + isPortalContent = 1 + isRADContent = 1 + + # Declarative security + security = ClassSecurityInfo() + security.declareObjectProtected(Permissions.AccessContentsInformation) + + # Declarative properties + property_sheets = ( PropertySheet.Base + , PropertySheet.XMLObject + , PropertySheet.CategoryCore + , PropertySheet.DublinCore + , PropertySheet.Item + , PropertySheet.Arrow + , PropertySheet.BusinessConfiguration + , PropertySheet.Comment + , PropertySheet.Url + , PropertySheet.Version + ) + + security.declareProtected(Permissions.View, 'isInitialConfigurationState') + def isInitialConfigurationState(self): + """ Check if the Business Configuration is on initial workflow state + """ + workflow = self.getResourceValue() + if workflow is not None: + return self.getCurrentState() == workflow.getSource() + return None + + security.declareProtected(Permissions.View, 'isDownloadConfigurationState') + def isDownloadConfigurationState(self): + """ Check if the Business Configuration is on Download State + """ + return self.getCurrentStateTitle() == DOWNLOAD_STATE_TITLE + + security.declareProtected(Permissions.View, 'isEndConfigurationState') + def isEndConfigurationState(self): + """ Check if the Business Configuration is on End State + """ + return self.getCurrentStateTitle() == END_STATE_TITLE + + security.declareProtected(Permissions.View, 'getNextTransition') + def getNextTransition(self): + """ Return next transition. """ + current_state = self.getCurrentStateValue() + if current_state is None: + return None + transition_list = current_state.getAvailableTransitionList(self) + transition_number = len(transition_list) + if transition_number > 1: + raise TypeError, "More than one transition is available." + elif transition_number == 0: + return None + + return transition_list[0] + + security.declarePrivate('_executeTransition') + def _executeTransition(self, \ + form_kw=None, + request_kw=None): + """ Execute the transition. """ + root_conf_save = None + if form_kw is None: + form_kw = {} + current_state = self.getCurrentStateValue() + transition = self.getNextTransition() + next_state = self.unrestrictedTraverse(transition.getDestination()) + ## it's possible that we have already saved a configuration save object + ## in workflow_history for this state so we use it + root_conf_save = self._getConfSaveForStateFromWorkflowHistory() + if root_conf_save is None: + ## we haven't saved any configuration save for this state so create new one + root_conf_save = self.newContent(portal_type='Configuration Save') + else: + ## we have already created configuration save for this state + ## so remove from it already existing configuration items + if root_conf_save!=self: ## don't delete ourselves + existing_conf_items = root_conf_save.objectIds() + existing_conf_items = map(None, existing_conf_items) + root_conf_save.manage_delObjects(existing_conf_items) + ## save ... + root_conf_save.edit(**form_kw) + ## Add some variables so we can get use them in workflow after scripts + form_kw['configuration_save_url'] = root_conf_save.getRelativeUrl() + form_kw['transition'] = transition.getRelativeUrl() + current_state.executeTransition(transition, self, form_kw=form_kw) + + security.declarePrivate('_displayNextForm') + def _displayNextForm(self, \ + validation_errors=None, \ + context=None, \ + transition=None): + """ Render next form. """ + if transition is None: + transition = self.getNextTransition() + while transition is not None: + form_id = transition.getTransitionFormId() + current_state = self.getCurrentStateValue() + if self.isDownloadConfigurationState(): + ## exec next transition for this business configuration + self._executeTransition() + transition = self.getNextTransition() + return None, None, None, None, None + if form_id is None: + ## go on until you find a form + self._executeTransition() + transition = self.getNextTransition() + else: + if context is None: + ## examine workflow_history for already saved + ## 'Configuration Save' objects for this state + context = self._getConfSaveForStateFromWorkflowHistory() + ## get form object in a proper context + form_html, form_title = self._renderFormInContext(form_id, context, validation_errors) + ## check if we've can shown 'Previous' button + previous = None + translate = self.Base_translateString + if self._isAlreadyConfSaveInWorkflowHistory(transition): + previous = translate("Previous") + return previous, form_html, form_title, \ + translate(transition.getTitle()), self.getServerBuffer() + + security.declarePrivate('_renderFormInContext') + def _renderFormInContext(self, form_id, context, validation_errors): + html = "" + html_forms = [] + isMultiEntryTransition = self._isMultiEntryTransition() + forms_number = isMultiEntryTransition + if context is None: + form = getattr(self, form_id) + if not isMultiEntryTransition: + if validation_errors is not None: + self.REQUEST.set('field_errors', form.ErrorFields(validation_errors)) + html = form() + else: + template_html = form() + for form_counter in range(0, forms_number): + form_html = self.Base_mainConfiguratorFormTemplate( + current_form_number = form_counter + 1, + max_form_numbers = forms_number, + form_title = form.title, + form_html = template_html) + html_forms.append(form_html) + else: + if not isMultiEntryTransition: + ## only one form saved under this context + form = getattr(context, form_id) + if validation_errors is not None: + self.REQUEST.set('field_errors', form.ErrorFields(validation_errors)) + html = form() + else: + ## we have many forms saved under this context + form = getattr(self, form_id) + field_ids = form.get_fields() + for form_counter in range(0, forms_number): + ## fill REQUEST with data as it will be used to render form + for field in field_ids: + field_value = getattr(context, "field_%s" %field.id, None) + if field_value is not None and len(field_value) > form_counter: + field_value = field_value[form_counter] + self.REQUEST.set(field.id, field_value) + else: + self.REQUEST.set(field.id, '') + form_html = self.Base_mainConfiguratorFormTemplate( \ + current_form_number = form_counter +1, \ + max_form_numbers = forms_number, \ + form_html = getattr(context, form_id)()) + html_forms.append(form_html) + if html_forms!=[]: + html = "\n".join(html_forms) + title = form.title + return html, title + + security.declarePrivate('_displayPreviousForm') + def _displayPreviousForm(self): + """ Render previous form using workflow history. """ + workflow_history = self.getCurrentStateValue().getWorkflowHistory(self, remove_undo=1) + workflow_history.reverse() + for wh in workflow_history: + ## go one step back + current_state = self.getCurrentStateValue() + current_state.undoTransition(self) + transition = self.unrestrictedTraverse(wh['transition']) + conf_save = self.unrestrictedTraverse(wh['configuration_save_url']) + ## check if this transition can be shown to user ... + if transition._checkPermission(self) and \ + transition.getTransitionFormId() is not None: + return self._displayNextForm(context=conf_save, transition=transition) + + security.declarePrivate('_validateNextForm') + def _validateNextForm(self, **kw): + """ Validate the form displayed to the user. """ + REQUEST = self.REQUEST + form = getattr(self, self.getNextTransition().getTransitionFormId()) + return _validateFormToRequest(form, REQUEST, **kw) + + ############# + ## misc ## + ############# + security.declarePrivate('_getConfigurationStack') + def _getConfigurationStack(self): + """ Return list of created by client configuration save objects + sort on id which is an integer. """ + result = self.objectValues('ERP5 Configuration Save') + result = map(None, result) + result.sort(lambda x, y: cmp(x.getIntIndex(x.getIntId()), + y.getIntIndex(y.getIntId()))) + return result + + security.declarePrivate('_getConfSaveForStateFromWorkflowHistory') + def _getConfSaveForStateFromWorkflowHistory(self): + """ Get from workflow history configuration save for this state """ + configuration_save = None + current_state = self.getCurrentStateValue() + transition = self.getNextTransition() + next_state = self.unrestrictedTraverse(transition.getDestination()) + workflow_history = current_state.getWorkflowHistory(self) + for wh in workflow_history: + wh_state = self.unrestrictedTraverse(wh['current_state']) + if wh_state == next_state: + configuration_save = self.unrestrictedTraverse(wh['configuration_save_url']) + return configuration_save + + security.declarePrivate('_isAlreadyConfSaveInWorkflowHistory') + def _isAlreadyConfSaveInWorkflowHistory(self, transition): + """ check if we have an entry in worklow history for this state """ + workflow_history = self.getCurrentStateValue().getWorkflowHistory(self, remove_undo=1) + workflow_history.reverse() + for wh in workflow_history: + wh_state = self.unrestrictedTraverse(wh['current_state']) + for wh_transition in wh_state.getAvailableTransitionList(self): + if wh_transition.getTransitionFormId() is not None and wh_transition!=transition: + return True + return False + + security.declarePrivate('_isMultiEntryTransition') + def _isMultiEntryTransition(self): + """ Return number of multiple forms to show for a transition. """ + next_transition = self.getNextTransition() + if next_transition is not None: + if getattr(aq_base(self), '_multi_entry_transitions', None) is not None: + multi_forms = self._multi_entry_transitions.get(next_transition.getRelativeUrl(), 0) + if multi_forms == 1: + # we have set '1' which means show one form which is not multiple forms + multi_forms = 0 + return multi_forms + else: + return 0 + else: + ## no transitions available + return 0 + + security.declareProtected(Permissions.ModifyPortalContent, 'setMultiEntryTransition') + def setMultiEntryTransition(self, transition_url, max_entry_number): + """ Set a transition as multiple - i.e max_entry_number of forms + which will be rendered. This method is called in after scripts + and usually this number is set by user in a web form. """ + if getattr(aq_base(self), '_multi_entry_transitions', None) is None: + self._multi_entry_transitions = PersistentMapping() + self._multi_entry_transitions[transition_url] = max_entry_number + + security.declareProtected(Permissions.ModifyPortalContent, 'setServerBuffer') + def setServerBuffer(self, **kw): + """ Set what we should return to client. """ + if getattr(aq_base(self), '_server_buffer', None) is None: + self._server_buffer = {} + for item, value in kw.items(): + self._server_buffer[item] = value + self._p_changed = 1 + + security.declareProtected(Permissions.View, 'getServerBuffer') + def getServerBuffer(self): + """ Get return buffer which will be sent to client and + afterwards deleted. """ + server_buffer = getattr(aq_base(self), '_server_buffer', {}) + self._server_buffer = {} + return server_buffer + + security.declareProtected(Permissions.ModifyPortalContent, 'setGlobalConfigurationAttr') + def setGlobalConfigurationAttr(self, **kw): + """ Set global business configuration attribute. """ + if getattr(aq_base(self), + '_global_configuration_attributes', None) is None: + self._global_configuration_attributes = PersistentMapping() + for key, value in kw.items(): + self._global_configuration_attributes[key] = value + + security.declareProtected(Permissions.View, 'getGlobalConfigurationAttr') + def getGlobalConfigurationAttr(self, key, default = None): + """ Get global business configuration attribute. """ + global_configuration_attributes = getattr(self, '_global_configuration_attributes', {}) + return global_configuration_attributes.get(key, default) + + security.declareProtected(Permissions.View, 'getBuiltBusinessConfigurationBT5List') + def getBuiltBusinessConfigurationBT5List(self): + """ + Get list of built business templates in a Wizard format. + """ + bt5_file_list = [] + portal = self.getPortalObject() + for bt_link in self.contentValues(portal_type="Link"): + bt5_item = dict(bt5_id = bt_link.getUrlString(), + bt5_filedata = "") + bt5_file_list.append(bt5_item) + + for bt_file in self.contentValues(portal_type="File"): + bt5_item = dict(bt5_id = bt_file.getId(), + bt5_filedata = bt_file.getData()) + bt5_file_list.append(bt5_item) + return bt5_file_list + + ############# Instance and Business Configuration ######################## + security.declareProtected(Permissions.ModifyPortalContent, 'buildConfiguration') + def buildConfiguration(self): + """ + Build list of business templates according to already saved + Configuration Saves (i.e. user input). + This is the actual implementation which can be used from workflow + actions and Configurator requets + """ + bt5_file_list = [] + start = time.time() + bc_id = self.getId() + LOG("CONFIGURATOR", INFO, + 'Build process started for %s' % self.getRelativeUrl()) + conf_item_list = [] + # build + for conf_save in self._getConfigurationStack(): + # XXX: check which items are configure-able + conf_item_list = [x for x in conf_save.contentValues()] + conf_item_list.sort(lambda x,y: cmp(x.getIntId(), y.getIntId())) + for conf_item in conf_item_list: + conf_save_id = conf_save.getId() + configuration_item_object = conf_item + LOG('CONFIGURATOR', INFO, 'Building --> %s' % conf_item) + start_build = time.time() + build_result = conf_item.build(self) + LOG('CONFIGURATOR', INFO, 'Built --> %s (%.02fs)' \ + % (conf_item, time.time()-start_build)) + + # save list of generated or reused bt5 ids in bc + LOG('CONFIGURATOR', INFO, + 'Build process started for %s ended after %.02fs' + %(self.getRelativeUrl(), time.time()-start)) + return bt5_file_list + + security.declareProtected(Permissions.ModifyPortalContent, 'resetBusinessConfiguration') + def resetBusinessConfiguration(self): + """ + Reset Business Confiration at server side. + Remove all traces from user input (i.e. Configuration Saves, workflow history). + """ + object_ids = [] + for obj in self.contentValues(filter = {'portal_type': ['Configuration Save', 'File', 'Link']}): + object_ids.append(obj.getId()) + self.manage_delObjects(object_ids) + del self.workflow_history + # ERP5 Workflow initialization + erp5_workflow = self.getResourceValue() + erp5_workflow.initializeDocument(self) + + def isStandardBT5(self, bt5_id): + """Is bt5_id standard gzipped bt5 id? + Use ERP5 site portal_templates to get list of bt5_ids from configured + repository. This relies on the fact that the host site have a + configured repository. + """ + # XXX This should be one API from portal_templates + bt5_title_list = [] + bt5_title = bt5_id.split('.')[0] + for bt5 in self.getPortalObject().portal_templates\ + .getRepositoryBusinessTemplateList(): + bt5_title_list.append(bt5.getTitle()) + return bt5_title in bt5_title_list + + def getPublicUrlForBT5Id(self, bt5_id): + """ Generate publicly accessible URL for business template """ + portal = self.getPortalObject() + return portal.portal_templates.getBusinessTemplateUrl(None, bt5_id) + + security.declareProtected(Permissions.ModifyPortalContent, 'installConfiguration') + def installConfiguration(self, execute_after_setup_script = 1): + """ + Install in remote instance already built list of business templates + which are saved in the Business Configuration. + """ + kw = dict(tag="start") + bt5_file_list = [] + portal = self.getPortalObject() + for bt_link in self.contentValues(portal_type="Link"): + portal.portal_templates.activate(**kw).updateBusinessTemplateFromUrl( + bt_link.getUrlString()) + LOG("Business COnfiguration", INFO, + "Install %s to %s" % (bt_link.getUrlString(), self.getRelativeUrl())) + kw["after_tag"] = kw["tag"] + kw["tag"] = bt_link.getTitle() + + for bt_file in self.contentValues(portal_type="File"): + if bt_file.getTitle("").replace(".bt5", "") == self.getSpecialiseTitle(): + bt5_io = StringIO(str(bt_file.getData())) + + # XXX FIXME (lucas): Why FAIL on the log message? + LOG("Business Configuration", INFO, + "[FAIL] Import of bt5 file (%s - %s)" % \ + (bt_file.getId(), bt_file.getTitle())) + + bc = portal.portal_templates.importFile(import_file=bt5_io, + batch_mode=1) + bc.activate(**kw).install() + kw["after_tag"] = kw["tag"] + kw["tag"] = bt_file.getTitle() + + if execute_after_setup_script: + customer_template = self.getSpecialiseValue() + customer_template_relative_url = customer_template.getRelativeUrl() + self.activate(**kw).ERP5Site_afterConfigurationSetup( + customer_template_relative_url=customer_template_relative_url, + alter_preferences=True) + LOG("Business Configuration", INFO, + "After setup script called (force) for %s : %s" % + (self.getRelativeUrl(), self.getSpecialise())) + diff --git a/product/ERP5Configurator/Document/CatalogKeywordKeyConfiguratorItem.py b/product/ERP5Configurator/Document/CatalogKeywordKeyConfiguratorItem.py new file mode 100644 index 0000000000..637f4f69c1 --- /dev/null +++ b/product/ERP5Configurator/Document/CatalogKeywordKeyConfiguratorItem.py @@ -0,0 +1,65 @@ +############################################################################## +# +# Copyright (c) 2008 Nexedi SARL and Contributors. All Rights Reserved. +# Yoshinori Okuji <yo@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 Products.ERP5Type import Permissions, PropertySheet +from Products.ERP5Type.XMLObject import XMLObject + +from Products.ERP5Configurator.mixin.configurator_item import ConfiguratorItemMixin + +class CatalogKeywordKeyConfiguratorItem(ConfiguratorItemMixin, XMLObject): + """Set up catalog keyword keys.""" + + meta_type = 'ERP5 Catalog Keyword Key Configurator Item' + portal_type = 'Catalog Keyword Key Configurator Item' + add_permission = Permissions.AddPortalContent + isPortalContent = 1 + isRADContent = 1 + + # Declarative security + security = ClassSecurityInfo() + security.declareObjectProtected(Permissions.AccessContentsInformation) + + # Declarative properties + property_sheets = ( PropertySheet.Base + , PropertySheet.XMLObject + , PropertySheet.CategoryCore + , PropertySheet.DublinCore ) + + def build(self, business_configuration): + portal = self.getPortalObject() + catalog = portal.portal_catalog.getSQLCatalog() + key_list = list(catalog.getProperty('sql_catalog_keyword_search_keys', ())) + for k in self.key_list: + if k not in key_list: + key_list.append(k) + key_list = tuple(key_list) + catalog._setPropValue('sql_catalog_keyword_search_keys', key_list) + bt = business_configuration.getSpecialiseValue() + bt.edit(template_catalog_keyword_key_list=key_list) + diff --git a/product/ERP5Configurator/Document/CategoriesSpreadsheetConfiguratorItem.py b/product/ERP5Configurator/Document/CategoriesSpreadsheetConfiguratorItem.py new file mode 100644 index 0000000000..0defee9149 --- /dev/null +++ b/product/ERP5Configurator/Document/CategoriesSpreadsheetConfiguratorItem.py @@ -0,0 +1,155 @@ +############################################################################## +# +# Copyright (c) 2008 Nexedi SA and Contributors. All Rights Reserved. +# Jerome Perrin <jerome@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 StringIO import StringIO + +from Acquisition import aq_base + +from AccessControl import ClassSecurityInfo +from Products.ERP5Type import Permissions, PropertySheet +from Products.ERP5Type.XMLObject import XMLObject +from Products.ERP5Configurator.mixin.configurator_item import ConfiguratorItemMixin + + +class UnrestrictedStringIO(StringIO): + __allow_access_to_unprotected_subobjects__ = 1 + + +class CategoriesSpreadsheetConfiguratorItem(ConfiguratorItemMixin, XMLObject): + """Import a categories spreadsheet. + """ + + meta_type = 'ERP5 Categories Spreadsheet Configurator Item' + portal_type = 'Categories Spreadsheet Configurator Item' + add_permission = Permissions.AddPortalContent + isPortalContent = 1 + isRADContent = 1 + + # Declarative security + security = ClassSecurityInfo() + security.declareObjectProtected(Permissions.AccessContentsInformation) + + # Declarative properties + property_sheets = ( PropertySheet.Base + , PropertySheet.XMLObject + , PropertySheet.CategoryCore + , PropertySheet.DublinCore + , PropertySheet.CategoriesSpreadsheetConfiguratorItem + ) + + def build(self, business_configuration): + portal = self.getPortalObject() + ctool = portal.portal_categories + + self._readSpreadSheet() + cache = self._category_cache + for bc_id, category_list in cache.items(): + if bc_id in ctool.objectIds(): + bc = ctool._getOb(bc_id) + else: + # TODO: test bc creation + # the bc should be added as base category in bt5 ? + bc = ctool.newContent(id=bc_id) + + for category_info in category_list: + path = bc + for cat in category_info['path'].split("/")[1:]: + if not cat in path.objectIds(): + path = path.newContent( + portal_type='Category', + id=cat,) + else: + path = path[cat] + + edit_dict = category_info.copy() + edit_dict.pop('path') + path.edit(**edit_dict) + ## add to customer template + self.install(path, business_configuration) + + def _readSpreadSheet(self): + """Read the spreadsheet and prepare internal category cache. + """ + aq_self = aq_base(self) + if getattr(aq_self, '_category_cache', None) is None: + # TODO use a invalid_spreadsheet_error_handler to report invalid + # spreadsheet messages (see http://svn.erp5.org?rev=24908&view=rev ) + aq_self._category_cache = self.Base_getCategoriesSpreadSheetMapping( + UnrestrictedStringIO(self.getDefaultCategoriesSpreadsheetData())) + + security.declareProtected(Permissions.ModifyPortalContent, + 'setDefaultCategoriesSpreadsheetFile') + def setDefaultCategoriesSpreadsheetFile(self, *args, **kw): + """Reset the spreadsheet cache.""" + self._setDefaultCategoriesSpreadsheetFile(*args, **kw) + self._category_cache = None + self.reindexObject() + + security.declareProtected(Permissions.ModifyPortalContent, + 'setCategoriesSpreadsheetFile') + setCategoriesSpreadsheetFile = setDefaultCategoriesSpreadsheetFile + + security.declareProtected(Permissions.AccessContentsInformation, + 'getCategoryTitleItemList') + def getCategoryTitleItemList(self, base_category_id, base=0): + """Returns title item list for a base category contained in this + spreadsheet. + """ + self._readSpreadSheet() + cache = self._category_cache + + result = [('', '')] + if base_category_id not in cache: + return result # TODO: return some kind of default. Where is this + # default ??? configurator_%s % base_category_id ? + # If we add default here, it should also be used in build + # ... + + category_path_dict = dict() + for item in cache[base_category_id]: + category_path_dict[item['path']] = item + + for item in cache[base_category_id]: + # the first item in this list is the base category itself, so we skip it. + if item['path'] == base_category_id: + continue + + # recreate logical path + path_element_list = [] + title_list = [] + for path_element in item['path'].split('/'): + path_element_list.append(path_element) + title_list.append(category_path_dict['/'.join(path_element_list)]['title']) + + if base: + result.append(('/'.join(title_list[1:]), item['path'])) + else: + result.append(('/'.join(title_list[1:]), + '/'.join(item['path'].split('/')[1:]))) + + return result diff --git a/product/ERP5Configurator/Document/CategoryConfiguratorItem.py b/product/ERP5Configurator/Document/CategoryConfiguratorItem.py new file mode 100644 index 0000000000..59390a1c2b --- /dev/null +++ b/product/ERP5Configurator/Document/CategoryConfiguratorItem.py @@ -0,0 +1,64 @@ +############################################################################## +# +# Copyright (c) 2006 Nexedi SARL and Contributors. All Rights Reserved. +# Ivan Tyagov <ivan@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 Products.ERP5Type import Permissions, PropertySheet +from Products.ERP5Type.XMLObject import XMLObject + +from Products.ERP5Configurator.mixin.configurator_item import ConfiguratorItemMixin + +class CategoryConfiguratorItem(ConfiguratorItemMixin, XMLObject): + """This class is meta build step for customization of ERP5 site.""" + + meta_type = 'ERP5 Category Configurator Item' + portal_type = 'Category Configurator Item' + add_permission = Permissions.AddPortalContent + isPortalContent = 1 + isRADContent = 1 + + # Declarative security + security = ClassSecurityInfo() + security.declareObjectProtected(Permissions.AccessContentsInformation) + + # Declarative properties + property_sheets = ( PropertySheet.Base + , PropertySheet.XMLObject + , PropertySheet.CategoryCore + , PropertySheet.DublinCore) + + def build(self, business_configuration): + portal = self.getPortalObject() + category_root = portal.portal_categories[self.category_root] + object_id = self.object_id + if object_id in category_root.objectIds(): + category_root.manage_delObjects(object_id) + category = category_root.newContent(portal_type='Category', + id = object_id, + title = self.getTitle()) + ## add to customer template + self.install(category, business_configuration) diff --git a/product/ERP5Configurator/Document/ConfigurationSave.py b/product/ERP5Configurator/Document/ConfigurationSave.py new file mode 100644 index 0000000000..fae53d8f26 --- /dev/null +++ b/product/ERP5Configurator/Document/ConfigurationSave.py @@ -0,0 +1,61 @@ +############################################################################## +# +# Copyright (c) 2006 Nexedi SARL and Contributors. All Rights Reserved. +# Romain Courteaud <romain@nexedi.com> +# Ivan Tyagov <ivan@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 Products.ERP5Type import Permissions, PropertySheet +from Products.ERP5.Document.Path import Path + +class ConfigurationSave(Path): + """ This class is the base class for all template items. """ + + portal_type = 'Configuration Save' + meta_type = 'ERP5 Configuration Save' + add_permission = Permissions.AddPortalContent + isPortalContent = 1 + isRADContent = 1 + + # Declarative security + security = ClassSecurityInfo() + security.declareObjectProtected(Permissions.AccessContentsInformation) + + # Declarative properties + property_sheets = ( PropertySheet.Base + , PropertySheet.XMLObject + , PropertySheet.CategoryCore + , PropertySheet.DublinCore + , PropertySheet.SortIndex ) + + def addConfigurationItem(self, configuration_item_class_name, **kw): + """ Add new configuration item. """ + ## remove manually specified a configration title + if kw.has_key('conf_title'): + self.setTitle(kw['conf_title']) + kw.pop('conf_title') + conf_item = self.newContent(portal_type = configuration_item_class_name, **kw) + return conf_item diff --git a/product/ERP5Configurator/Document/CurrencyConfiguratorItem.py b/product/ERP5Configurator/Document/CurrencyConfiguratorItem.py new file mode 100644 index 0000000000..504ba62b77 --- /dev/null +++ b/product/ERP5Configurator/Document/CurrencyConfiguratorItem.py @@ -0,0 +1,73 @@ +############################################################################## +# +# Copyright (c) 2006 Nexedi SARL and Contributors. All Rights Reserved. +# Ivan Tyagov <ivan@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 Products.ERP5Type import Permissions, PropertySheet +from Products.ERP5Type.XMLObject import XMLObject + +from Products.ERP5Configurator.mixin.configurator_item import ConfiguratorItemMixin + +class CurrencyConfiguratorItem(ConfiguratorItemMixin, XMLObject): + """ Setup currency. """ + + meta_type = 'ERP5 Currency Configurator Item' + portal_type = 'Currency Configurator Item' + add_permission = Permissions.AddPortalContent + isPortalContent = 1 + isRADContent = 1 + + # Declarative security + security = ClassSecurityInfo() + security.declareObjectProtected(Permissions.AccessContentsInformation) + + # Declarative properties + property_sheets = ( PropertySheet.Base + , PropertySheet.XMLObject + , PropertySheet.CategoryCore + , PropertySheet.DublinCore + , PropertySheet.Price + , PropertySheet.Resource + , PropertySheet.Reference ) + + def build(self, business_configuration): + portal = self.getPortalObject() + currency_module = portal._getOb('currency_module') + title = self.getTitle() + reference = self.getReference() + base_unit_quantity = self.getBaseUnitQuantity() + # XXX FIXME This is not exactly desired behaviour + currency = self.portal_catalog.getResultValue(id=reference, + portal_type="Currency") + if currency is None: + currency = currency_module.newContent(portal_type = "Currency", + id = reference, + title = title, + reference = reference, + base_unit_quantity = base_unit_quantity) + ## add to customer template + self.install(currency, business_configuration) diff --git a/product/ERP5Configurator/Document/CustomerBT5ConfiguratorItem.py b/product/ERP5Configurator/Document/CustomerBT5ConfiguratorItem.py new file mode 100644 index 0000000000..d15cd561d0 --- /dev/null +++ b/product/ERP5Configurator/Document/CustomerBT5ConfiguratorItem.py @@ -0,0 +1,64 @@ +############################################################################## +# +# Copyright (c) 2006 Nexedi SARL and Contributors. All Rights Reserved. +# Ivan Tyagov <ivan@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 Products.ERP5Type import Permissions, PropertySheet +from Products.ERP5Type.XMLObject import XMLObject +from Products.CMFCore.utils import getToolByName +from Products.ERP5Configurator.mixin.configurator_item import ConfiguratorItemMixin + +class CustomerBT5ConfiguratorItem(ConfiguratorItemMixin, XMLObject): + """ Create a new bt5 for customer configuration. + + This business template is not installed locally, only build. + """ + + meta_type = 'ERP5 Customer BT5 Configurator Item' + portal_type = 'Customer BT5 Configurator Item' + add_permission = Permissions.AddPortalContent + isPortalContent = 1 + isRADContent = 1 + + # Declarative security + security = ClassSecurityInfo() + security.declareObjectProtected(Permissions.AccessContentsInformation) + + # Declarative properties + property_sheets = ( PropertySheet.Base + , PropertySheet.XMLObject + , PropertySheet.CategoryCore + , PropertySheet.DublinCore ) + + def build(self, business_configuration): + template_tool = getToolByName(self.getPortalObject(), + 'portal_templates') + bt5 = template_tool.newContent(portal_type="Business Template", \ + title=self.bt5_id) + + ## ..and set it as current + business_configuration.setSpecialise(bt5.getRelativeUrl()) diff --git a/product/ERP5Configurator/Document/ExportCustomerBT5ConfiguratorItem.py b/product/ERP5Configurator/Document/ExportCustomerBT5ConfiguratorItem.py new file mode 100644 index 0000000000..89d3d9b2c3 --- /dev/null +++ b/product/ERP5Configurator/Document/ExportCustomerBT5ConfiguratorItem.py @@ -0,0 +1,66 @@ +############################################################################## +# +# Copyright (c) 2006 Nexedi SARL and Contributors. All Rights Reserved. +# Ivan Tyagov <ivan@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 Products.ERP5Type import Permissions, PropertySheet +from Products.ERP5Type.XMLObject import XMLObject +from Products.CMFCore.utils import getToolByName +from Products.ERP5Configurator.mixin.configurator_item import ConfiguratorItemMixin + +class ExportCustomerBT5ConfiguratorItem(XMLObject, ConfiguratorItemMixin): + """ Create a new bt5 for customer configuration. """ + + meta_type = 'ERP5 Export Customer BT5 Configurator Item' + portal_type = 'Export Customer BT5 Configurator Item' + add_permission = Permissions.AddPortalContent + isPortalContent = 1 + isRADContent = 1 + + # Declarative security + security = ClassSecurityInfo() + security.declareObjectProtected(Permissions.AccessContentsInformation) + + # Declarative properties + property_sheets = ( PropertySheet.Base + , PropertySheet.XMLObject + , PropertySheet.CategoryCore + , PropertySheet.DublinCore ) + + def build(self, business_configuration): + portal = self.getPortalObject() + template_tool = getToolByName(portal, 'portal_templates') + bt5_obj = business_configuration.getSpecialiseValue() + if bt5_obj.getBuildingState() != 'built': + ## build template so it can be exported + bt5_obj.edit() + bt5_obj.build() + bt5_data = template_tool.export(bt5_obj) + business_configuration.newContent( + portal_type='File', + title = bt5_obj.getTitle(), + data = bt5_data) diff --git a/product/ERP5Configurator/Document/OrganisationConfiguratorItem.py b/product/ERP5Configurator/Document/OrganisationConfiguratorItem.py new file mode 100644 index 0000000000..128229c258 --- /dev/null +++ b/product/ERP5Configurator/Document/OrganisationConfiguratorItem.py @@ -0,0 +1,78 @@ +############################################################################## +# +# Copyright (c) 2006 Nexedi SARL and Contributors. All Rights Reserved. +# Ivan Tyagov <ivan@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 Products.ERP5Type import Permissions, PropertySheet +from Products.ERP5Type.XMLObject import XMLObject + +from Products.ERP5Configurator.mixin.configurator_item import ConfiguratorItemMixin + +class OrganisationConfiguratorItem(ConfiguratorItemMixin, XMLObject): + """ This class install a Organisation.""" + + meta_type = 'ERP5 Organisation Configurator Item' + portal_type = 'Organisation Configurator Item' + add_permission = Permissions.AddPortalContent + isPortalContent = 1 + isRADContent = 1 + + # Declarative security + security = ClassSecurityInfo() + security.declareObjectProtected(Permissions.AccessContentsInformation) + + # Declarative properties + property_sheets = ( PropertySheet.Base + , PropertySheet.XMLObject + , PropertySheet.CategoryCore + , PropertySheet.DublinCore + , PropertySheet.Organisation ) + + def build(self, business_configuration): + """ Setup organisation. """ + portal = self.getPortalObject() + organisation = portal.organisation_module.newContent(portal_type="Organisation") + + org_dict = {'price_currency': 'currency_module/%s' % self.getPriceCurrency(), + 'group': self.getGroup(), + 'title': self.getTitle(), + 'corporate_name': self.getCorporateName(), + 'default_address_city': self.getDefaultAddressCity(), + 'default_email_text': self.getDefaultEmailText(), + 'default_telephone_text': self.getDefaultTelephoneText(), + 'default_address_zip_code': self.getDefaultAddressZipCode(), + 'default_address_region': self.getDefaultAddressRegion(), + 'default_address_street_address': self.getDefaultAddressStreetAddress(), + 'site':'main', # First customer's organisation is always main site. + } + organisation.edit(**org_dict) + + # store globally organization_id + business_configuration.setGlobalConfigurationAttr(organisation_id=organisation.getId()) + + ## add to customer template + self.install(organisation, business_configuration) diff --git a/product/ERP5Configurator/Document/PermissionConfiguratorItem.py b/product/ERP5Configurator/Document/PermissionConfiguratorItem.py new file mode 100644 index 0000000000..8c635eefe7 --- /dev/null +++ b/product/ERP5Configurator/Document/PermissionConfiguratorItem.py @@ -0,0 +1,83 @@ +############################################################################## +# +# Copyright (c) 2006 Nexedi SARL and Contributors. All Rights Reserved. +# Ivan Tyagov <ivan@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 Products.ERP5Type import Permissions, PropertySheet +from Products.ERP5Type.XMLObject import XMLObject +from Products.ERP5Configurator.mixin.configurator_item import ConfiguratorItemMixin + +class PermissionConfiguratorItem(ConfiguratorItemMixin, XMLObject): + """ Set permission matrix on module.""" + + meta_type = 'ERP5 Permission Configurator Item' + portal_type = 'Permission Configurator Item' + add_permission = Permissions.AddPortalContent + isPortalContent = 1 + isRADContent = 1 + + # Declarative security + security = ClassSecurityInfo() + security.declareObjectProtected(Permissions.AccessContentsInformation) + + # Declarative properties + property_sheets = ( PropertySheet.Base + , PropertySheet.XMLObject + , PropertySheet.CategoryCore + , PropertySheet.DublinCore ) + + def build(self, business_configuration): + template_module_id_list = [] + module_permissions_map = {} + sheets_dict = business_configuration.ConfigurationTemplate_readOOCalcFile(\ + self.filename) + for module_id, permissions in sheets_dict.items(): + module_permissions = {} + for permission in permissions: + roles = [] + permission_name = permission.pop('permission') + for role, checked in permission.items(): + if checked == '1': roles.append(role) + module_permissions[permission_name] = roles + # add to module map + module_permissions_map[module_id] = module_permissions + + # set permissions in fake site + portal = self.getPortalObject() + for module_id, permissions_map in module_permissions_map.items(): + if permissions_map != {}: + template_module_id_list.append(module_id) + module = portal[module_id] + for permission_name, roles in permissions_map.items(): + # we must alway include additionally 'Manager' and 'Owner' + roles.extend(['Manager', 'Owner']) + module.manage_permission(permission_name, tuple(roles), 0) + + # add customized module to customer's bt5 + if len(template_module_id_list): + bt5_obj = business_configuration.getSpecialiseValue() + bt5_obj.setTemplateModuleIdList(template_module_id_list) \ No newline at end of file diff --git a/product/ERP5Configurator/Document/PersonConfiguratorItem.py b/product/ERP5Configurator/Document/PersonConfiguratorItem.py new file mode 100644 index 0000000000..5207872d4b --- /dev/null +++ b/product/ERP5Configurator/Document/PersonConfiguratorItem.py @@ -0,0 +1,96 @@ +############################################################################## +# +# Copyright (c) 2006 Nexedi SARL and Contributors. All Rights Reserved. +# Ivan Tyagov <ivan@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 Acquisition import aq_base +from AccessControl import ClassSecurityInfo +from Products.ERP5Type import Permissions, PropertySheet +from Products.ERP5Type.XMLObject import XMLObject +from DateTime import DateTime +from Products.ERP5Configurator.mixin.configurator_item import ConfiguratorItemMixin + +class PersonConfiguratorItem(XMLObject, ConfiguratorItemMixin): + """ Setup user. """ + + meta_type = 'ERP5 Person Configurator Item' + portal_type = 'Person Configurator Item' + add_permission = Permissions.AddPortalContent + isPortalContent = 1 + isRADContent = 1 + + # Declarative security + security = ClassSecurityInfo() + security.declareObjectProtected(Permissions.AccessContentsInformation) + + # Declarative properties + property_sheets = ( PropertySheet.Base + , PropertySheet.XMLObject + , PropertySheet.CategoryCore + , PropertySheet.DublinCore + , PropertySheet.Reference + , PropertySheet.Person + , PropertySheet.Login) + + def build(self, business_configuration): + portal = self.getPortalObject() + person = portal.person_module.newContent(portal_type="Person") + group_id = getattr(aq_base(self), 'group_id', None) + site_id = getattr(aq_base(self), 'site_id', None) + + if getattr(aq_base(self), 'organisation_id', None) is not None: + person.setCareerSubordination('organisation_module/%s' %self.organisation_id) + + # save + person_dict = {'default_email_text': self.getDefaultEmailText(), + 'default_telephone_text': self.getDefaultTelephoneText(), + 'first_name': self.getFirstName(), + 'career_function': self.getFunction(), + 'last_name': self.getLastName(), + 'password': self.getPassword(), + } + person.edit(**person_dict) + + # explicitly use direct mutator to avoid uniqueness checks in Person.setReference + # which work in main ERP5 site context (uses catalog and cache) + # this is a problem when customer's entered reference is the same as + # already exisitng one in main ERP5 site one + person._setReference(self.getReference()) + + assignment = person.newContent(portal_type="Assignment") + assignment.setFunction(self.getFunction()) + assignment.setGroup(group_id) + assignment.setSite(site_id) + + # Set dates are required to create valid assigments. + now = DateTime() + assignment.setStartDate(now) + # XXX Is it required to set stop date? + # Define valid for 10 years. + assignment.setStopDate(now + (365*10)) + + ## add to customer template + self.install(person, business_configuration) diff --git a/product/ERP5Configurator/Document/PortalTypeConfiguratorItem.py b/product/ERP5Configurator/Document/PortalTypeConfiguratorItem.py new file mode 100644 index 0000000000..a091efef57 --- /dev/null +++ b/product/ERP5Configurator/Document/PortalTypeConfiguratorItem.py @@ -0,0 +1,79 @@ +############################################################################## +# +# Copyright (c) 2008 Nexedi SA and Contributors. All Rights Reserved. +# TAHARA Yusei <yusei@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 Products.ERP5Type import Permissions, PropertySheet +from Products.ERP5Type.XMLObject import XMLObject +from Products.ERP5Configurator.mixin.configurator_item import ConfiguratorItemMixin + +class PortalTypeConfiguratorItem(ConfiguratorItemMixin, XMLObject): + """Configure Portal Type.""" + + meta_type = 'ERP5 Portal Type Configurator Item' + portal_type = 'Portal Type Configurator Item' + add_permission = Permissions.AddPortalContent + isPortalContent = 1 + isRADContent = 1 + + # Declarative security + security = ClassSecurityInfo() + security.declareObjectProtected(Permissions.AccessContentsInformation) + + # Declarative properties + property_sheets = ( PropertySheet.Base + , PropertySheet.XMLObject + , PropertySheet.CategoryCore + , PropertySheet.DublinCore ) + + def build(self, business_configuration): + portal = self.getPortalObject() + + # Support adding new property sheet to portal type information. + # arguments: + # * target_portal_type + # * add_propertysheet_list + type_information = getattr(portal.portal_types, self.target_portal_type) + for name in self.add_propertysheet_list: + if not name in type_information.property_sheet_list: + new_property_sheet_list = list(type_information.property_sheet_list) + new_property_sheet_list.append(name) + type_information.property_sheet_list = tuple(new_property_sheet_list) + bt5_obj = business_configuration.getSpecialiseValue() + + old_property_sheet_list = bt5_obj.getTemplatePortalTypePropertySheetList() + new_property_sheet_list = (list(old_property_sheet_list) + + ['%s | %s' % (self.target_portal_type, name) + for name in self.add_propertysheet_list] + ) + + bt5_obj.edit( + template_portal_type_property_sheet_list=new_property_sheet_list) + + # + # TODO:This class must support many other features we can use in ZMI. + # diff --git a/product/ERP5Configurator/Document/PortalTypeRolesSpreadsheetConfiguratorItem.py b/product/ERP5Configurator/Document/PortalTypeRolesSpreadsheetConfiguratorItem.py new file mode 100644 index 0000000000..32765bb59e --- /dev/null +++ b/product/ERP5Configurator/Document/PortalTypeRolesSpreadsheetConfiguratorItem.py @@ -0,0 +1,103 @@ +############################################################################## +# +# Copyright (c) 2008 Nexedi SA and Contributors. All Rights Reserved. +# Jerome Perrin <jerome@nexedi.com> +# +############################################################################## + +from Acquisition import aq_base + +from AccessControl import ClassSecurityInfo +from Products.ERP5Type import Permissions, PropertySheet +from Products.ERP5Type.XMLObject import XMLObject +from Products.ERP5Configurator.mixin.configurator_item import ConfiguratorItemMixin + + +class PortalTypeRolesSpreadsheetConfiguratorItem(ConfiguratorItemMixin, XMLObject): + """Import a portal type roles spreadsheet. + """ + + meta_type = 'ERP5 Portal Type Roles Spreadsheet Configurator Item' + portal_type = 'Portal Type Roles Spreadsheet Configurator Item' + add_permission = Permissions.AddPortalContent + isPortalContent = 1 + isRADContent = 1 + + # Declarative security + security = ClassSecurityInfo() + security.declareObjectProtected(Permissions.AccessContentsInformation) + + # Declarative properties + property_sheets = ( PropertySheet.Base + , PropertySheet.XMLObject + , PropertySheet.CategoryCore + , PropertySheet.DublinCore + , PropertySheet.PortalTypeRolesSpreadsheetConfiguratorItem + ) + + def build(self, business_configuration): + portal = self.getPortalObject() + self._readSpreadSheet() + for type_name, role_list in self._spreadsheet_cache.items(): + portal_type = portal.portal_types.getTypeInfo(type_name) + for role in role_list: + # rebuild a category from Group / Site & Function + category_list = [] + for bc in ('Group', 'Site', 'Function'): + if role.get(bc): + category_list.append(role[bc]) + #category = '\n'.join(category_list) + role_dict = { + #'title': 'role', + 'description': role.get('Description', ''), + 'role_name_list': role.get('Role'), + 'role_category_list': category_list, + 'role_base_category_list': role.get('Base_Category', ''), + 'role_base_category_script_id': role.get('Base_Category_Script', + role.get('Script', ''))} + portal_type.newContent(portal_type='Role Information', \ + **role_dict) + + ## Update BT5 + bt5_obj = business_configuration.getSpecialiseValue() + bt5_obj.edit(template_portal_type_roles_list=self._spreadsheet_cache.keys()) + + def checkSpreadSheetConsistency(self): + """Check that the spread sheet is consistent with categories spreadsheet. + + - all roles have a name ('Name' or 'Role') + - all roles have a portal type ('Name' or 'Role') + - all roles uses valid group & function categories + + XXX do we want to use constraint framework here ? + """ + + def _readSpreadSheet(self): + """Read the spreadsheet and prepare internal category cache. + """ + aq_self = aq_base(self) + if getattr(aq_self, '_spreadsheet_cache', None) is None: + role_dict = dict() + info_dict = self.ConfigurationTemplate_readOOCalcFile( + 'default_portal_type_roles_spreadsheet') + for sheet_name, table in self.ConfigurationTemplate_readOOCalcFile( + 'default_portal_type_roles_spreadsheet').items(): + for line in table: + if 'Portal_Type' in line: + ptype_role_list = role_dict.setdefault(line['Portal_Type'], []) + ptype_role_list.append(line) + + aq_self._spreadsheet_cache = role_dict + + security.declareProtected(Permissions.ModifyPortalContent, + 'setDefaultPortalTypeRolesSpreadsheetFile') + def setDefaultPortalTypeRolesSpreadsheetFile(self, *args, **kw): + """Reset the spreadsheet cache.""" + self._setDefaultPortalTypeRolesSpreadsheetFile(*args, **kw) + self._spreadsheet_cache = None + self.reindexObject() + + security.declareProtected(Permissions.ModifyPortalContent, + 'setPortalTypeRolesSpreadsheetFile') + setPortalTypeRolesSpreadsheetFile = setDefaultPortalTypeRolesSpreadsheetFile + diff --git a/product/ERP5Configurator/Document/PreferenceConfiguratorItem.py b/product/ERP5Configurator/Document/PreferenceConfiguratorItem.py new file mode 100644 index 0000000000..a3835e64e6 --- /dev/null +++ b/product/ERP5Configurator/Document/PreferenceConfiguratorItem.py @@ -0,0 +1,118 @@ +############################################################################## +# +# Copyright (c) 2006 Nexedi SARL and Contributors. All Rights Reserved. +# Ivan Tyagov <ivan@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 Products.ERP5Type import Permissions, PropertySheet +from Products.ERP5Type.XMLObject import XMLObject +from Products.ERP5Configurator.mixin.configurator_item import ConfiguratorItemMixin + +class PreferenceConfiguratorItem(ConfiguratorItemMixin, XMLObject): + """ Setup preference. """ + + meta_type = 'ERP5 Preference Configurator Item' + portal_type = 'Preference Configurator Item' + add_permission = Permissions.AddPortalContent + isPortalContent = 1 + isRADContent = 1 + + # Declarative security + security = ClassSecurityInfo() + security.declareObjectProtected(Permissions.AccessContentsInformation) + + # Declarative properties + property_sheets = ( PropertySheet.Base + , PropertySheet.XMLObject + , PropertySheet.CategoryCore + , PropertySheet.DublinCore ) + + def _getPreferenceNameList(self): + """Returns all existing preference names. + + TODO: this should be done by introspecting property sheet. + """ + return ( 'preferred_category_child_item_list_method_id', + 'preferred_accounting_transaction_from_date', + 'preferred_accounting_transaction_at_date', + 'preferred_section_category', + 'preferred_section', + 'preferred_accounting_transaction_section_category', + 'preferred_accounting_transaction_source_section', + 'preferred_accounting_transaction_currency', + 'preferred_accounting_transaction_gap', + 'preferred_accounting_transaction_simulation_state_list', + 'preferred_text_format', + 'preferred_text_editor', + 'preferred_date_order', + 'preferred_listbox_view_mode_line_count', + 'preferred_listbox_list_mode_line_count', + 'preferred_string_field_width', + 'preferred_textarea_width', + 'preferred_textarea_height', + 'preferred_money_quantity_field_width', + 'preferred_quantity_field_width', + 'preferred_report_style', + 'preferred_report_format', + 'preferred_html_style_access_tab', + ) + + + def build(self, business_configuration): + portal = self.getPortalObject() + organisation_id = business_configuration.\ + getGlobalConfigurationAttr('organisation_id') + organisation_path = 'organisation_module/%s' % organisation_id + + preference = portal.portal_preferences._getOb(self.object_id, None) + if preference is None: + preference = portal.portal_preferences.newContent( + portal_type='Preference', + id=self.object_id, + title = self.title, + description = self.description, + priority = 1) + + # XXX this have to be translated in user language. + preference_dict = {} + + marker = [] + for preference_name in self._getPreferenceNameList(): + preference_value = getattr(self, preference_name, + preference.getProperty(preference_name, marker)) + if preference_value is not marker: + preference_dict[preference_name] = preference_value + + preference_dict['preferred_accounting_transaction_source_section'] = \ + organisation_path + preference_dict['preferred_section'] = organisation_path + preference.edit(**preference_dict) + bt5_obj = business_configuration.getSpecialiseValue() + current_template_preference_list = list(bt5_obj.getTemplatePreferenceList()) + if preference.getId() not in current_template_preference_list: + current_template_preference_list.append(preference.getId()) + bt5_obj.edit(template_preference_list=current_template_preference_list,) + diff --git a/product/ERP5Configurator/Document/RoleConfiguratorItem.py b/product/ERP5Configurator/Document/RoleConfiguratorItem.py new file mode 100644 index 0000000000..ada138083e --- /dev/null +++ b/product/ERP5Configurator/Document/RoleConfiguratorItem.py @@ -0,0 +1,98 @@ +############################################################################## +# +# Copyright (c) 2006 Nexedi SARL and Contributors. All Rights Reserved. +# Ivan Tyagov <ivan@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 Acquisition import aq_base +from AccessControl import ClassSecurityInfo +from Products.ERP5Type import Permissions, PropertySheet +from Products.ERP5Type.XMLObject import XMLObject +from Products.ERP5Configurator.mixin.configurator_item import ConfiguratorItemMixin +from zLOG import LOG, INFO + +class RoleConfiguratorItem(ConfiguratorItemMixin, XMLObject): + """ Setup role per module basis. """ + + meta_type = 'ERP5 Role Configurator Item' + portal_type = 'Role Configurator Item' + add_permission = Permissions.AddPortalContent + isPortalContent = 1 + isRADContent = 1 + + # Declarative security + security = ClassSecurityInfo() + security.declareObjectProtected(Permissions.AccessContentsInformation) + + # Declarative properties + property_sheets = ( PropertySheet.Base + , PropertySheet.XMLObject + , PropertySheet.CategoryCore + , PropertySheet.DublinCore ) + + def build(self, business_configuration): + object_list = business_configuration.ConfigurationTemplate_readOOCalcFile(self.filename) + portal = self.getPortalObject() + + portal_type_dict = {} + # we may pass some override dynamic values from outside + # Example:we post 'group_id' and in column we have it then + # it will be replaced with value if not configuration file matters + dynamic_values = dict(group_id = getattr(aq_base(self), 'group_id', None), + function_id = getattr(aq_base(self), 'function_id', None), + site_id = getattr(aq_base(self), 'site_id', None),) + for oo_module_dict in object_list: + mod_conf_list = [] + portal_type = oo_module_dict.pop('portal_type') + for category, role_list_string in oo_module_dict.items(): + # passed from outside (it has higher priority than configuratiohn file) + category = dynamic_values.get(category, category) + title = category.replace('/', '_') + role_name_list = [x.strip() for x in role_list_string.split(';')] + role_category_list=[category] + conf_dict = {'title': title, + 'description': 'Configured by Nexedi Configurator', + 'role_name_list': role_name_list, + 'role_category_list': role_category_list} + mod_conf_list.append(conf_dict) + portal_type_dict[portal_type] = mod_conf_list + ## Update fake site + # XXX rafael: improve this, the ignore list is quite ugly. + ignore_list = [] + portal_type_id_list = portal.portal_types.objectIds() + for portal_type, role_list in portal_type_dict.items(): + for role_dict in role_list: + if portal_type in portal_type_id_list: + portal.portal_types[portal_type].newContent(portal_type='Role Information', \ + **role_dict) + else: + ignore_list.append(portal_type) + LOG("CONFIGURATOR", INFO, "Fail to define Roles for %s" % portal_type) + ## Update BT5 + bt5_obj = business_configuration.getSpecialiseValue() + # keep existing roles definition (from previous configuration saves) + for existing_type in bt5_obj.getTemplatePortalTypeRolesList(): + portal_type_dict[existing_type] = 1 + bt5_obj.edit(template_portal_type_roles_list=[i for i in portal_type_dict.keys() if i not in ignore_list]) diff --git a/product/ERP5Configurator/Document/RuleConfiguratorItem.py b/product/ERP5Configurator/Document/RuleConfiguratorItem.py new file mode 100644 index 0000000000..d0c3193f3d --- /dev/null +++ b/product/ERP5Configurator/Document/RuleConfiguratorItem.py @@ -0,0 +1,71 @@ +############################################################################## +# +# Copyright (c) 2010 Nexedi SA and Contributors. All Rights Reserved. +# Lucas Carvalho <lucas@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 Acquisition import aq_base +from AccessControl import ClassSecurityInfo +from Products.ERP5Type import Permissions, PropertySheet +from Products.ERP5Type.XMLObject import XMLObject +from Products.ERP5Configurator.mixin.configurator_item import ConfiguratorItemMixin +from zLOG import LOG, INFO + +class RuleConfiguratorItem(ConfiguratorItemMixin, XMLObject): + """ Setup Rules. """ + + meta_type = 'ERP5 Rule Configurator Item' + portal_type = 'Rule Configurator Item' + add_permission = Permissions.AddPortalContent + isPortalContent = 1 + isRADContent = 1 + + # Declarative security + security = ClassSecurityInfo() + security.declareObjectProtected(Permissions.AccessContentsInformation) + + # Declarative properties + property_sheets = ( PropertySheet.Base + , PropertySheet.XMLObject + , PropertySheet.CategoryCore + , PropertySheet.DublinCore ) + + def build(self, business_configuration): + portal = self.getPortalObject() + simulation_rule_dict = portal.ERPSite_getConfiguratorSimulationRuleDict() + for key, value in simulation_rule_dict.iteritems(): + reference = value.get('default_reference') + result = portal.portal_rules.searchFolder(sort_on='version', + sort_order='descending', + reference=reference) + if len(result): + value['version'] = int(result[0].getVersion()) + 1 + rule = portal.portal_rules.newContent(**value) + + content_list = value.pop('content_list') + for content_dict in content_list: + sub_object = rule.newContent(**content_dict) + + self.install(rule, business_configuration) diff --git a/product/ERP5Configurator/Document/ServiceConfiguratorItem.py b/product/ERP5Configurator/Document/ServiceConfiguratorItem.py new file mode 100644 index 0000000000..6bc6feaac1 --- /dev/null +++ b/product/ERP5Configurator/Document/ServiceConfiguratorItem.py @@ -0,0 +1,66 @@ +############################################################################## +# +# Copyright (c) 2008 Nexedi SA and Contributors. All Rights Reserved. +# TAHARA Yusei <yusei@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 Products.ERP5Type import Permissions, PropertySheet +from Products.ERP5Type.XMLObject import XMLObject +from Products.ERP5Configurator.mixin.configurator_item import ConfiguratorItemMixin + +class ServiceConfiguratorItem(ConfiguratorItemMixin, XMLObject): + """Create default service documents.""" + + meta_type = 'ERP5 Service Configurator Item' + portal_type = 'Service Configurator Item' + add_permission = Permissions.AddPortalContent + isPortalContent = 1 + isRADContent = 1 + + # Declarative security + security = ClassSecurityInfo() + security.declareObjectProtected(Permissions.AccessContentsInformation) + + # Declarative properties + property_sheets = ( PropertySheet.Base + , PropertySheet.XMLObject + , PropertySheet.CategoryCore + , PropertySheet.DublinCore + , PropertySheet.ServiceConfiguratorItem ) + + def build(self, business_configuration): + portal = self.getPortalObject() + module = portal.service_module + for service_id, service_title in self.getServiceList(): + # XXX FIXME We cannot define service_id like this, + # because it cause conflict when configurator is + # used twice. + document = module.newContent(portal_type='Service', + #id=service_id, + title=service_title, + ) + ## add to customer template + self.install(document, business_configuration) diff --git a/product/ERP5Configurator/Document/SitePropertyConfiguratorItem.py b/product/ERP5Configurator/Document/SitePropertyConfiguratorItem.py new file mode 100644 index 0000000000..139b7f6600 --- /dev/null +++ b/product/ERP5Configurator/Document/SitePropertyConfiguratorItem.py @@ -0,0 +1,64 @@ +############################################################################## +# +# Copyright (c) 2008 Nexedi SARL and Contributors. All Rights Reserved. +# Yoshinori Okuji <yo@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 Products.ERP5Type import Permissions, PropertySheet +from Products.ERP5Type.XMLObject import XMLObject +from Products.ERP5Configurator.mixin.configurator_item import ConfiguratorItemMixin + +class SitePropertyConfiguratorItem(ConfiguratorItemMixin, XMLObject): + """Set up site properties.""" + + meta_type = 'ERP5 Site Property Configurator Item' + portal_type = 'Site Property Configurator Item' + add_permission = Permissions.AddPortalContent + isPortalContent = 1 + isRADContent = 1 + + # Declarative security + security = ClassSecurityInfo() + security.declareObjectProtected(Permissions.AccessContentsInformation) + + # Declarative properties + property_sheets = ( PropertySheet.Base + , PropertySheet.XMLObject + , PropertySheet.CategoryCore + , PropertySheet.DublinCore + , PropertySheet.SitePropertyConfiguratorItem ) + + def build(self, business_configuration): + portal = self.getPortalObject() + id_list = [] + for id, value, prop_type in self.getSitePropertyList(): + if portal.hasProperty(id): + portal._delProperty(id) + portal._setProperty(id, value, type=prop_type) + id_list.append(id) + bt = business_configuration.getSpecialiseValue() + bt.edit(template_site_property_id_list=id_list) + diff --git a/product/ERP5Configurator/Document/StandardBT5ConfiguratorItem.py b/product/ERP5Configurator/Document/StandardBT5ConfiguratorItem.py new file mode 100644 index 0000000000..aec4d411a7 --- /dev/null +++ b/product/ERP5Configurator/Document/StandardBT5ConfiguratorItem.py @@ -0,0 +1,107 @@ +############################################################################## +# +# Copyright (c) 2006 Nexedi SARL and Contributors. All Rights Reserved. +# Ivan Tyagov <ivan@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 Products.CMFCore.utils import getToolByName +from AccessControl import ClassSecurityInfo +from Products.ERP5Type import Permissions, PropertySheet +from Products.ERP5Type.XMLObject import XMLObject +from Products.ERP5Configurator.mixin.configurator_item import ConfiguratorItemMixin +import transaction + +class StandardBT5ConfiguratorItem(ConfiguratorItemMixin, XMLObject): + """ This class will install standard ERP5 template from a repository to + fake site. """ + + meta_type = 'ERP5 Standard BT5 Configurator Item' + portal_type = 'Standard BT5 Configurator Item' + add_permission = Permissions.AddPortalContent + isPortalContent = 1 + isRADContent = 1 + + # Declarative security + security = ClassSecurityInfo() + security.declareObjectProtected(Permissions.AccessContentsInformation) + + # Declarative properties + property_sheets = ( PropertySheet.Base + , PropertySheet.XMLObject + , PropertySheet.CategoryCore + , PropertySheet.DublinCore + , PropertySheet.StandardBT5ConfiguratorItem + ) + + def build(self, business_configuration): + bt5_id = self.getBt5Id() + bt5_copy_id = '%s_copy' % bt5_id + portal = self.getPortalObject() + template_tool = getToolByName(portal, 'portal_templates') + + ## Is this standard template already gzipped? + filename_bt5_id = '%s.bt5' % bt5_id + if business_configuration.isStandardBT5(filename_bt5_id): + bt_url = business_configuration.getPublicUrlForBT5Id(filename_bt5_id) + + business_configuration.newContent(portal_type='Link', + url_string = bt_url, title = filename_bt5_id) + else: + ## we need to make a copy of template to be able to export it + if not bt5_copy_id in template_tool.objectIds(): + bt5 = template_tool.getInstalledBusinessTemplate(bt5_id) + template_copy = template_tool.manage_copyObjects(ids=(bt5.getId(),)) + new_id_list = template_tool.manage_pasteObjects(template_copy) + new_bt5_id = new_id_list[0]['new_id'] + template_tool.manage_renameObject(new_bt5_id, bt5_copy_id) + ## we are sure that we have this business template + self._current_bt_id = bt5_copy_id + return self.get_it_built(business_configuration) + + def get_it_built(self, business_configuration): + portal = self.getPortalObject() + template_tool = getToolByName(portal, 'portal_templates') + bt5_obj = self._getCurrentBT(business_configuration) + if bt5_obj.getBuildingState() != 'built': + ## build template so it can be exported + bt5_obj.edit() + bt5_obj.build() + # XXX Due a bug into Business Templates it is not possible build + # the business template and export when this have one + # ActionTemplateItem. This is a TEMPORARY CHANGE and it should be + # removed as soon as Business Template is FIXED. + transaction.savepoint(optimistic=True) + bt5_data = template_tool.export(bt5_obj) + business_configuration.newContent(portal_type='File', + title = '%s.bt5' % bt5_obj.getId(), + data = bt5_data) + + def _getCurrentBT(self, business_configuration): + """ Return current bt5 file. """ + portal = self.getPortalObject() + template_tool = portal.portal_templates + bt5_id = self._current_bt_id + bt5_obj = portal.portal_templates[bt5_id] + return bt5_obj diff --git a/product/ERP5Configurator/Document/SystemPreferenceConfiguratorItem.py b/product/ERP5Configurator/Document/SystemPreferenceConfiguratorItem.py new file mode 100644 index 0000000000..6ec1e4cd3b --- /dev/null +++ b/product/ERP5Configurator/Document/SystemPreferenceConfiguratorItem.py @@ -0,0 +1,120 @@ +############################################################################## +# +# Copyright (c) 2006 Nexedi SARL and Contributors. All Rights Reserved. +# Ivan Tyagov <ivan@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 Products.ERP5Type import Permissions, PropertySheet +from Products.ERP5Type.XMLObject import XMLObject +from Products.ERP5Configurator.mixin.configurator_item import ConfiguratorItemMixin + +class SystemPreferenceConfiguratorItem(ConfiguratorItemMixin, XMLObject): + """ + Setup system preference. + """ + + meta_type = 'ERP5 System Preference Configurator Item' + portal_type = 'System Preference Configurator Item' + add_permission = Permissions.AddPortalContent + isPortalContent = 1 + isRADContent = 1 + + # Declarative security + security = ClassSecurityInfo() + security.declareObjectProtected(Permissions.AccessContentsInformation) + + # Declarative properties + property_sheets = ( PropertySheet.Base + , PropertySheet.XMLObject + , PropertySheet.CategoryCore + , PropertySheet.DublinCore ) + + def _getPreferenceNameList(self): + """Returns all existing preference names. + + TODO: this should be done by introspecting property sheet. + """ + return ( # CRM + 'preferred_campaign_resource_list', + 'preferred_event_assessment_form_id_list', + 'preferred_event_resource_list', + 'preferred_event_sender_email', + 'preferred_meeting_resource_list', + 'preferred_sale_opportunity_resource_list', + 'preferred_support_request_resource_list', + # DMS + 'preferred_ooodoc_server_address', + 'preferred_ooodoc_server_port_number', + 'preferred_conversion_cache_factory', + 'preferred_document_email_ingestion_address', + 'preferred_document_reference_method_id', + 'preferred_document_file_name_regular_expression', + 'preferred_document_reference_regular_expression', + 'preferred_document_classification', + 'preferred_synchronous_metadata_discovery', + 'preferred_redirect_to_document', + # PDM + 'preferred_product_individual_variation_base_category_list', + 'preferred_component_individual_variation_base_category_list', + 'preferred_service_individual_variation_base_category_list', + # Trade + 'preferred_supplier_role_list', + 'preferred_client_role_list', + 'preferred_sale_use_list', + 'preferred_purchase_use_list', + 'preferred_packing_use_list', + # Express + ) + + + def build(self, business_configuration): + portal = self.getPortalObject() + preference = portal.portal_preferences._getOb(self.object_id, None) + if preference is None: + preference = portal.portal_preferences.newContent( + portal_type = 'System Preference', + id = self.object_id, + title = self.title, + description = self.description, + priority = 1) + + # XXX this have to be translated in user language. + preference_dict = {} + + marker = [] + for preference_name in self._getPreferenceNameList(): + preference_value = getattr(self, preference_name, + preference.getProperty(preference_name, marker)) + if preference_value is not marker: + preference_dict[preference_name] = preference_value + + preference.edit(**preference_dict) + bt5_obj = business_configuration.getSpecialiseValue() + current_template_preference_list = list(bt5_obj.getTemplatePreferenceList()) + if preference.getId() not in current_template_preference_list: + current_template_preference_list.append(preference.getId()) + bt5_obj.edit(template_preference_list=current_template_preference_list,) + diff --git a/product/ERP5Configurator/Document/WorkflowSecurityConfiguratorItem.py b/product/ERP5Configurator/Document/WorkflowSecurityConfiguratorItem.py new file mode 100644 index 0000000000..00e001a8ba --- /dev/null +++ b/product/ERP5Configurator/Document/WorkflowSecurityConfiguratorItem.py @@ -0,0 +1,123 @@ +############################################################################## +# +# Copyright (c) 2006 Nexedi SARL and Contributors. All Rights Reserved. +# Ivan Tyagov <ivan@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 Products.ERP5Type import Permissions, PropertySheet +from Products.ERP5Type.XMLObject import XMLObject +from Products.ERP5Configurator.mixin.configurator_item import ConfiguratorItemMixin + +class WorkflowSecurityConfiguratorItem(ConfiguratorItemMixin, XMLObject): + """ Setup workflow for different roles. Use passed OO file. """ + + meta_type = 'ERP5 Workflow Security Configurator Item' + portal_type = 'Workflow Security Configurator Item' + add_permission = Permissions.AddPortalContent + isPortalContent = 1 + isRADContent = 1 + + # Declarative security + security = ClassSecurityInfo() + security.declareObjectProtected(Permissions.AccessContentsInformation) + + # Declarative properties + property_sheets = ( PropertySheet.Base + , PropertySheet.XMLObject + , PropertySheet.CategoryCore + , PropertySheet.DublinCore ) + + def build(self, business_configuration): + ## NOT TESTED + return + table_dict = business_configuration.ConfigurationTemplate_readOOCalcFile(self.filename) + portal = self.getPortalObject() + suffix = '_security' + suffix_len = len(suffix) + if self.filename[-suffix_len:] == suffix: + workflow_id = self.filename[:-suffix_len] + else: + raise "NoValidName" + + # Configure state permission + view_permission_list = ['View'] + access_permission_list = ['Access contents information'] + modify_permission_list = ['Modify portal content'] + add_content_permission_list = ['Add portal content'] + # Configure list of variable on the workflow + permission_list = view_permission_list + \ + access_permission_list + \ + modify_permission_list + \ + add_content_permission_list + # Remove permission list + workflow = portal.portal_workflow[workflow_id] + workflow.delManagedPermissions(workflow.permissions) + # Add new permission list + for permission in permission_list: + workflow.addManagedPermission(permission) + # Configure state permission matrix + state_list = table_dict['state'] + for state_config in state_list: + state_id = state_config.pop('state') + state = workflow.states[state_id] + # Clean the state matrix + for permission in permission_list: + state.setPermission(permission, 0, []) + # Update state matrix + permission_dict = dict([(x, []) for x in permission_list]) + for role, perm_symbol in state_config.items(): + managed_permission_list = [] + if 'A' in perm_symbol: + managed_permission_list.extend(access_permission_list) + if 'V' in perm_symbol: + managed_permission_list.extend(view_permission_list) + if 'C' in perm_symbol: + managed_permission_list.extend(add_content_permission_list) + if 'M' in perm_symbol: + managed_permission_list.extend(modify_permission_list) + for permission in managed_permission_list: + permission_dict[permission].append(role.capitalize()) + for permission, roles in permission_dict.items(): + state.setPermission(permission, 0, roles) + # XXX To be deleted + # for permission in permission_list: + # module.manage_permission(permission, ['Manager'], 0) + + # Configure transition guard + transition_list = table_dict['transition'] + for transition_conf in transition_list: + transition_id = transition_conf.pop('transition') + transition = workflow.transitions[transition_id] + guard = transition.getGuard() + role_list = [x.capitalize() for x in transition_conf.keys()] + role_string = ';'.join(role_list) + guard.changeFromProperties({'guard_roles': role_string}) + # Update business template + bt5_obj = business_configuration.getSpecialiseValue() + template_workflow_id_list = list(bt5_obj.getTemplateWorkflowIdList()) + if workflow_id not in template_workflow_id_list: + template_workflow_id_list.append(workflow_id) + bt5_obj.edit(template_workflow_id_list=template_workflow_id_list,) diff --git a/product/ERP5Configurator/Document/__init__.py b/product/ERP5Configurator/Document/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/product/ERP5Configurator/Extensions/ConfigurationTemplate_readOOoCalcFile.py b/product/ERP5Configurator/Extensions/ConfigurationTemplate_readOOoCalcFile.py new file mode 100644 index 0000000000..4a5711720e --- /dev/null +++ b/product/ERP5Configurator/Extensions/ConfigurationTemplate_readOOoCalcFile.py @@ -0,0 +1,126 @@ +############################################################################## +# +# Copyright (c) 2006 Nexedi SARL and Contributors. All Rights Reserved. +# Romain Courteaud <romain@nexedi.com> +# Ivan Tyagov <ivan@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 StringIO + +def read(self, filename): + """ + Return a OOCalc as a StringIO + """ + oo_template_file = getattr(self, filename) + fp = StringIO.StringIO(oo_template_file) + fp.filename = filename + return fp + +def getIdFromString(string): + """ + This function transform a string to a safe id. + It is used here to create a safe category id from a string. + """ + if string is None: + return None + clean_id = '' + translation_map = { "a": ['\xe0'] + , "e": ['\xe9', '\xe8'] + } + #string = string.lower() + string = string.strip() + # oocalc inserts some strange chars when you press - key in a text cell. + # Following line is a workaround for this, + # because \u2013 does not exist in latin1 + string = string.replace(u'\u2013', '-') + for char in string.encode('utf-8'):#('iso8859_1'): + if char == '_' or char.isalnum(): + clean_id += char + elif char.isspace() or char in ('+', '-'): + clean_id += '_' + else: + for (safe_char, char_list) in translation_map.items(): + if char in char_list: + clean_id += safe_char + break + return clean_id + +def convert(self, filename): + from Products.ERP5OOo.OOoUtils import OOoParser + OOoParser = OOoParser() + import_file = read(self, filename) + + # Extract tables from the speadsheet file + OOoParser.openFile(import_file) + filename = OOoParser.getFilename() + spreadsheets = OOoParser.getSpreadsheetsMapping() + + table_dict = {} + for table_name, table in spreadsheets.items(): + if not table: + continue + # Get the header of the table + columns_header = table[0] + # Get the mapping to help us to know the property according a cell index + property_map = {} + column_index = 0 + for column in columns_header: + column_id = getIdFromString(column) + # The column has no header information + # The column has a normal header + property_map[column_index] = column_id + column_index += 1 + + # Construct categories data (with absolut path) from table lines + object_list = [] + + for line in table[1:]: + object_property_dict = {} + + # Exclude empty lines + if line.count('') + line.count(None) == len(line): + continue + + # Analyse every cells of the line + cell_index = 0 + for cell in line: + # Ignore empty cells, do the test on the generated id + # because getIdFromString() is more restrictive + cell_id = getIdFromString(cell) + if cell_id not in ('', None): + # Get the property corresponding to the cell data + property_id = property_map[cell_index] + # Convert the value to something like '\xc3\xa9' not '\xc3\xa9' + object_property_dict[property_id] = cell.encode('UTF-8') + cell_index += 1 + + if len(object_property_dict) > 0: + object_list.append(object_property_dict) + table_dict[table_name.encode('UTF-8')] = object_list + + if len(table_dict.keys()) == 1: + return object_list + else: + return table_dict diff --git a/product/ERP5Configurator/Permissions.py b/product/ERP5Configurator/Permissions.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/product/ERP5Configurator/PropertySheet/BusinessConfiguration.py b/product/ERP5Configurator/PropertySheet/BusinessConfiguration.py new file mode 100644 index 0000000000..d04fd8bb57 --- /dev/null +++ b/product/ERP5Configurator/PropertySheet/BusinessConfiguration.py @@ -0,0 +1,42 @@ +############################################################################## +# +# Copyright (c) 2006 Nexedi SARL and Contributors. All Rights Reserved. +# Romain Courteaud <romain@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 BusinessConfiguration: + """ + Properties of a Business Configuration. + """ + _properties = ( + { 'id' : 'configuration_after_script_id', + 'description' : 'Defines the Id of the script to be ran after' + 'the configuration.', + 'type' : 'string', + 'default' : 'BusinessConfiguration_afterConfiguration', + 'mode' : 'w' }, + ) + + _categories = ("current_state", "resource", "specialise") diff --git a/product/ERP5Configurator/PropertySheet/CategoriesSpreadsheetConfiguratorItem.py b/product/ERP5Configurator/PropertySheet/CategoriesSpreadsheetConfiguratorItem.py new file mode 100644 index 0000000000..9800d59abd --- /dev/null +++ b/product/ERP5Configurator/PropertySheet/CategoriesSpreadsheetConfiguratorItem.py @@ -0,0 +1,47 @@ +############################################################################## +# +# Copyright (c) 2008 Nexedi SA and Contributors. All Rights Reserved. +# +# 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 CategoriesSpreadsheetConfiguratorItem: + """ Properties of a CategoriesSpreadsheetConfiguratorItem.""" + + _properties = ( + { 'id' : 'categories_spreadsheet', + 'storage_id' : 'default_categories_spreadsheet', + 'description' : 'A spreadsheet with categories definition', + 'type' : 'content', + # XXX maybe it can just be a File, so that we don't have to depend on DMS + 'portal_type' : ('Spreadsheet',), + 'acquired_property_id' : ('file', 'content_type', 'data'), + 'acquisition_base_category' : (), + 'acquisition_portal_type' : (), + 'acquisition_copy_value' : 0, + 'acquisition_mask_value' : 1, + 'acquisition_sync_value' : 0, + 'acquisition_accessor_id' : 'getDefaultCategoriesSpreadsheetValue', + 'acquisition_depends' : None, + 'mode' : 'w' }, + ) diff --git a/product/ERP5Configurator/PropertySheet/ConfigurationItem.py b/product/ERP5Configurator/PropertySheet/ConfigurationItem.py new file mode 100644 index 0000000000..c2411a18f4 --- /dev/null +++ b/product/ERP5Configurator/PropertySheet/ConfigurationItem.py @@ -0,0 +1,37 @@ +############################################################################## +# +# Copyright (c) 2006 Nexedi SARL and Contributors. All Rights Reserved. +# Ivan Tyagov <ivan@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 ConfigurationItem: + """ Properties of a Configuration Item.""" + + _properties = ({'id' : 'configuration_class_name', + 'description' : 'Configuration class name', + 'type' : 'string', + 'mode' : 'w', + 'default' : '' }, + ) diff --git a/product/ERP5Configurator/PropertySheet/PortalTypeRolesSpreadsheetConfiguratorItem.py b/product/ERP5Configurator/PropertySheet/PortalTypeRolesSpreadsheetConfiguratorItem.py new file mode 100644 index 0000000000..da76b867af --- /dev/null +++ b/product/ERP5Configurator/PropertySheet/PortalTypeRolesSpreadsheetConfiguratorItem.py @@ -0,0 +1,46 @@ +############################################################################## +# +# Copyright (c) 2008 Nexedi SA and Contributors. All Rights Reserved. +# +# 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 PortalTypeRolesSpreadsheetConfiguratorItem: + """ Properties of a PortalTypeRolesSpreadsheetConfiguratorItem.""" + + _properties = ( + { 'id' : 'portal_type_roles_spreadsheet', + 'storage_id' : 'default_portal_type_roles_spreadsheet', + 'description' : 'The spreadsheet for portal type roles configuration', + 'type' : 'content', + 'portal_type' : ('Spreadsheet',), + 'acquired_property_id' : ('file', 'content_type', 'data'), + 'acquisition_base_category' : (), + 'acquisition_portal_type' : (), + 'acquisition_copy_value' : 0, + 'acquisition_mask_value' : 1, + 'acquisition_sync_value' : 0, + 'acquisition_accessor_id' : 'getDefaultPortalTypeRolesSpreadsheetValue', + 'acquisition_depends' : None, + 'mode' : 'w' }, + ) diff --git a/product/ERP5Configurator/PropertySheet/ServiceConfiguratorItem.py b/product/ERP5Configurator/PropertySheet/ServiceConfiguratorItem.py new file mode 100644 index 0000000000..12269f1e5b --- /dev/null +++ b/product/ERP5Configurator/PropertySheet/ServiceConfiguratorItem.py @@ -0,0 +1,37 @@ +############################################################################## +# +# Copyright (c) 2006 Nexedi SARL and Contributors. All Rights Reserved. +# Ivan Tyagov <ivan@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 ServiceConfiguratorItem: + """ Properties of a ServiceConfiguratorItem.""" + + _properties = ({'id' : 'service', + 'description' : 'Services', + 'type' : 'lines', + 'mode' : 'w', + 'default' : []}, + ) diff --git a/product/ERP5Configurator/PropertySheet/SitePropertyConfiguratorItem.py b/product/ERP5Configurator/PropertySheet/SitePropertyConfiguratorItem.py new file mode 100644 index 0000000000..e835e05e13 --- /dev/null +++ b/product/ERP5Configurator/PropertySheet/SitePropertyConfiguratorItem.py @@ -0,0 +1,36 @@ +############################################################################## +# +# Copyright (c) 2006 Nexedi SARL and Contributors. All Rights Reserved. +# Ivan Tyagov <ivan@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 SitePropertyConfiguratorItem: + """ Site Properties Configurator Item.""" + + _properties = ({'id' : 'site_property_list', + 'description' : 'Site Property List', + 'type' : 'lines', + 'mode' : 'w', + 'default' : []},) diff --git a/product/ERP5Configurator/PropertySheet/StandardBT5ConfiguratorItem.py b/product/ERP5Configurator/PropertySheet/StandardBT5ConfiguratorItem.py new file mode 100644 index 0000000000..930a33723b --- /dev/null +++ b/product/ERP5Configurator/PropertySheet/StandardBT5ConfiguratorItem.py @@ -0,0 +1,37 @@ +############################################################################## +# +# Copyright (c) 2006 Nexedi SARL and Contributors. All Rights Reserved. +# Ivan Tyagov <ivan@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 StandardBT5ConfiguratorItem: + """ Properties of a ReturnStandardBT5ConfiguratorItem.""" + + _properties = ({'id' : 'bt5_id', + 'description' : 'Business Template ID', + 'type' : 'string', + 'mode' : 'w', + 'default' : 'erp5_base' }, + ) diff --git a/product/ERP5Configurator/PropertySheet/__init__.py b/product/ERP5Configurator/PropertySheet/__init__.py new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/product/ERP5Configurator/PropertySheet/__init__.py @@ -0,0 +1 @@ + diff --git a/product/ERP5Configurator/Tool/ConfiguratorTool.py b/product/ERP5Configurator/Tool/ConfiguratorTool.py new file mode 100644 index 0000000000..9640e683f6 --- /dev/null +++ b/product/ERP5Configurator/Tool/ConfiguratorTool.py @@ -0,0 +1,476 @@ +############################################################################## +# +# Copyright (c) 2006-2010 Nexedi SA and Contributors. All Rights Reserved. +# Romain Courteaud <romain@nexedi.com> +# Ivan Tyagov <ivan@nexedi.com> +# Rafael Monnerat <rafael@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 Products.ERP5Type.Globals import DTMLFile +from Products.ERP5Type.Accessor.Constant import PropertyGetter as \ + ConstantGetter +from Products.ERP5Type.Tool.BaseTool import BaseTool +from Products.ERP5Type import Permissions +from Products.ERP5Configurator import _dtmldir +from Products.CMFCore.utils import getToolByName +from Products.Formulator.Errors import FormValidationError +import cookielib +from base64 import encodestring +from urllib import quote +from DateTime import DateTime + +# global (RAM) cookie storage +cookiejar = cookielib.CookieJar() +last_loggedin_user_and_password = None +referer = None +installation_status = {'bt5': {'current': 0, + 'all': 0,}, + 'activity_list': [],} + +# cookie name to store user's preferred language name +LANGUAGE_COOKIE_NAME = 'configurator_user_preferred_language' +BUSINESS_CONFIGURATION_COOKIE_NAME = 'business_configuration_key' + +def getAvailableLanguageFromHttpAcceptLanguage(http_accept_language, + available_language_list, + default='en'): + for language_set in http_accept_language.split(','): + language_tag = language_set.split(';')[0] + language = language_tag.split('-')[0] + if language in available_language_list: + return language + return default + +def _isUserAcknowledged(cookiejar): + """ Is user authenticated to remote system through a cookie. """ + for cookie in cookiejar: + if cookie.name == '__ac' and cookie.value != '': + return 1 + return 0 + +def _validateFormToRequest(form, REQUEST, **kw): + """ Validate form to REQUEST. """ + form_kw = {} + REQUEST.form = kw + try: + form.validate_all_to_request(REQUEST) + validation_status = 0 + validation_errors = None + except FormValidationError, validation_errors: + ## not all fields valid + validation_status = 1 + except Exception, validation_errors: + ## missing fields + validation_status = 2 + ## extract form arguments and remove leading prefixes + if validation_status==0: + for field in form.get_fields(): + field_id = field.id + value = getattr(REQUEST, field_id, None) + for prefix in ('my_', 'your_',): + if field_id.startswith(prefix): + attr_id = field_id[len(prefix):] + form_kw[attr_id] = value + for del_key in (field.generate_field_key(validation=1), field_id): + try: + REQUEST.other.pop(del_key) + except KeyError: + pass + return validation_status, form_kw, validation_errors + + +class ConfiguratorTool(BaseTool): + """ + This tool provides a Configurator Tool. + """ + + id = 'portal_configurator' + title = 'Configurator Tool' + meta_type = 'ERP5 Configurator Tool' + portal_type = 'Configurator Tool' + + isPortalContent = ConstantGetter('isPortalContent', value=True) + + security = ClassSecurityInfo() + + security.declareProtected(Permissions.ManagePortal, 'manage_overview') + manage_overview = DTMLFile('explainConfiguratorTool', _dtmldir ) + + def getConfiguratorUserPreferredLanguage(self): + """ Get configuration language as selected by user """ + REQUEST = getattr(self, 'REQUEST', None) + configurator_user_preferred_language = None + if REQUEST is not None: + # language value will be in cookie or REQUEST itself. + configurator_user_preferred_language = REQUEST.get(LANGUAGE_COOKIE_NAME, + None) + if configurator_user_preferred_language is None: + # Find a preferred language from HTTP_ACCEPT_LANGUAGE + available_language_list = [i[1] for i in self\ + .ConfiguratorTool_getConfigurationLanguageList()] + configurator_user_preferred_language = \ + getAvailableLanguageFromHttpAcceptLanguage( + REQUEST.get('HTTP_ACCEPT_LANGUAGE', 'en'), + available_language_list) + if configurator_user_preferred_language is None: + configurator_user_preferred_language = 'en' + return configurator_user_preferred_language + + ###################################################### + ## Navigation ## + ###################################################### + def login(self, REQUEST): + """ Login client and show next form. """ + password = REQUEST.get('field_my_ac_key', '') + if self._isCorrectConfigurationKey(password): + # set user preferred configuration language + user_preferred_language = REQUEST.get( + 'field_my_user_preferred_language', None) + if user_preferred_language: + # Set language value to request so that next page after login + # can get the value. Because cookie value is available from + # next request. + REQUEST.set(LANGUAGE_COOKIE_NAME, user_preferred_language) + REQUEST.RESPONSE.setCookie(LANGUAGE_COOKIE_NAME, + user_preferred_language, + path='/', + expires=(DateTime()+30).rfc822()) + # set encoded __ac_key cookie at client's browser + __ac_key = quote(encodestring(password)) + expires = (DateTime() + 1).toZone('GMT').rfc822() + REQUEST.RESPONSE.setCookie('__ac_key', + __ac_key, + expires = expires) + REQUEST.set('__ac_key', __ac_key) + bc = REQUEST.get('field_your_business_configuration') + REQUEST.RESPONSE.setCookie(BUSINESS_CONFIGURATION_COOKIE_NAME, + bc, + expires = expires) + REQUEST.set(BUSINESS_CONFIGURATION_COOKIE_NAME, bc) + return self.next(REQUEST=REQUEST) + else: + REQUEST.set('portal_status_message', + self.Base_translateString('Incorrect Configuration Key')) + return self.view() + + def _isCorrectConfigurationKey(self, password=None): + """ Is configuration key correct """ + if password is None: + password = self.REQUEST.get('__ac_key', None) + # Not still not finished yet. + return 1 + + #security.declareProtected(Permissions.ModifyPortalContent, 'next') + def next(self, REQUEST): + """ Validate settings and return a new form to the user. """ + # check if user is allowed to access service + portal = self.getPortalObject() + if not self._isCorrectConfigurationKey(): + REQUEST.set('portal_status_message', + self.Base_translateString('Incorrect Configuration Key')) + return self.view() + kw = self.REQUEST.form.copy() + business_configuration = REQUEST.get(BUSINESS_CONFIGURATION_COOKIE_NAME) + bc = portal.restrictedTraverse(business_configuration) + if bc is None: + REQUEST.set('portal_status_message', + self.Base_translateString( + 'You cannot Continue. Unable to find your Business Configuration.')) + return self.view() + response = self._next(business_configuration=bc,kw=kw) + ## Parse server response + command = response["command"] + if command == "show": + return self.ConfiguratorTool_dialogForm(previous=response['previous'], + form_html=response["data"], + next = response['next']) + elif command == "install": + return self.startInstallation(bc, REQUEST=REQUEST) + + def _next(self, business_configuration, kw): + """ Return next configuration form and validate previous. """ + form_kw = {} + need_validation = 1 + validation_errors = None + response = {} + portal = self.getPortalObject() + + ## initial state no previous form to validate + if business_configuration.isInitialConfigurationState(): + need_validation = 0 + + ## client can not go further hist business configuration is already built + if business_configuration.isEndConfigurationState() or \ + business_configuration.getNextTransition() == None: + return self._terminateConfigurationProcess(response, + 'no_available_transitions') + + isMultiEntryTransition = business_configuration._isMultiEntryTransition() + ## validate multiple forms + if isMultiEntryTransition: + html_forms = [] + failed_forms_counter = 0 + transition = business_configuration.getNextTransition() + form = getattr(business_configuration, transition.getTransitionFormId()) + for form_key in filter(lambda x: x.startswith('field_'), kw.keys()): + form_kw[form_key] = kw[form_key] + ## iterate all forms + for form_counter in range(0, isMultiEntryTransition): + single_form_kw = {} + for key,value in form_kw.items(): + if isinstance(value, list) or isinstance(value, tuple): + ## we have more than one form shown + single_form_kw[key] = value[form_counter] + # save original value in request in some cases of multiple forms + # we need it for validation + single_form_kw['_original_%s' %key] = value + else: + ## even though we have multiple entry transition customer wants + ## ONE form! + single_form_kw[key] = value + ## update properly REQUEST with current form data + for key,value in single_form_kw.items(): + self.REQUEST.set(key, value) + ## get validation status + validation_status, dummy, validation_errors = \ + business_configuration._validateNextForm(**single_form_kw) + + ## clean up REQUEST from traces from validate_all_to_request + ## otherwise next form will use previous forms details + cleanup_keys = filter(lambda x: x.startswith('my_') or + x.startswith('your_'), + self.REQUEST.other.keys()) + for key in cleanup_keys: + self.REQUEST.other.pop(key, None) + ## render HTML code + if validation_status != 0: + failed_forms_counter += 1 + ## XXX: form can fail because a new + ## http://localhost:9080/erp5/portal_wizard/next is issued + ## without arguments. Improve this + try: + self.REQUEST.set('field_errors', + form.ErrorFields(validation_errors)) + except: + pass + single_form_html = form() + self.REQUEST.other.pop('field_errors', None) + self.REQUEST.form = {} + else: + single_form_html = form() + ## wrap in form template + single_form_html = self.Base_mainConfiguratorFormTemplate( + current_form_number = form_counter +1, + max_form_numbers = isMultiEntryTransition, + form_html = single_form_html) + ## add to list of forms as html code + html_forms.append(single_form_html) + ## return if failure + if failed_forms_counter > 0: + next_state = self.restrictedTraverse(business_configuration.getNextTransition()\ + .getDestination()) + html_data = self.Base_mainConfiguratorTemplate( + form_html = "\n".join(html_forms), + current_state = next_state, + business_configuration = business_configuration) + response.update(command = "show", + previous = self.Base_translateString("Previous"), + next = self.Base_translateString(transition.getTitle()), + data = html_data) + return response + + ## show next form in transitions + rendered = False + while rendered is False: + if need_validation == 1: + if isMultiEntryTransition: + ## multiple forms must be validated before + validation_status = 0 + else: + validation_status, form_kw, validation_errors = \ + business_configuration._validateNextForm(**kw) + if validation_status==1: + need_validation = 0 + elif validation_status==2: + rendered = True + need_validation = 0 + if business_configuration.getNextTransition() == None: + ### client can not continue at the momen + return self._terminateConfigurationProcess(response, + reason='no_available_transitions') + response["previous"], html, form_title, response["next"], \ + response['server_buffer'] = business_configuration._displayNextForm() + else: + ## validation passed + need_validation = 0 + business_configuration._executeTransition(form_kw=form_kw, request_kw=kw) + elif need_validation == 0: + if business_configuration.getNextTransition() == None: + return self._terminateConfigurationProcess(response, + 'no_available_transitions') + ## validation failure + rendered = True + response["previous"], html, form_title, response["next"], \ + response['server_buffer'] = business_configuration.\ + _displayNextForm(validation_errors=validation_errors) + + if html is None: + ## we have no more forms proceed to build + response.update(command = "install", data = None) + else: + ## we have more forms + next_state = self.restrictedTraverse(business_configuration.getNextTransition()\ + .getDestination()) + html_data = self.Base_mainConfiguratorTemplate( + form_html = html, + current_state = next_state, + business_configuration = business_configuration) + response.update(command = "show", data = html_data) + return response + + def _terminateConfigurationProcess(self, response, reason=''): + """ Terminate process and return some explanations to client why + he can no longer continue. """ + if reason == 'no_available_transitions': + form_html = self.BusinessConfiguration_viewStopForm() + response.update(command = "show", next = None, \ + previous = None, data = form_html) + elif reason == 'authentification_failure': + form_html = self.BusinessConfiguration_viewUnauthenticatedForm() + response.update(command = "show", data = form_html, + next = None, previous = None,) + + return response + + #security.declareProtected(Permissions.ModifyPortalContent, 'previous') + def previous(self, REQUEST): + """ Display the previous form. """ + # check if user is allowed to access service + portal = self.getPortalObject() + if not self._isCorrectConfigurationKey(): + REQUEST.set('portal_status_message', + self.Base_translateString('Incorrect Configuration Key')) + return self.view() + kw = self.REQUEST.form.copy() + business_configuration = REQUEST.get(BUSINESS_CONFIGURATION_COOKIE_NAME) + bc = portal.restrictedTraverse(business_configuration) + response = self._previous(business_configuration=bc, kw=kw) + return self.ConfiguratorTool_dialogForm(previous=response['previous'], + form_html=response['data'], + next=response['next']) + + def _previous(self, business_configuration, kw): + """ Returns previous form. """ + response = {} + ## is client is not allowed access ? + if business_configuration is None: + form_html = self.BusinessConfiguration_viewUnauthenticatedForm() + return self.ConfiguratorTool_dialogForm(form_html = form_html) + ## client can not go further his business configuration is already built + if business_configuration.isEndConfigurationState(): + form_html = self.BusinessConfiguration_viewStopForm() + return self.ConfiguratorTool_dialogForm(form_html = form_html, + next = "Next") + + response['previous'], form_html, form_title, response['next'], server_buffer = \ + business_configuration._displayPreviousForm() + + next_state = self.restrictedTraverse( + business_configuration.getNextTransition().getDestination()) + + response['data'] = self.Base_mainConfiguratorTemplate( + form_html = form_html, + current_state = next_state, + business_configuration = business_configuration) + return response + + security.declarePublic(Permissions.AccessContentsInformation, + 'getInstallationStatusReport') + def getInstallationStatusReport(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. + """ + global installation_status + portal_activities = getToolByName(self.getPortalObject(), + 'portal_activities') + is_bt5_installation_over = (portal_activities.countMessageWithTag( + 'initialERP5Setup')==0) + if 0 == len(portal_activities.getMessageList()) and \ + is_bt5_installation_over: + html = self.ConfiguratorTool_viewSuccessfulConfigurationMessageRenderer() + else: + if is_bt5_installation_over: + # only if bt5s are installed start tracking number of activities + activity_list = portal_activities.getMessageList() + installation_status['activity_list'].append(len(activity_list)) + html = self.ConfiguratorTool_viewRunningInstallationMessage( + installation_status = installation_status) + # set encoding as this is usually called from asynchronous JavaScript call + self.REQUEST.RESPONSE.setHeader('Content-Type', + 'text/html; charset=utf-8') + return html + + security.declareProtected(Permissions.ModifyPortalContent, 'startInstallation') + def startInstallation(self, business_configuration, 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. """ + global installation_status + # init installation status + bt5_file_list = len(business_configuration.contentValues( + portal_types=["File", "Link"])) or 1 + installation_status['bt5']['all'] = bt5_file_list + installation_status['bt5']['current'] = 0 + installation_status['activity_list'] = [] + active_process = self.portal_activities.newActiveProcess() + REQUEST.set('active_process_id', active_process.getId()) + request_restore_dict = {'__ac_key': REQUEST.get('__ac_key', + None),} + self.activate(active_process=active_process, tag = 'initialERP5Setup' + ).initialERP5Setup(business_configuration.getRelativeUrl(), request_restore_dict) + return self.ConfiguratorTool_viewInstallationStatus(REQUEST) + + security.declareProtected(Permissions.ModifyPortalContent, + 'initialERP5Setup') + def initialERP5Setup(self, business_configuration, request_restore_dict={}): + """ Get from remote generation server customized bt5 template files + and then install them. """ + # restore some REQUEST variables as this method is executed in an activity + # and there's no access to real original REQUEST + for key, value in request_restore_dict.items(): + self.REQUEST.set(key, value) + + bc = self.restrictedTraverse(business_configuration) + # XXX FIXME we just have to build once. + bc.build() + bc.install() + + finalize_method = getattr(self, 'ConfiguratorTool_finalizeInstallation', None) + if finalize_method is not None and callable(finalize_method): + finalize_method(business_configuration = bc, + **request_restore_dict) diff --git a/product/ERP5Configurator/Tool/__init__.py b/product/ERP5Configurator/Tool/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/product/ERP5Configurator/VERSION.txt b/product/ERP5Configurator/VERSION.txt new file mode 100644 index 0000000000..8542ced240 --- /dev/null +++ b/product/ERP5Configurator/VERSION.txt @@ -0,0 +1 @@ +ERP5Configurator 5.4.7 diff --git a/product/ERP5Configurator/__init__.py b/product/ERP5Configurator/__init__.py new file mode 100644 index 0000000000..373672c675 --- /dev/null +++ b/product/ERP5Configurator/__init__.py @@ -0,0 +1,60 @@ +############################################################################## +# +# Copyright (c) 2006 Nexedi SARL and Contributors. All Rights Reserved. +# Romain Courteaud <romain@nexedi.com> +# Ivan Tyagov <ivan@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. +# +############################################################################## +""" + ERP5Configurator is a product containing everything needed to the + configurator. +""" + +# Update ERP5 Globals +from Products.ERP5Type.Utils import initializeProduct, updateGlobals +import sys, Permissions +this_module = sys.modules[ __name__ ] +document_classes = updateGlobals(this_module, globals(), + permissions_module=Permissions) + +from Tool import ConfiguratorTool + +# Define object classes and tools +object_classes = () +portal_tools = (ConfiguratorTool.ConfiguratorTool, + ) + +content_classes = () +content_constructors = () + +# Finish installation +def initialize(context): + import Document + 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) diff --git a/product/ERP5Configurator/help/README b/product/ERP5Configurator/help/README new file mode 100644 index 0000000000..a763127a26 --- /dev/null +++ b/product/ERP5Configurator/help/README @@ -0,0 +1 @@ +ERP5Configurator help diff --git a/product/ERP5Configurator/interfaces/__init__.py b/product/ERP5Configurator/interfaces/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/product/ERP5Configurator/interfaces/configurator_item.py b/product/ERP5Configurator/interfaces/configurator_item.py new file mode 100644 index 0000000000..8a2e6692d3 --- /dev/null +++ b/product/ERP5Configurator/interfaces/configurator_item.py @@ -0,0 +1,46 @@ +############################################################################## +# +# Copyright (c) 2010 Nexedi SARL and Contributors. All Rights Reserved. +# Rafael Monnerat <rafael@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 zope.interface import Interface + +class IConfiguratorItem(Interface): + """ + Configurator Item interface specification. + + Documents which implement the IConfiguratorItem interface + can be used to build an ERP5 Configuration. + """ + + def build(business_configuration): + """ + Build new ERP5 Documents based on stored parameters during + the configuraton process. + + business_configuration - Business Configuration Document that is + been used to configure. + """ diff --git a/product/ERP5Configurator/mixin/__init__.py b/product/ERP5Configurator/mixin/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/product/ERP5Configurator/mixin/configurator_item.py b/product/ERP5Configurator/mixin/configurator_item.py new file mode 100644 index 0000000000..04facf1ef3 --- /dev/null +++ b/product/ERP5Configurator/mixin/configurator_item.py @@ -0,0 +1,50 @@ +############################################################################## +# +# Copyright (c) 2006 Nexedi SARL and Contributors. All Rights Reserved. +# Romain Courteaud <romain@nexedi.com> +# Ivan Tyagov <ivan@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 ConfiguratorItemMixin: + """ This is the base class for all configurator item. """ + + def install(self, object, business_configuration, prefix = ''): + """ Add object to customer customization template. """ + bt5_obj = business_configuration.getSpecialiseValue() + if object.getPortalType() in ['Category', 'Base Category']: + prefix = "portal_categories/" + template_path_list = ['%s%s' % (prefix, object.getRelativeUrl()), + '%s%s/**' % (prefix, object.getRelativeUrl())] + current_template_path_list = list(bt5_obj.getTemplatePathList()) + current_template_path_list.extend(template_path_list) + bt5_obj.edit(template_path_list=current_template_path_list) + + def addToCustomerBT5ByRelativeUrl(self, business_configuration, relative_url_list): + """ Add object to customer customization template object by its relative url. """ + bt5_obj = business_configuration.getSpecialiseValue() + current_template_path_list = list(bt5_obj.getTemplatePathList()) + current_template_path_list.extend(relative_url_list) + bt5_obj.edit(template_path_list=current_template_path_list) + diff --git a/product/ERP5Configurator/skins/__init__.py b/product/ERP5Configurator/skins/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/product/ERP5Configurator/tests/__init__.py b/product/ERP5Configurator/tests/__init__.py new file mode 100644 index 0000000000..e16c76dff8 --- /dev/null +++ b/product/ERP5Configurator/tests/__init__.py @@ -0,0 +1 @@ +"" diff --git a/product/ERP5Configurator/tool.png b/product/ERP5Configurator/tool.png new file mode 100644 index 0000000000000000000000000000000000000000..681679219796ba6657aba4a13518b632ac71e05f GIT binary patch literal 287 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPFP2=EDU{r~@eSlZ52yFYq_OpMDr zIA`r!-?$~0@BhlIJyp?l)6%(U*NLyreszX6DL|F2GwyBQ$vPdVm$4+sFPOpM*^M+H zN36s(q9iy!t)x7$D3u{SGtH<VFI~Y%&qU8?ahy9JOoelPZf<H`34?E9N~%J6W=V#E zyQgnJU8?L<pbD0BPq%a+%~ccmHW)~Idb&7<RLn_EaA5qy6wtPbd5U1z2iZdx-pBAJ z*4Sk;EQ{oKp1MM|;li7^fW}Fz0(F_<GoF?8>|rpht@LE@InT~<Hic)iloB(;m2G@x U`&o}v01ajEboFyt=akR{0Hft)-v9sr literal 0 HcmV?d00001 -- 2.30.9