diff --git a/2fa.php b/2fa.php
new file mode 100644
index 00000000..a7e3dbb2
--- /dev/null
+++ b/2fa.php
@@ -0,0 +1,64 @@
+getParameter('login');
+if ($cl_login == null && $request->isGet()) $cl_login = @$_COOKIE[LOGIN_COOKIE_NAME];
+$cl_password = $request->getParameter('password');
+$cl_auth_code = $request->getParameter('auth_code');
+
+$form = new Form('twoFactorAuthForm');
+$form->addInput(array('type'=>'text','maxlength'=>'100','name'=>'login','value'=>$cl_login));
+$form->getElement('login')->setEnabled(false);
+$form->addInput(array('type'=>'password','maxlength'=>'50','name'=>'password','value'=>$cl_password));
+$form->addInput(array('type'=>'text','maxlength'=>'100','name'=>'auth_code','value'=>$cl_auth_code));
+$form->addInput(array('type'=>'submit','name'=>'btn_login','value'=>$i18n->get('button.login')));
+
+if ($request->isPost()) {
+ // Validate user input.
+ if (!ttValidString($cl_login)) $err->add($i18n->get('error.field'), $i18n->get('label.login'));
+ if (!ttValidString($cl_password)) $err->add($i18n->get('error.field'), $i18n->get('label.password'));
+ if (!ttValidString($cl_auth_code)) $err->add($i18n->get('error.field'), $i18n->get('form.2fa.2fa_code'));
+
+ if ($err->no()) {
+ // Get user id.
+ $user_id = ttUserHelper::getUserIdByTmpRef($cl_auth_code);
+ if (!$user_id)
+ $err->add($i18n->get('error.2fa_code'));
+
+ if ($err->no()) {
+ // Additionally check user password for better protection
+ // against brute force attacks guessing 2FA codes.
+ $user = new ttUser(null, $user_id); // Note: reusing $user from initialize.php.
+ // Check user password.
+ if (!$auth->doLogin($user->login, $cl_password))
+ $err->add($i18n->get('error.auth'));
+ }
+
+ if ($err->no()) {
+ // Redirect, depending on user role.
+ if ($user->isClient()) {
+ header('Location: reports.php');
+ } else {
+ header('Location: time.php');
+ }
+ exit();
+ }
+ }
+} // isPost
+
+$smarty->assign('forms', array($form->getName()=>$form->toArray()));
+$smarty->assign('onload', 'onLoad="document.twoFactorAuthForm.auth_code.focus()"');
+$smarty->assign('title', $i18n->get('title.2fa'));
+$smarty->assign('content_page_name', '2fa.tpl');
+$smarty->display('index.tpl');
diff --git a/README.md b/README.md
index cf2f8601..022c0c03 100644
--- a/README.md
+++ b/README.md
@@ -3,6 +3,9 @@
## About
Anuko [Time Tracker](https://www.anuko.com/time-tracker/index.htm) is an open source, web-based time tracking application written in PHP. It allows you to track the time that employees or colleagues spend working on projects and tasks. It needs a web server such as Apache, IIS, etc. to run on, and a database to keep the data in, such as MySQL.
+## Terminal Illness of the Owner
+December 28, 2023: Nik Okuntseff, the owner and lead software developer at Anuko, developed a terminal illness. Anuko, the company behind Time Tracker, will cease to exist at soon.
+
## Free Hosting
[Anuko](https://www.anuko.com) provides [free hosting](https://www.anuko.com/time-tracker/free-hosting/index.htm) of Time Tracker to individuals and small groups up to 5 users. To start using Time Tracker immediately, create a group at https://timetracker.anuko.com
diff --git a/WEB-INF/lib/DateAndTime.class.php b/WEB-INF/lib/DateAndTime.class.php
deleted file mode 100644
index e7224598..00000000
--- a/WEB-INF/lib/DateAndTime.class.php
+++ /dev/null
@@ -1,348 +0,0 @@
-$sDate parsed, or false on error.
- */
-
-function my_strptime($sDate, $sFormat)
-{
- $aResult = array
- (
- 'tm_sec' => 0,
- 'tm_min' => 0,
- 'tm_hour' => 0,
- 'tm_mday' => 1,
- 'tm_mon' => 0,
- 'tm_year' => 0,
- 'tm_wday' => 0,
- 'tm_yday' => 0,
- 'unparsed' => $sDate,
- );
-
- while($sFormat != "")
- {
- // ===== Search a %x element, Check the static string before the %x =====
- $nIdxFound = strpos($sFormat, '%');
- if($nIdxFound === false)
- {
-
- // There is no more format. Check the last static string.
- $aResult['unparsed'] = ($sFormat == $sDate) ? "" : $sDate;
- break;
- }
-
- $sFormatBefore = mb_substr($sFormat, 0, $nIdxFound);
- $sDateBefore = mb_substr($sDate, 0, $nIdxFound);
-
- if($sFormatBefore != $sDateBefore) break;
-
- // ===== Read the value of the %x found =====
- $sFormat = mb_substr($sFormat, $nIdxFound);
- $sDate = mb_substr($sDate, $nIdxFound);
-
- $aResult['unparsed'] = $sDate;
-
- $sFormatCurrent = mb_substr($sFormat, 0, 2);
- $sFormatAfter = mb_substr($sFormat, 2);
-
- $nValue = -1;
- $sDateAfter = "";
- switch($sFormatCurrent)
- {
- case '%S': // Seconds after the minute (0-59)
-
- sscanf($sDate, "%2d%[^\\n]", $nValue, $sDateAfter);
-
- if(($nValue < 0) || ($nValue > 59)) return false;
-
- $aResult['tm_sec'] = $nValue;
- break;
-
- // ----------
- case '%M': // Minutes after the hour (0-59)
- sscanf($sDate, "%2d%[^\\n]", $nValue, $sDateAfter);
-
- if(($nValue < 0) || ($nValue > 59)) return false;
-
- $aResult['tm_min'] = $nValue;
- break;
-
- // ----------
- case '%H': // Hour since midnight (0-23)
- sscanf($sDate, "%2d%[^\\n]", $nValue, $sDateAfter);
-
- if(($nValue < 0) || ($nValue > 23)) return false;
-
- $aResult['tm_hour'] = $nValue;
- break;
-
- // ----------
- case '%d': // Day of the month (1-31)
- sscanf($sDate, "%2d%[^\\n]", $nValue, $sDateAfter);
-
- if(($nValue < 1) || ($nValue > 31)) return false;
-
- $aResult['tm_mday'] = $nValue;
- break;
-
- // ----------
- case '%m': // Months since January (0-11)
- sscanf($sDate, "%2d%[^\\n]", $nValue, $sDateAfter);
-
- if(($nValue < 1) || ($nValue > 12)) return false;
-
- $aResult['tm_mon'] = ($nValue - 1);
- break;
-
- // ----------
- case '%Y': // Years since 1900
- sscanf($sDate, "%4d%[^\\n]", $nValue, $sDateAfter);
-
- if($nValue < 1900) return false;
-
- $aResult['tm_year'] = ($nValue - 1900);
- break;
-
- // ----------
- default:
- //sscanf($sDate, "%s%[^\\n]", $skip, $sDateAfter);
- preg_match('/^(.+)(\s|$)/uU', $sDate, $matches);
- if (isset($matches[1])) {
- $sDateAfter = mb_substr($sDate, mb_strlen($matches[1]));
- } else {
- $sDateAfter = '';
- }
- //break 2; // Break Switch and while
- break;
- }
-
- // ===== Next please =====
- $sFormat = $sFormatAfter;
- $sDate = $sDateAfter;
-
- $aResult['unparsed'] = $sDate;
-
- } // END while($sFormat != "")
-
-
- // ===== Create the other value of the result array =====
- $nParsedDateTimestamp = mktime($aResult['tm_hour'], $aResult['tm_min'], $aResult['tm_sec'],
- $aResult['tm_mon'] + 1, $aResult['tm_mday'], $aResult['tm_year'] + 1900);
-
- // Before PHP 5.1 return -1 when error
- if(($nParsedDateTimestamp === false)
- ||($nParsedDateTimestamp === -1)) return false;
-
- $aResult['tm_wday'] = (int) strftime("%w", $nParsedDateTimestamp); // Days since Sunday (0-6)
- $aResult['tm_yday'] = (strftime("%j", $nParsedDateTimestamp) - 1); // Days since January 1 (0-365)
-
- return $aResult;
-} // END of function
-
-class DateAndTime {
- var $mHour = 0;
- var $mMinute = 0;
- var $mSecond = 0;
- var $mMonth;
- var $mDay; // day of week
- var $mDate; // day of month
- var $mYear;
- var $mIntrFormat = "%d.%m.%Y %H:%M:%S"; //29.02.2004 16:21:42 internal format date
- var $mLocalFormat;
- var $mParseResult = 0;
- var $mAutoComplete = true;
-
- /**
- * Constructor
- *
- * @param String $format
- * @param String $strfDateTime
- * @return DateAndTime
- */
- function __construct($format="",$strfDateTime="") {
- $this->mLocalFormat = ($format ? $format : $this->mIntrFormat);
- $d = ($strfDateTime ? $strfDateTime : $this->do_strftime($this->mLocalFormat));
- $this->parseVal($d);
- }
-
- function setFormat($format) {
- $this->mLocalFormat = $format;
- }
-
- function getFormat() {
- return $this->mLocalFormat;
- }
-
- //01 to 31
- function getDate() { return $this->mDate; }
-
- //0 (for Sunday) through 6 (for Saturday)
- function getDay() { return $this->mDay; }
-
- //01 through 12
- function getMonth() { return $this->mMonth; }
-
- //1999 or 2003
- function getYear() { return $this->mYear; }
-
- function setDate($value) { $this->mDate = $value; }
- function setMonth($value) { $this->mMonth = $value; }
- function setYear($value) { $this->mYear = $value; }
-
- function setTimestamp($ts) {
- $this->mDate = date("d",$ts);
- $this->mDay = date("w",$ts);
- $this->mMonth = date("m",$ts);
- $this->mYear = date("Y",$ts);
- $this->mHour = date("H",$ts);
- $this->mMinute = date("i",$ts);
- $this->mSecond = date("s",$ts);
- }
-
- /**
- * Return UNIX timestamp
- */
- function getTimestamp() {
- return @mktime($this->mHour, $this->mMinute, $this->mSecond, $this->mMonth, $this->mDate, $this->mYear);
- }
-
- function compare($datetime) {
- $ts1 = $this->getTimestamp();
- $ts2 = $datetime->getTimestamp();
- if ($ts1<$ts2) return -1;
- if ($ts1==$ts2) return 0;
- if ($ts1>$ts2) return 1;
- }
-
- function toString($format="") {
- if ($this->mParseResult==0) {
- if ($format) {
- return $this->do_strftime($format, $this->getTimestamp());
- } else {
- return $this->do_strftime($this->mLocalFormat, $this->getTimestamp());
- }
- } else {
- if ($format) {
- return $this->do_strftime($format);
- } else {
- return $this->do_strftime($this->mLocalFormat);
- }
- }
- }
-
- function parseVal($szDate, $format="") {
- $useformat = ($format ? $format : $this->mLocalFormat);
- $res = my_strptime($szDate, $useformat);
- if ($res !== false) {
- $this->mDate = $res['tm_mday'];
- $this->mDay = $res['tm_wday'];
- $this->mMonth = $res['tm_mon'] + 1; // tm_mon - Months since January (0-11)
- $this->mYear = 1900 + $res['tm_year']; // tm_year - Years since 1900
- $this->mHour = $res['tm_hour'];
- $this->mMinute = $res['tm_min'];
- $this->mSecond = $res['tm_sec'];
- $this->mParseResult = 0;
- } elseif ($this->mAutoComplete) {
- $this->setTimestamp(time());
- $this->mParseResult = 1;
- }
- }
-
- function isError() {
- if ($this->mParseResult != 0) return true;
- return false;
- }
-
- function before(/*DateAndTime*/ $obj) {
- if ($this->getTimestamp() < $obj->getTimestamp()) return true;
- return false;
- }
-
- function after(/*DateAndTime*/ $obj) {
- if ($this->getTimestamp() > $obj->getTimestamp()) return true;
- return false;
- }
-
- function equals(/*DateAndTime*/ $obj) {
- if ($this->getTimestamp() == $obj->getTimestamp()) return true;
- return false;
- }
-
- function decDay(/*int*/$days=1) {
- $this->setTimestamp(@mktime($this->mHour, $this->mMinute, $this->mSecond, $this->mMonth, $this->mDate - $days, $this->mYear));
- }
-
- function incDay(/*int*/$days=1) {
- $this->setTimestamp(@mktime($this->mHour, $this->mMinute, $this->mSecond, $this->mMonth, $this->mDate + $days, $this->mYear));
- }
-
- /**
- * @param $format string Datetime format string
- * @return string Preprocessed string with all locale-depended format
- * characters replaced by localized i18n strings.
- */
- function preprocessFormatString($format) {
- global $i18n;
-
- // replace locale-dependent strings
- $format = str_replace('%a', mb_substr($i18n->getWeekDayName($this->mDay), 0, 3, 'utf-8'), $format);
- $format = str_replace('%A', $i18n->getWeekDayName($this->mDay), $format);
- /* This block is commented out because we currently do not use these formatters.
- $abbrev_month = mb_substr($i18n->monthNames[$this->mMonth], 0, 3, 'utf-8');
- $format = str_replace('%b', $abbrev_month, $format);
- $format = str_replace('%h', $abbrev_month, $format);
- $format = str_replace('%z', date('O'), $format);
- $format = str_replace('%Z', date('O'), $format); // format as 'O' for consistency with JS strftime
- if (strpos($format, '%c') !== false) {
- $format = str_replace('%c', $this->preprocessFormatString('%a %d %b %Y %T %Z'), $format);
- }*/
- return $format;
- }
-
- function do_strftime($format, $timestamp = null)
- {
- if (!is_null($timestamp)) {
- return strftime($this->preprocessFormatString($format), $timestamp);
- } else {
- return strftime($this->preprocessFormatString($format));
- }
- }
-}
diff --git a/WEB-INF/lib/I18n.class.php b/WEB-INF/lib/I18n.class.php
index 1760da48..bd4e9ffd 100644
--- a/WEB-INF/lib/I18n.class.php
+++ b/WEB-INF/lib/I18n.class.php
@@ -51,9 +51,16 @@ function get($key) {
return $value;
}
+ // get - keyExists determines if a key exists.
+ function keyExists($key) {
+ $value = $this->get($key);
+ return ($value !== null);
+ }
+
// load - loads localized strings into $keys array by first going through the default file (en.lang.php)
- // and then through the requested language file (which is supplied as parameter).
- // This means we end up with default English strings when keys are missing in the translation file.
+ // and then through the requested language file (which is supplied as parameter),
+ // (this means we end up with default English strings when keys are missing in the translation file),
+ // and then from a group custom translation field, if available.
function load($langName) {
// Load default English keys first.
$defaultFileName = RESOURCE_DIR . '/' . $this->defaultLang . '.lang.php';
@@ -105,6 +112,35 @@ function load($langName) {
}
}
}
+
+ // Now load custom translation for group.
+ global $user;
+ $customTranslation = $user->getCustomTranslation();
+ if ($customTranslation != null) {
+ $lines = preg_split("/\r\n|\n|\r/", $customTranslation);
+ for ($i = 0; $i < count($lines); $i++) {
+ $parts = explode('=', $lines[$i]);
+ if (count($parts) != 2) continue;
+
+ $key = trim($parts[0]);
+ $value = trim($parts[1]);
+ // Escape single quotes and backslashes.
+ $value = addcslashes($value, "'\\");
+ $value = htmlspecialchars($value);
+
+ $pos = strpos($key, ".");
+ if (!($pos === false)) {
+ $p = explode(".", $key);
+ $str = "";
+ foreach ($p as $w) {
+ $str .= "[\"".$w."\"]";
+ }
+ eval("\$this->keys".$str."='".$value."';");
+ } else {
+ $this->keys[$key] = $value;
+ }
+ }
+ }
}
// hasLang determines if a file for requested language exists.
diff --git a/WEB-INF/lib/Period.class.php b/WEB-INF/lib/Period.class.php
deleted file mode 100644
index 57ea0e44..00000000
--- a/WEB-INF/lib/Period.class.php
+++ /dev/null
@@ -1,136 +0,0 @@
-getWeekStart();
-
- $this->startDate = new DateAndTime();
- $this->startDate->setFormat($date_point->getFormat());
- $this->endDate = new DateAndTime();
- $this->endDate->setFormat($date_point->getFormat());
- $t_arr = localtime($date_point->getTimestamp());
- $t_arr[5] = $t_arr[5] + 1900;
-
- if ($t_arr[6] < $weekStartDay) {
- $startWeekBias = $weekStartDay - 7;
- } else {
- $startWeekBias = $weekStartDay;
- }
-
- switch ($period_type) {
- case INTERVAL_THIS_DAY:
- $this->startDate->setTimestamp($date_point->getTimestamp());
- $this->endDate->setTimestamp($date_point->getTimestamp());
- break;
-
- case INTERVAL_LAST_DAY:
- $this->startDate->setTimestamp(mktime(0,0,0,$t_arr[4]+1,$t_arr[3]-1,$t_arr[5]));
- $this->endDate->setTimestamp(mktime(0,0,0,$t_arr[4]+1,$t_arr[3]-1,$t_arr[5]));
- break;
-
- case INTERVAL_THIS_WEEK:
- $this->startDate->setTimestamp(mktime(0,0,0,$t_arr[4]+1,$t_arr[3]-$t_arr[6]+$startWeekBias,$t_arr[5]));
- $this->endDate->setTimestamp(mktime(0,0,0,$t_arr[4]+1,$t_arr[3]-$t_arr[6]+6+$startWeekBias,$t_arr[5]));
- break;
- case INTERVAL_LAST_WEEK:
- $this->startDate->setTimestamp(mktime(0,0,0,$t_arr[4]+1,$t_arr[3]-$t_arr[6]-7+$startWeekBias,$t_arr[5]));
- $this->endDate->setTimestamp(mktime(0,0,0,$t_arr[4]+1,$t_arr[3]-$t_arr[6]-1+$startWeekBias,$t_arr[5]));
- break;
- case INTERVAL_THIS_MONTH:
- $this->startDate->setTimestamp(mktime(0,0,0,$t_arr[4]+1,1,$t_arr[5]));
- $this->endDate->setTimestamp(mktime(0,0,0,$t_arr[4]+2,0,$t_arr[5]));
- break;
- case INTERVAL_LAST_MONTH:
- $this->startDate->setTimestamp(mktime(0,0,0,$t_arr[4],1,$t_arr[5]));
- $this->endDate->setTimestamp(mktime(0,0,0,$t_arr[4]+1,0,$t_arr[5]));
- break;
-
- case INTERVAL_THIS_YEAR:
- $this->startDate->setTimestamp(mktime(0, 0, 0, 1, 1, $t_arr[5]));
- $this->endDate->setTimestamp(mktime(0, 0, 0, 12, 31, $t_arr[5]));
- break;
- }
- }
-
- function setPeriod($b_date, $e_date) {
- $this->startDate = $b_date;
- $this->endDate = $e_date;
- }
-
- // return date string
- function getStartDate($format="") {
- return $this->startDate->toString($format);
- }
-
- // return date string
- function getEndDate($format="") {
- return $this->endDate->toString($format);
- }
-}
diff --git a/WEB-INF/lib/common.lib.php b/WEB-INF/lib/common.lib.php
index db00923a..79aba106 100644
--- a/WEB-INF/lib/common.lib.php
+++ b/WEB-INF/lib/common.lib.php
@@ -126,7 +126,7 @@ function isTrue($val)
}
// ttValidString is used to check user input to validate a string.
-function ttValidString($val, $emptyValid = false)
+function ttValidString($val, $emptyValid = false, $maxChars = 0)
{
if (is_null($val)) {
return $emptyValid ? true : false;
@@ -140,6 +140,10 @@ function ttValidString($val, $emptyValid = false)
if (stristr($val, '';
} elseif ($encode === 'javascript_charcode') {
- $string = '' . $text . '';
for ($x = 0, $_length = strlen($string); $x < $_length; $x++) {
$ord[] = ord($string[ $x ]);
}
- return '';
+ return '';
} elseif ($encode === 'hex') {
preg_match('!^(.*)(\?.*)$!', $address, $match);
if (!empty($match[ 2 ])) {
@@ -129,6 +137,6 @@ function smarty_function_mailto($params)
return '' . $text_encode . '';
} else {
// no encoding
- return '' . $text . '';
+ return $string;
}
}
diff --git a/WEB-INF/lib/smarty/plugins/function.math.php b/WEB-INF/lib/smarty/plugins/function.math.php
index fd5b3d16..f9cf67fe 100644
--- a/WEB-INF/lib/smarty/plugins/function.math.php
+++ b/WEB-INF/lib/smarty/plugins/function.math.php
@@ -69,8 +69,8 @@ function smarty_function_math($params, $template)
// Adapted from https://www.php.net/manual/en/function.eval.php#107377
$number = '(?:\d+(?:[,.]\d+)?|pi|π)'; // What is a number
$functionsOrVars = '((?:0x[a-fA-F0-9]+)|([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*))';
- $operators = '[+\/*\^%-]'; // Allowed math operators
- $regexp = '/^(('.$number.'|'.$functionsOrVars.'|('.$functionsOrVars.'\s*\((?1)+\)|\((?1)+\)))(?:'.$operators.'(?1))?)+$/';
+ $operators = '[,+\/*\^%-]'; // Allowed math operators
+ $regexp = '/^(('.$number.'|'.$functionsOrVars.'|('.$functionsOrVars.'\s*\((?1)*\)|\((?1)*\)))(?:'.$operators.'(?1))?)+$/';
if (!preg_match($regexp, $equation)) {
trigger_error("math: illegal characters", E_USER_WARNING);
diff --git a/WEB-INF/lib/smarty/plugins/modifier.capitalize.php b/WEB-INF/lib/smarty/plugins/modifier.capitalize.php
index c5fc400a..2903d61d 100644
--- a/WEB-INF/lib/smarty/plugins/modifier.capitalize.php
+++ b/WEB-INF/lib/smarty/plugins/modifier.capitalize.php
@@ -22,6 +22,8 @@
*/
function smarty_modifier_capitalize($string, $uc_digits = false, $lc_rest = false)
{
+ $string = (string) $string;
+
if (Smarty::$_MBSTRING) {
if ($lc_rest) {
// uppercase (including hyphenated words)
diff --git a/WEB-INF/lib/smarty/plugins/modifier.count.php b/WEB-INF/lib/smarty/plugins/modifier.count.php
new file mode 100644
index 00000000..ca35fc11
--- /dev/null
+++ b/WEB-INF/lib/smarty/plugins/modifier.count.php
@@ -0,0 +1,36 @@
+ Prior to PHP 8.0.0, if the parameter was neither an array nor an object that implements the Countable interface,
+ * > 1 would be returned, unless value was null, in which case 0 would be returned.
+ */
+
+ if ($arrayOrObject instanceof Countable || is_array($arrayOrObject)) {
+ return count($arrayOrObject, (int) $mode);
+ } elseif ($arrayOrObject === null) {
+ return 0;
+ }
+ return 1;
+}
diff --git a/WEB-INF/lib/smarty/plugins/modifier.date_format.php b/WEB-INF/lib/smarty/plugins/modifier.date_format.php
index 8e7e0b6e..e3589fd0 100644
--- a/WEB-INF/lib/smarty/plugins/modifier.date_format.php
+++ b/WEB-INF/lib/smarty/plugins/modifier.date_format.php
@@ -78,7 +78,8 @@ function smarty_modifier_date_format($string, $format = null, $default_date = ''
}
$format = str_replace($_win_from, $_win_to, $format);
}
- return strftime($format, $timestamp);
+ // @ to suppress deprecation errors when running in PHP8.1 or higher.
+ return @strftime($format, $timestamp);
} else {
return date($format, $timestamp);
}
diff --git a/WEB-INF/lib/smarty/plugins/modifier.escape.php b/WEB-INF/lib/smarty/plugins/modifier.escape.php
index 47489aa9..11e44682 100644
--- a/WEB-INF/lib/smarty/plugins/modifier.escape.php
+++ b/WEB-INF/lib/smarty/plugins/modifier.escape.php
@@ -23,95 +23,25 @@
*/
function smarty_modifier_escape($string, $esc_type = 'html', $char_set = null, $double_encode = true)
{
- static $_double_encode = true;
static $is_loaded_1 = false;
static $is_loaded_2 = false;
if (!$char_set) {
$char_set = Smarty::$_CHARSET;
}
+
+ $string = (string)$string;
+
switch ($esc_type) {
case 'html':
- if ($_double_encode) {
- // php >=5.3.2 - go native
- return htmlspecialchars($string, ENT_QUOTES, $char_set, $double_encode);
- } else {
- if ($double_encode) {
- // php <5.2.3 - only handle double encoding
- return htmlspecialchars($string, ENT_QUOTES, $char_set);
- } else {
- // php <5.2.3 - prevent double encoding
- $string = preg_replace('!&(#?\w+);!', '%%%SMARTY_START%%%\\1%%%SMARTY_END%%%', $string);
- $string = htmlspecialchars($string, ENT_QUOTES, $char_set);
- $string = str_replace(
- array(
- '%%%SMARTY_START%%%',
- '%%%SMARTY_END%%%'
- ),
- array(
- '&',
- ';'
- ),
- $string
- );
- return $string;
- }
- }
+ return htmlspecialchars($string, ENT_QUOTES, $char_set, $double_encode);
// no break
case 'htmlall':
if (Smarty::$_MBSTRING) {
- // mb_convert_encoding ignores htmlspecialchars()
- if ($_double_encode) {
- // php >=5.3.2 - go native
- $string = htmlspecialchars($string, ENT_QUOTES, $char_set, $double_encode);
- } else {
- if ($double_encode) {
- // php <5.2.3 - only handle double encoding
- $string = htmlspecialchars($string, ENT_QUOTES, $char_set);
- } else {
- // php <5.2.3 - prevent double encoding
- $string = preg_replace('!&(#?\w+);!', '%%%SMARTY_START%%%\\1%%%SMARTY_END%%%', $string);
- $string = htmlspecialchars($string, ENT_QUOTES, $char_set);
- $string =
- str_replace(
- array(
- '%%%SMARTY_START%%%',
- '%%%SMARTY_END%%%'
- ),
- array(
- '&',
- ';'
- ),
- $string
- );
- return $string;
- }
- }
- // htmlentities() won't convert everything, so use mb_convert_encoding
- return mb_convert_encoding($string, 'HTML-ENTITIES', $char_set);
+ $string = mb_convert_encoding($string, 'UTF-8', $char_set);
+ return htmlentities($string, ENT_QUOTES, 'UTF-8', $double_encode);
}
// no MBString fallback
- if ($_double_encode) {
- return htmlentities($string, ENT_QUOTES, $char_set, $double_encode);
- } else {
- if ($double_encode) {
- return htmlentities($string, ENT_QUOTES, $char_set);
- } else {
- $string = preg_replace('!&(#?\w+);!', '%%%SMARTY_START%%%\\1%%%SMARTY_END%%%', $string);
- $string = htmlentities($string, ENT_QUOTES, $char_set);
- $string = str_replace(
- array(
- '%%%SMARTY_START%%%',
- '%%%SMARTY_END%%%'
- ),
- array(
- '&',
- ';'
- ),
- $string
- );
- return $string;
- }
- }
+ return htmlentities($string, ENT_QUOTES, $char_set, $double_encode);
// no break
case 'url':
return rawurlencode($string);
diff --git a/WEB-INF/lib/smarty/plugins/modifier.explode.php b/WEB-INF/lib/smarty/plugins/modifier.explode.php
new file mode 100644
index 00000000..5186fde3
--- /dev/null
+++ b/WEB-INF/lib/smarty/plugins/modifier.explode.php
@@ -0,0 +1,25 @@
+=8.1
+ return explode($separator, $string ?? '', $limit ?? PHP_INT_MAX);
+}
diff --git a/WEB-INF/lib/smarty/plugins/modifier.number_format.php b/WEB-INF/lib/smarty/plugins/modifier.number_format.php
new file mode 100644
index 00000000..8c612601
--- /dev/null
+++ b/WEB-INF/lib/smarty/plugins/modifier.number_format.php
@@ -0,0 +1,26 @@
+=8.1
+ return number_format($num ?? 0.0, $decimals, $decimal_separator, $thousands_separator);
+}
diff --git a/WEB-INF/lib/smarty/plugins/modifier.truncate.php b/WEB-INF/lib/smarty/plugins/modifier.truncate.php
index 33e7e53a..80dcdb53 100644
--- a/WEB-INF/lib/smarty/plugins/modifier.truncate.php
+++ b/WEB-INF/lib/smarty/plugins/modifier.truncate.php
@@ -42,8 +42,8 @@ function smarty_modifier_truncate($string, $length = 80, $etc = '...', $break_wo
if (!$middle) {
return mb_substr($string, 0, $length, Smarty::$_CHARSET) . $etc;
}
- return mb_substr($string, 0, $length / 2, Smarty::$_CHARSET) . $etc .
- mb_substr($string, -$length / 2, $length, Smarty::$_CHARSET);
+ return mb_substr($string, 0, intval($length / 2), Smarty::$_CHARSET) . $etc .
+ mb_substr($string, -intval($length / 2), $length, Smarty::$_CHARSET);
}
return $string;
}
@@ -56,7 +56,7 @@ function smarty_modifier_truncate($string, $length = 80, $etc = '...', $break_wo
if (!$middle) {
return substr($string, 0, $length) . $etc;
}
- return substr($string, 0, $length / 2) . $etc . substr($string, -$length / 2);
+ return substr($string, 0, intval($length / 2)) . $etc . substr($string, -intval($length / 2));
}
return $string;
}
diff --git a/WEB-INF/lib/smarty/plugins/modifiercompiler.escape.php b/WEB-INF/lib/smarty/plugins/modifiercompiler.escape.php
index 70b95cc9..602c3dbf 100644
--- a/WEB-INF/lib/smarty/plugins/modifiercompiler.escape.php
+++ b/WEB-INF/lib/smarty/plugins/modifiercompiler.escape.php
@@ -18,12 +18,10 @@
* @param Smarty_Internal_TemplateCompilerBase $compiler
*
* @return string with compiled code
- * @throws \SmartyException
+ * @throws SmartyException
*/
function smarty_modifiercompiler_escape($params, Smarty_Internal_TemplateCompilerBase $compiler)
{
- static $_double_encode = true;
- static $is_loaded = false;
$compiler->template->_checkPlugins(
array(
array(
@@ -41,53 +39,30 @@ function smarty_modifiercompiler_escape($params, Smarty_Internal_TemplateCompile
}
switch ($esc_type) {
case 'html':
- if ($_double_encode) {
- return 'htmlspecialchars(' . $params[ 0 ] . ', ENT_QUOTES, ' . var_export($char_set, true) . ', ' .
- var_export($double_encode, true) . ')';
- } elseif ($double_encode) {
- return 'htmlspecialchars(' . $params[ 0 ] . ', ENT_QUOTES, ' . var_export($char_set, true) . ')';
- } else {
- // fall back to modifier.escape.php
- }
+ return 'htmlspecialchars((string)' . $params[ 0 ] . ', ENT_QUOTES, ' . var_export($char_set, true) . ', ' .
+ var_export($double_encode, true) . ')';
// no break
case 'htmlall':
if (Smarty::$_MBSTRING) {
- if ($_double_encode) {
- // php >=5.2.3 - go native
- return 'mb_convert_encoding(htmlspecialchars(' . $params[ 0 ] . ', ENT_QUOTES, ' .
- var_export($char_set, true) . ', ' . var_export($double_encode, true) .
- '), "HTML-ENTITIES", ' . var_export($char_set, true) . ')';
- } elseif ($double_encode) {
- // php <5.2.3 - only handle double encoding
- return 'mb_convert_encoding(htmlspecialchars(' . $params[ 0 ] . ', ENT_QUOTES, ' .
- var_export($char_set, true) . '), "HTML-ENTITIES", ' . var_export($char_set, true) . ')';
- } else {
- // fall back to modifier.escape.php
- }
+ return 'htmlentities(mb_convert_encoding((string)' . $params[ 0 ] . ', \'UTF-8\', ' .
+ var_export($char_set, true) . '), ENT_QUOTES, \'UTF-8\', ' .
+ var_export($double_encode, true) . ')';
}
// no MBString fallback
- if ($_double_encode) {
- // php >=5.2.3 - go native
- return 'htmlentities(' . $params[ 0 ] . ', ENT_QUOTES, ' . var_export($char_set, true) . ', ' .
- var_export($double_encode, true) . ')';
- } elseif ($double_encode) {
- // php <5.2.3 - only handle double encoding
- return 'htmlentities(' . $params[ 0 ] . ', ENT_QUOTES, ' . var_export($char_set, true) . ')';
- } else {
- // fall back to modifier.escape.php
- }
+ return 'htmlentities((string)' . $params[ 0 ] . ', ENT_QUOTES, ' . var_export($char_set, true) . ', ' .
+ var_export($double_encode, true) . ')';
// no break
case 'url':
- return 'rawurlencode(' . $params[ 0 ] . ')';
+ return 'rawurlencode((string)' . $params[ 0 ] . ')';
case 'urlpathinfo':
- return 'str_replace("%2F", "/", rawurlencode(' . $params[ 0 ] . '))';
+ return 'str_replace("%2F", "/", rawurlencode((string)' . $params[ 0 ] . '))';
case 'quotes':
// escape unescaped single quotes
- return 'preg_replace("%(? "\\\\\\\\", "\'" => "\\\\\'", "\"" => "\\\\\"", "\\r" => "\\\\r", "\\n" => "\\\n", "" => "<\/", "