? phone-1.patch Index: README.int.txt =================================================================== RCS file: README.int.txt diff -N README.int.txt --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ README.int.txt 21 Jan 2008 22:56:12 -0000 @@ -0,0 +1,56 @@ +About the International Phone Number format +=========================================== + +These rules have been formulated off of the E.123 (for display) and +E.164 (for input) specifications published by the ITU. In order to +prevent ambiguity, we have chosen to restrict some of the stipulations +these specifications give. + +Reference materials can be found here: +- http://www.itu.int/rec/T-REC-E.123/en +- http://www.itu.int/rec/T-REC-E.164/en + +Modifications to E.123 +---------------------- + +7.1: The international prefix symbol "+" MUST prefix international +phone numbers. All numbers missing this symbol will be assumed to be in +the default country code. + +When reformatting numbers to a standard format, the following conventions +will be taken: + +7.2: Parentheses will be normalized to spaces. + +We do not support the multiple phone numbers as described by (7.4); users +can always specify that multiple values are allowed if this is desired. +The functionality specified by 7.5, 7.6 and 8 IS NOT implemented. + +9.2 specifies that spacing SHALL OCCUR between the country code, the trunk +code and the subscriber number. As trunk codes are omitted by convention, +this means the only guaranteed separation will be between the country code +and subscriber number. Our implementation MAY treat hyphens, spaces and +parentheses as advisory indicators as to where spaces should be placed. +However, +1 7329060489 will stay as it was specified, while +1 (732) 906-0489 +will be normalized to +1 732 906 0489. As a future feature, rules may +be implemented for country codes specifying these conventions, however, +I have deemed such functionality out of scope for now. + +The Drupal task specifies that we should validate country codes, however, +due to the highly volatile nature of these codes, the author does not +believe that it is a good idea to maintain a list of valid country codes. +Thus, we only validate that the country code is three or less digits. + +Modifications to E.164 +---------------------- + +Our processing for NDD's will be similarly constrained. As per +7.3.2, we will treat 0 as a valid trunk code for all countries. +Other digits may be specified if the fall in the form of (X), where X is +a single digit that is 7 or 8. + +Postscript +---------- + +Modifications to our implementation will occur as necessary by user bug +reports. \ No newline at end of file Index: README.txt =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/phone/README.txt,v retrieving revision 1.1.1.3.2.3 diff -u -r1.1.1.3.2.3 README.txt --- README.txt 23 Dec 2007 15:31:07 -0000 1.1.1.3.2.3 +++ README.txt 21 Jan 2008 22:14:35 -0000 @@ -9,7 +9,7 @@ Features: --------- -* Validation of phone numbers : support of French, British, Italian, Russian, US and Canadian phone numbers +* Validation of phone numbers : support of French, British, Italian, Russian, US, Canadian and generic international phone numbers * Formating of phone numbers * Option for internationalization phone numbers * IPhone support Index: phone.int.inc =================================================================== RCS file: phone.int.inc diff -N phone.int.inc --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ phone.int.inc 21 Jan 2008 22:51:50 -0000 @@ -0,0 +1,73 @@ + 15) return false; + // Extract country code and see if it's correct + preg_match('/^\+(\d+)/', $number, $matches); + $cc = $matches[1]; + if (strlen($cc) > 3) return false; + // TODO: Check if parentheses/brackets add up + return true; +} + +/** + * Formats $number into the standard representation of international + * numbers as per E.123. + * + * @param $number + * International phone number to format + * @return + * Formatted international phone number + */ +function format_int_phone_number($number, $field = array()) { + $number = trim($number); + if ($number === '') return ''; + $number = _normalize_country_code($number, $field); + $bits = preg_split('/[.()\[\]\- ]/', $number, -1, PREG_SPLIT_NO_EMPTY); + // $bits[0] is the country code WITH a plus sign + if (isset($bits[1])) { + // This is the first non-CC segment, so it could have the NDN + switch ($bits[1][0]) { + case 0: + $bits[1] = substr($bits[1], 1); + break; + } + switch ($bits[1]) { + case 0: + case 7: + case 8: + array_splice($bits, 1, 1); + break; + } + } + return implode(' ', $bits); +} + +/** + * Adds a country code to a phone number if necessary. + * + * @param $number + * International or local phone number to format + * @return + * International phone number with country code + */ +function _normalize_country_code($number, $field = array()) { + if ($number[0] !== '+') { + $cc = isset($field['phone_default_country_code']) ? $field['phone_default_country_code'] : '1'; + return "+$cc $number"; + } + return $number; +} Index: phone.module =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/phone/phone.module,v retrieving revision 1.1.1.5.2.8 diff -u -r1.1.1.5.2.8 phone.module --- phone.module 23 Dec 2007 15:31:07 -0000 1.1.1.5.2.8 +++ phone.module 21 Jan 2008 22:50:40 -0000 @@ -20,6 +20,7 @@ 'ca_phone' => array('label' => t('US & Canadian Phone Numbers')), 'uk_phone' => array('label' => t('British (UK) Phone Numbers')), 'ru_phone' => array('label' => t('Russian Phone Numbers')), + 'int_phone'=> array('label' => t('International Phone Numbers')), ); } @@ -34,18 +35,26 @@ '#type' => 'checkbox', '#title' => t('Add the country code if not filled by the user'), '#default_value' => isset($field['phone_country_code']) ? $field['phone_country_code'] : '', - ); + ); + if ($field['type'] == 'int_phone') { + $form['phone_default_country_code'] = array( + '#type' => 'textfield', + '#title' => t('Default country code to add to international numbers without one (omit + sign)'), + '#default_value' => isset($field['phone_default_country_code']) ? $field['phone_default_country_code'] : '1', + ); + } return $form; case 'save': - return array('phone_country_code'); + return array('phone_country_code', 'phone_default_country_code'); case 'database columns': if ($field['type'] == 'fr_phone' || $field['type'] == 'it_phone' || $field['type'] == 'ca_phone' || $field['type'] == 'uk_phone' - || $field['type'] == 'ru_phone'){ + || $field['type'] == 'ru_phone' + || $field['type'] == 'int_phone'){ $columns = array( 'value' => array('type' => 'varchar', 'length' => 255, 'not null' => TRUE), ); @@ -85,7 +94,7 @@ return array( 'default' => array( 'label' => 'Default', - 'field types' => array('fr_phone', 'it_phone', 'ca_phone', 'uk_phone', 'ru_phone'), + 'field types' => array('fr_phone', 'it_phone', 'ca_phone', 'uk_phone', 'ru_phone', 'int_phone'), ), ); } @@ -117,7 +126,7 @@ return array( 'phone' => array( 'label' => t('Textfield'), - 'field types' => array('fr_phone', 'it_phone', 'ca_phone', 'uk_phone', 'ru_phone'), + 'field types' => array('fr_phone', 'it_phone', 'ca_phone', 'uk_phone', 'ru_phone', 'int_phone'), ), ); } @@ -179,55 +188,44 @@ return $form; case 'process form values': - if (is_array($node_field)) { - foreach ($node_field as $delta => $item) { - //format the phone number - if ($item['value'] != '') - { - if ($field['type'] == 'fr_phone') { - $node_field[0]['value'] = format_phone_number('fr', $node_field[0]['value'], $field); - } - if ($field['type'] == 'it_phone') { - $node_field[0]['value'] = format_phone_number('it', $node_field[0]['value'], $field); - } - if ($field['type'] == 'ca_phone') { - $node_field[0]['value'] = format_phone_number('ca', $node_field[0]['value'], $field); - } - if ($field['type'] == 'uk_phone') { - $node_field[0]['value'] = format_phone_number('uk', $node_field[0]['value'], $field); - } - if ($field['type'] == 'ru_phone') { - $node_field[0]['value'] = format_phone_number('ru', $node_field[0]['value'], $field); - } } - } - } - break; + if (is_array($node_field)) { + foreach ($node_field as $delta => $item) { + //format the phone number + if ($item['value'] != '') { + list($lang) = explode('_', $field['type']); + $node_field[0]['value'] = format_phone_number($lang, $node_field[0]['value'], $field); + } + } + } + break; case 'validate': if (is_array($node_field)) { - foreach ($node_field as $delta => $item) { - - if ($item['value'] != '') - { - if ($field['type'] == 'fr_phone' && !valid_phone_number('fr', $item['value'])) { - form_set_error($field['field_name'],t('"%value" is not a valid French phone number
French phone numbers should only contain numbers and spaces and be like 99 99 99 99 99', array('%value' => $item['value']))); - } - if ($field['type'] == 'it_phone' && !valid_phone_number('it', $item['value'])) { - form_set_error($field['field_name'],t('"%value" is not a valid Italian phone number
Italian phone numbers should only ...', array('%value' => $item['value']))); - } - if ($field['type'] == 'ca_phone' && !valid_phone_number('ca', $item['value'])) { - form_set_error($field['field_name'],t('"%value" is not a valid North American phone number
North American Phone numbers should only contain numbers and + and - and ( and ) and spaces and be like 999-999-9999. Please enter a valid ten-digit phone number with optional extension.', array('%value' => $item['value']))); - } - if ($field['type'] == 'uk_phone' && !valid_phone_number('uk', $item['value'])) { - form_set_error($field['field_name'],t('"%value" is not a valid British phone number
British Phone numbers should .... ', array('%value' => $item['value']))); - } - if ($field['type'] == 'ru_phone' && !valid_phone_number('ru', $item['value'])) { - form_set_error($field['field_name'],t('"%value" is not a valid Russian phone number
British Phone numbers should .... ', array('%value' => $item['value']))); - } } + foreach ($node_field as $delta => $item) { + if ($item['value'] != '') { + if ($field['type'] == 'fr_phone' && !valid_phone_number('fr', $item['value'])) { + form_set_error($field['field_name'],t('"%value" is not a valid French phone number
French phone numbers should only contain numbers and spaces and be like 99 99 99 99 99', array('%value' => $item['value']))); + } + elseif ($field['type'] == 'it_phone' && !valid_phone_number('it', $item['value'])) { + form_set_error($field['field_name'],t('"%value" is not a valid Italian phone number
Italian phone numbers should only ...', array('%value' => $item['value']))); + } + elseif ($field['type'] == 'ca_phone' && !valid_phone_number('ca', $item['value'])) { + form_set_error($field['field_name'],t('"%value" is not a valid North American phone number
North American Phone numbers should only contain numbers and + and - and ( and ) and spaces and be like 999-999-9999. Please enter a valid ten-digit phone number with optional extension.', array('%value' => $item['value']))); + } + elseif ($field['type'] == 'uk_phone' && !valid_phone_number('uk', $item['value'])) { + form_set_error($field['field_name'],t('"%value" is not a valid British phone number', array('%value' => $item['value']))); + } + elseif ($field['type'] == 'ru_phone' && !valid_phone_number('ru', $item['value'])) { + form_set_error($field['field_name'],t('"%value" is not a valid Russian phone number', array('%value' => $item['value']))); + } + elseif ($field['type'] == 'int_phone' && !valid_phone_number('int', $item['value'])) { + form_set_error($field['field_name'],t('"%value" is not a valid international phone number
International phone numbers should contain a country code prefixed by a plus sign followed by the local number.', array('%value' => $item['value']))); + } } - } + } + } break; - } + } } /** @@ -246,7 +244,8 @@ || $countrycode == 'it' || $countrycode == 'ca' || $countrycode == 'uk' - || $countrycode == 'ru') { + || $countrycode == 'ru' + || $countrycode == 'int') { //drupal_set_message('langue = ' . $countrycode, 'error'); @@ -282,7 +281,8 @@ || $countrycode == 'it' || $countrycode == 'ca' || $countrycode == 'uk' - || $countrycode == 'ru') { + || $countrycode == 'ru' + || $countrycode == 'int') { //drupal_set_message('langue = ' . $countrycode, 'error'); Index: tests/phone.int.test =================================================================== RCS file: tests/phone.int.test diff -N tests/phone.int.test --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ tests/phone.int.test 21 Jan 2008 22:53:48 -0000 @@ -0,0 +1,68 @@ + t('International phone number test'), + 'desc' => t('Tests international phone number validation.'), + 'group' => t('Phone module'), + ); + } + + function assertConversion($input, $expect = true, $field = array()) { + if ($expect === false) { + $this->assertFalse(valid_int_phone_number($input, $field)); + return; + } else if ($expect === true) { + $expect = $input; + } + $this->assertTrue(valid_int_phone_number($input, $field)); + $result = format_int_phone_number($input, $field); + $this->assertIdentical($result, $expect); + } + + function testBasic() { + $this->assertConversion('+1 7329018493'); + } + + function testBasicWithThreeCountryCode() { + $this->assertConversion('+672 565434'); + } + + function testBasicWithFourCountryCode() { + $this->assertConversion('+6724 565434', false); + } + + function testBasicWithSpaces() { + $this->assertConversion('+1 732 901 8493'); + } + + function testBasicNormalizeOtherCharacters() { + $this->assertConversion('+1 (732) 901-8493', '+1 732 901 8493'); + } + + function testRemoveNDD() { + $this->assertConversion('+54 0435344', '+54 435344'); + } + + function testRemoveNonStandardNDD() { + $this->assertConversion('+374 (8) 435344', '+374 435344'); + } + + function testAddCountryCode() { + $this->assertConversion('732 343 2333', '+1 732 343 2333', array('phone_default_country_code' => '1')); + } + + function testOverlongNumber() { + $this->assertConversion('+123 456 789 012 3456', false); + } + + function testOverlongNumberWithoutCountryCode() { + $this->assertConversion('456 789 012 3456', false, array('phone_default_country_code' => '123')); + } + +}