Issues (4069)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

include/Localization/Localization.php (3 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
3
/*********************************************************************************
4
 * SugarCRM Community Edition is a customer relationship management program developed by
5
 * SugarCRM, Inc. Copyright (C) 2004-2013 SugarCRM Inc.
6
7
 * SuiteCRM is an extension to SugarCRM Community Edition developed by Salesagility Ltd.
8
 * Copyright (C) 2011 - 2014 Salesagility Ltd.
9
 *
10
 * This program is free software; you can redistribute it and/or modify it under
11
 * the terms of the GNU Affero General Public License version 3 as published by the
12
 * Free Software Foundation with the addition of the following permission added
13
 * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
14
 * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
15
 * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
16
 *
17
 * This program is distributed in the hope that it will be useful, but WITHOUT
18
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
19
 * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
20
 * details.
21
 *
22
 * You should have received a copy of the GNU Affero General Public License along with
23
 * this program; if not, see http://www.gnu.org/licenses or write to the Free
24
 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
25
 * 02110-1301 USA.
26
 *
27
 * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
28
 * SW2-130, Cupertino, CA 95014, USA. or at email address [email protected].
29
 *
30
 * The interactive user interfaces in modified source and object code versions
31
 * of this program must display Appropriate Legal Notices, as required under
32
 * Section 5 of the GNU Affero General Public License version 3.
33
 *
34
 * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
35
 * these Appropriate Legal Notices must retain the display of the "Powered by
36
 * SugarCRM" logo and "Supercharged by SuiteCRM" logo. If the display of the logos is not
37
 * reasonably feasible for  technical reasons, the Appropriate Legal Notices must
38
 * display the words  "Powered by SugarCRM" and "Supercharged by SuiteCRM".
39
 ********************************************************************************/
40
41
/**
42
 * Localization manager
43
 * @api
44
 */
45
class Localization {
46
	var $availableCharsets = array(
47
		'BIG-5',        //Taiwan and Hong Kong
48
		/*'CP866'			  // ms-dos Cyrillic */
49
		/*'CP949'			  //Microsoft Korean */
50
		'CP1251',       //MS Cyrillic
51
		'CP1252',       //MS Western European & US
52
		'EUC-CN',       //Simplified Chinese GB2312
53
		'EUC-JP',       //Unix Japanese
54
		'EUC-KR',       //Korean
55
		'EUC-TW',       //Taiwanese
56
		'ISO-2022-JP',  //Japanese
57
		'ISO-2022-KR',  //Korean
58
		'ISO-8859-1',   //Western European and US
59
		'ISO-8859-2',   //Central and Eastern European
60
		'ISO-8859-3',   //Latin 3
61
		'ISO-8859-4',   //Latin 4
62
		'ISO-8859-5',   //Cyrillic
63
		'ISO-8859-6',   //Arabic
64
		'ISO-8859-7',   //Greek
65
		'ISO-8859-8',   //Hebrew
66
		'ISO-8859-9',   //Latin 5
67
		'ISO-8859-10',  //Latin 6
68
		'ISO-8859-13',  //Latin 7
69
		'ISO-8859-14',  //Latin 8
70
		'ISO-8859-15',  //Latin 9
71
		'KOI8-R',       //Cyrillic Russian
72
		'KOI8-U',       //Cyrillic Ukranian
73
		'SJIS',         //MS Japanese
74
		'UTF-8',        //UTF-8
75
		);
76
	var $localeNameFormat;
77
	var $localeNameFormatDefault;
78
	var $default_export_charset = 'UTF-8';
79
	var $default_email_charset = 'UTF-8';
80
	var $currencies = array(); // array loaded with current currencies
81
    var $invalidNameFormatUpgradeFilename = 'upgradeInvalidLocaleNameFormat.php';
82
    /* Charset mappings for iconv */
83
    var $iconvCharsetMap = array(
84
        'KS_C_5601-1987' => 'CP949',
85
        'ISO-8859-8-I' => 'ISO-8859-8'
86
        );
87
88
	/**
89
	 * sole constructor
90
	 */
91
	public function __construct() {
92
		global $sugar_config;
93
		$this->localeNameFormatDefault = empty($sugar_config['locale_name_format_default']) ? 's f l' : $sugar_config['default_name_format'];
94
		$this->loadCurrencies();
95
	}
96
97
    /**
98
     * @deprecated deprecated since version 7.6, PHP4 Style Constructors are deprecated and will be remove in 7.8, please update your code, use __construct instead
99
     */
100
    public function Localization(){
101
        $deprecatedMessage = 'PHP4 Style Constructors are deprecated and will be remove in 7.8, please update your code';
102
        if(isset($GLOBALS['log'])) {
103
            $GLOBALS['log']->deprecated($deprecatedMessage);
104
        }
105
        else {
106
            trigger_error($deprecatedMessage, E_USER_DEPRECATED);
107
        }
108
        self::__construct();
109
    }
110
111
112
	/**
113
	 * returns an array of Sugar Config defaults that are determined by locale settings
114
	 * @return array
115
	 */
116 81
	function getLocaleConfigDefaults() {
117
		$coreDefaults = array(
118 81
			'currency'								=> '',
119 81
			'datef'									=> 'm/d/Y',
120 81
			'timef'									=> 'H:i',
121 81
			'default_currency_significant_digits'	=> 2,
122 81
			'default_currency_symbol'				=> '$',
123 81
			'default_export_charset'				=> $this->default_export_charset,
124 81
			'default_locale_name_format'			=> 's f l',
125
            'name_formats'                          => array('s f l' => 's f l', 'f l' => 'f l', 's l' => 's l', 'l, s f' => 'l, s f',
126
                                                            'l, f' => 'l, f', 's l, f' => 's l, f', 'l s f' => 'l s f', 'l f s' => 'l f s'),
127 81
			'default_number_grouping_seperator'		=> ',',
128 81
			'default_decimal_seperator'				=> '.',
129 81
			'export_delimiter'						=> ',',
130 81
			'default_email_charset'					=> $this->default_email_charset,
131
		);
132
133 81
		return $coreDefaults;
134
	}
135
136
	/**
137
	 * abstraction of precedence
138
	 * @param string prefName Name of preference to retrieve based on overrides
139
	 * @param object user User in focus, default null (current_user)
140
	 * @return string pref Most significant preference
141
	 */
142 81
	function getPrecedentPreference($prefName, $user=null, $sugarConfigPrefName = '') {
143 81
		global $current_user;
144 81
		global $sugar_config;
145
146 81
		$userPref = '';
147 81
		$coreDefaults = $this->getLocaleConfigDefaults();
148 81
		$pref = isset($coreDefaults[$prefName]) ? $coreDefaults[$prefName] : ''; // defaults, even before config.php
149
150 81
		if($user != null) {
151 64
			$userPref = $user->getPreference($prefName);
152 17
		} elseif(!empty($current_user)) {
153 17
			$userPref = $current_user->getPreference($prefName);
154
		}
155
		// Bug 39171 - If we are asking for default_email_charset, check in emailSettings['defaultOutboundCharset'] as well
156 81
		if ( $prefName == 'default_email_charset' ) {
157 7
		    if($user != null) {
158
                $emailSettings = $user->getPreference('emailSettings', 'Emails');
159 7
            } elseif(!empty($current_user)) {
160 7
                $emailSettings = $current_user->getPreference('emailSettings', 'Emails');
161
            }
162 7
            if ( isset($emailSettings['defaultOutboundCharset']) ) {
163
                $userPref = $emailSettings['defaultOutboundCharset'];
164
            }
165
		}
166
167
		// set fallback defaults defined in this class
168 81
		if(isset($this->$prefName)) {
169 9
			$pref = $this->$prefName;
170
		}
171
		//rrs: 33086 - give the ability to pass in the preference name as stored in $sugar_config.
172 81
		if(!empty($sugarConfigPrefName)){
173
			$prefName = $sugarConfigPrefName;
174
		}
175
		// cn: 9549 empty() call on a value of 0 (0 significant digits) resulted in a false-positive.  changing to "isset()"
176 81
		$pref = (!isset($sugar_config[$prefName]) || (empty($sugar_config[$prefName]) && $sugar_config[$prefName] !== '0')) ? $pref : $sugar_config[$prefName];
177 81
		$pref = (empty($userPref) && $userPref !== '0') ? $pref : $userPref;
178 81
		return $pref;
179
	}
180
181
	///////////////////////////////////////////////////////////////////////////
182
	////	CURRENCY HANDLING
183
	/**
184
	 * wrapper for whatever currency system we implement
185
	 */
186
	function loadCurrencies() {
187
		// doing it dirty here
188
		global $db;
189
		global $sugar_config;
190
191
		if(empty($db)) {
192
			return array();
193
		}
194
195
        $load = sugar_cache_retrieve('currency_list');
196
        if ( !is_array($load) ) {
197
			// load default from config.php
198
			$this->currencies['-99'] = array(
199
				'name'		=> $sugar_config['default_currency_name'],
200
				'symbol'	=> $sugar_config['default_currency_symbol'],
201
				'conversion_rate' => 1
202
				);
203
204
            $q = "SELECT id, name, symbol, conversion_rate FROM currencies WHERE status = 'Active' and deleted = 0";
205
            $r = $db->query($q);
206
207
            while($a = $db->fetchByAssoc($r)) {
208
                $load = array();
209
                $load['name'] = $a['name'];
210
                $load['symbol'] = $a['symbol'];
211
                $load['conversion_rate'] = $a['conversion_rate'];
212
213
                $this->currencies[$a['id']] = $load;
214
            }
215
            sugar_cache_put('currency_list',$this->currencies);
216
        } else {
217
            $this->currencies = $load;
218
        }
219
220
221
	}
222
223
	/**
224
	 * getter for currencies array
225
	 * @return array $this->currencies returns array( id => array(name => X, etc
226
	 */
227
	function getCurrencies() {
228
		return $this->currencies;
229
	}
230
231
	/**
232
	 * retrieves default OOTB currencies for sugar_config and installer.
233
	 * @return array ret Array of default currencies keyed by ISO4217 code
234
	 */
235
	function getDefaultCurrencies() {
236
		$ret = array(
237
			'AUD' => array(	'name'		=> 'Australian Dollars',
238
							'iso4217'	=> 'AUD',
239
							'symbol'	=> '$'),
240
			'BRL' => array(	'name'		=> 'Brazilian Reais',
241
							'iso4217'	=> 'BRL',
242
							'symbol'	=> 'R$'),
243
			'GBP' => array(	'name'		=> 'British Pounds',
244
							'iso4217'	=> 'GBP',
245
							'symbol'	=> '£'),
246
			'CAD' => array(	'name'		=> 'Canadian Dollars',
247
							'iso4217'	=> 'CAD',
248
							'symbol'	=> '$'),
249
			'CNY' => array(	'name'		=> 'Chinese Yuan',
250
							'iso4217'	=> 'CNY',
251
							'symbol'	=> 'ï¿¥'),
252
			'EUR' => array(	'name'		=> 'Euro',
253
							'iso4217'	=> 'EUR',
254
							'symbol'	=> '€'),
255
			'HKD' => array(	'name'		=> 'Hong Kong Dollars',
256
							'iso4217'	=> 'HKD',
257
							'symbol'	=> '$'),
258
			'INR' => array(	'name'		=> 'Indian Rupees',
259
							'iso4217'	=> 'INR',
260
							'symbol'	=> '₨'),
261
			'KRW' => array(	'name'		=> 'Korean Won',
262
							'iso4217'	=> 'KRW',
263
							'symbol'	=> 'â‚©'),
264
			'YEN' => array(	'name'		=> 'Japanese Yen',
265
							'iso4217'	=> 'JPY',
266
							'symbol'	=> 'Â¥'),
267
			'MXM' => array(	'name'		=> 'Mexican Pesos',
268
							'iso4217'	=> 'MXM',
269
							'symbol'	=> '$'),
270
			'SGD' => array(	'name'		=> 'Singaporean Dollars',
271
							'iso4217'	=> 'SGD',
272
							'symbol'	=> '$'),
273
			'CHF' => array(	'name'		=> 'Swiss Franc',
274
							'iso4217'	=> 'CHF',
275
							'symbol'	=> 'SFr.'),
276
			'THB' => array(	'name'		=> 'Thai Baht',
277
							'iso4217'	=> 'THB',
278
							'symbol'	=> '฿'),
279
			'USD' => array(	'name'		=> 'US Dollars',
280
							'iso4217'	=> 'USD',
281
							'symbol'	=> '$'),
282
		);
283
284
		return $ret;
285
	}
286
	////	END CURRENCY HANDLING
287
	///////////////////////////////////////////////////////////////////////////
288
289
290
	///////////////////////////////////////////////////////////////////////////
291
	////	CHARSET TRANSLATION
292
	/**
293
	 * returns a mod|app_strings array in the target charset
294
	 * @param array strings $mod_string, et.al.
295
	 * @param string charset Target charset
296
	 * @return array Translated string pack
297
	 */
298
	function translateStringPack($strings, $charset) {
299
		// handle recursive
300
		foreach($strings as $k => $v) {
301
			if(is_array($v)) {
302
				$strings[$k] = $this->translateStringPack($v, $charset);
303
			} else {
304
				$strings[$k] = $this->translateCharset($v, 'UTF-8', $charset);
305
			}
306
		}
307
		ksort($strings);
308
		return $strings;
309
	}
310
311
	/**
312
	 * translates the passed variable for email sending (export)
313
	 * @param	mixed the var (array or string) to translate
314
	 * @return	mixed the translated variable
315
	 */
316
	function translateForEmail($var) {
317
		if(is_array($var)) {
318
			foreach($var as $k => $v) {
319
				$var[$k] = $this->translateForEmail($v);
320
			}
321
			return $var;
322
		} elseif(!empty($var)) {
323
			return $this->translateCharset($var, 'UTF-8', $this->getOutboundEmailCharset());
324
		}
325
	}
326
327
	/**
328
	 * prepares a bean for export by translating any text fields into the export
329
	 * character set
330
	 * @param bean object A SugarBean
331
	 * @return bean object The bean with translated strings
332
	 */
333
    function prepBeanForExport($bean)
334
    {
335
        foreach($bean->field_defs as $k => $field)
336
        {
337
            if (is_string($bean->$k))
0 ignored issues
show
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
338
            {
339
			   // $bean->$k = $this->translateCharset($bean->$k, 'UTF-8', $this->getExportCharset());
340
            }
341
            else
342
            {
343
                $bean->$k = '';
344
            }
345
        }
346
347
        return $bean;
348
    }
349
350
	/**
351
	 * translates a character set from one encoding to another encoding
352
	 * @param string string the string to be translated
353
	 * @param string fromCharset the charset the string is currently in
354
	 * @param string toCharset the charset to translate into (defaults to UTF-8)
355
	 * @param bool   forceIconv force using the iconv library instead of mb_string
356
	 * @return string the translated string
357
	 */
358 9
    function translateCharset($string, $fromCharset, $toCharset='UTF-8', $forceIconv = false)
359
    {
360 9
        $GLOBALS['log']->debug("Localization: translating [{$string}] from {$fromCharset} into {$toCharset}");
361
362
        // Bug #35413 Function has to use iconv if $fromCharset is not in mb_list_encodings
363 9
        $isMb = function_exists('mb_convert_encoding') && !$forceIconv;
364 9
        $isIconv = function_exists('iconv');
365 9
        if ($isMb == true)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
366
        {
367 9
            $fromCharset = strtoupper($fromCharset);
368 9
            $listEncodings = mb_list_encodings();
369 9
            $isFound = false;
370 9
            foreach ($listEncodings as $encoding)
371
            {
372 9
                if (strtoupper($encoding) == $fromCharset)
373
                {
374 8
                    $isFound = true;
375 9
                    break;
376
                }
377
            }
378 9
            $isMb = $isFound;
379
        }
380
381 9
        if($isMb)
382
        {
383 8
            return mb_convert_encoding($string, $toCharset, $fromCharset);
384
        }
385 1
        elseif($isIconv)
386
        {
387 1
            $newFromCharset = $fromCharset;
388 1
            if (isset($this->iconvCharsetMap[$fromCharset])) {
389
                $newFromCharset = $this->iconvCharsetMap[$fromCharset];
390
                $GLOBALS['log']->debug("Localization: iconv using charset {$newFromCharset} instead of {$fromCharset}");
391
            }
392 1
            $newToCharset = $toCharset;
393 1
            if (isset($this->iconvCharsetMap[$toCharset])) {
394
                $newToCharset = $this->iconvCharsetMap[$toCharset];
395
                $GLOBALS['log']->debug("Localization: iconv using charset {$newToCharset} instead of {$toCharset}");
396
            }
397 1
            return iconv($newFromCharset, $newToCharset, $string);
398
        }
399
        else
400
        {
401
            return $string;
402
        } // end else clause
403
    }
404
405
	/**
406
	 * translates a character set from one to another, and the into MIME-header friendly format
407
	 */
408 3
	function translateCharsetMIME($string, $fromCharset, $toCharset='UTF-8', $encoding="Q") {
409 3
		$previousEncoding = mb_internal_encoding();
410 3
		mb_internal_encoding($fromCharset);
411 3
		$result = mb_encode_mimeheader($string, $toCharset, $encoding);
412 3
		mb_internal_encoding($previousEncoding);
413 3
		return $result;
414
	}
415
416
	function normalizeCharset($charset) {
417
		$charset = strtolower(preg_replace("/[\-\_]*/", "", $charset));
418
		return $charset;
419
	}
420
421
	/**
422
	 * returns an array of charsets with keys for available translations; appropriate for get_select_options_with_id()
423
	 */
424
	function getCharsetSelect() {
425
    //jc:12293 - the "labels" or "human-readable" representations of the various charsets
426
    //should be translatable
427
    $translated = array();
428
    foreach($this->availableCharsets as $key)
429
    {
430
     	 //$translated[$key] = translate($value);
431
         $translated[$key] = translate($key);
432
    }
433
434
		return $translated;
435
    //end:12293
436
	}
437
438
	/**
439
	 * returns the charset preferred in descending order: User, Sugar Config, DEFAULT
440
	 * @param string charset to override ALL, pass a valid charset here
441
	 * @return string charset the chosen character set
442
	 */
443 2
	function getExportCharset($charset='', $user=null) {
444 2
		$charset = $this->getPrecedentPreference('default_export_charset', $user);
445 2
		return $charset;
446
	}
447
448
	/**
449
	 * returns the charset preferred in descending order: User, Sugar Config, DEFAULT
450
	 * @return string charset the chosen character set
451
	 */
452
	function getOutboundEmailCharset($user=null) {
453
		$charset = $this->getPrecedentPreference('default_email_charset', $user);
454
		return $charset;
455
	}
456
	////	END CHARSET TRANSLATION
457
	///////////////////////////////////////////////////////////////////////////
458
459
	///////////////////////////////////////////////////////////////////////////
460
	////	NUMBER DISPLAY FORMATTING CODE
461
	function getDecimalSeparator($user=null) {
462
        // Bug50887 this is purposefully misspelled as ..._seperator to match the way it's defined throughout the app.
463
		$dec = $this->getPrecedentPreference('default_decimal_seperator', $user);
464
		return $dec;
465
	}
466
467
	function getNumberGroupingSeparator($user=null) {
468
		$sep = $this->getPrecedentPreference('default_number_grouping_seperator', $user);
469
		return $sep;
470
	}
471
472 2
	function getPrecision($user=null) {
473 2
		$precision = $this->getPrecedentPreference('default_currency_significant_digits', $user);
474 2
		return $precision;
475
	}
476
477
	function getCurrencySymbol($user=null) {
478
		$dec = $this->getPrecedentPreference('default_currency_symbol', $user);
479
		return $dec;
480
	}
481
482
	/**
483
	 * returns a number formatted by user preference or system default
484
	 * @param string number Number to be formatted and returned
485
	 * @param string currencySymbol Currency symbol if override is necessary
486
	 * @param bool is_currency Flag to also return the currency symbol
487
	 * @return string Formatted number
488
	 */
489
	function getLocaleFormattedNumber($number, $currencySymbol='', $is_currency=true, $user=null) {
490
		$fnum			= $number;
491
		$majorDigits	= '';
492
		$minorDigits	= '';
493
		$dec			= $this->getDecimalSeparator($user);
494
		$thou			= $this->getNumberGroupingSeparator($user);
495
		$precision		= $this->getPrecision($user);
496
		$symbol			= empty($currencySymbol) ? $this->getCurrencySymbol($user) : $currencySymbol;
497
498
		$exNum = explode($dec, $number);
499
		// handle grouping
500
		if(is_array($exNum) && count($exNum) > 0) {
501
			if(strlen($exNum[0]) > 3) {
502
				$offset = strlen($exNum[0]) % 3;
503
				if($offset > 0) {
504
					for($i=0; $i<$offset; $i++) {
505
						$majorDigits .= $exNum[0]{$i};
506
					}
507
				}
508
509
				$tic = 0;
510
				for($i=$offset; $i<strlen($exNum[0]); $i++) {
511
					if($tic % 3 == 0 && $i != 0) {
512
						$majorDigits .= $thou; // add separator
513
					}
514
515
					$majorDigits .= $exNum[0]{$i};
516
					$tic++;
517
				}
518
			} else {
519
				$majorDigits = $exNum[0]; // no formatting needed
520
			}
521
			$fnum = $majorDigits;
522
		}
523
524
		// handle decimals
525
		if($precision > 0) { // we toss the minor digits otherwise
526
			if(is_array($exNum) && isset($exNum[1])) {
0 ignored issues
show
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
527
528
			}
529
		}
530
531
532
		if($is_currency) {
533
			$fnum = $symbol.$fnum;
534
		}
535
		return $fnum;
536
	}
537
538
	/**
539
	 * returns Javascript to format numbers and currency for ***DISPLAY***
540
	 */
541
	function getNumberJs() {
542
		$out = <<<eoq
543
544
			var exampleDigits = '123456789.000000';
545
546
			// round parameter can be negative for decimal, precision has to be postive
547
			function formatNumber(n, sep, dec, precision) {
548
				var majorDigits;
549
				var minorDigits;
550
				var formattedMajor = '';
551
				var formattedMinor = '';
552
553
				var nArray = n.split('.');
554
				majorDigits = nArray[0];
555
				if(nArray.length < 2) {
556
					minorDigits = 0;
557
				} else {
558
					minorDigits = nArray[1];
559
				}
560
561
				// handle grouping
562
				if(sep.length > 0) {
563
					var strlength = majorDigits.length;
564
565
					if(strlength > 3) {
566
						var offset = strlength % 3; // find how many to lead off by
567
568
						for(j=0; j<offset; j++) {
569
							formattedMajor += majorDigits[j];
570
						}
571
572
						tic=0;
573
						for(i=offset; i<strlength; i++) {
574
							if(tic % 3 == 0 && i != 0)
575
								formattedMajor += sep;
576
577
							formattedMajor += majorDigits.substr(i,1);
578
							tic++;
579
						}
580
					}
581
				} else {
582
					formattedMajor = majorDigits; // no grouping marker
583
				}
584
585
				// handle decimal precision
586
				if(precision > 0) {
587
					for(i=0; i<precision; i++) {
588
						if(minorDigits[i] != undefined)
589
							formattedMinor += minorDigits[i];
590
						else
591
							formattedMinor += '0';
592
					}
593
				} else {
594
					// we're just returning the major digits, no decimal marker
595
					dec = ''; // just in case
596
				}
597
598
				return formattedMajor + dec + formattedMinor;
599
			}
600
601
			function setSigDigits() {
602
				var sym = document.getElementById('symbol').value;
603
				var thou = document.getElementById('default_number_grouping_seperator').value;
604
				var dec = document.getElementById('default_decimal_seperator').value;
605
				var precision = document.getElementById('sigDigits').value;
606
				//umber(n, num_grp_sep, dec_sep, round, precision)
607
				var newNumber = sym + formatNumber(exampleDigits, thou, dec, precision, precision);
608
				document.getElementById('sigDigitsExample').value = newNumber;
609
			}
610
eoq;
611
		return $out;
612
	}
613
614
	////	END NUMBER DISPLAY FORMATTING CODE
615
	///////////////////////////////////////////////////////////////////////////
616
617
	///////////////////////////////////////////////////////////////////////////
618
	////	NAME DISPLAY FORMATTING CODE
619
	/**
620
	 * get's the Name format macro string, preferring $current_user
621
	 * @return string format Name Format macro for locale
622
	 */
623 68
	function getLocaleFormatMacro($user=null) {
624 68
		$returnFormat = $this->getPrecedentPreference('default_locale_name_format', $user);
625 68
		return $returnFormat;
626
	}
627
628
	/**
629
	 * returns formatted name according to $current_user's locale settings
630
	 *
631
	 * @param string firstName
632
	 * @param string lastName
633
	 * @param string salutation
634
	 * @param string title
635
	 * @param string format If a particular format is desired, then pass this optional parameter as a simple string.
636
	 * sfl is "Salutation FirstName LastName", "l, f s" is "LastName[comma][space]FirstName[space]Salutation"
637
	 * @param object user object
638
	 * @param bool returnEmptyStringIfEmpty true if we should return back an empty string rather than a single space
639
	 * when the formatted name would be blank
640
	 * @return string formattedName
641
	 */
642 93
	function getLocaleFormattedName($firstName, $lastName, $salutationKey='', $title='', $format="", $user=null, $returnEmptyStringIfEmpty = false) {
643 93
		global $current_user;
644 93
		global $app_list_strings;
645
646 93
		if ( $user == null ) {
647 93
		    $user = $current_user;
648
		}
649
650 93
		$salutation = $salutationKey;
651 93
		if(!empty($salutationKey) && !empty($app_list_strings['salutation_dom'][$salutationKey])) {
652
			$salutation = (!empty($app_list_strings['salutation_dom'][$salutationKey]) ? $app_list_strings['salutation_dom'][$salutationKey] : $salutationKey);
653
		}
654
655
        //check to see if passed in variables are set, if so, then populate array with value,
656
        //if not, then populate array with blank ''
657 93
		$names = array();
658 93
		$names['f'] = (empty($firstName)	&& $firstName	!= 0) ? '' : $firstName;
659 93
		$names['l'] = (empty($lastName)	&& $lastName	!= 0) ? '' : $lastName;
660 93
		$names['s'] = (empty($salutation)	&& $salutation	!= 0) ? '' : $salutation;
661 93
		$names['t'] = (empty($title)		&& $title		!= 0) ? '' : $title;
662
663
		//Bug: 39936 - if all of the inputs are empty, then don't try to format the name.
664 93
		$allEmpty = true;
665 93
		foreach($names as $key => $val){
666 93
			if(!empty($val)){
667 63
				$allEmpty = false;
668 93
				break;
669
			}
670
		}
671 93
		if($allEmpty){
672 39
			return $returnEmptyStringIfEmpty ? '' : ' ';
673
		}
674
		//end Bug: 39936
675
676 63
		if(empty($format)) {
677 63
			$this->localeNameFormat = $this->getLocaleFormatMacro($user);
678
		} else {
679
			$this->localeNameFormat = $format;
680
		}
681
682
		// parse localeNameFormat
683 63
		$formattedName = '';
684 63
		for($i=0; $i<strlen($this->localeNameFormat); $i++) {
685 63
			$formattedName .= array_key_exists($this->localeNameFormat{$i}, $names) ? $names[$this->localeNameFormat{$i}] : $this->localeNameFormat{$i};
686
		}
687
688 63
		$formattedName = trim($formattedName);
689 63
        if (strlen($formattedName)==0) {
690
            return $returnEmptyStringIfEmpty ? '' : ' ';
691
        }
692
693 63
		if(strpos($formattedName,',',strlen($formattedName)-1)) { // remove trailing commas
694
			$formattedName = substr($formattedName, 0, strlen($formattedName)-1);
695
		}
696 63
		return trim($formattedName);
697
	}
698
699
	/**
700
	 * outputs some simple Javascript to show a preview of Name format in "My Account" and "Admin->Localization"
701
	 * @param string first First Name, use app_strings default if not specified
702
	 * @param string last Last Name, use app_strings default if not specified
703
	 * @param string salutation Saluation, use app_strings default if not specified
704
	 * @return string some Javascript
705
	 */
706
	function getNameJs($first='', $last='', $salutation='', $title='') {
707
		global $app_strings;
708
709
		$salutation	= !empty($salutation) ? $salutation : $app_strings['LBL_LOCALE_NAME_EXAMPLE_SALUTATION'];
710
		$first		= !empty($first) ? $first : $app_strings['LBL_LOCALE_NAME_EXAMPLE_FIRST'];
711
		$last		= !empty($last) ? $last : $app_strings['LBL_LOCALE_NAME_EXAMPLE_LAST'];
712
		$title		= !empty($title) ? $title : $app_strings['LBL_LOCALE_NAME_EXAMPLE_TITLE'];
713
714
		$ret = "
715
		function setPreview() {
716
			format = document.getElementById('default_locale_name_format').value;
717
			field = document.getElementById('nameTarget');
718
719
			stuff = new Object();
720
721
			stuff['s'] = '{$salutation}';
722
			stuff['f'] = '{$first}';
723
			stuff['l'] = '{$last}';
724
			stuff['t'] = '{$title}';
725
726
			var name = '';
727
			for(i=0; i<format.length; i++) {
728
                if(stuff[format.substr(i,1)] != undefined) {
729
                    name += stuff[format.substr(i,1)];
730
				} else {
731
                    name += format.substr(i,1);
732
		}
733
			}
734
735
			//alert(name);
736
			field.value = name;
737
		}
738
739
        ";
740
741
		return $ret;
742
	}
743
744
    /**
745
     * Checks to see that the characters in $name_format are allowed:  s, f, l, space/tab or punctuation
746
     * @param $name_format
747
     * @return bool
748
     */
749
    public function isAllowedNameFormat($name_format) {
750
        // will result in a match as soon as a disallowed char is hit in $name_format
751
        $match = preg_match('/[^sfl[:punct:][:^alnum:]\s]/', $name_format);
752
        if ($match !== false && $match === 0) {
753
            return true;
754
        }
755
        return false;
756
    }
757
758
    /**
759
     * Checks to see if there was an invalid Name Format encountered during the upgrade
760
     * @return bool true if there was an invalid name, false if all went well.
761
     */
762
    public function invalidLocaleNameFormatUpgrade() {
763
        return file_exists($this->invalidNameFormatUpgradeFilename);
764
    }
765
766
    /**
767
     * Creates the file that is created when there is an invalid name format during an upgrade
768
     */
769
    public function createInvalidLocaleNameFormatUpgradeNotice() {
770
        $fh = fopen($this->invalidNameFormatUpgradeFilename,'w');
771
        fclose($fh);
772
    }
773
774
    /**
775
     * Removes the file that is created when there is an invalid name format during an upgrade
776
     */
777
    public function removeInvalidLocaleNameFormatUpgradeNotice() {
778
        if ($this->invalidLocaleNameFormatUpgrade()) {
779
            unlink($this->invalidNameFormatUpgradeFilename);
780
        }
781
    }
782
783
784
    /**
785
     * Creates dropdown items that have localized example names while filtering out invalid formats
786
     *
787
     * @param array un-prettied dropdown list
788
     * @return array array of dropdown options
789
     */
790
    public function getUsableLocaleNameOptions($options) {
791
        global $app_strings;
792
793
        $examples = array('s' => $app_strings['LBL_LOCALE_NAME_EXAMPLE_SALUTATION'],
794
                        'f' => $app_strings['LBL_LOCALE_NAME_EXAMPLE_FIRST'],
795
                        'l' => $app_strings['LBL_LOCALE_NAME_EXAMPLE_LAST']);
796
        $newOpts = array();
797
        foreach ($options as $key => $val) {
798
            if ($this->isAllowedNameFormat($key) && $this->isAllowedNameFormat($val)) {
799
                $newVal = '';
800
                $pieces = str_split($val);
801
                foreach ($pieces as $piece) {
802
                    if (isset($examples[$piece])) {
803
                        $newVal .= $examples[$piece];
804
                    } else {
805
                        $newVal .= $piece;
806
                    }
807
                }
808
                $newOpts[$key] = $newVal;
809
            }
810
        }
811
        return $newOpts;
812
    }
813
	////	END NAME DISPLAY FORMATTING CODE
814
	///////////////////////////////////////////////////////////////////////////
815
816
    /**
817
     * Attempts to detect the charset used in the string
818
     *
819
     * @param  $str string
820
     * @param $strict bool default false (use strict encoding?)
821
     * @return string
822
     */
823 2
    public function detectCharset($str, $strict=false)
824
    {
825 2
        if ( function_exists('mb_convert_encoding') )
826 2
            return mb_detect_encoding($str,'ASCII,JIS,UTF-8,EUC-JP,SJIS,ISO-8859-1',$strict);
827
828
        return false;
829
    }
830
} // end class def
831
832
?>