init commit

This commit is contained in:
2025-02-23 20:52:25 +01:00
parent 5b272d6536
commit 9971cd719b
1719 changed files with 281982 additions and 2 deletions

View File

@ -0,0 +1,503 @@
<?php
namespace XFramework;
/**
* Roundcube Plus Framework plugin.
*
* This class handles the date/time formats and their conversions to the formats used by different systems/components.
*
* Copyright 2016, Tecorama LLC.
*
* @license Commercial. See the LICENSE file for details.
*/
require_once "Singleton.php";
class Format
{
use Singleton;
const DECIMAL_SEPARATOR_SYMBOL = 0;
const GROUPING_SEPARATOR_SYMBOL = 1;
const MONETARY_SEPARATOR_SYMBOL = 2;
const MONETARY_GROUPING_SEPARATOR_SYMBOL = 3;
private $rcmail;
private $dateFormats = [];
private $timeFormats = [];
private $dmFormats = [];
private $separators = [
'sq_AL' => [0 => ',', 1 => ' ', 2 => ',', 3 => ' '],
'ar' => [0 => '٫', 1 => '٬', 2 => '٫', 3 => '٬'],
'ar_SA' => [0 => '٫', 1 => '٬', 2 => '٫', 3 => '٬'],
'hy_AM' => [0 => ',', 1 => '.', 2 => ',', 3 => '.'],
'ast' => [0 => '.', 1 => ',', 2 => '.', 3 => ','],
'az_AZ' => [0 => ',', 1 => '.', 2 => ',', 3 => '.'],
'eu_ES' => [0 => ',', 1 => '.', 2 => ',', 3 => '.'],
'be_BE' => [0 => ',', 1 => ' ', 2 => ',', 3 => ' '],
'bn_BD' => [0 => '.', 1 => ',', 2 => '.', 3 => ','],
'bs_BA' => [0 => ',', 1 => '.', 2 => ',', 3 => '.'],
'br' => [0 => ',', 1 => ' ', 2 => ',', 3 => ' '],
'bg_BG' => [0 => ',', 1 => ' ', 2 => ',', 3 => ' '],
'ca_ES' => [0 => ',', 1 => '.', 2 => ',', 3 => '.'],
'zh_CN' => [0 => '.', 1 => ',', 2 => '.', 3 => ','],
'zh_TW' => [0 => '.', 1 => ',', 2 => '.', 3 => ','],
'hr_HR' => [0 => ',', 1 => '.', 2 => ',', 3 => '.'],
'cs_CZ' => [0 => ',', 1 => ' ', 2 => ',', 3 => ' '],
'da_DK' => [0 => ',', 1 => '.', 2 => ',', 3 => '.'],
'fa_AF' => [0 => '٫', 1 => '٬', 2 => '٫', 3 => '٬'],
'de_DE' => [0 => ',', 1 => '.', 2 => ',', 3 => '.'],
'de_CH' => [0 => '.', 1 => "'", 2 => '.', 3 => "'"],
'nl_NL' => [0 => ',', 1 => '.', 2 => ',', 3 => '.'],
'en_CA' => [0 => '.', 1 => ',', 2 => '.', 3 => ','],
'en_GB' => [0 => '.', 1 => ',', 2 => '.', 3 => ','],
'en_US' => [0 => '.', 1 => ',', 2 => '.', 3 => ','],
'eo' => [0 => ',', 1 => ' ', 2 => ',', 3 => ' '],
'et_EE' => [0 => ',', 1 => ' ', 2 => ',', 3 => ' '],
'fo_FO' => [0 => ',', 1 => '.', 2 => ',', 3 => '.'],
'fi_FI' => [0 => ',', 1 => ' ', 2 => ',', 3 => ' '],
'nl_BE' => [0 => ',', 1 => '.', 2 => ',', 3 => '.'],
'fr_FR' => [0 => ',', 1 => ' ', 2 => ',', 3 => ' '],
'gl_ES' => [0 => ',', 1 => '.', 2 => ',', 3 => '.'],
'ka_GE' => [0 => ',', 1 => ' ', 2 => ',', 3 => ' '],
'el_GR' => [0 => ',', 1 => '.', 2 => ',', 3 => '.'],
'he_IL' => [0 => '.', 1 => ',', 2 => '.', 3 => ','],
'hi_IN' => [0 => '.', 1 => ',', 2 => '.', 3 => ','],
'hu_HU' => [0 => ',', 1 => ' ', 2 => ',', 3 => ' '],
'is_IS' => [0 => ',', 1 => '.', 2 => ',', 3 => '.'],
'id_ID' => [0 => ',', 1 => '.', 2 => ',', 3 => '.'],
'ia' => [0 => '.', 1 => ',', 2 => '.', 3 => ','],
'ga_IE' => [0 => '.', 1 => ',', 2 => '.', 3 => ','],
'it_IT' => [0 => ',', 1 => '.', 2 => ',', 3 => '.'],
'ja_JP' => [0 => '.', 1 => ',', 2 => '.', 3 => ','],
'km_KH' => [0 => ',', 1 => '.', 2 => ',', 3 => '.'],
'kn_IN' => [0 => '.', 1 => ',', 2 => '.', 3 => ','],
'ko_KR' => [0 => '.', 1 => ',', 2 => '.', 3 => ','],
'ku' => [0 => '.', 1 => ',', 2 => '.', 3 => ','],
'lv_LV' => [0 => ',', 1 => ' ', 2 => ',', 3 => ' '],
'lt_LT' => [0 => ',', 1 => ' ', 2 => ',', 3 => ' '],
'lb_LU' => [0 => '.', 1 => ',', 2 => '.', 3 => ','],
'mk_MK' => [0 => ',', 1 => '.', 2 => ',', 3 => '.'],
'mn_MN' => [0 => '.', 1 => ',', 2 => '.', 3 => ','],
'ms_MY' => [0 => '.', 1 => ',', 2 => '.', 3 => ','],
'ml_IN' => [0 => '.', 1 => ',', 2 => '.', 3 => ','],
'mr_IN' => [0 => '.', 1 => ',', 2 => '.', 3 => ','],
'ne_NP' => [0 => '.', 1 => ',', 2 => '.', 3 => ','],
'nb_NO' => [0 => ',', 1 => ' ', 2 => ',', 3 => ' '],
'nn_NO' => [0 => ',', 1 => ' ', 2 => ',', 3 => ' '],
'ps' => [0 => '٫', 1 => '٬', 2 => '٫', 3 => '٬'],
'fa_IR' => [0 => '٫', 1 => '٬', 2 => '٫', 3 => '٬'],
'pl_PL' => [0 => ',', 1 => ' ', 2 => ',', 3 => ' '],
'pt_BR' => [0 => ',', 1 => '.', 2 => ',', 3 => '.'],
'pt_PT' => [0 => ',', 1 => ' ', 2 => ',', 3 => ' '],
'ro_RO' => [0 => ',', 1 => '.', 2 => ',', 3 => '.'],
'ru_RU' => [0 => ',', 1 => ' ', 2 => ',', 3 => ' '],
'sr_CS' => [0 => ',', 1 => '.', 2 => ',', 3 => '.'],
'si_LK' => [0 => '.', 1 => ',', 2 => '.', 3 => ','],
'sk_SK' => [0 => ',', 1 => ' ', 2 => ',', 3 => ' '],
'sl_SI' => [0 => ',', 1 => '.', 2 => ',', 3 => '.'],
'es_AR' => [0 => ',', 1 => '.', 2 => ',', 3 => '.'],
'es_ES' => [0 => ',', 1 => ' ', 2 => ',', 3 => ' '],
'es_419' => [0 => '.', 1 => ',', 2 => '.', 3 => ','],
'sv_SE' => [0 => ',', 1 => ' ', 2 => ':', 3 => ' '],
'ta_IN' => [0 => '.', 1 => ',', 2 => '.', 3 => ','],
'ti' => [0 => '.', 1 => ',', 2 => '.', 3 => ','],
'th_TH' => [0 => '.', 1 => ',', 2 => '.', 3 => ','],
'tr_TR' => [0 => ',', 1 => '.', 2 => ',', 3 => '.'],
'uk_UA' => [0 => ',', 1 => ' ', 2 => ',', 3 => ' '],
'ur_PK' => [0 => '.', 1 => ',', 2 => '.', 3 => ','],
'vi_VN' => [0 => ',', 1 => '.', 2 => ',', 3 => '.'],
'cy_GB' => [0 => '.', 1 => ',', 2 => '.', 3 => ','],
'fy_NL' => [0 => '.', 1 => ',', 2 => '.', 3 => ','],
];
/**
* Format constructor
*
* @param \rcmail $rcmail
* @codeCoverageIgnore
*/
public function __construct() {
$this->rcmail = xrc();
// $this->rcmail might be null when running some /bin scripts
$this->rcmail && $this->loadFormats();
}
/**
* Returns the user date format.
*
* @param string $type
* @return string
*/
public function getDateFormat(string $type = "php"): string
{
$valid = in_array($type, ["php", "moment", "datepicker", "flatpickr"]);
return $valid ? $this->dateFormats[$type] : "";
}
/**
* Returns the user's time format.
*
* @param string $type
* @return string
*/
public function getTimeFormat(string $type = "php"): string
{
$valid = in_array($type, ["php", "moment", "flatpickr"]);
return $valid ? $this->timeFormats[$type] : "";
}
/**
* Returns the user's date and time format.
*
* @param string $type
* @return string
*/
public function getDateTimeFormat(string $type = "php"): string
{
return $this->getDateFormat($type) . " " . $this->getTimeFormat($type);
}
public function getDateFormats(): array
{
return $this->dateFormats;
}
public function getTimeFormats(): array
{
return $this->timeFormats;
}
public function getDmFormats(): array
{
return $this->dmFormats;
}
/**
* Converts string to time taking into consideration the user's date/time format. It fixes the problem of
* strtotime() or DateTime() not working properly with formats day/month/year and converting considering them
* month/day/year.
*
* @param string $dateTimeString
* @param string $type
* @return bool|int
*/
public function stringToTimeWithFormat(string $dateTimeString, string $type = "php")
{
$dateTimeString = trim($dateTimeString);
$format = $this->getDateFormat($type);
if (strpos($dateTimeString, " ") !== false) {
$format .= " " . $this->getTimeFormat($type);
}
if (!($dateTime = \DateTime::createFromFormat($format, $dateTimeString))) {
return false;
}
return $dateTime->getTimestamp();
}
/**
* Formats the date according to the format specified in the user's config.
*
* @param string|int $date - string or integer representation of date
* @param string|bool $textOnEmpty
* @return string|bool
*/
public function formatDate($date, $textOnEmpty = false)
{
if (empty($date)) {
return $textOnEmpty;
}
return date($this->getDateFormat(), is_numeric($date) ? $date : strtotime($date));
}
/**
* Formats the time according to the format specified in the user's config.
*
* @param string|int $date - string or integer representation of date
* @param string|bool $textOnEmpty
* @return string|bool
*/
public function formatTime($time, $textOnEmpty = false)
{
if (empty($time)) {
return $textOnEmpty;
}
return date($this->getTimeFormat(), is_numeric($time) ? $time : strtotime($time));
}
/**
* Formats the date and time according to the format specified in the user's config.
*
* @param string|int $date - string or integer representation of date
* @param string|bool $textOnEmpty
* @return string|bool
*/
public function formatDateTime($date, $textOnEmpty = false)
{
if (empty($date)) {
return $textOnEmpty;
}
return date($this->getDateFormat() . " " . $this->getTimeFormat(), is_numeric($date) ? $date : strtotime($date));
}
/**
* Formats a currency number using the locale-specific separators.
*
* @param $number
* @param bool|int $decimals
* @param bool|string $locale
* @return string
*/
public function formatCurrency($number, $decimals = false, $locale = false): string
{
return $this->formatNumberOrCurrency("monetary", $number, $decimals, $locale);
}
/**
* Formats a regular number using the locale-specific separators.
*
* @param $number
* @param bool|int $decimals
* @param bool|string $locale
* @return string
*/
public function formatNumber($number, $decimals = false, $locale = false): string
{
return $this->formatNumberOrCurrency("decimal", $number, $decimals, $locale);
}
/**
* Returns the locale specific number formatting separatators.
*
* @param bool|string $locale
* @return array
*/
public function getSeparators($locale = false): array
{
if (!$locale) {
$locale = $this->rcmail->user->language;
}
return $this->separators[$locale];
}
/**
* Converts a float to string without regard for the locale. PHP automatically changes the delimiter used depending
* on the locale set using setlocale(), so depending on the language selected by the user we might end up with
* 3,1415 when converting floats to strings. This function leaves the dot delimiter intact when converting to
* string.
*
* @param $float
* @return string
*/
public static function floatToString($float): string
{
if (!is_float($float)) {
return $float;
}
$conv = localeconv();
return str_replace($conv['decimal_point'], '.', $float);
}
/**
* Returns a formatted number, either decimal or currency.
*
* @param string $type Specify 'monetary' or 'decimal'.
* @param $number
* @param bool|int $decimals
* @param bool|string $locale
* @return string
*/
protected function formatNumberOrCurrency(string $type, $number, $decimals = false, $locale = false): string
{
if ($type == "monetary") {
$separator = Format::MONETARY_SEPARATOR_SYMBOL;
$groupingSeparator = Format::MONETARY_GROUPING_SEPARATOR_SYMBOL;
} else {
$separator = Format::DECIMAL_SEPARATOR_SYMBOL;
$groupingSeparator = Format::GROUPING_SEPARATOR_SYMBOL;
}
if (!$decimals) {
// uncomment to trim the trailing zeros from decimals: 2.1 instead of 2.10
//$decimals = strlen(trim((string)(($number - round($number)) * 100), 0));
$decimals = $number - round($number) == 0 ? 0 : 2;
}
if (!$locale) {
$locale = $this->rcmail->user->language;
}
return number_format(
$number,
$decimals,
$this->separators[$locale][$separator],
$this->separators[$locale][$groupingSeparator]
);
}
/**
* Different components use different formats for date and time, we're creating an array of converted formats
* that can be used in javascript.
*/
protected function loadFormats()
{
// date format
$dateFormat = $this->rcmail->config->get("date_format", "m/d/Y");
$this->dateFormats = [
"php" => $dateFormat,
"moment" => $this->getMomentDateFormat($dateFormat),
"datepicker" => $this->getDatepickerDateFormat($dateFormat),
"flatpickr" => $dateFormat, // doesn't need any conversion
];
// time format
$timeFormat = $this->rcmail->config->get("time_format", "H:i");
$this->timeFormats = [
"php" => $timeFormat,
"moment" => $this->getMomentTimeFormat($timeFormat),
"datepicker" => $this->getDatepickerTimeFormat($timeFormat),
"flatpickr" => $this->getFlatpickrTimeFormat($timeFormat),
];
// day/month format
$dmFormat = trim(str_replace("Y", "", $dateFormat), " /.-");
$this->dmFormats = [
"php" => $dmFormat,
"moment" => $this->getMomentDateFormat($dmFormat),
"datepicker" => $this->getDatepickerDateFormat($dmFormat),
"flatpickr" => $dmFormat, // doesn't need any conversion
];
// set js variables
if (!empty($this->rcmail->output)) {
$this->rcmail->output->set_env("dateFormats", $this->dateFormats);
$this->rcmail->output->set_env("dmFormats", $this->dmFormats);
$this->rcmail->output->set_env("timeFormats", $this->timeFormats);
}
}
/**
* Returns the user php date format converted to the javascript moment format.
*
* @param string $format
* @return string
*/
protected function getMomentDateFormat(string $format): string
{
$replace = [
"D" => "*1",
"d" => "DD",
"l" => "dddd",
"j" => "D",
"*1" => "ddd",
"n" => "*2",
"M" => "MMM",
"m" => "MM",
"F" => "MMMM",
"*2" => "M",
"Y" => "YYYY",
"y" => "YY",
];
return str_replace(array_keys($replace), array_values($replace), $format);
}
/**
* Returns the user php time format converted to the javascript moment format.
*
* @param string $format
* @return string
*/
protected function getMomentTimeFormat(string $format): string
{
$replace = [
"H" => "HH",
"G" => "H",
"h" => "hh",
"g" => "h",
"i" => "mm",
"s" => "ss",
];
return str_replace(array_keys($replace), array_values($replace), $format);
}
/**
* Returns the user php date format converted to the jquery ui datepicker format.
*
* @param string $format
* @return string
*/
protected function getDatepickerDateFormat(string $format): string
{
$replace = [
"d" => "dd",
"j" => "d",
"m" => "mm",
"n" => "m",
"F" => "MM",
"Y" => "yy",
];
return str_replace(array_keys($replace), array_values($replace), $format);
}
/**
* Returns the user php date format converted to the jquery ui datepicker format.
*
* @param string $format
* @return string
*/
protected function getDatepickerTimeFormat(string $format): string
{
$replace = [
"H" => "HH",
"G" => "H",
"h" => "hh",
"g" => "h",
"i" => "mm",
"s" => "ss",
"a" => "tt",
"A" => "TT",
];
return str_replace(array_keys($replace), array_values($replace), $format);
}
/**
* Returns the user php time format converted to the flatpickr format.
* (There's no getFlatpickrDateFormat() function because flatpickr uses the PHP format for dates one-to-one.)
*
* https://www.php.net/manual/en/datetime.format.php
* https://flatpickr.js.org/formatting
*
* @param string $format
* @return string
*/
protected function getFlatpickrTimeFormat(string $format): string
{
$replace = [
"a" => "K",
"A" => "K",
"G" => "H",
"h" => "G",
"g" => "h",
//"H" => "H", -- doesn't need converting
//"i" => "i", -- doesn't need converting
"s" => "S",
];
return str_replace(array_keys($replace), array_values($replace), $format);
}
}