# -*- coding: utf-8 -*- # # File: TeleServicesTool.py # # Copyright (c) 2007 by CommunesPlone # Generator: ArchGenXML Version 1.5.3 dev/svn # http://plone.org/products/archgenxml # # GNU General Public License (GPL) # # 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., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301, USA. # __author__ = """Gauthier BASTIEN , Stephan GEULETTE """ __docformat__ = 'plaintext' from AccessControl import ClassSecurityInfo from Products.Archetypes.atapi import * from Products.TeleServices.config import * from Products.CMFCore.utils import UniqueObject ##code-section module-header #fill in your manual code here from Products.CMFCore.utils import getToolByName import logging from Products.CMFPlone.i18nl10n import utranslate from Products.Archetypes import listTypes from Products.TeleServices import ManageTeleServices from ZODB.PersistentMapping import PersistentMapping logger = logging.getLogger('TeleServices') from Products.validation.interfaces.IValidator import IValidator from Products.validation import validation from math import fmod class ValidAccountValidator: """ Validates the structuredComBegin field : integer and max length of x chars """ __implements__ = (IValidator, ) def __init__(self, name): self.name = name def __call__(self, value, *args, **kwargs): account = '' for c in value: if c.isdigit(): account += c if account: if len(account) != 12: return "12 numbers are needed." first_part = int(account[:10]) checkdigit = int(account[10:]) verif = fmod(first_part, 97) if verif: if checkdigit != verif: return "Number not valid." elif checkdigit != 97: return "Number not valid." validation.register(ValidAccountValidator('isValidAccount')) ##/code-section module-header schema = Schema(( StringField( name='populationTeleServiceEmails', widget=StringWidget( description="Multiple addresses must be separated by a ;", label='Populationteleserviceemails', label_msgid='TeleServices_label_populationTeleServiceEmails', description_msgid='TeleServices_help_populationTeleServiceEmails', i18n_domain='TeleServices', ) ), StringField( name='workTeleServiceEmails', widget=StringWidget( description="Multiple addresses must be separated by a ;", label='Workteleserviceemails', label_msgid='TeleServices_label_workTeleServiceEmails', description_msgid='TeleServices_help_workTeleServiceEmails', i18n_domain='TeleServices', ) ), StringField( name='emailFromAddress', widget=StringWidget( description="This address is displayed in field 'From:' when notification mails are sent (example : noreply@notification.be)", label='Emailfromaddress', label_msgid='TeleServices_label_emailFromAddress', description_msgid='TeleServices_help_emailFromAddress', i18n_domain='TeleServices', ) ), LinesField( name='enabledTypes', widget=InAndOutWidget( description="Enabled items will be in the right part", label='Enabledtypes', label_msgid='TeleServices_label_enabledTypes', description_msgid='TeleServices_help_enabledTypes', i18n_domain='TeleServices', ), enforceVocabulary=True, mode="rw", vocabulary='listDemandContentTypes', required=False ), StringField( name='accountNumber', widget=StringWidget( description="Enter the account number in the following format xxx-xxxxxxx-xx", label='Accountnumber', label_msgid='TeleServices_label_accountNumber', description_msgid='TeleServices_help_accountNumber', i18n_domain='TeleServices', ), validators=('isValidAccount',) ), LinesField( name='enabledPaymentModes', widget=InAndOutWidget( description="Enabled items will be in the right part", label='Enabledpaymentmodes', label_msgid='TeleServices_label_enabledPaymentModes', description_msgid='TeleServices_help_enabledPaymentModes', i18n_domain='TeleServices', ), enforceVocabulary=True, mode="rw", vocabulary='listAvailablePaymentModes' ), ), ) ##code-section after-local-schema #fill in your manual code here ##/code-section after-local-schema TeleServicesTool_schema = OrderedBaseFolderSchema.copy() + \ schema.copy() ##code-section after-schema #fill in your manual code here ##/code-section after-schema class TeleServicesTool(UniqueObject, OrderedBaseFolder): """ """ security = ClassSecurityInfo() __implements__ = (getattr(UniqueObject,'__implements__',()),) + (getattr(OrderedBaseFolder,'__implements__',()),) # This name appears in the 'add' box archetype_name = 'TeleServices Configuration' meta_type = 'TeleServicesTool' portal_type = 'TeleServicesTool' allowed_content_types = ['Topic', 'Folder'] filter_content_types = 1 global_allow = 0 #content_icon = 'TeleServicesTool.gif' immediate_view = 'base_view' default_view = 'base_view' suppl_views = () typeDescription = "TeleServices Configuration" typeDescMsgId = 'description_edit_teleservicestool' #toolicon = 'TeleServicesTool.gif' _at_rename_after_creation = True schema = TeleServicesTool_schema ##code-section class-header #fill in your manual code here schema = schema.copy() schema['title'].widget.visible = 0 left_slots = ['here/portlet_prefs/macros/portlet'] right_slots = [] ##/code-section class-header # tool-constructors have no id argument, the id is fixed def __init__(self, id=None): OrderedBaseFolder.__init__(self,'portal_teleservices') self.setTitle('TeleServices Configuration') ##code-section constructor-footer #fill in your manual code here ##/code-section constructor-footer # tool should not appear in portal_catalog def at_post_edit_script(self): self.unindexObject() ##code-section post-edit-method-footer #fill in your manual code here ##/code-section post-edit-method-footer # Methods security.declarePublic('isTeleServicesManager') def isTeleServicesManager(self, context): """" If the connected user has the "TeleServicesManager" role somewhere in the folder where are in, he has someting to manage so, we show him the administration interface The context is the folder where he is """ member = getToolByName(self, 'portal_membership').getAuthenticatedMember() for role in MANAGER_ROLES: if member.has_role(role, context): return True return False security.declarePublic('getTermObject') def getTermObject(self, className, termName, termId): """ get a term object from a className and a term type """ configFolderId = className.lower() + '_config' termFolderId = termName.lower() + 's' if hasattr(self, configFolderId): configFolder = getattr(self, configFolderId) if hasattr(configFolder, termFolderId): demandFolder = getattr(configFolder, termFolderId) if hasattr(demandFolder, termId): return getattr(demandFolder, termId) return None security.declarePublic('getDemandTypeFromRequest') def getDemandTypeFromRequest(self): """ This will return the demand_type parameter that could be passed as parameter to createObject """ portal = getToolByName(self, 'portal_url').getPortalObject() try: if portal.REQUEST.form.has_key('demand_type'): portal.REQUEST.SESSION.set('demand_type', portal.REQUEST.form.get('demand_type')) return portal.REQUEST.form.get('demand_type') else: if portal.REQUEST.form.has_key('type_name'): if portal.REQUEST.SESSION.has_key('demand_type'): del portal.REQUEST.SESSION['demand_type'] else: if portal.REQUEST.SESSION.has_key('demand_type'): return portal.REQUEST.SESSION.get('demand_type') return DEFAULT_VOCAB_VALUE except: return DEFAULT_VOCAB_VALUE security.declarePublic('getDemandFolder') def getDemandFolder(self, theobject=False): """ This will check the existence of the demands folder (create it if not) and return the path """ portal = getToolByName(self, 'portal_url').getPortalObject() home_folder = portal.portal_membership.getHomeFolder() if home_folder is None: #necessary for the admin zope user return '' if not hasattr(home_folder, ROOT_FOLDER_NAME): #we call here a python script containing only a call to createTeleservicesFolder() #we do so to permit to another product to overwrite the script to do more in another context (like in the Container product) portal.create_myteleservices_folder() if theobject: return getattr(home_folder, ROOT_FOLDER_NAME) return home_folder.absolute_url() + '/' + ROOT_FOLDER_NAME security.declarePublic('createTeleservicesFolder') def createTeleservicesFolder(self): """ creates the demand folder """ portal = getToolByName(self, 'portal_url').getPortalObject() home_folder = portal.portal_membership.getHomeFolder() id = home_folder.invokeFactory(type_name="Folder", id=ROOT_FOLDER_NAME) obj = getattr(home_folder, id) obj.setTitle(utranslate(msgid=ROOT_FOLDER_NAME, domain="TeleServices", context=self)) klasses = listTypes(PROJECTNAME) alloweds = [] for klasse in klasses: if klasse['name'] not in EXCLUDED_TYPES: alloweds.append(klasse['name']) obj.setConstrainTypesMode(1) obj.setLocallyAllowedTypes(alloweds) #we HAVE TO setImmediatelyAddableTypes here because of a bug #--> if immediatelyAddableTypes is not set, we have a validation problem if we try to add a folder here #--> we do not add Folder here but well... ;-) obj.setImmediatelyAddableTypes(alloweds) obj.setLayout('myteleservices_view') obj.manage_addProperty('left_slots', '', 'lines') obj.manage_permission('TeleServices: Add WorkTeleService',('Manager', 'Owner' ),acquire=0) obj.manage_permission('TeleServices: Add PopulationTeleService',('Manager', 'Owner' ),acquire=0) obj.manage_permission('TeleServices: Add BelDonorTeleService',('Manager', 'Owner' ),acquire=0) obj.reindexObject() return obj.absolute_url() security.declarePublic('listTermObjects') def listTermObjects(self, className, termName, onStates = []): """ return a list of term objects corresponding to termname and classname """ wft = self.portal_workflow lst = [] configFolderId = className.lower() + '_config' termFolderId = termName.lower() + 's' if hasattr(self, configFolderId): configFolder = getattr(self, configFolderId) if hasattr(configFolder, termFolderId): demandFolder = getattr(configFolder, termFolderId) for term in demandFolder.objectValues([termName]): #if onStates is empty, we return all terms. #if not, we returns terms when state is in the list if onStates and wft.getInfoFor(term, 'review_state') not in onStates: continue lst.append(term) return lst security.declarePublic('listTermsVocab') def listTermsVocab(self, className, termName, onStates = []): """ return a DisplayList of term ids corresponding to termname and classname """ lst = [] for term in self.listTermObjects(className, termName, onStates): lst.append((term.getId(), term.Title())) if lst: return DisplayList(tuple(lst)) else: return DisplayList(((DEFAULT_VOCAB_VALUE,''),)) security.declarePublic('listRefVocab') def listRefVocab(self, obj, refFieldName, onStates = []): """ return a vocabulary for an object having a reference field attribute (refFieldName). The vocab contains id/title of the referenced objects We filter on workflow states if state values in onStates list """ wft = self.portal_workflow lst = obj.getField(refFieldName).getAccessor(obj)() if lst: vocab = [] #adding default value to force the user choice (need an adapted validator) #empty value means here "choose a value" if ADD_EMPTY_VOCAB_VALUE: val = utranslate(msgid=EMPTY_VOCAB_VALUE, domain='TeleServices', context=self) vocab.append(('', val)) for referred_obj in lst: if onStates and wft.getInfoFor(referred_obj, 'review_state') not in onStates: continue vocab.append((referred_obj.getId(),referred_obj.Title())) return DisplayList(tuple(vocab)) else: return DisplayList(((DEFAULT_VOCAB_VALUE,''),)) security.declarePublic('listDemandContentTypes') def listDemandContentTypes(self): """ return the list of types of our product that will be addable by a common user """ #we list every types of our product list_klasses = listTypes(PROJECTNAME) ind_dict = 0 klasses = [] #we remove the EXCLUDED_TYPES for klasse in list_klasses: if klasse['name'] not in EXCLUDED_TYPES: klasses.append(klasse) #here we have only commonly addable types, we return a DisplayList based on it vocab = [] for klass in klasses: vocab.append((klass['name'], klass['name'])) return DisplayList(tuple(vocab)) security.declarePublic('checkTSMemberRole') def checkTSMemberRole(self, className, member): """ test if the member is manager for the TS type """ if member.has_role('Manager') or (CLASS_ROLES.has_key(className) and member.has_role(CLASS_ROLES[className])): return True return False security.declarePublic('generateStructuredCommunication') def generateStructuredCommunication(self, className, termName, termId): """ generate the structuredcommunication field of PopulationTeleService """ term = self.getTermObject(className, termName, termId) scb = term.getStructuredComBegin() # logger.info("scb : %s"%scb) if scb in ('', None): return None # changed by sge : no reason to not generate a structured communication # scb = '0' database conflict error counters = self.getCounters() #the key is added when the structuredComBegin is set in demandTypeTerm. It must exist !! if not counters.has_key(scb): raise KeyError, utranslate(msgid='key_not_found_in_scc', mapping={'key':scb}, domain='TeleServices', context=self, default="key '%s' not found in structured_com_counters of portal_teleservices. Contact the portal administrator"%scb).encode('iso-8859-1') #we generate the structured communication with : # the structuredComBegin set in the demandTypeTerm at the beginning # 0 repeated a number of times to have the length of the first part equal to 10 # a counter at the end of the first part # the checkdigit try: import string from math import fmod # logger.info(counters) counter = counters[scb] + 1 # logger.info("counter : %s"%counter) if len(str(counter)) > (10-SCB_LENGTH): counter_str = str(counter) logger.warn("length of stored counter (%s) for key %s > 3 chars" % (counter_str,scb)) counter = int(counter_str[-3:]) # logger.info("after check") # logger.info("scb : %s"%scb) # logger.info("type scb : %s"%type(scb)) first_part = scb + string.zfill(str(counter), 10-len(str(scb))) # logger.info("first_part : %s"%first_part) checkdigit = fmod(int(first_part), 97) if not checkdigit: checkdigit = 97 result = first_part + string.zfill(str(int(checkdigit)), 2) counters[scb] +=1 return result[0:3] + '/' + result[3:7] + '/' + result[7:12] except TypeError, errmsg: logger.error("TypeError : %s"%errmsg) return None except Exception, errmsg: logger.error("error when calculating structured communication: %s"%errmsg) return None security.declarePublic('getRawEnabledTypes') def getRawEnabledTypes(self): return self.getEnabledTypes() security.declarePublic('getEnabledTypes') def getEnabledTypes(self): """ return a list of globally allowed project class names """ list_klasses = listTypes(PROJECTNAME) klasses = [] #we remove the EXCLUDED_TYPES for klasse in list_klasses: if klasse['name'] not in EXCLUDED_TYPES: klasses.append(klasse) #inner method : get global_allow for 'type_name' used in portal_types... def getGlobalAllow(type_name): ptt = self.portal_types #we test, but it should really have this attribute ! if hasattr(ptt, type_name): return getattr(ptt, type_name).global_allow else: logger.error("TeleServicesTool.py: a selected portal_type was not found in portal_types.listActionInfo() !!!") klasses_to_keep = [] if klasses: for klass in klasses: if getGlobalAllow(klass['name']): klasses_to_keep.append(klass['name']) #we have to setEnabledTypes because what we see in the form could be different that what is set in the tool really... #self.setEnabledTypes(list(klass['name'] for klass in klasses)) return klasses_to_keep security.declarePublic('setEnabledTypes') def setEnabledTypes(self, enabled_types): """ set globally allowed project class names """ #we will disable the 'global_allow' attribute for types wich are not in EXCLUDED_TYPES and are not returned by getEnabledTypes #we list every types of our product list_klasses = listTypes(PROJECTNAME) ind_dict = 0 klasses = [] #we remove the EXCLUDED_TYPES for klasse in list_klasses: if klasse['name'] not in EXCLUDED_TYPES: klasses.append(klasse) #inner method : set global_allow to 'value' for 'type_name' used under... def setGlobalAllow(type_name, value): ptt = self.portal_types #we test, but it should really have this attribute ! if hasattr(ptt, type_name): _type = getattr(ptt, type_name) _type.manage_changeProperties(global_allow=value) else: logger.error("TeleServicesTool.py: a selected portal_type was not found in portal_types.listActionInfo() !!!") if klasses: for klass in klasses: if klass['name'] not in enabled_types: #the content_type has not been selected, we set his global_allow to False setGlobalAllow(klass['name'], False) else: #the content_type has benn selected, we set his global_allow to True setGlobalAllow(klass['name'], True) #we set the field of the tool... ??? field = self.getField('enabledTypes') field.set(self, enabled_types) security.declarePublic('getCounters') def getCounters(self): """ create (if not exist) and return a new persistent property """ if not hasattr(self, 'structured_com_counters'): self.structured_com_counters = PersistentMapping() return self.structured_com_counters security.declarePublic('listAvailablePaymentModes') def listAvailablePaymentModes(self): """ return all availables payment modes defined in config.py PAYMENT_MODES """ ret = [] for pm in PAYMENT_MODES: ret.append((pm, utranslate(msgid=pm, domain="TeleServices", context=self, default="Bank transfer"))) return DisplayList(tuple(ret)) security.declarePublic('getDefaultNoValue') def getDefaultNoValue(self): """ return the default no value """ return DEFAULT_NO_VALUE security.declarePublic('getEnabledPaymentModesAsDisplayList') def getEnabledPaymentModesAsDisplayList(self): """ return every enabled payment modes as a display list so we can retrieve the key and the translation of the key """ lst = [] #currently enabled modes enabled_modes = self.getEnabledPaymentModes() #all available payment modes available_modes = self.listAvailablePaymentModes() #we loop on enabled payment modes (keys) and we retrieve the value for mode in enabled_modes: #if the available mode is enabled, we keep it #we do not add a try here lst.append((mode, available_modes.getValue(mode))) if lst: return DisplayList(tuple(lst)) else: return DisplayList(((DEFAULT_VOCAB_VALUE,''),)) security.declarePublic('getEnabledTypesObjects') def getEnabledTypesObjects(self): """ return a list of globally allowed project class objects """ list_klasses = self.getEnabledTypes() klasses = [] ptt = self.portal_types for type_name in list_klasses: #we test, but it should really have this attribute ! if hasattr(ptt, type_name): klasses.append(getattr(ptt, type_name)) else: logger.error("TeleServicesTool.py: a selected portal_type was not found in portal_types.listActionInfo() !!!") return klasses security.declarePublic('checkIfEidRequired') def checkIfEidRequired(self, demandType, onStates): """ check if there is demands with eid required for a ts type """ for obj in self.listTermObjects(demandType, 'DemandTypeTerm', onStates): if obj.getEidRequired(): return True return False security.declarePublic('checkIfDestinationTerms') def checkIfDestinationTerms(self, demandType, onStates): """ check if there is demands with destinationterms for a ts type. we return : - False if no demand has destinationTerms - True if there are demands with destinationTerms """ #print 'in destinations' ret = False for obj in self.listTermObjects(demandType, 'DemandTypeTerm', onStates): if obj.getDestinationTerms(): ret = True return ret registerType(TeleServicesTool, PROJECTNAME) # end of class TeleServicesTool ##code-section module-footer #fill in your manual code here ##/code-section module-footer