Version 4.5.0
This commit is contained in:
1 parent
1a0c7fd3c2
commit
f79dcf067a
3791 files changed
+248032
-76372
No files matched your search
+375
-109
@@ -11,7 +11,7 @@
|
||||
namespace IPS;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
@@ -24,6 +24,17 @@ class _Lang extends \IPS\Node\Model
|
||||
{
|
||||
/* !Lang - Static */
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Caches
|
||||
* @note Defined cache keys will be cleared automatically as needed
|
||||
*/
|
||||
protected $caches = array( 'languages', 'listFormats', 'updatecount_languages' );
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Database ID Fields
|
||||
*/
|
||||
protected static $databaseIdFields = array('lang_id', 'lang_marketplace_id');
|
||||
|
||||
/**
|
||||
* @brief Have fetched all?
|
||||
*/
|
||||
@@ -81,6 +92,21 @@ class _Lang extends \IPS\Node\Model
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get data store
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getStore()
|
||||
{
|
||||
if ( !isset( \IPS\Data\Store::i()->languages ) )
|
||||
{
|
||||
\IPS\Data\Store::i()->languages = iterator_to_array( \IPS\Db::i()->select( '*', 'core_sys_lang', NULL, 'lang_order' )->setKeyField('lang_id') );
|
||||
}
|
||||
|
||||
return \IPS\Data\Store::i()->languages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Languages
|
||||
*
|
||||
@@ -93,15 +119,7 @@ class _Lang extends \IPS\Node\Model
|
||||
{
|
||||
if( $iterator === NULL )
|
||||
{
|
||||
if ( isset( \IPS\Data\Store::i()->languages ) )
|
||||
{
|
||||
$rows = \IPS\Data\Store::i()->languages;
|
||||
}
|
||||
else
|
||||
{
|
||||
$rows = iterator_to_array( \IPS\Db::i()->select( '*', 'core_sys_lang', NULL, 'lang_order' )->setKeyField('lang_id') );
|
||||
\IPS\Data\Store::i()->languages = $rows;
|
||||
}
|
||||
$rows = static::getStore();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -190,7 +208,7 @@ class _Lang extends \IPS\Node\Model
|
||||
/**
|
||||
* Auto detect language
|
||||
*
|
||||
* @param string $acceptLanguage HTTP Accept-Language header
|
||||
* @param string $httpAcceptLanguage HTTP Accept-Language header
|
||||
* @return int|NULL ID Of preferred language or NULL if could not be autodetected
|
||||
*/
|
||||
public static function autoDetectLanguage( $httpAcceptLanguage )
|
||||
@@ -239,20 +257,29 @@ class _Lang extends \IPS\Node\Model
|
||||
/**
|
||||
* Save translatable language strings
|
||||
*
|
||||
* @param string $app Application key
|
||||
* @param string $key Word key
|
||||
* @param string|array $values The values
|
||||
* @param bool $js Expose to JavaScript?
|
||||
* @param string|int|\IPS\Application|\IPS\Plugin $appOrPlugin Application key or Plugin ID
|
||||
* @param string $key Word key
|
||||
* @param string|array $values The values
|
||||
* @param bool $js Expose to JavaScript?
|
||||
* @param string|NULL $default If the administrator added this word, this is the default value.
|
||||
* @return void
|
||||
*/
|
||||
public static function saveCustom( $app, $key, $values, $js=FALSE )
|
||||
public static function saveCustom( $appOrPlugin, $key, $values, $js=FALSE, $default=NULL )
|
||||
{
|
||||
$default = '';
|
||||
|
||||
/* Values is a string, so use this value for all languages */
|
||||
if ( !is_array( $values ) )
|
||||
$isCustom = FALSE;
|
||||
if ( $default !== NULL )
|
||||
{
|
||||
$default = $values;
|
||||
$isCustom = TRUE;
|
||||
}
|
||||
|
||||
if ( !\is_array( $values ) )
|
||||
{
|
||||
if ( $default === NULL )
|
||||
{
|
||||
$default = $values;
|
||||
}
|
||||
|
||||
$values = array();
|
||||
|
||||
foreach ( static::languages() as $lang )
|
||||
@@ -262,7 +289,7 @@ class _Lang extends \IPS\Node\Model
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( count( $values ) == 0 )
|
||||
if ( \count( $values ) == 0 )
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -272,7 +299,7 @@ class _Lang extends \IPS\Node\Model
|
||||
{
|
||||
$values[ $lang->id ] = '';
|
||||
}
|
||||
else if ( $lang->default )
|
||||
else if ( $lang->default AND $default === NULL )
|
||||
{
|
||||
$default = $values[ $lang->id ];
|
||||
}
|
||||
@@ -288,15 +315,45 @@ class _Lang extends \IPS\Node\Model
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Db::i()->replace( 'core_sys_lang_words', array(
|
||||
if ( $appOrPlugin instanceof \IPS\Application )
|
||||
{
|
||||
$appOrPlugin = $appOrPlugin->directory;
|
||||
}
|
||||
elseif ( $appOrPlugin instanceof \IPS\Plugin )
|
||||
{
|
||||
$appOrPlugin = $appOrPlugin->_id;
|
||||
}
|
||||
|
||||
$insert = array(
|
||||
'lang_id' => $langId,
|
||||
'word_app' => $app,
|
||||
'word_app' => ( \is_string( $appOrPlugin ) ) ? $appOrPlugin : NULL,
|
||||
'word_plugin' => ( \is_numeric( $appOrPlugin ) ) ? $appOrPlugin : NULL,
|
||||
'word_key' => $key,
|
||||
'word_default' => $default,
|
||||
'word_custom' => $value,
|
||||
'word_js' => $js,
|
||||
'word_export' => FALSE,
|
||||
) );
|
||||
'word_is_custom'=> ( $isCustom === TRUE )
|
||||
);
|
||||
|
||||
try
|
||||
{
|
||||
\IPS\Db::i()->replace( 'core_sys_lang_words', $insert );
|
||||
}
|
||||
catch( \IPS\Db\Exception $e )
|
||||
{
|
||||
/* The upgrader can try to insert custom language strings before the word_is_custom column is added in 4.5 */
|
||||
if( $e->getCode() == 1054 )
|
||||
{
|
||||
unset( $insert['word_is_custom'] );
|
||||
|
||||
\IPS\Db::i()->replace( 'core_sys_lang_words', $insert );
|
||||
}
|
||||
else
|
||||
{
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( isset( static::$multitons[ $langId ] ) )
|
||||
@@ -313,6 +370,11 @@ class _Lang extends \IPS\Node\Model
|
||||
{
|
||||
unset( \IPS\Data\Store::i()->listFormats );
|
||||
}
|
||||
|
||||
if ( \substr( $key, 0, 10 ) === 'num_short_' )
|
||||
{
|
||||
unset( \IPS\Data\Store::i()->shortFormats );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -472,7 +534,7 @@ class _Lang extends \IPS\Node\Model
|
||||
$result = setlocale( LC_ALL, $this->short );
|
||||
|
||||
/* Some locales in some PHP versions break things drastically */
|
||||
if( in_array( 'ips\\db\\_select', get_declared_classes() ) AND !in_array( 'IPS\\Db\\_Select', get_declared_classes() ) )
|
||||
if( \in_array( 'ips\\db\\_select', get_declared_classes() ) AND !\in_array( 'IPS\\Db\\_Select', get_declared_classes() ) )
|
||||
{
|
||||
setlocale( LC_CTYPE, 'en_US.UTF-8' );
|
||||
}
|
||||
@@ -502,9 +564,9 @@ class _Lang extends \IPS\Node\Model
|
||||
if( mb_strpos( $locale, '=' ) !== FALSE )
|
||||
{
|
||||
$parts = explode( "=", $locale );
|
||||
if( in_array( $parts[0], array( 'LC_ALL', 'LC_COLLATE', 'LC_CTYPE', 'LC_MONETARY', 'LC_NUMERIC', 'LC_TIME' ) ) )
|
||||
if( \in_array( $parts[0], array( 'LC_ALL', 'LC_COLLATE', 'LC_CTYPE', 'LC_MONETARY', 'LC_NUMERIC', 'LC_TIME' ) ) )
|
||||
{
|
||||
setlocale( constant( $parts[0] ), $parts[1] );
|
||||
setlocale( \constant( $parts[0] ), $parts[1] );
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -553,7 +615,7 @@ class _Lang extends \IPS\Node\Model
|
||||
}
|
||||
|
||||
/* And only if iconv() exists */
|
||||
if( !function_exists( 'iconv' ) )
|
||||
if( !\function_exists( 'iconv' ) )
|
||||
{
|
||||
return $text;
|
||||
}
|
||||
@@ -564,8 +626,22 @@ class _Lang extends \IPS\Node\Model
|
||||
return $text;
|
||||
}
|
||||
|
||||
/* Convert the codepage to UTF-8 and return */
|
||||
return iconv( "CP" . $this->codepage, "UTF-8", $text );
|
||||
/* Convert the codepage to UTF-8 (if it is valid) and return */
|
||||
try
|
||||
{
|
||||
if( ( $converted = iconv( "CP" . $this->codepage, "UTF-8", $text ) ) !== FALSE )
|
||||
{
|
||||
return $converted;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new \ErrorException;
|
||||
}
|
||||
}
|
||||
catch( \ErrorException $e )
|
||||
{
|
||||
return $text;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -627,11 +703,11 @@ class _Lang extends \IPS\Node\Model
|
||||
$return = array();
|
||||
$keysToLoad = array();
|
||||
|
||||
if ( is_array( $key ) )
|
||||
if ( \is_array( $key ) )
|
||||
{
|
||||
foreach( $key as $k )
|
||||
{
|
||||
if ( in_array( $k, array_keys( $this->words ) ) )
|
||||
if ( \in_array( $k, array_keys( $this->words ), true ) )
|
||||
{
|
||||
$return[ $k ] = $this->words[ $k ];
|
||||
}
|
||||
@@ -641,7 +717,7 @@ class _Lang extends \IPS\Node\Model
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! count( $keysToLoad ) )
|
||||
if ( !\count( $keysToLoad ) )
|
||||
{
|
||||
return $return;
|
||||
}
|
||||
@@ -665,11 +741,11 @@ class _Lang extends \IPS\Node\Model
|
||||
}
|
||||
|
||||
/* If we're using an array, fill any missings strings with NULL to prevent duplicate queries */
|
||||
if ( is_array( $key ) )
|
||||
if ( \is_array( $key ) )
|
||||
{
|
||||
foreach( $key as $k )
|
||||
{
|
||||
if ( ! in_array( $k, $return ) and ! array_key_exists( $k, $this->words ) )
|
||||
if ( !\in_array( $k, $return ) and ! array_key_exists( $k, $this->words ) )
|
||||
{
|
||||
$return[ $k ] = NULL;
|
||||
$this->words[ $k ] = NULL;
|
||||
@@ -677,12 +753,12 @@ class _Lang extends \IPS\Node\Model
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! count( $return ) )
|
||||
if ( !\count( $return ) )
|
||||
{
|
||||
throw new \UnderflowException( ( is_string( $key ) ? 'lang_not_exists__' . $key : 'lang_not_exists__' . implode( ',', $key ) ) );
|
||||
throw new \UnderflowException( ( \is_string( $key ) ? 'lang_not_exists__' . $key : 'lang_not_exists__' . implode( ',', $key ) ) );
|
||||
}
|
||||
|
||||
return is_string( $key ) ? $this->words[ $key ] : $return;
|
||||
return \is_string( $key ) ? $this->words[ $key ] : $return;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -711,7 +787,7 @@ class _Lang extends \IPS\Node\Model
|
||||
$this->outputStack[ $id ]['key'] = $key;
|
||||
$this->outputStack[ $id ]['options'] = $options;
|
||||
$this->outputStack[ $id ]['vle'] = $vle;
|
||||
|
||||
|
||||
/* Return */
|
||||
return $id;
|
||||
}
|
||||
@@ -721,17 +797,20 @@ class _Lang extends \IPS\Node\Model
|
||||
*
|
||||
* @param string $string Language string to pluralize
|
||||
* @param array $params Parameters to pluraizlie with
|
||||
* @param string $formatter Number formatter (default is formatNumber)
|
||||
* @return string
|
||||
* @note You can use the following wildcards to do special things
|
||||
* @li ? is a fallback, so anything not matched will use it
|
||||
* @li * is a beginning wildcard, so anything that ENDS with the number supplied will match
|
||||
* @li % is an ending wildcard, so anything that BEGINS with the number supplied will match
|
||||
* @li # (optional) will be replaced with the actual value
|
||||
* @example {# [1:test][*2:tests][%3:no tests][?:finals]} will result in 1 test, 2 tests, 12 tests, 3 no tests, 35 no tests, 8 finals
|
||||
* @example {!1#[1:January][2:February][3:March][4:April][5:May][6:June][7:July][8:August][9:September][10:October][11:November][12:December]} {0#}
|
||||
* @example {!#[?:%s liked]} %s
|
||||
* @code
|
||||
{# [1:test][*2:tests][%3:no tests][?:finals]} will result in 1 test, 2 tests, 12 tests, 3 no tests, 35 no tests, 8 finals
|
||||
{!1#[1:January][2:February][3:March][4:April][5:May][6:June][7:July][8:August][9:September][10:October][11:November][12:December]} {0#}
|
||||
{!#[?:%s liked]} %s
|
||||
* @endcode
|
||||
*/
|
||||
public function pluralize( $string, $params )
|
||||
public function pluralize( $string, $params, $formatter=NULL )
|
||||
{
|
||||
$i = 0;
|
||||
$openCurly = '--' . mt_rand() . '--';
|
||||
@@ -746,7 +825,7 @@ class _Lang extends \IPS\Node\Model
|
||||
}, $string );
|
||||
|
||||
$numberFormatter = array( $this, 'formatNumber' );
|
||||
return preg_replace_callback( '/\{!?(\d+?)?#(.*?)\}/', function( $format ) use ( $params, $i, $numberFormatter, $openCurly, $closeCurly )
|
||||
return preg_replace_callback( '/\{!?(\d+?)?#(.*?)\}/', function( $format ) use ( $params, $i, $numberFormatter, $openCurly, $closeCurly, $formatter )
|
||||
{
|
||||
$originalNumber = $format[1];
|
||||
if ( !$format[1] or $format[1] == '!' )
|
||||
@@ -756,11 +835,12 @@ class _Lang extends \IPS\Node\Model
|
||||
}
|
||||
|
||||
/* Format now so that 0 is really 0 and not '' or null */
|
||||
$params[ $format[1] ] = call_user_func( $numberFormatter, $params[ $format[1] ] );
|
||||
$preFormattedNumber = $params[ $format[1] ]; // We need 7300 later, not 7,3000
|
||||
$params[ $format[1] ] = $numberFormatter( $params[ $format[1] ] );
|
||||
$fallback = NULL;
|
||||
$value = NULL;
|
||||
$token = '--' . mt_rand() . '--';
|
||||
|
||||
|
||||
/* We want to ensure that manually escaped # are not switched */
|
||||
$format[2] = str_replace( '\#', $token, $format[2] );
|
||||
|
||||
@@ -791,9 +871,10 @@ class _Lang extends \IPS\Node\Model
|
||||
}
|
||||
}
|
||||
|
||||
$finalFormattedNumber = ( $formatter == 'short' ) ? $this->formatNumberShort( $preFormattedNumber ) : $params[ $format[1] ];
|
||||
$return = rtrim( ltrim( $format[0], '{' ), '}' );
|
||||
$return = str_replace( "!{$originalNumber}#", '', $return );
|
||||
$return = str_replace( array( "{$format[1]}#", '#' ), $params[ $format[1] ], $return );
|
||||
$return = str_replace( array( "{$format[1]}#", '#' ), $finalFormattedNumber, $return );
|
||||
$return = preg_replace( '/\[.+\]/', ( $value === NULL ? $fallback : $value ), $return );
|
||||
|
||||
$return = str_replace( $token, '#', $return );
|
||||
@@ -811,9 +892,84 @@ class _Lang extends \IPS\Node\Model
|
||||
*/
|
||||
public function formatNumber( $number, $decimals=0 )
|
||||
{
|
||||
return number_format( floatval( $number ), floatval( $decimals ), $this->locale['decimal_point'], $this->locale['thousands_sep'] );
|
||||
return number_format( \floatval( $number ), \floatval( $decimals ), $this->locale['decimal_point'], $this->locale['thousands_sep'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a number in a short format (e.g. 1.6k, instead of 1,558)
|
||||
*
|
||||
* @param number $number The number to format
|
||||
* @param int $postDecimal Number of numbers to show after the decimal (1.1k, 1.12k, etc)
|
||||
* @return string
|
||||
*/
|
||||
public function formatNumberShort( $number, $postDecimal=1 )
|
||||
{
|
||||
$lang = NULL;
|
||||
$origNumber = $number;
|
||||
|
||||
if ( \IPS\IN_DEV )
|
||||
{
|
||||
foreach( array( 'num_short_billion', 'num_short_million', 'num_short_thousand' ) as $word )
|
||||
{
|
||||
$format[ $word ] = $this->words[ $word ];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( !isset( \IPS\Data\Store::i()->shortFormats ) )
|
||||
{
|
||||
$formats = array();
|
||||
foreach ( \IPS\Db::i()->select( array( 'lang_id', 'word_key', 'word_custom', 'word_default' ), 'core_sys_lang_words', array( \IPS\Db::i()->in('word_key', array( 'num_short_billion', 'num_short_million', 'num_short_thousand' ) ) ) ) as $row )
|
||||
{
|
||||
$formats[ $row['lang_id'] ][ $row['word_key'] ] = $row['word_custom'] ?: $row['word_default'];
|
||||
}
|
||||
\IPS\Data\Store::i()->shortFormats = $formats;
|
||||
}
|
||||
|
||||
$format = \IPS\Data\Store::i()->shortFormats[ $this->id ];
|
||||
}
|
||||
|
||||
/* Hundreds */
|
||||
if ( $number >= 1000 )
|
||||
{
|
||||
$number = round( $number, -3 + $postDecimal );
|
||||
}
|
||||
|
||||
/* 1 billion or more */
|
||||
if ( $number >= 1000000000 )
|
||||
{
|
||||
$number = number_format( $number / 1000000000, $postDecimal, $this->locale['decimal_point'], $this->locale['thousands_sep'] );
|
||||
$lang = 'num_short_billion';
|
||||
}
|
||||
|
||||
/* all the millions */
|
||||
else if ( $number >= 1000000 )
|
||||
{
|
||||
$number = number_format( $number / 1000000, $postDecimal, $this->locale['decimal_point'], $this->locale['thousands_sep'] );
|
||||
$lang = 'num_short_million';
|
||||
}
|
||||
|
||||
/* 1,000 to 999,000 */
|
||||
else if ( $number >= 1000 )
|
||||
{
|
||||
$number = number_format( $number / 1000, $postDecimal, $this->locale['decimal_point'], $this->locale['thousands_sep'] );
|
||||
$lang = 'num_short_thousand';
|
||||
}
|
||||
|
||||
// Ensure we don't return 1.0, but rather 1
|
||||
if ( \substr( $number, -2 ) === $this->locale['decimal_point'] . "0" )
|
||||
{
|
||||
$number = \substr($number, 0, -2);
|
||||
}
|
||||
|
||||
if ( $lang )
|
||||
{
|
||||
return sprintf( $format[ $lang ], $number );
|
||||
}
|
||||
|
||||
return $origNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format List
|
||||
* Takes an array and returns a string, appropriate for the language (e.g. "a, b and c")
|
||||
@@ -859,7 +1015,7 @@ class _Lang extends \IPS\Node\Model
|
||||
preg_match( '/(^|^(.+?)\s)\b(a)\b(.+?\s?)\b(b)\b(.+?\s?)\b(c)\b(.+?)?$/i', $format, $matches );
|
||||
|
||||
$return = $matches[1];
|
||||
for ( $i = 0; $i<count( $items ); $i++ )
|
||||
for ( $i = 0; $i<\count( $items ); $i++ )
|
||||
{
|
||||
/* Pluralize can be used after, so avoid triggering the '#', '#[...]' syntax there */
|
||||
$items[ $i ] = preg_replace( '/#(\s|\[)/', '\#\1', $items[ $i ] );
|
||||
@@ -868,7 +1024,7 @@ class _Lang extends \IPS\Node\Model
|
||||
{
|
||||
$upper = ( $matches[3] === 'A' );
|
||||
}
|
||||
elseif ( $i == count( $items ) - 1 )
|
||||
elseif ( $i == \count( $items ) - 1 )
|
||||
{
|
||||
$upper = ( $matches[7] === 'C' );
|
||||
}
|
||||
@@ -879,11 +1035,11 @@ class _Lang extends \IPS\Node\Model
|
||||
|
||||
$return .= ( $upper ? ucfirst( $items[ $i ] ) : $items[ $i ] );
|
||||
|
||||
if ( $i == count( $items ) - 2 )
|
||||
if ( $i == \count( $items ) - 2 )
|
||||
{
|
||||
$return .= $matches[6];
|
||||
}
|
||||
elseif ( $i != count( $items ) - 1 )
|
||||
elseif ( $i != \count( $items ) - 1 )
|
||||
{
|
||||
$return .= $matches[4];
|
||||
}
|
||||
@@ -1036,7 +1192,8 @@ class _Lang extends \IPS\Node\Model
|
||||
/**
|
||||
* Add locale field to form
|
||||
*
|
||||
* @param \IPS\Helpers\Form $form The form
|
||||
* @param \IPS\Helpers\Form $form The form
|
||||
* @param string $current The current locale
|
||||
* @return void
|
||||
*/
|
||||
public static function localeField( &$form, $current='en_US' )
|
||||
@@ -1068,7 +1225,7 @@ class _Lang extends \IPS\Node\Model
|
||||
$form->hiddenValues['lang_short'] = 'x';
|
||||
}
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'lang_short_custom', !in_array( $current, $commonLocales ) ? $current : NULL, FALSE, array( 'placeholder' => 'en_US' ), '\IPS\Lang::validateLocale', NULL, NULL, 'locale_custom' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'lang_short_custom', !\in_array( $current, $commonLocales ) ? $current : NULL, FALSE, array( 'placeholder' => 'en_US' ), '\IPS\Lang::validateLocale', NULL, NULL, 'locale_custom' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1090,9 +1247,10 @@ class _Lang extends \IPS\Node\Model
|
||||
|
||||
if( isset( $values['lang_short'] ) )
|
||||
{
|
||||
$currentLocale = setlocale( LC_ALL, '0' );
|
||||
$currentLocale = setlocale( LC_ALL, '0' );
|
||||
$localesToTest = ( mb_strtolower( mb_substr( PHP_OS, 0, 3 ) ) !== 'win' ) ? array( "{$values['lang_short']}.UTF-8", "{$values['lang_short']}.UTF8" ) : array( $values['lang_short'] );
|
||||
|
||||
foreach ( array( "{$values['lang_short']}.UTF-8", "{$values['lang_short']}.UTF8" ) as $l )
|
||||
foreach ( $localesToTest as $l )
|
||||
{
|
||||
$test = setlocale( LC_ALL, $l );
|
||||
if ( $test !== FALSE )
|
||||
@@ -1115,7 +1273,6 @@ class _Lang extends \IPS\Node\Model
|
||||
{
|
||||
$this->enabled = TRUE;
|
||||
\IPS\Db::i()->update( 'core_sys_lang', array( 'lang_default' => 0 ) );
|
||||
unset( \IPS\Data\Store::i()->languages );
|
||||
}
|
||||
|
||||
return $values;
|
||||
@@ -1141,6 +1298,54 @@ class _Lang extends \IPS\Node\Model
|
||||
{
|
||||
return "ipsFlag ipsFlag-{$this->getCountry()}";
|
||||
}
|
||||
|
||||
/**
|
||||
* [Node] Return the custom badge for each row
|
||||
*
|
||||
* @return NULL|array Null for no badge, or an array of badge data (0 => CSS class type, 1 => language string, 2 => optional raw HTML to show instead of language string)
|
||||
*/
|
||||
protected function get__badge()
|
||||
{
|
||||
/* Is there an update to show? */
|
||||
$badge = NULL;
|
||||
|
||||
if ( $this->update_data )
|
||||
{
|
||||
$data = json_decode( $this->update_data, TRUE );
|
||||
|
||||
if( !empty( $data['longversion'] ) AND $data['longversion'] > $this->version_long )
|
||||
{
|
||||
$released = NULL;
|
||||
|
||||
if( $data['released'] AND \intval( $data['released'] ) == $data['released'] AND \strlen( $data['released'] ) == 10 )
|
||||
{
|
||||
$released = (string) \IPS\DateTime::ts( $data['released'] )->localeDate();
|
||||
}
|
||||
else if( $data['released'] )
|
||||
{
|
||||
$released = $data['released'];
|
||||
}
|
||||
|
||||
$badge = array(
|
||||
0 => 'new',
|
||||
1 => '',
|
||||
2 => \IPS\Theme::i()->getTemplate( 'global', 'core' )->updatebadge( $data['version'], $data['updateurl'], $released )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $badge;
|
||||
}
|
||||
|
||||
/**
|
||||
* [Node] Get Description
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function get__description()
|
||||
{
|
||||
return \IPS\Theme::i()->getTemplate( 'customization', 'core' )->langDescription( $this );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return country code
|
||||
@@ -1234,7 +1439,7 @@ class _Lang extends \IPS\Node\Model
|
||||
|
||||
$buttons = array_merge( $buttons, parent::getButtons( $url, $subnode ) );
|
||||
|
||||
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'languages', 'lang_words' ) )
|
||||
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'languages', 'lang_words' ) AND !$this->marketplace_id )
|
||||
{
|
||||
$buttons['upload'] = array(
|
||||
'icon' => 'upload',
|
||||
@@ -1268,7 +1473,7 @@ class _Lang extends \IPS\Node\Model
|
||||
$buttons['devimport'] = array(
|
||||
'icon' => 'cogs',
|
||||
'title' => 'lang_dev_import',
|
||||
'link' => \IPS\Http\Url::internal( "app=core&module=languages&controller=languages&do=devimport&id={$this->_id}" ),
|
||||
'link' => \IPS\Http\Url::internal( "app=core&module=languages&controller=languages&do=devimport&id={$this->_id}" )->csrf(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1391,24 +1596,43 @@ class _Lang extends \IPS\Node\Model
|
||||
public function set_short( $val )
|
||||
{
|
||||
$oldLocale = setlocale( LC_ALL, '0' );
|
||||
setlocale( LC_ALL, $val );
|
||||
$result = setlocale( LC_ALL, $val );
|
||||
|
||||
/* Some locales in some PHP versions break things drastically */
|
||||
if( in_array( 'ips\\db\\_select', get_declared_classes() ) )
|
||||
if( \in_array( 'ips\\db\\_select', \get_declared_classes() ) )
|
||||
{
|
||||
setlocale( LC_CTYPE, 'en_US.UTF-8' );
|
||||
}
|
||||
|
||||
/* If this is Windows, store the codepage as we will need it again later */
|
||||
if( mb_strtoupper( mb_substr( PHP_OS, 0, 3 ) ) === 'WIN' )
|
||||
{
|
||||
$codepage = preg_replace( "/^(.+?)\.(.+?)$/i", "$2", $result );
|
||||
|
||||
if( $codepage !== $result )
|
||||
{
|
||||
$this->codepage = $codepage;
|
||||
}
|
||||
}
|
||||
|
||||
$this->locale = localeconv();
|
||||
|
||||
foreach( $this->locale as $k => $v )
|
||||
{
|
||||
if( \is_string( $v ) )
|
||||
{
|
||||
$this->locale[ $k ] = $this->convertString( $v );
|
||||
}
|
||||
}
|
||||
|
||||
foreach( explode( ";", $oldLocale ) as $locale )
|
||||
{
|
||||
if( mb_strpos( $locale, '=' ) !== FALSE )
|
||||
{
|
||||
$parts = explode( "=", $locale );
|
||||
if( in_array( $parts[0], array( 'LC_ALL', 'LC_COLLATE', 'LC_CTYPE', 'LC_MONETARY', 'LC_NUMERIC', 'LC_TIME' ) ) )
|
||||
if( \in_array( $parts[0], array( 'LC_ALL', 'LC_COLLATE', 'LC_CTYPE', 'LC_MONETARY', 'LC_NUMERIC', 'LC_TIME' ) ) )
|
||||
{
|
||||
setlocale( constant( $parts[0] ), $parts[1] );
|
||||
setlocale( \constant( $parts[0] ), $parts[1] );
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1417,18 +1641,7 @@ class _Lang extends \IPS\Node\Model
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save Changed Columns
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
parent::save();
|
||||
unset( \IPS\Data\Store::i()->languages );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Delete Record
|
||||
*
|
||||
@@ -1438,7 +1651,6 @@ class _Lang extends \IPS\Node\Model
|
||||
{
|
||||
parent::delete();
|
||||
\IPS\Db::i()->delete( 'core_sys_lang_words', array( 'lang_id=?', $this->id ) );
|
||||
unset( \IPS\Data\Store::i()->languages );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1450,7 +1662,7 @@ class _Lang extends \IPS\Node\Model
|
||||
public function parseOutputForDisplay( &$output )
|
||||
{
|
||||
/* Do we actually have any? */
|
||||
if( !count( $this->outputStack ) )
|
||||
if( !\count( $this->outputStack ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -1466,7 +1678,7 @@ class _Lang extends \IPS\Node\Model
|
||||
}
|
||||
}
|
||||
|
||||
if( !$this->wordsLoaded === TRUE AND count( $keys ) and !\IPS\IN_DEV )
|
||||
if( !$this->wordsLoaded === TRUE AND \count( $keys ) and !\IPS\IN_DEV )
|
||||
{
|
||||
foreach( \IPS\Db::i()->select( 'word_key, word_default, word_custom', 'core_sys_lang_words', array( "lang_id=? AND word_key IN(" . implode( ",", $keys ) . ") and word_js=0", $this->id ) ) as $row )
|
||||
{
|
||||
@@ -1537,12 +1749,12 @@ class _Lang extends \IPS\Node\Model
|
||||
/**
|
||||
* Strip VLE tags, useful for AJAX responses where you can't edit the string anyways
|
||||
*
|
||||
* @param string $output The output string
|
||||
* @return string
|
||||
* @param string|array $output The output string
|
||||
* @return string|array
|
||||
*/
|
||||
public function stripVLETags( $output )
|
||||
{
|
||||
if( !is_array( $output ) )
|
||||
if( !\is_array( $output ) )
|
||||
{
|
||||
return preg_replace( "/#VLE#.+?#\!#\[(.+?)\]#\!##/", "$1", $output );
|
||||
}
|
||||
@@ -1556,20 +1768,6 @@ class _Lang extends \IPS\Node\Model
|
||||
|
||||
return $replacement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert <wbr> tags into long words
|
||||
*
|
||||
* @deprecated This is left here to prevent breaking third party addons, but no longer breaks words.
|
||||
* @note You should use a container div with classes ipsContained and ipsType_break instead.
|
||||
* Often you may want to also use ipsTruncate and ipsTruncate_line on an inline element (i.e. an <a> tag) wrapping the text as well to truncate.
|
||||
* @param string $data The string
|
||||
* @return string
|
||||
*/
|
||||
public static function wordbreak( $data )
|
||||
{
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace values in array recursively
|
||||
@@ -1585,8 +1783,13 @@ class _Lang extends \IPS\Node\Model
|
||||
/* Reduce the number of str_replace with arrays */
|
||||
static $replaceTable = array();
|
||||
|
||||
if ( ! is_array( $haystack ) )
|
||||
if ( !\is_array( $haystack ) )
|
||||
{
|
||||
if( !\is_string( $haystack ) )
|
||||
{
|
||||
return $haystack;
|
||||
}
|
||||
|
||||
$hash = md5( json_encode($find) . json_encode($replace) . $haystack );
|
||||
|
||||
if ( isset( $replaceTable[ $hash ] ) )
|
||||
@@ -1672,6 +1875,14 @@ class _Lang extends \IPS\Node\Model
|
||||
},
|
||||
$replacement
|
||||
);
|
||||
$replacement = preg_replace_callback(
|
||||
"/{internal\.(.+?)\.csrf}/",
|
||||
function ( $matches )
|
||||
{
|
||||
return \IPS\Http\Url::internal( $matches[ 1 ] )->csrf();
|
||||
},
|
||||
$replacement
|
||||
);
|
||||
$replacement = preg_replace_callback(
|
||||
"/{internal\.(.+?)}/",
|
||||
function ( $matches )
|
||||
@@ -1681,7 +1892,16 @@ class _Lang extends \IPS\Node\Model
|
||||
$replacement
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/* We do pluralize first in case you have something like "{!#[1:%s has][?:%s have]} %s: %s" */
|
||||
if ( !empty( $values[ 'options' ][ 'pluralize' ] ) )
|
||||
{
|
||||
$replacement = $this->pluralize(
|
||||
$replacement,
|
||||
$values[ 'options' ][ 'pluralize' ],
|
||||
isset( $values[ 'options' ]['format'] ) ? $values[ 'options' ]['format'] : NULL
|
||||
);
|
||||
}
|
||||
|
||||
$sprintf = array();
|
||||
|
||||
@@ -1707,7 +1927,7 @@ class _Lang extends \IPS\Node\Model
|
||||
{
|
||||
return htmlspecialchars( trim( $val ), ENT_QUOTES | ENT_DISALLOWED, 'UTF-8', false );
|
||||
},
|
||||
( is_array(
|
||||
( \is_array(
|
||||
$values[ 'options' ][ 'sprintf' ]
|
||||
) ? $values[ 'options' ][ 'sprintf' ] : explode( ',', $values[ 'options' ][ 'sprintf' ] ) )
|
||||
);
|
||||
@@ -1717,7 +1937,7 @@ class _Lang extends \IPS\Node\Model
|
||||
{
|
||||
$sprintf = array_merge(
|
||||
$sprintf,
|
||||
( is_array(
|
||||
( \is_array(
|
||||
$values[ 'options' ][ 'htmlsprintf' ]
|
||||
) ? $values[ 'options' ][ 'htmlsprintf' ] : explode(
|
||||
',',
|
||||
@@ -1726,7 +1946,7 @@ class _Lang extends \IPS\Node\Model
|
||||
);
|
||||
}
|
||||
|
||||
if ( count( $sprintf ) )
|
||||
if ( \count( $sprintf ) )
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -1743,17 +1963,10 @@ class _Lang extends \IPS\Node\Model
|
||||
// If there's the wrong number of parameters because the translator's done it wrong, we can just use empty strings for replacements
|
||||
}
|
||||
}
|
||||
|
||||
if ( !empty( $values[ 'options' ][ 'pluralize' ] ) )
|
||||
{
|
||||
$replacement = $this->pluralize(
|
||||
$replacement,
|
||||
$values[ 'options' ][ 'pluralize' ]
|
||||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$values['options']['escape'] = TRUE; // Prevent accidentally introducing an XSS vulnerability by using raw strings where something expects a language string
|
||||
$replacement = $values[ 'key' ];
|
||||
}
|
||||
}
|
||||
@@ -1800,6 +2013,16 @@ class _Lang extends \IPS\Node\Model
|
||||
$replacement = strip_tags( $replacement );
|
||||
}
|
||||
|
||||
if ( isset( $values[ 'options' ][ 'urlencode' ] ) )
|
||||
{
|
||||
$replacement = urlencode( $replacement );
|
||||
}
|
||||
|
||||
if ( isset( $values[ 'options' ][ 'rawurlencode' ] ) )
|
||||
{
|
||||
$replacement = rawurlencode( $replacement );
|
||||
}
|
||||
|
||||
if ( isset( $values[ 'options' ][ 'escape' ] ) )
|
||||
{
|
||||
$replacement = htmlspecialchars( $replacement, ENT_QUOTES | ENT_DISALLOWED, 'UTF-8', false );
|
||||
@@ -1826,6 +2049,11 @@ class _Lang extends \IPS\Node\Model
|
||||
$replacement = sprintf( $values[ 'options' ][ 'returnInto' ], $replacement );
|
||||
}
|
||||
|
||||
if ( isset( $values[ 'options' ][ 'removeLazyLoad' ] ) )
|
||||
{
|
||||
$replacement = \IPS\Text\Parser::removeLazyLoad( $replacement );
|
||||
}
|
||||
|
||||
$replacements[ $key ] = $replacement;
|
||||
}
|
||||
|
||||
@@ -1837,4 +2065,42 @@ class _Lang extends \IPS\Node\Model
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Word Form
|
||||
*
|
||||
* @param \IPS\Helpers\Form|NULL An existing form object or NULL to create new
|
||||
* @return void
|
||||
*/
|
||||
public static function wordForm( ?\IPS\Helpers\Form &$form = NULL ): void
|
||||
{
|
||||
if ( $form === NULL )
|
||||
{
|
||||
$form = new \IPS\Helpers\Form;
|
||||
}
|
||||
if ( \IPS\Application::appIsEnabled( 'cms' ) )
|
||||
{
|
||||
$form->addMessage( 'add_word_reason_pages', 'ipsMessage ipsMessage_info' );
|
||||
}
|
||||
else
|
||||
{
|
||||
$form->addMessage( 'add_word_reason', 'ipsMessage ipsMessage_info' );
|
||||
}
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'word_key', NULL, TRUE, array( 'regex' => '/^([A-Z_][A-Z0-9_]+?)$/i', 'placeholder' => 'my_custom_phrase' ), function( $val ) {
|
||||
try
|
||||
{
|
||||
/* This is a generic check - if key exists in *any* language, don't allow. */
|
||||
\IPS\Db::i()->select( '*', 'core_sys_lang_words', array( "word_key=?", $val ) )->first();
|
||||
|
||||
/* Here? No excepton thrown so error */
|
||||
throw new \DomainException( 'word_key_exists' );
|
||||
}
|
||||
catch( \UnderflowException $e ) {}
|
||||
} ) );
|
||||
$form->add( new \IPS\Helpers\Form\TextArea( 'word_default', NULL, TRUE, array( 'placeholder' => \IPS\Member::loggedIn()->language()->addToStack( 'my_custom_phrase' ) ) ) );
|
||||
if ( \count( static::languages() ) > 1 )
|
||||
{
|
||||
$form->add( new \IPS\Helpers\Form\Translatable( 'word_custom', NULL, FALSE, array( 'textArea' => TRUE ) ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user