############################################################################## # # Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved. # Jean-Paul Smets-Solanes <jp@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, Constraint, Interface from Products.ERP5Type.Base import Base from Products.ERP5.Document.Coordinate import Coordinate from zLOG import LOG import re class Telephone(Coordinate, Base): """ A telephone is a coordinate which stores a telephone number The telephone class may be used by multiple content types (ex. Fax, Mobile Phone, Fax, Remote Access, etc.). A telephone is a terminating leaf in the OFS. It can not contain anything. Telephone inherits from Base and from the mix-in Coordinate A list of I18N telephone codes can be found here:: http://kropla.com/dialcode.htm """ meta_type = 'ERP5 Telephone' portal_type = 'Telephone' add_permission = Permissions.AddPortalContent isPortalContent = 1 isRADContent = 1 # Declarative security security = ClassSecurityInfo() security.declareObjectProtected(Permissions.AccessContentsInformation) # Declarative properties property_sheets = ( PropertySheet.Base , PropertySheet.SimpleItem , PropertySheet.Telephone ) # This is a list of regex. # Each regex into the list handle a single (or should handle) input. # The list is a priority list, # be carefull to add a new regex. regex_list = [ # Country, Area, City, Number, Extension* "\+(?P<country>[\d ]+)(\(0\)|\ |\-)(?P<area>\d+)(\-|\ )(?P<city>\d+)(\-|\ )(?P<number>[\d\ \-]*)(?:\/)?(?P<ext>\d+|)", # Area, City, Number, Extension* "^(\(0\)|0)?(?P<area>\d+)(\-|\ |\/)(?P<city>\d+)(\-|\ )(?P<number>[\d\ \-]*)(?:\/)?(?P<ext>\d+|)", "^\+(\(0\)|0)+(?P<area>\d+)(\-|\ |\/)(?P<city>\d+)(\-|\ )(?P<number>[\d\ \-]*)(?:\/)?(?P<ext>\d+|)", # Country, Area, Number, Extension* # +11(0)1-11111111/111 or +11(0)1-11111111/ or +11(0)1-11111111 # +11(0)1-1111-1111/111 or +11(0)1-1111-1111/ or +11(0)1-1111-1111 # + 11 (0)1-11 11 01 01/111 or + 11 (0)1-11 11 01 01/ or + 11 (0)1-11 11 01 01 # +11 (0)11 1011 1100/111 or +11 (0)11 1011 1100/ or +11 (0)11 1011 1100 "\+(?P<country>[\d\ ]*)\(0\)(?P<area>\d+)(\-|\ )(?P<number>[\d\ \-]*)(?:\/)?(?P<ext>\d+|)", # Country, Area, Number, Extension* # +11-1-11111111/111 or +11-1-11111111/ or +11-1-11111111 "\+(?P<country>\d+)-(?P<area>\d+)-(?P<number>[\d\ \-]*)(?:\/)?(?P<ext>\d+|)", # Missing area # +11(0)-11111111/111" or +11(0)-11111111/ or +11(0)-11111111 "\+(?P<country>\d+)\(0\)\-(?P<number>[\d\ ]*)(?:\/)?(?P<ext>\d+|)", # Country, Area, Number, Extension* # +11(1)11111111/111 or +11(1)11111111/ or +11(1)11111111 "\+(?P<country>\d+)\((?P<area>\d+)\)(?P<number>[\d\ ]*)(?:\/)?(?P<ext>\d+|)", # Country, Area, Number, Extension* # +111-1-11111111/111 or +111-1-11111111/ or +111-1-11111111 "\+(?P<country>\d+)-(?P<area>\d+)-(?P<number>[\d\ \-]*)(?:\/)?(?P<ext>\d+|)", # Missing country # +(0)1-11111111/111" or +(0)1-11111111/ or +(0)1-11111111 "\+\(0\)(?P<area>\d+)\-(?P<number>[\d\ ]*)(?:\/)?(?P<ext>\d+|)", # Missing Country and Area # +(0)-11111111/111" or +(0)-11111111/ or +(0)-11111111 "\+\(0\)\-(?P<number>[\d\ ]*)(?:\/)?(?P<ext>\d+|)", # Area, Number Extension* # Area between parenthesis. # (11)11111111/111 or (11)11111111/ or (11)11111111 "\((?P<area>\d+)\)(?P<number>[\d\ \-]*)(?:\/)?(?P<ext>\d+|)", # Missing country # +(1)11111111/111" or +(1)11111111/ or +(1)11111111 "\+\((?P<area>\d+)\)(?P<number>[\d\ \-]*)(?:\/)?(?P<ext>\d+|)", # Country, Area, Number and Extension* # Country space area space number slash extension or not # +11 1 011111111/1 or +11 1 011111111/ or +11 1 011111111 # + 111 1 1101 101/111 or + 111 1 1101 101/ or + 111 1 1101 101/111 "\+(?P<space>[\ ]*)(?P<country>\d+)\ (?P<area>\d+)\ (?P<number>[\d\ ]*)(?:\/)?(?P<ext>\d+|)", # This regex is to handle two inputs very similar # but with different behavior # 111 11 11/111 or 111 11 11 or 111 11 11 # will result in {'area':'', 'number':'111 11 11', 'ext':'111 or empty'} # # 111-11 11/111 or 111-11 11 or 111-11 11 # will result in {'area':'111', 'number':'11 11', 'ext':'111 or empty'} "^(?:0)?((?P<area>\d+)-)?(?P<number>[\d\-\ ]*)(?:\/)?(?P<ext>\d+|)$", # Area, Number, Extension* # It is a common input in France # and in Japan but with different behavior. # 011-111-1111/111 or 011-111-1111/ or 011-111-1111 # will result in {'area':'11', 'number':'111-1111', \ # 'ext':'111 or empty'} <= France # will result in {'area':'011', 'number':'111-1111', # 'ext':'111 or empty'} <= Japan # so we have here two regex: # To France: "^0(?P<area>\d+)-(?P<number>[\d\-\ ]*)(?:\/)?(?P<ext>\d+|)$", # To Japan: "^(?P<area>\d+)-(?P<number>[\d\-\ ]*)(?:\/)?(?P<ext>\d+|)$", "^0(?P<area>\d+)-(?P<number>[\d\-\ ]*)(?:\/)?(?P<ext>\d+|)$", # Area, Number, Extension* # It is a common input in France and in Japan but with different behavior. # 011(111)1111/111 or 011(111)1111/ or 011(111)1111 # will result in {'area':'11', 'number':'111)1111', # 'ext':'111 or empty'} <= France # will result in {'area':'011', 'number':'111)1111', # 'ext':'111 or empty'} <= Japan # so we have here two regex: #To France: # "^0(?P<area>\d+)\((?P<number>[\d\)\(\ \-]*)(?:\/)?(?P<ext>\d+|)$", #To Japan: # "^(?P<area>\d+)\((?P<number>[\d\)\(\ \-]*)(?:\/)?(?P<ext>\d+|)$", "^0(?P<area>\d+)\((?P<number>[\d\)\(\ \-]*)(?:\/)?(?P<ext>\d+|)$", # Missing area # +11()11111111/111" or +11()11111111/ or +11()11111111 "\+(?P<country>\d+)\(\)(?P<number>[\d\ \-]*)(?:\/)?(?P<ext>\d+|)", # Country, area, number, extension* # Space between country and (0). # Space between (0) and area. # Space between area and number. # +111 (0) 1 111 11011/111 or +111 (0) 1 111 11011/ or +111 (0) 1 111 11011 "\+(?P<country>\d+)\ \(0\)\ (?P<area>\d+)\ (?P<number>[\d\ \-]*)(?:\/)?(?P<ext>\d+|)", # Country and number # (0) between country and number # +111 (0) 111111101-01/111 or +111 (0) 111111101-01/ or +111 (0) 111111101-01 "\+(?P<country>\d+)\ \(0\)\ (?P<number>[\d\ \-]*)(?:\/)?(?P<ext>\d+|)", # Country, area, number and extension* # +11 (11) 1111 1111/111 or +11 (11) 1111 1111/ or +11 (11) 1111 1111 # +11 (11)-10111111/111 or +11 (11)-10111111/ or +11 (11)-10111111 # +11(11)-10111111/111 or +11(11)-10111111/ or +11(11)-10111111 # 1 (111) 1101-101/111 or 1 (111) 1101-101/ or 1 (111) 1101-101/ "(\+|)(?P<country>\d+)\ \((?P<area>\d+)\)(\ |\-|)(?P<number>[\d\ \-]*)(?:\/)?(?P<ext>\d+|)", # +110 1111111/111 or +110 1111111/ or +110 1111111 "\+(?P<country>\d+)\ (?P<number>[\d\ \-]*)(?:\/)?(?P<ext>\d+|)", # Missing country # 111/111-1111/111 or 111/111-1111/ or 111/111-1111 "(?P<area>\d+)\/(?P<number>[\d\ \-]*)(?:\/)?(?P<ext>\d+|)", # Country, area, number, extension* # Hyphen between country and area. # +11-1 11 11 01 11/111 or +11-1 11 11 01 11/ or +11-1 11 11 01 11 "\+(?P<country>\d+)\-(?P<area>\d+)\ (?P<number>[\d\ \-]*)(?:\/)?(?P<ext>\d+|)", # Missing area # +111-1101110/111 or +111-1101110/ or +111-1101110 "\+(?P<country>\d+)\-(?P<number>[\d\ \-]*)(?:\/)?(?P<ext>\d+|)", # Missing country # Dot between area number and telephone number. # 111.111.1111/111 or 111.111.1111/ or 111.111.1111 "(?P<area>\d+)\.(?P<number>[\d\ \-\.]*)(?:\/)?(?P<ext>\d+|)", # Country, area, number and extensioin* # (111 11) 111111/111 or (111 11) 111111/ or (111 11) 111111 # (111 11) 111-11-11/111 or (111 11) 111-11-11/ or (111 11) 111-11-11 # (111 11)101011/111 or (111 11)101011/ or (111 11)101011 # +(111 11) 100-11-11/111 or +(111 11) 100-11-11 or +(111 11) 100-11-11 "(\+|)\((?P<country>\d+)\ (?P<area>\d+)\)(\ |)(?P<number>[\d\ \-]*)(?:\/)?(?P<ext>\d+|)", # Country and number # (+111)101111111/111 or (+111)101111111/ or (+111)101111111 # (+111) 101111111/111 or (+111) 101111111/ or (+111) 101111111 "\(\+(?P<country>\d+)\)(\ |)(?P<number>[\d\ \-]*)(?:\/)?(?P<ext>\d+|)", # Country, area, number and extension* # (+11-111) 1111111/111 or (+11-111) 1111111/ or (+11-111) 1111111 # (11-11) 111-1111/111 or (11-11) 111-1111/ or (11-11) 111-1111 # (11-1) 1.111.111/111 or (11-1) 1.111.111/ or (11-1) 1.111.111 "\((\+|)(?P<country>\d+)\-(?P<area>\d+)\)(\ |\-|)(?P<number>[\d\ \-\.]*)(?:\/)?(?P<ext>\d+|)", # Country, area, number and extension* # + 111-11-1110111/111 or + 111-11-1110111/ or + 111-11-1110111 # +111-11-1110111/111 or +111-11-1110111/ or +111-11-1110111 # +111/1/1111 1100/111 or +111/1/1111 1100/ or +111/1/1111 1100 "\+(?P<spaces>[\ ]*)(?P<country>\d+)(\-|\/)(?P<area>\d+)(\-|\/)(?P<number>[\d\ \-\.]*)(?:\/)?(?P<ext>\d+|)", # + (111) 111-111/111 or + (111) 111-111/ or + (111) 111-111 "\+(?P<spaces>[\ ]*)\((?P<country>\d+)\)\ (?P<number>[\d\ \-\.]*)(?:\/)?(?P<ext>\d+|)" ] security.declareProtected(Permissions.ModifyPortalContent, 'fromText') def fromText(self, coordinate_text): """ See ICoordinate.fromText """ method = self._getTypeBasedMethod('fromText') if method is not None: return method(text=coordinate_text) if coordinate_text is None: coordinate_text = '' # Removing the spaces of the begin and end. coordinate_text = str(coordinate_text).strip() # This regexp get the coordinate text # and extract number and letters input_regex_without_markup = '[0-9A-Za-z]' input_without_markup = ''.join(re.findall(input_regex_without_markup,\ coordinate_text)) # Test if coordinate_text has or not markups. if len(coordinate_text) > len(input_without_markup): number_match = None for regex in self._getRegexList(): possible_number_match = re.match(regex, coordinate_text) if possible_number_match not in [None]: number_match = possible_number_match number_dict = number_match.groupdict() break if number_match == None: from zLOG import LOG, WARNING msg = "Doesn't exist a regex to handle this telephone: ", \ coordinate_text LOG('Telephone.fromText', WARNING, msg) number_dict = {'number' : input_without_markup} else: number_dict = {'number' : coordinate_text} country = number_dict.get('country','') area = number_dict.get('area','') city = number_dict.get('city','') number = number_dict.get('number','') extension = number_dict.get('ext','') if ((country in ['', None]) and \ (area in ['', None]) and \ (city in ['', None]) and \ (number in ['', None]) and \ (extension in ['', None])): country = area = city = number = extension = '' else: # Trying to get the country and area from dict, # but if it fails must be get from preference preference_tool = self.portal_preferences if country in ['', None]: country = preference_tool.getPreferredTelephoneDefaultCountryNumber('') if area in ['', None]: area = preference_tool.getPreferredTelephoneDefaultAreaNumber('') if city in ['', None]: city = preference_tool.getPreferredTelephoneDefaultCityNumber('') country = country.strip() area = area.strip() city = city.strip() number = number.strip() extension = extension.strip() # Formating the number. # Removing any ")", "(", "-", "." and " " for token in [")", "(", "-" ,"." ," "]: country = country.replace(token, '') number = number.replace(token, '') self.edit(telephone_country = country, telephone_area = area, telephone_city = city, telephone_number = number, telephone_extension = extension) security.declareProtected(Permissions.ModifyPortalContent, '_setText') _setText = fromText security.declareProtected(Permissions.View, 'asText') def asText(self): """ Returns the telephone number in standard format """ script = self._getTypeBasedMethod('asText') if script is not None: return script() country = self.getTelephoneCountry('') area = self.getTelephoneArea('') city = self.getTelephoneCity('') number = self.getTelephoneNumber('') extension = self.getTelephoneExtension('') # If country, area, number, extension are blank # the method should to return blank. if ((country == '') and \ (area == '') and \ (city == '') and \ (number == '') and \ (extension == '')): return '' # Define the notation notation = self._getNotation() if notation not in [None, '']: notation = notation.replace('<country>',country) notation = notation.replace('<area>',area) if city == "": notation = notation.replace('<city>-', '') else: notation = notation.replace('<city>',city) notation = notation.replace('<number>',number) notation = notation.replace('<ext>',extension) if extension == '': notation = notation.replace('/','') return notation security.declareProtected(Permissions.AccessContentsInformation, 'asURL') def asURL(self): """Returns a text representation of the Url if defined or None else. """ telephone_country = self.getTelephoneCountry() if telephone_country is not None: url_string = '+%s' % telephone_country else : url_string = '0' telephone_area = self.getTelephoneArea() if telephone_area is not None: url_string += telephone_area telephone_city = self.getTelephoneCity() if telephone_city is not None: url_string += telephone_city telephone_number = self.getTelephoneNumber() if telephone_number is not None: url_string += telephone_number if url_string == '0': return None return 'tel:%s' % (url_string.replace(' ','')) security.declareProtected(Permissions.View, 'getText') getText = asText security.declareProtected(Permissions.View, 'standardTextFormat') def standardTextFormat(self): """ Returns the standard text formats for telephone numbers """ return ("+33(0)6-62 05 76 14",) def _getNotation(self): """ Returns the notation that will be used by asText method. """ # The notation can be changed. # But needs to have <country>, <area>, <city>, <number> and <ext> return "+<country>(0)<area>-<city>-<number>/<ext>" def _getRegexList(self): """ Returns the regex list that will be used by fromText method. """ return self.regex_list