diff --git a/.htaccess b/.htaccess index 189e6b4b8..3b6168c75 100644 --- a/.htaccess +++ b/.htaccess @@ -2,7 +2,7 @@ AddDefaultCharset utf-8 # Restrict access to Time Tracker only from certain IPs. # -# See https://www.anuko.com/time_tracker/faq/restrict_access_by_ip.htm for help. +# See https://www.anuko.com/time-tracker/faq/restrict-access-by-ip.htm for help. # For this to work make sure AllowOverride is set to All in web server config file. # Uncomment 3 lines below and set your IP accordingly. # diff --git a/2fa.php b/2fa.php new file mode 100644 index 000000000..a7e3dbb29 --- /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/CONTRIBUTING.md b/CONTRIBUTING.md index b7311238a..58c971801 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,6 @@ # Resources -* [docs](https://www.anuko.com/time_tracker/features.htm) - detailed documentation about this project (needs updating). +* [docs](https://www.anuko.com/time-tracker/features.htm) - detailed documentation about this project (needs updating). * [forum](https://www.anuko.com/forum/viewforum.php?f=4) - general discussion. @@ -29,4 +29,4 @@ usr: admin psw: secret ``` -Without docker, perform a manual install of a web server, php, database server, and Time Tracker. Full installation and setup guide can be found [here](https://www.anuko.com/time_tracker/install_guide/index.htm). +Without docker, perform a manual install of a web server, php, database server, and Time Tracker. Full installation and setup guide can be found [here](https://www.anuko.com/time-tracker/install-guide/index.htm). diff --git a/README.md b/README.md index cf2f8601f..022c0c030 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/config.php.dist b/WEB-INF/config.php.dist index 9d9d90dfd..6b6a68d84 100644 --- a/WEB-INF/config.php.dist +++ b/WEB-INF/config.php.dist @@ -88,7 +88,7 @@ define('SENDER', 'Anuko Time Tracker '); // MAIL_MODE - mail sending mode. Can be 'mail' or 'smtp'. // 'mail' - sending through php mail() function. // 'smtp' - sending directly through SMTP server. -// See https://www.anuko.com/time_tracker/install_guide/mail.htm +// See https://www.anuko.com/time-tracker/install-guide/mail.htm // define('MAIL_MODE', 'smtp'); define('MAIL_SMTP_HOST', 'localhost'); // For gmail use 'ssl://smtp.gmail.com' instead of 'localhost' and port 465. @@ -133,7 +133,7 @@ define('REPORT_FOOTER', true); define('AUTH_MODULE', 'db'); // LDAP authentication examples. -// Go to https://www.anuko.com/time_tracker/install_guide/ldap_auth/index.htm for detailed configuration instructions. +// Go to https://www.anuko.com/time-tracker/install-guide/ldap-auth/index.htm for detailed configuration instructions. // Configuration example for OpenLDAP server: // define('AUTH_MODULE', 'ldap'); diff --git a/WEB-INF/lib/Auth.class.php b/WEB-INF/lib/Auth.class.php index ccf2d915d..783721090 100644 --- a/WEB-INF/lib/Auth.class.php +++ b/WEB-INF/lib/Auth.class.php @@ -23,7 +23,7 @@ // | // +----------------------------------------------------------------------+ // | Contributors: -// | https://www.anuko.com/time_tracker/credits.htm +// | https://www.anuko.com/time-tracker/credits.htm // +----------------------------------------------------------------------+ class Auth { diff --git a/WEB-INF/lib/DateAndTime.class.php b/WEB-INF/lib/DateAndTime.class.php deleted file mode 100644 index 1184bd853..000000000 --- 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 ed8380815..bd4e9ffd3 100644 --- a/WEB-INF/lib/I18n.class.php +++ b/WEB-INF/lib/I18n.class.php @@ -23,7 +23,7 @@ // | // +----------------------------------------------------------------------+ // | Contributors: -// | https://www.anuko.com/time_tracker/credits.htm +// | https://www.anuko.com/time-tracker/credits.htm // +----------------------------------------------------------------------+ class I18n { @@ -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 2d2269e52..000000000 --- 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 d5b43f57f..79aba106b 100644 --- a/WEB-INF/lib/common.lib.php +++ b/WEB-INF/lib/common.lib.php @@ -126,16 +126,24 @@ 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; + } + $val = trim($val); if (strlen($val) == 0 && !$emptyValid) return false; - + // String must not be XSS evil (to insert JavaScript). if (stristr($val, ''; + return ''; } elseif ($encode === 'javascript_charcode') { - $string = '' . $text . ''; - for ($x = 0, $y = strlen($string); $x < $y; $x++) { + for ($x = 0, $_length = strlen($string); $x < $_length; $x++) { $ord[] = ord($string[ $x ]); } - $_ret = "\n"; - return $_ret; + return ''; } elseif ($encode === 'hex') { preg_match('!^(.*)(\?.*)$!', $address, $match); if (!empty($match[ 2 ])) { @@ -132,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 7348d9649..f9cf67fe7 100644 --- a/WEB-INF/lib/smarty/plugins/function.math.php +++ b/WEB-INF/lib/smarty/plugins/function.math.php @@ -12,7 +12,7 @@ * Name: math * Purpose: handle math computations in template * - * @link http://www.smarty.net/manual/en/language.function.math.php {math} + * @link https://www.smarty.net/manual/en/language.function.math.php {math} * (Smarty online manual) * @author Monte Ohrt * @@ -28,7 +28,12 @@ function smarty_function_math($params, $template) 'int' => true, 'abs' => true, 'ceil' => true, + 'acos' => true, + 'acosh' => true, 'cos' => true, + 'cosh' => true, + 'deg2rad' => true, + 'rad2deg' => true, 'exp' => true, 'floor' => true, 'log' => true, @@ -39,27 +44,51 @@ function smarty_function_math($params, $template) 'pow' => true, 'rand' => true, 'round' => true, + 'asin' => true, + 'asinh' => true, 'sin' => true, + 'sinh' => true, 'sqrt' => true, 'srand' => true, - 'tan' => true + 'atan' => true, + 'atanh' => true, + 'tan' => true, + 'tanh' => true ); + // be sure equation parameter is present if (empty($params[ 'equation' ])) { trigger_error("math: missing equation parameter", E_USER_WARNING); return; } $equation = $params[ 'equation' ]; + + // Remove whitespaces + $equation = preg_replace('/\s+/', '', $equation); + + // 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))?)+$/'; + + if (!preg_match($regexp, $equation)) { + trigger_error("math: illegal characters", E_USER_WARNING); + return; + } + // make sure parenthesis are balanced if (substr_count($equation, '(') !== substr_count($equation, ')')) { trigger_error("math: unbalanced parenthesis", E_USER_WARNING); return; } + // disallow backticks if (strpos($equation, '`') !== false) { trigger_error("math: backtick character not allowed in equation", E_USER_WARNING); return; } + // also disallow dollar signs if (strpos($equation, '$') !== false) { trigger_error("math: dollar signs not allowed in equation", E_USER_WARNING); @@ -96,6 +125,7 @@ function smarty_function_math($params, $template) } $smarty_math_result = null; eval("\$smarty_math_result = " . $equation . ";"); + if (empty($params[ 'format' ])) { if (empty($params[ 'assign' ])) { return $smarty_math_result; diff --git a/WEB-INF/lib/smarty/plugins/modifier.capitalize.php b/WEB-INF/lib/smarty/plugins/modifier.capitalize.php index c5fc400a6..2903d61d7 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 000000000..ca35fc115 --- /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 c8e88c5c9..e3589fd07 100644 --- a/WEB-INF/lib/smarty/plugins/modifier.date_format.php +++ b/WEB-INF/lib/smarty/plugins/modifier.date_format.php @@ -15,7 +15,7 @@ * - format: strftime format for output * - default_date: default date if $string is empty * - * @link http://www.smarty.net/manual/en/language.modifier.date.format.php date_format (Smarty online manual) + * @link https://www.smarty.net/manual/en/language.modifier.date.format.php date_format (Smarty online manual) * @author Monte Ohrt * * @param string $string input date string @@ -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 150901c7c..11e44682e 100644 --- a/WEB-INF/lib/smarty/plugins/modifier.escape.php +++ b/WEB-INF/lib/smarty/plugins/modifier.escape.php @@ -11,7 +11,7 @@ * Name: escape * Purpose: escape string for output * - * @link http://www.smarty.net/docs/en/language.modifier.escape + * @link https://www.smarty.net/docs/en/language.modifier.escape * @author Monte Ohrt * * @param string $string input string @@ -23,98 +23,25 @@ */ function smarty_modifier_escape($string, $esc_type = 'html', $char_set = null, $double_encode = true) { - static $_double_encode = null; static $is_loaded_1 = false; static $is_loaded_2 = false; - if ($_double_encode === null) { - $_double_encode = version_compare(PHP_VERSION, '5.2.3', '>='); - } 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); @@ -184,7 +111,11 @@ function smarty_modifier_escape($string, $esc_type = 'html', $char_set = null, $ '"' => '\\"', "\r" => '\\r', "\n" => '\\n', - ' '<\/' + ' '<\/', + // see https://html.spec.whatwg.org/multipage/scripting.html#restrictions-for-contents-of-script-elements + '#ms', '', $source); // capture html elements not to be messed with $_offset = 0; diff --git a/WEB-INF/lib/smarty/plugins/shared.escape_special_chars.php b/WEB-INF/lib/smarty/plugins/shared.escape_special_chars.php index 6b18d3eec..a204b092c 100644 --- a/WEB-INF/lib/smarty/plugins/shared.escape_special_chars.php +++ b/WEB-INF/lib/smarty/plugins/shared.escape_special_chars.php @@ -20,13 +20,7 @@ function smarty_function_escape_special_chars($string) { if (!is_array($string)) { - if (version_compare(PHP_VERSION, '5.2.3', '>=')) { - $string = htmlspecialchars($string, ENT_COMPAT, Smarty::$_CHARSET, false); - } else { - $string = preg_replace('!&(#?\w+);!', '%%%SMARTY_START%%%\\1%%%SMARTY_END%%%', $string); - $string = htmlspecialchars($string); - $string = str_replace(array('%%%SMARTY_START%%%', '%%%SMARTY_END%%%'), array('&', ';'), $string); - } + $string = htmlspecialchars($string, ENT_COMPAT, Smarty::$_CHARSET, false); } return $string; } diff --git a/WEB-INF/lib/smarty/plugins/shared.mb_str_replace.php b/WEB-INF/lib/smarty/plugins/shared.mb_str_replace.php index 206cf9ea6..7e85f7aae 100644 --- a/WEB-INF/lib/smarty/plugins/shared.mb_str_replace.php +++ b/WEB-INF/lib/smarty/plugins/shared.mb_str_replace.php @@ -44,9 +44,43 @@ function smarty_mb_str_replace($search, $replace, $subject, &$count = 0) } } } else { - $parts = mb_split(preg_quote($search), $subject); + $mb_reg_charset = mb_regex_encoding(); + // Check if mbstring regex is using UTF-8 + $reg_is_unicode = !strcasecmp($mb_reg_charset, "UTF-8"); + if(!$reg_is_unicode) { + // ...and set to UTF-8 if not + mb_regex_encoding("UTF-8"); + } + + // See if charset used by Smarty is matching one used by regex... + $current_charset = mb_regex_encoding(); + $convert_result = (bool)strcasecmp(Smarty::$_CHARSET, $current_charset); + if($convert_result) { + // ...convert to it if not. + $subject = mb_convert_encoding($subject, $current_charset, Smarty::$_CHARSET); + $search = mb_convert_encoding($search, $current_charset, Smarty::$_CHARSET); + $replace = mb_convert_encoding($replace, $current_charset, Smarty::$_CHARSET); + } + + $parts = mb_split(preg_quote($search), $subject ?? "") ?: array(); + // If original regex encoding was not unicode... + if(!$reg_is_unicode) { + // ...restore original regex encoding to avoid breaking the system. + mb_regex_encoding($mb_reg_charset); + } + if($parts === false) { + // This exception is thrown if call to mb_split failed. + // Usually it happens, when $search or $replace are not valid for given mb_regex_encoding(). + // There may be other cases for it to fail, please file an issue if you find a reproducible one. + throw new SmartyException("Source string is not a valid $current_charset sequence (probably)"); + } + $count = count($parts) - 1; $subject = implode($replace, $parts); + // Convert results back to charset used by Smarty, if needed. + if($convert_result) { + $subject = mb_convert_encoding($subject, Smarty::$_CHARSET, $current_charset); + } } return $subject; } diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_cacheresource.php b/WEB-INF/lib/smarty/sysplugins/smarty_cacheresource.php index 91e9f3924..db68f9bfd 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_cacheresource.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_cacheresource.php @@ -205,11 +205,11 @@ public static function load(Smarty $smarty, $type = null) } // try sysplugins dir if (isset(self::$sysplugins[ $type ])) { - $cache_resource_class = 'Smarty_Internal_CacheResource_' . ucfirst($type); + $cache_resource_class = 'Smarty_Internal_CacheResource_' . smarty_ucfirst_ascii($type); return $smarty->_cache[ 'cacheresource_handlers' ][ $type ] = new $cache_resource_class(); } // try plugins dir - $cache_resource_class = 'Smarty_CacheResource_' . ucfirst($type); + $cache_resource_class = 'Smarty_CacheResource_' . smarty_ucfirst_ascii($type); if ($smarty->loadPlugin($cache_resource_class)) { return $smarty->_cache[ 'cacheresource_handlers' ][ $type ] = new $cache_resource_class(); } diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_cacheresource_keyvaluestore.php b/WEB-INF/lib/smarty/sysplugins/smarty_cacheresource_keyvaluestore.php index 59bf1d4a8..4b1c0f6d8 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_cacheresource_keyvaluestore.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_cacheresource_keyvaluestore.php @@ -244,7 +244,7 @@ protected function getTemplateUid(Smarty $smarty, $resource_name) */ protected function sanitize($string) { - $string = trim($string, '|'); + $string = trim((string)$string, '|'); if (!$string) { return ''; } @@ -428,7 +428,7 @@ protected function listInvalidationKeys( $t[] = 'IVK#COMPILE' . $_compile; } $_name .= '#'; - $cid = trim($cache_id, '|'); + $cid = trim((string)$cache_id, '|'); if (!$cid) { return $t; } diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_cacheresource_file.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_cacheresource_file.php index 61618449d..c77ae9e17 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_cacheresource_file.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_cacheresource_file.php @@ -196,12 +196,8 @@ public function clear(Smarty $smarty, $resource_name, $cache_id, $compile_id, $e */ public function hasLock(Smarty $smarty, Smarty_Template_Cached $cached) { - if (version_compare(PHP_VERSION, '5.3.0', '>=')) { - clearstatcache(true, $cached->lock_id); - } else { - clearstatcache(); - } - if (is_file($cached->lock_id)) { + clearstatcache(true, $cached->lock_id ?? ''); + if (null !== $cached->lock_id && is_file($cached->lock_id)) { $t = filemtime($cached->lock_id); return $t && (time() - $t < $smarty->locking_timeout); } else { diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_block.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_block.php index 8ff15d8e5..cbaccd2b3 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_block.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_block.php @@ -125,7 +125,7 @@ public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler, $ // setup buffer for template function code $compiler->parser->current_buffer = new Smarty_Internal_ParseTree_Template(); $output = "cStyleComment(" {block {$_name}} ") . "\n"; $output .= "class {$_className} extends Smarty_Internal_Block\n"; $output .= "{\n"; foreach ($_block as $property => $value) { @@ -155,7 +155,7 @@ public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler, $ } $output .= "}\n"; $output .= "}\n"; - $output .= "/* {/block {$_name}} */\n\n"; + $output .= $compiler->cStyleComment(" {/block {$_name}} ") . "\n\n"; $output .= "?>\n"; $compiler->parser->current_buffer->append_subtree( $compiler->parser, diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_for.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_for.php index 3f113e56d..969e22c1a 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_for.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_for.php @@ -18,7 +18,7 @@ class Smarty_Internal_Compile_For extends Smarty_Internal_CompileBase { /** * Compiles code for the {for} tag - * Smarty 3 does implement two different syntax's: + * Smarty supports two different syntax's: * - {for $var in $array} * For looping over arrays or iterators * - {for $x=0; $x<$y; $x++} diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_foreach.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_foreach.php index a68da5409..edfe358be 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_foreach.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_foreach.php @@ -219,9 +219,9 @@ public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler) if (isset($itemAttr[ 'index' ])) { $output .= "{$itemVar}->index = -1;\n"; } - $output .= "{$itemVar}->do_else = true;\n"; + $output .= "{$itemVar}->do_else = true;\n"; $output .= "if (\$_from !== null) foreach (\$_from as {$keyTerm}{$itemVar}->value) {\n"; - $output .= "{$itemVar}->do_else = false;\n"; + $output .= "{$itemVar}->do_else = false;\n"; if (isset($attributes[ 'key' ]) && isset($itemAttr[ 'key' ])) { $output .= "\$_smarty_tpl->tpl_vars['{$key}']->value = {$itemVar}->key;\n"; } diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_function.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_function.php index d0f2b0f4a..b05a82b74 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_function.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_function.php @@ -134,7 +134,7 @@ public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler) if ($compiler->template->compiled->has_nocache_code) { $compiler->parent_compiler->tpl_function[ $_name ][ 'call_name_caching' ] = $_funcNameCaching; $output = "cStyleComment(" {$_funcNameCaching} ") . "\n"; $output .= "if (!function_exists('{$_funcNameCaching}')) {\n"; $output .= "function {$_funcNameCaching} (Smarty_Internal_Template \$_smarty_tpl,\$params) {\n"; $output .= "ob_start();\n"; @@ -157,9 +157,9 @@ public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler) $output = "template->compiled->nocache_hash}%%*/smarty->ext->_tplFunction->restoreTemplateVariables(\\\$_smarty_tpl, '{$_name}');?>\n"; $output .= "/*/%%SmartyNocache:{$compiler->template->compiled->nocache_hash}%%*/\";\n?>"; - $output .= "template->compiled->nocache_hash}', \$_smarty_tpl->compiled->nocache_hash, ob_get_clean());\n"; + $output .= "template->compiled->nocache_hash}', \$_smarty_tpl->compiled->nocache_hash ?? '', ob_get_clean());\n"; $output .= "}\n}\n"; - $output .= "/*/ {$_funcName}_nocache */\n\n"; + $output .= $compiler->cStyleComment("/ {$_funcName}_nocache ") . "\n\n"; $output .= "?>\n"; $compiler->parser->current_buffer->append_subtree( $compiler->parser, @@ -179,7 +179,7 @@ public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler) } $compiler->parent_compiler->tpl_function[ $_name ][ 'call_name' ] = $_funcName; $output = "cStyleComment(" {$_funcName} ") . "\n"; $output .= "if (!function_exists('{$_funcName}')) {\n"; $output .= "function {$_funcName}(Smarty_Internal_Template \$_smarty_tpl,\$params) {\n"; $output .= $_paramsCode; @@ -196,7 +196,7 @@ public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler) ); $compiler->parser->current_buffer->append_subtree($compiler->parser, $_functionCode); $output = "cStyleComment("/ {$_funcName} ") . "\n\n"; $output .= "?>\n"; $compiler->parser->current_buffer->append_subtree( $compiler->parser, diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_include.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_include.php index 716c91d49..bf62461bc 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_include.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_include.php @@ -318,14 +318,14 @@ public function compileInlineTemplate( } // get compiled code $compiled_code = "cStyleComment(" Start inline template \"{$sourceInfo}\" =============================") . "\n"; $compiled_code .= "function {$tpl->compiled->unifunc} (Smarty_Internal_Template \$_smarty_tpl) {\n"; $compiled_code .= "?>\n" . $tpl->compiler->compileTemplateSource($tpl, null, $compiler->parent_compiler); $compiled_code .= "\n"; $compiled_code .= $tpl->compiler->postFilter($tpl->compiler->blockOrFunctionCode); $compiled_code .= "cStyleComment(" End inline template \"{$sourceInfo}\" =============================") . "\n"; $compiled_code .= '?>'; unset($tpl->compiler); if ($tpl->compiled->has_nocache_code) { diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_include_php.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_include_php.php deleted file mode 100644 index 1b0fdaad3..000000000 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_include_php.php +++ /dev/null @@ -1,110 +0,0 @@ -smarty instanceof SmartyBC)) { - throw new SmartyException("{include_php} is deprecated, use SmartyBC class to enable"); - } - // check and get attributes - $_attr = $this->getAttributes($compiler, $args); - /** - * - * - * @var Smarty_Internal_Template $_smarty_tpl - * used in evaluated code - */ - $_smarty_tpl = $compiler->template; - $_filepath = false; - $_file = null; - eval('$_file = @' . $_attr[ 'file' ] . ';'); - if (!isset($compiler->smarty->security_policy) && file_exists($_file)) { - $_filepath = $compiler->smarty->_realpath($_file, true); - } else { - if (isset($compiler->smarty->security_policy)) { - $_dir = $compiler->smarty->security_policy->trusted_dir; - } else { - $_dir = $compiler->smarty->trusted_dir; - } - if (!empty($_dir)) { - foreach ((array)$_dir as $_script_dir) { - $_path = $compiler->smarty->_realpath($_script_dir . DIRECTORY_SEPARATOR . $_file, true); - if (file_exists($_path)) { - $_filepath = $_path; - break; - } - } - } - } - if ($_filepath === false) { - $compiler->trigger_template_error("{include_php} file '{$_file}' is not readable", null, true); - } - if (isset($compiler->smarty->security_policy)) { - $compiler->smarty->security_policy->isTrustedPHPDir($_filepath); - } - if (isset($_attr[ 'assign' ])) { - // output will be stored in a smarty variable instead of being displayed - $_assign = $_attr[ 'assign' ]; - } - $_once = '_once'; - if (isset($_attr[ 'once' ])) { - if ($_attr[ 'once' ] === 'false') { - $_once = ''; - } - } - if (isset($_assign)) { - return "assign({$_assign},ob_get_clean());\n?>"; - } else { - return "\n"; - } - } -} diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_insert.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_insert.php index 4bdc3952e..29031d910 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_insert.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_insert.php @@ -89,11 +89,11 @@ public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler) if (isset($compiler->smarty->security_policy)) { $_dir = $compiler->smarty->security_policy->trusted_dir; } else { - $_dir = $compiler->smarty instanceof SmartyBC ? $compiler->smarty->trusted_dir : null; + $_dir = null; } if (!empty($_dir)) { foreach ((array)$_dir as $_script_dir) { - $_script_dir = rtrim($_script_dir, '/\\') . DIRECTORY_SEPARATOR; + $_script_dir = rtrim($_script_dir ?? '', '/\\') . DIRECTORY_SEPARATOR; if (file_exists($_script_dir . $_script)) { $_filepath = $_script_dir . $_script; break; diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_private_foreachsection.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_private_foreachsection.php index d3aab24bb..246350dc8 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_private_foreachsection.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_private_foreachsection.php @@ -143,7 +143,7 @@ public function matchProperty($source) foreach ($this->resultOffsets as $key => $offset) { foreach ($match[ $offset ] as $m) { if (!empty($m)) { - $this->matchResults[ $key ][ strtolower($m) ] = true; + $this->matchResults[ $key ][ smarty_strtolower_ascii($m) ] = true; } } } @@ -213,12 +213,12 @@ public function matchBlockSource(Smarty_Internal_TemplateCompilerBase $compiler) */ public function compileSpecialVariable($args, Smarty_Internal_TemplateCompilerBase $compiler, $parameter) { - $tag = strtolower(trim($parameter[ 0 ], '"\'')); + $tag = smarty_strtolower_ascii(trim($parameter[ 0 ], '"\'')); $name = isset($parameter[ 1 ]) ? $compiler->getId($parameter[ 1 ]) : false; if (!$name) { $compiler->trigger_template_error("missing or illegal \$smarty.{$tag} name attribute", null, true); } - $property = isset($parameter[ 2 ]) ? strtolower($compiler->getId($parameter[ 2 ])) : false; + $property = isset($parameter[ 2 ]) ? smarty_strtolower_ascii($compiler->getId($parameter[ 2 ])) : false; if (!$property || !in_array($property, $this->nameProperties)) { $compiler->trigger_template_error("missing or illegal \$smarty.{$tag} property attribute", null, true); } diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_private_modifier.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_private_modifier.php index 72773fff8..aea082f01 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_private_modifier.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_private_modifier.php @@ -109,6 +109,9 @@ public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler, $ if (!is_object($compiler->smarty->security_policy) || $compiler->smarty->security_policy->isTrustedPhpModifier($modifier, $compiler) ) { + trigger_error('Using php-function "' . $modifier . '" as a modifier is deprecated and will be ' . + 'removed in a future release. Use Smarty::registerPlugin to explicitly register ' . + 'a custom modifier.', E_USER_DEPRECATED); $output = "{$modifier}({$params})"; } $compiler->known_modifier_type[ $modifier ] = $type; diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_private_php.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_private_php.php deleted file mode 100644 index ff48c6fbc..000000000 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_private_php.php +++ /dev/null @@ -1,253 +0,0 @@ -getAttributes($compiler, $args); - $compiler->has_code = false; - if ($_attr[ 'type' ] === 'xml') { - $compiler->tag_nocache = true; - $output = addcslashes($_attr[ 'code' ], "'\\"); - $compiler->parser->current_buffer->append_subtree( - $compiler->parser, - new Smarty_Internal_ParseTree_Tag( - $compiler->parser, - $compiler->processNocacheCode( - "\n", - true - ) - ) - ); - return ''; - } - if ($_attr[ 'type' ] !== 'tag') { - if ($compiler->php_handling === Smarty::PHP_REMOVE) { - return ''; - } elseif ($compiler->php_handling === Smarty::PHP_QUOTE) { - $output = - preg_replace_callback( - '#(<\?(?:php|=)?)|(<%)|()|(\?>)|(%>)|(<\/script>)#i', - array($this, 'quote'), - $_attr[ 'code' ] - ); - $compiler->parser->current_buffer->append_subtree( - $compiler->parser, - new Smarty_Internal_ParseTree_Text($output) - ); - return ''; - } elseif ($compiler->php_handling === Smarty::PHP_PASSTHRU || $_attr[ 'type' ] === 'unmatched') { - $compiler->tag_nocache = true; - $output = addcslashes($_attr[ 'code' ], "'\\"); - $compiler->parser->current_buffer->append_subtree( - $compiler->parser, - new Smarty_Internal_ParseTree_Tag( - $compiler->parser, - $compiler->processNocacheCode( - "\n", - true - ) - ) - ); - return ''; - } elseif ($compiler->php_handling === Smarty::PHP_ALLOW) { - if (!($compiler->smarty instanceof SmartyBC)) { - $compiler->trigger_template_error( - '$smarty->php_handling PHP_ALLOW not allowed. Use SmartyBC to enable it', - null, - true - ); - } - $compiler->has_code = true; - return $_attr[ 'code' ]; - } else { - $compiler->trigger_template_error('Illegal $smarty->php_handling value', null, true); - } - } else { - $compiler->has_code = true; - if (!($compiler->smarty instanceof SmartyBC)) { - $compiler->trigger_template_error( - '{php}{/php} tags not allowed. Use SmartyBC to enable them', - null, - true - ); - } - $ldel = preg_quote($compiler->smarty->left_delimiter, '#'); - $rdel = preg_quote($compiler->smarty->right_delimiter, '#'); - preg_match("#^({$ldel}php\\s*)((.)*?)({$rdel})#", $_attr[ 'code' ], $match); - if (!empty($match[ 2 ])) { - if ('nocache' === trim($match[ 2 ])) { - $compiler->tag_nocache = true; - } else { - $compiler->trigger_template_error("illegal value of option flag '{$match[2]}'", null, true); - } - } - return preg_replace( - array("#^{$ldel}\\s*php\\s*(.)*?{$rdel}#", "#{$ldel}\\s*/\\s*php\\s*{$rdel}$#"), - array(''), - $_attr[ 'code' ] - ); - } - } - - /** - * Lexer code for PHP tags - * - * This code has been moved from lexer here fo easier debugging and maintenance - * - * @param Smarty_Internal_Templatelexer $lex - * - * @throws \SmartyCompilerException - */ - public function parsePhp(Smarty_Internal_Templatelexer $lex) - { - $lex->token = Smarty_Internal_Templateparser::TP_PHP; - $close = 0; - $lex->taglineno = $lex->line; - $closeTag = '?>'; - if (strpos($lex->value, 'is_xml = true; - $lex->phpType = 'xml'; - return; - } elseif (strpos($lex->value, 'phpType = 'php'; - } elseif (strpos($lex->value, '<%') === 0) { - $lex->phpType = 'asp'; - $closeTag = '%>'; - } elseif (strpos($lex->value, '%>') === 0) { - $lex->phpType = 'unmatched'; - } elseif (strpos($lex->value, '?>') === 0) { - if ($lex->is_xml) { - $lex->is_xml = false; - $lex->phpType = 'xml'; - return; - } - $lex->phpType = 'unmatched'; - } elseif (strpos($lex->value, 'phpType = 'script'; - $closeTag = ''; - } elseif (strpos($lex->value, $lex->smarty->left_delimiter) === 0) { - if ($lex->isAutoLiteral()) { - $lex->token = Smarty_Internal_Templateparser::TP_TEXT; - return; - } - $closeTag = "{$lex->smarty->left_delimiter}/php{$lex->smarty->right_delimiter}"; - if ($lex->value === $closeTag) { - $lex->compiler->trigger_template_error("unexpected closing tag '{$closeTag}'"); - } - $lex->phpType = 'tag'; - } - if ($lex->phpType === 'unmatched') { - return; - } - if (($lex->phpType === 'php' || $lex->phpType === 'asp') - && - ($lex->compiler->php_handling === Smarty::PHP_PASSTHRU || - $lex->compiler->php_handling === Smarty::PHP_QUOTE) - ) { - return; - } - $start = $lex->counter + strlen($lex->value); - $body = true; - if (preg_match('~' . preg_quote($closeTag, '~') . '~i', $lex->data, $match, PREG_OFFSET_CAPTURE, $start)) { - $close = $match[ 0 ][ 1 ]; - } else { - $lex->compiler->trigger_template_error("missing closing tag '{$closeTag}'"); - } - while ($body) { - if (preg_match( - '~([/][*])|([/][/][^\n]*)|(\'[^\'\\\\]*(?:\\.[^\'\\\\]*)*\')|("[^"\\\\]*(?:\\.[^"\\\\]*)*")~', - $lex->data, - $match, - PREG_OFFSET_CAPTURE, - $start - ) - ) { - $value = $match[ 0 ][ 0 ]; - $from = $pos = $match[ 0 ][ 1 ]; - if ($pos > $close) { - $body = false; - } else { - $start = $pos + strlen($value); - $phpCommentStart = $value === '/*'; - if ($phpCommentStart) { - $phpCommentEnd = preg_match('~([*][/])~', $lex->data, $match, PREG_OFFSET_CAPTURE, $start); - if ($phpCommentEnd) { - $pos2 = $match[ 0 ][ 1 ]; - $start = $pos2 + strlen($match[ 0 ][ 0 ]); - } - } - while ($close > $pos && $close < $start) { - if (preg_match( - '~' . preg_quote($closeTag, '~') . '~i', - $lex->data, - $match, - PREG_OFFSET_CAPTURE, - $from - ) - ) { - $close = $match[ 0 ][ 1 ]; - $from = $close + strlen($match[ 0 ][ 0 ]); - } else { - $lex->compiler->trigger_template_error("missing closing tag '{$closeTag}'"); - } - } - if ($phpCommentStart && (!$phpCommentEnd || $pos2 > $close)) { - $lex->taglineno = $lex->line + substr_count(substr($lex->data, $lex->counter, $start), "\n"); - $lex->compiler->trigger_template_error("missing PHP comment closing tag '*/'"); - } - } - } else { - $body = false; - } - } - $lex->value = substr($lex->data, $lex->counter, $close + strlen($closeTag) - $lex->counter); - } - - /* - * Call back function for $php_handling = PHP_QUOTE - * - */ - /** - * @param $match - * - * @return string - */ - private function quote($match) - { - return htmlspecialchars($match[ 0 ], ENT_QUOTES); - } -} diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_private_print_expression.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_private_print_expression.php index 23cae8aef..96bd37244 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_private_print_expression.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_private_print_expression.php @@ -93,7 +93,7 @@ public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler, $ } // autoescape html if ($compiler->template->smarty->escape_html) { - $output = "htmlspecialchars({$output}, ENT_QUOTES, '" . addslashes(Smarty::$_CHARSET) . "')"; + $output = "htmlspecialchars((string) {$output}, ENT_QUOTES, '" . addslashes(Smarty::$_CHARSET) . "')"; } // loop over registered filters if (!empty($compiler->template->smarty->registered_filters[ Smarty::FILTER_VARIABLE ])) { diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_private_special_variable.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_private_special_variable.php index d53ef51ff..590cba5af 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_private_special_variable.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_private_special_variable.php @@ -29,7 +29,7 @@ class Smarty_Internal_Compile_Private_Special_Variable extends Smarty_Internal_C public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler, $parameter) { $_index = preg_split("/\]\[/", substr($parameter, 1, strlen($parameter) - 2)); - $variable = strtolower($compiler->getId($_index[ 0 ])); + $variable = smarty_strtolower_ascii($compiler->getId($_index[ 0 ])); if ($variable === false) { $compiler->trigger_template_error("special \$Smarty variable name index can not be variable", null, true); } @@ -40,7 +40,7 @@ public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler, $ case 'foreach': case 'section': if (!isset(Smarty_Internal_TemplateCompilerBase::$_tag_objects[ $variable ])) { - $class = 'Smarty_Internal_Compile_' . ucfirst($variable); + $class = 'Smarty_Internal_Compile_' . smarty_ucfirst_ascii($variable); Smarty_Internal_TemplateCompilerBase::$_tag_objects[ $variable ] = new $class; } return Smarty_Internal_TemplateCompilerBase::$_tag_objects[ $variable ]->compileSpecialVariable( @@ -76,7 +76,7 @@ public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler, $ $compiler->trigger_template_error("(secure mode) super globals not permitted"); break; } - $compiled_ref = '$_' . strtoupper($variable); + $compiled_ref = '$_' . smarty_strtoupper_ascii($variable); break; case 'template': return 'basename($_smarty_tpl->source->filepath)'; diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_config_file_compiler.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_config_file_compiler.php index 90c5dcefa..469b9667a 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_config_file_compiler.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_config_file_compiler.php @@ -157,10 +157,12 @@ public function compileTemplate(Smarty_Internal_Template $template) $this->smarty->_debug->end_compile($this->template); } // template header code - $template_header = - "template->source->filepath}' */ ?>\n"; + $template_header = sprintf( + "\n", + Smarty::SMARTY_VERSION, + date("Y-m-d H:i:s"), + str_replace('*/', '* /' , $this->template->source->filepath) + ); $code = 'smarty->ext->configLoad->_loadConfigVars($_smarty_tpl, ' . var_export($this->config_data, true) . '); ?>'; return $template_header . $this->template->smarty->ext->_codeFrame->create($this->template, $code); diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_data.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_data.php index 98e3e57b3..1b64185b8 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_data.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_data.php @@ -121,7 +121,7 @@ public function assign($tpl_var, $value = null, $nocache = false) * appends values to template variables * * @api Smarty::append() - * @link http://www.smarty.net/docs/en/api.append.tpl + * @link https://www.smarty.net/docs/en/api.append.tpl * * @param array|string $tpl_var the template variable name(s) * @param mixed $value the value to append @@ -182,7 +182,7 @@ public function assignByRef($tpl_var, &$value, $nocache = false) * Returns a single or all template variables * * @api Smarty::getTemplateVars() - * @link http://www.smarty.net/docs/en/api.get.template.vars.tpl + * @link https://www.smarty.net/docs/en/api.get.template.vars.tpl * * @param string $varName variable name or null * @param \Smarty_Internal_Data|\Smarty_Internal_Template|\Smarty $_ptr optional pointer to data object @@ -195,26 +195,6 @@ public function getTemplateVars($varName = null, Smarty_Internal_Data $_ptr = nu return $this->ext->getTemplateVars->getTemplateVars($this, $varName, $_ptr, $searchParents); } - /** - * gets the object of a Smarty variable - * - * @param string $variable the name of the Smarty variable - * @param Smarty_Internal_Data $_ptr optional pointer to data object - * @param boolean $searchParents search also in parent data - * @param bool $error_enable - * - * @return Smarty_Variable|Smarty_Undefined_Variable the object of the variable - * @deprecated since 3.1.28 please use Smarty_Internal_Data::getTemplateVars() instead. - */ - public function getVariable( - $variable = null, - Smarty_Internal_Data $_ptr = null, - $searchParents = true, - $error_enable = true - ) { - return $this->ext->getTemplateVars->_getVariable($this, $variable, $_ptr, $searchParents, $error_enable); - } - /** * Follow the parent chain an merge template and config variables * diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_debug.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_debug.php index 24b233e26..570819d26 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_debug.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_debug.php @@ -210,7 +210,7 @@ public function display_debug($obj, $full = false) // copy the working dirs from application $debObj->setCompileDir($smarty->getCompileDir()); // init properties by hand as user may have edited the original Smarty class - $debObj->setPluginsDir(is_dir(dirname(__FILE__) . '/../plugins') ? dirname(__FILE__) . + $debObj->setPluginsDir(is_dir(__DIR__ . '/../plugins') ? __DIR__ . '/../plugins' : $smarty->getPluginsDir()); $debObj->force_compile = false; $debObj->compile_check = Smarty::COMPILECHECK_ON; @@ -221,7 +221,7 @@ public function display_debug($obj, $full = false) $debObj->debugging_ctrl = 'NONE'; $debObj->error_reporting = E_ALL & ~E_NOTICE; $debObj->debug_tpl = - isset($smarty->debug_tpl) ? $smarty->debug_tpl : 'file:' . dirname(__FILE__) . '/../debug.tpl'; + isset($smarty->debug_tpl) ? $smarty->debug_tpl : 'file:' . __DIR__ . '/../debug.tpl'; $debObj->registered_plugins = array(); $debObj->registered_resources = array(); $debObj->registered_filters = array(); diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_errorhandler.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_errorhandler.php index 56dca18fa..c2b653ef4 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_errorhandler.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_errorhandler.php @@ -1,61 +1,60 @@ previousErrorHandler = set_error_handler([$this, 'handleError']); + } + + /** + * Disable error handler + */ + public function deactivate() { + restore_error_handler(); + $this->previousErrorHandler = null; } /** * Error Handler to mute expected messages * - * @link http://php.net/set_error_handler + * @link https://php.net/set_error_handler * * @param integer $errno Error level * @param $errstr @@ -65,49 +64,21 @@ public static function muteExpectedErrors() * * @return bool */ - public static function mutingErrorHandler($errno, $errstr, $errfile, $errline, $errcontext = array()) + public function handleError($errno, $errstr, $errfile, $errline, $errcontext = []) { - $_is_muted_directory = false; - // add the SMARTY_DIR to the list of muted directories - if (!isset(self::$mutedDirectories[ SMARTY_DIR ])) { - $smarty_dir = realpath(SMARTY_DIR); - if ($smarty_dir !== false) { - self::$mutedDirectories[ SMARTY_DIR ] = - array('file' => $smarty_dir, 'length' => strlen($smarty_dir),); - } + if ($this->allowUndefinedVars && $errstr == 'Attempt to read property "value" on null') { + return; // suppresses this error } - // walk the muted directories and test against $errfile - foreach (self::$mutedDirectories as $key => &$dir) { - if (!$dir) { - // resolve directory and length for speedy comparisons - $file = realpath($key); - if ($file === false) { - // this directory does not exist, remove and skip it - unset(self::$mutedDirectories[ $key ]); - continue; - } - $dir = array('file' => $file, 'length' => strlen($file),); - } - if (!strncmp($errfile, $dir[ 'file' ], $dir[ 'length' ])) { - $_is_muted_directory = true; - break; - } - } - // pass to next error handler if this error did not occur inside SMARTY_DIR - // or the error was within smarty but masked to be ignored - if (!$_is_muted_directory || ($errno && $errno & error_reporting())) { - if (self::$previousErrorHandler) { - return call_user_func( - self::$previousErrorHandler, - $errno, - $errstr, - $errfile, - $errline, - $errcontext - ); - } else { - return false; - } + + if ($this->allowUndefinedArrayKeys && preg_match( + '/^(Undefined array key|Trying to access array offset on value of type null)/', + $errstr + )) { + return; // suppresses this error } + + // pass all other errors through to the previous error handler or to the default PHP error handler + return $this->previousErrorHandler ? + call_user_func($this->previousErrorHandler, $errno, $errstr, $errfile, $errline, $errcontext) : false; } } diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_extension_handler.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_extension_handler.php index b07615526..3ef040ab1 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_extension_handler.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_extension_handler.php @@ -36,6 +36,7 @@ * @property Smarty_Internal_Method_RegisterPlugin $registerPlugin * @property mixed|\Smarty_Template_Cached configLoad */ +#[\AllowDynamicProperties] class Smarty_Internal_Extension_Handler { public $objType = null; @@ -88,20 +89,19 @@ public function _callExternalMethod(Smarty_Internal_Data $data, $name, $args) $objType = $data->_objType; $propertyType = false; if (!isset($this->resolvedProperties[ $match[ 0 ] ][ $objType ])) { - $property = isset($this->resolvedProperties[ 'property' ][ $basename ]) ? - $this->resolvedProperties[ 'property' ][ $basename ] : - $property = $this->resolvedProperties[ 'property' ][ $basename ] = strtolower( - join( - '_', - preg_split( - '/([A-Z][^A-Z]*)/', - $basename, - -1, - PREG_SPLIT_NO_EMPTY | - PREG_SPLIT_DELIM_CAPTURE - ) + $property = $this->resolvedProperties['property'][$basename] ?? + $this->resolvedProperties['property'][$basename] = smarty_strtolower_ascii( + join( + '_', + preg_split( + '/([A-Z][^A-Z]*)/', + $basename, + -1, + PREG_SPLIT_NO_EMPTY | + PREG_SPLIT_DELIM_CAPTURE ) - ); + ) + ); if ($property !== false) { if (property_exists($data, $property)) { $propertyType = $this->resolvedProperties[ $match[ 0 ] ][ $objType ] = 1; @@ -145,7 +145,7 @@ public function _callExternalMethod(Smarty_Internal_Data $data, $name, $args) public function upperCase($name) { $_name = explode('_', $name); - $_name = array_map('ucfirst', $_name); + $_name = array_map('smarty_ucfirst_ascii', $_name); return implode('_', $_name); } diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_append.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_append.php index 881375efe..e207734e8 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_append.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_append.php @@ -22,7 +22,7 @@ class Smarty_Internal_Method_Append * appends values to template variables * * @api Smarty::append() - * @link http://www.smarty.net/docs/en/api.append.tpl + * @link https://www.smarty.net/docs/en/api.append.tpl * * @param \Smarty_Internal_Data|\Smarty_Internal_Template|\Smarty $data * @param array|string $tpl_var the template variable name(s) diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_appendbyref.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_appendbyref.php index c95904460..b5be69b54 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_appendbyref.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_appendbyref.php @@ -15,7 +15,7 @@ class Smarty_Internal_Method_AppendByRef * appends values to template variables by reference * * @api Smarty::appendByRef() - * @link http://www.smarty.net/docs/en/api.append.by.ref.tpl + * @link https://www.smarty.net/docs/en/api.append.by.ref.tpl * * @param \Smarty_Internal_Data|\Smarty_Internal_Template|\Smarty $data * @param string $tpl_var the template variable name diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_clearallassign.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_clearallassign.php index 29ff2ffb0..6fb0c8f3d 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_clearallassign.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_clearallassign.php @@ -22,7 +22,7 @@ class Smarty_Internal_Method_ClearAllAssign * clear all the assigned template variables. * * @api Smarty::clearAllAssign() - * @link http://www.smarty.net/docs/en/api.clear.all.assign.tpl + * @link https://www.smarty.net/docs/en/api.clear.all.assign.tpl * * @param \Smarty_Internal_Data|\Smarty_Internal_Template|\Smarty $data * diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_clearallcache.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_clearallcache.php index 30d55f7d2..b74d30580 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_clearallcache.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_clearallcache.php @@ -22,7 +22,7 @@ class Smarty_Internal_Method_ClearAllCache * Empty cache folder * * @api Smarty::clearAllCache() - * @link http://www.smarty.net/docs/en/api.clear.all.cache.tpl + * @link https://www.smarty.net/docs/en/api.clear.all.cache.tpl * * @param \Smarty $smarty * @param integer $exp_time expiration time diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_clearassign.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_clearassign.php index 22bfa2d31..12b755c06 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_clearassign.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_clearassign.php @@ -22,7 +22,7 @@ class Smarty_Internal_Method_ClearAssign * clear the given assigned template variable(s). * * @api Smarty::clearAssign() - * @link http://www.smarty.net/docs/en/api.clear.assign.tpl + * @link https://www.smarty.net/docs/en/api.clear.assign.tpl * * @param \Smarty_Internal_Data|\Smarty_Internal_Template|\Smarty $data * @param string|array $tpl_var the template variable(s) to clear diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_clearcache.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_clearcache.php index a5dd4e26e..df766eee8 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_clearcache.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_clearcache.php @@ -22,7 +22,7 @@ class Smarty_Internal_Method_ClearCache * Empty cache for a specific template * * @api Smarty::clearCache() - * @link http://www.smarty.net/docs/en/api.clear.cache.tpl + * @link https://www.smarty.net/docs/en/api.clear.cache.tpl * * @param \Smarty $smarty * @param string $template_name template name diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_clearcompiledtemplate.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_clearcompiledtemplate.php index bf4929807..db0a49b00 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_clearcompiledtemplate.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_clearcompiledtemplate.php @@ -22,7 +22,7 @@ class Smarty_Internal_Method_ClearCompiledTemplate * Delete compiled template file * * @api Smarty::clearCompiledTemplate() - * @link http://www.smarty.net/docs/en/api.clear.compiled.template.tpl + * @link https://www.smarty.net/docs/en/api.clear.compiled.template.tpl * * @param \Smarty $smarty * @param string $resource_name template name diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_clearconfig.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_clearconfig.php index 15bf492a1..d1b730322 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_clearconfig.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_clearconfig.php @@ -22,7 +22,7 @@ class Smarty_Internal_Method_ClearConfig * clear a single or all config variables * * @api Smarty::clearConfig() - * @link http://www.smarty.net/docs/en/api.clear.config.tpl + * @link https://www.smarty.net/docs/en/api.clear.config.tpl * * @param \Smarty_Internal_Data|\Smarty_Internal_Template|\Smarty $data * @param string|null $name variable name or null diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_configload.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_configload.php index 2e6254880..c3174d2d0 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_configload.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_configload.php @@ -22,7 +22,7 @@ class Smarty_Internal_Method_ConfigLoad * load a config file, optionally load just selected sections * * @api Smarty::configLoad() - * @link http://www.smarty.net/docs/en/api.config.load.tpl + * @link https://www.smarty.net/docs/en/api.config.load.tpl * * @param \Smarty_Internal_Data|\Smarty_Internal_Template|\Smarty $data * @param string $config_file filename @@ -42,7 +42,7 @@ public function configLoad(Smarty_Internal_Data $data, $config_file, $sections = * load a config file, optionally load just selected sections * * @api Smarty::configLoad() - * @link http://www.smarty.net/docs/en/api.config.load.tpl + * @link https://www.smarty.net/docs/en/api.config.load.tpl * * @param \Smarty|\Smarty_Internal_Data|\Smarty_Internal_Template $data * @param string $config_file filename diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_createdata.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_createdata.php index f95097519..c684c0870 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_createdata.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_createdata.php @@ -22,7 +22,7 @@ class Smarty_Internal_Method_CreateData * creates a data object * * @api Smarty::createData() - * @link http://www.smarty.net/docs/en/api.create.data.tpl + * @link https://www.smarty.net/docs/en/api.create.data.tpl * * @param \Smarty_Internal_TemplateBase|\Smarty_Internal_Template|\Smarty $obj * @param \Smarty_Internal_Template|\Smarty_Internal_Data|\Smarty_Data|\Smarty $parent next higher level of Smarty diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_getconfigvars.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_getconfigvars.php index 1d11e44c1..763bdf989 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_getconfigvars.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_getconfigvars.php @@ -22,7 +22,7 @@ class Smarty_Internal_Method_GetConfigVars * Returns a single or all config variables * * @api Smarty::getConfigVars() - * @link http://www.smarty.net/docs/en/api.get.config.vars.tpl + * @link https://www.smarty.net/docs/en/api.get.config.vars.tpl * * @param \Smarty_Internal_Data|\Smarty_Internal_Template|\Smarty $data * @param string $varname variable name or null diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_getregisteredobject.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_getregisteredobject.php index df6ede130..0b3a071d3 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_getregisteredobject.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_getregisteredobject.php @@ -22,7 +22,7 @@ class Smarty_Internal_Method_GetRegisteredObject * return a reference to a registered object * * @api Smarty::getRegisteredObject() - * @link http://www.smarty.net/docs/en/api.get.registered.object.tpl + * @link https://www.smarty.net/docs/en/api.get.registered.object.tpl * * @param \Smarty_Internal_TemplateBase|\Smarty_Internal_Template|\Smarty $obj * @param string $object_name object name diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_gettags.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_gettags.php index c07ae07ec..0d1335a8d 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_gettags.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_gettags.php @@ -22,7 +22,7 @@ class Smarty_Internal_Method_GetTags * Return array of tag/attributes of all tags used by an template * * @api Smarty::getTags() - * @link http://www.smarty.net/docs/en/api.get.tags.tpl + * @link https://www.smarty.net/docs/en/api.get.tags.tpl * * @param \Smarty_Internal_TemplateBase|\Smarty_Internal_Template|\Smarty $obj * @param null|string|Smarty_Internal_Template $template diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_gettemplatevars.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_gettemplatevars.php index 9ef7d46bb..0470785b1 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_gettemplatevars.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_gettemplatevars.php @@ -22,7 +22,7 @@ class Smarty_Internal_Method_GetTemplateVars * Returns a single or all template variables * * @api Smarty::getTemplateVars() - * @link http://www.smarty.net/docs/en/api.get.template.vars.tpl + * @link https://www.smarty.net/docs/en/api.get.template.vars.tpl * * @param \Smarty_Internal_Data|\Smarty_Internal_Template|\Smarty $data * @param string $varName variable name or null diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_loadfilter.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_loadfilter.php index 66d80d474..af788a24e 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_loadfilter.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_loadfilter.php @@ -30,7 +30,7 @@ class Smarty_Internal_Method_LoadFilter * * @api Smarty::loadFilter() * - * @link http://www.smarty.net/docs/en/api.load.filter.tpl + * @link https://www.smarty.net/docs/en/api.load.filter.tpl * * @param \Smarty_Internal_TemplateBase|\Smarty_Internal_Template|\Smarty $obj * @param string $type filter type diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_loadplugin.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_loadplugin.php index 3bd659cb8..6ddcaec94 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_loadplugin.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_loadplugin.php @@ -40,7 +40,7 @@ public function loadPlugin(Smarty $smarty, $plugin_name, $check) throw new SmartyException("plugin {$plugin_name} is not a valid name format"); } if (!empty($match[ 2 ])) { - $file = SMARTY_SYSPLUGINS_DIR . strtolower($plugin_name) . '.php'; + $file = SMARTY_SYSPLUGINS_DIR . smarty_strtolower_ascii($plugin_name) . '.php'; if (isset($this->plugin_files[ $file ])) { if ($this->plugin_files[ $file ] !== false) { return $this->plugin_files[ $file ]; @@ -60,7 +60,7 @@ public function loadPlugin(Smarty $smarty, $plugin_name, $check) } // plugin filename is expected to be: [type].[name].php $_plugin_filename = "{$match[1]}.{$match[4]}.php"; - $_lower_filename = strtolower($_plugin_filename); + $_lower_filename = smarty_strtolower_ascii($_plugin_filename); if (isset($this->plugin_files)) { if (isset($this->plugin_files[ 'plugins_dir' ][ $_lower_filename ])) { if (!$smarty->use_include_path || $this->plugin_files[ 'plugins_dir' ][ $_lower_filename ] !== false) { diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_mustcompile.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_mustcompile.php index 39318838e..381346c8f 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_mustcompile.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_mustcompile.php @@ -32,7 +32,7 @@ public function mustCompile(Smarty_Internal_Template $_template) { if (!$_template->source->exists) { if ($_template->_isSubTpl()) { - $parent_resource = " in '$_template->parent->template_resource}'"; + $parent_resource = " in '{$_template->parent->template_resource}'"; } else { $parent_resource = ''; } diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_registercacheresource.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_registercacheresource.php index 648365619..5608b3fd0 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_registercacheresource.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_registercacheresource.php @@ -22,7 +22,7 @@ class Smarty_Internal_Method_RegisterCacheResource * Registers a resource to fetch a template * * @api Smarty::registerCacheResource() - * @link http://www.smarty.net/docs/en/api.register.cacheresource.tpl + * @link https://www.smarty.net/docs/en/api.register.cacheresource.tpl * * @param \Smarty_Internal_TemplateBase|\Smarty_Internal_Template|\Smarty $obj * @param string $name name of resource type diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_registerclass.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_registerclass.php index 8d18547e2..76a69c6e5 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_registerclass.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_registerclass.php @@ -22,7 +22,7 @@ class Smarty_Internal_Method_RegisterClass * Registers static classes to be used in templates * * @api Smarty::registerClass() - * @link http://www.smarty.net/docs/en/api.register.class.tpl + * @link https://www.smarty.net/docs/en/api.register.class.tpl * * @param \Smarty_Internal_TemplateBase|\Smarty_Internal_Template|\Smarty $obj * @param string $class_name diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_registerdefaultpluginhandler.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_registerdefaultpluginhandler.php index a9fb78dc6..4cda5b056 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_registerdefaultpluginhandler.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_registerdefaultpluginhandler.php @@ -22,7 +22,7 @@ class Smarty_Internal_Method_RegisterDefaultPluginHandler * Registers a default plugin handler * * @api Smarty::registerDefaultPluginHandler() - * @link http://www.smarty.net/docs/en/api.register.default.plugin.handler.tpl + * @link https://www.smarty.net/docs/en/api.register.default.plugin.handler.tpl * * @param \Smarty_Internal_TemplateBase|\Smarty_Internal_Template|\Smarty $obj * @param callable $callback class/method name diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_registerfilter.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_registerfilter.php index c0f9fff10..9719eb2b6 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_registerfilter.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_registerfilter.php @@ -30,7 +30,7 @@ class Smarty_Internal_Method_RegisterFilter * * @api Smarty::registerFilter() * - * @link http://www.smarty.net/docs/en/api.register.filter.tpl + * @link https://www.smarty.net/docs/en/api.register.filter.tpl * * @param \Smarty_Internal_TemplateBase|\Smarty_Internal_Template|\Smarty $obj * @param string $type filter type diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_registerobject.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_registerobject.php index 4646e4f22..8e6fe0521 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_registerobject.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_registerobject.php @@ -22,7 +22,7 @@ class Smarty_Internal_Method_RegisterObject * Registers object to be used in templates * * @api Smarty::registerObject() - * @link http://www.smarty.net/docs/en/api.register.object.tpl + * @link https://www.smarty.net/docs/en/api.register.object.tpl * * @param \Smarty_Internal_TemplateBase|\Smarty_Internal_Template|\Smarty $obj * @param string $object_name diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_registerplugin.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_registerplugin.php index ed18d84bb..74c0ae908 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_registerplugin.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_registerplugin.php @@ -22,7 +22,7 @@ class Smarty_Internal_Method_RegisterPlugin * Registers plugin to be used in templates * * @api Smarty::registerPlugin() - * @link http://www.smarty.net/docs/en/api.register.plugin.tpl + * @link https://www.smarty.net/docs/en/api.register.plugin.tpl * * @param \Smarty_Internal_TemplateBase|\Smarty_Internal_Template|\Smarty $obj * @param string $type plugin type diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_registerresource.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_registerresource.php index 7c7d0f78a..302657ae0 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_registerresource.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_registerresource.php @@ -22,25 +22,18 @@ class Smarty_Internal_Method_RegisterResource * Registers a resource to fetch a template * * @api Smarty::registerResource() - * @link http://www.smarty.net/docs/en/api.register.resource.tpl + * @link https://www.smarty.net/docs/en/api.register.resource.tpl * * @param \Smarty_Internal_TemplateBase|\Smarty_Internal_Template|\Smarty $obj * @param string $name name of resource type - * @param Smarty_Resource|array $resource_handler or instance of - * Smarty_Resource, - * or array of - * callbacks to - * handle - * resource - * (deprecated) + * @param Smarty_Resource $resource_handler instance of Smarty_Resource * * @return \Smarty|\Smarty_Internal_Template */ - public function registerResource(Smarty_Internal_TemplateBase $obj, $name, $resource_handler) + public function registerResource(Smarty_Internal_TemplateBase $obj, $name, Smarty_Resource $resource_handler) { $smarty = $obj->_getSmartyObj(); - $smarty->registered_resources[ $name ] = - $resource_handler instanceof Smarty_Resource ? $resource_handler : array($resource_handler, false); + $smarty->registered_resources[ $name ] = $resource_handler; return $obj; } } diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_unloadfilter.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_unloadfilter.php index 55e1596be..e41e8dffc 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_unloadfilter.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_unloadfilter.php @@ -16,7 +16,7 @@ class Smarty_Internal_Method_UnloadFilter extends Smarty_Internal_Method_LoadFil * * @api Smarty::unloadFilter() * - * @link http://www.smarty.net/docs/en/api.unload.filter.tpl + * @link https://www.smarty.net/docs/en/api.unload.filter.tpl * * @param \Smarty_Internal_TemplateBase|\Smarty_Internal_Template|\Smarty $obj * @param string $type filter type diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_unregistercacheresource.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_unregistercacheresource.php index b99903867..377397e97 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_unregistercacheresource.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_unregistercacheresource.php @@ -22,7 +22,7 @@ class Smarty_Internal_Method_UnregisterCacheResource * Registers a resource to fetch a template * * @api Smarty::unregisterCacheResource() - * @link http://www.smarty.net/docs/en/api.unregister.cacheresource.tpl + * @link https://www.smarty.net/docs/en/api.unregister.cacheresource.tpl * * @param \Smarty_Internal_TemplateBase|\Smarty_Internal_Template|\Smarty $obj * @param $name diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_unregisterfilter.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_unregisterfilter.php index 9cb494a52..ebc9337d0 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_unregisterfilter.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_unregisterfilter.php @@ -16,7 +16,7 @@ class Smarty_Internal_Method_UnregisterFilter extends Smarty_Internal_Method_Reg * * @api Smarty::unregisterFilter() * - * @link http://www.smarty.net/docs/en/api.unregister.filter.tpl + * @link https://www.smarty.net/docs/en/api.unregister.filter.tpl * * @param \Smarty_Internal_TemplateBase|\Smarty_Internal_Template|\Smarty $obj * @param string $type filter type diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_unregisterobject.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_unregisterobject.php index 1e592b339..77d619637 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_unregisterobject.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_unregisterobject.php @@ -22,7 +22,7 @@ class Smarty_Internal_Method_UnregisterObject * Registers plugin to be used in templates * * @api Smarty::unregisterObject() - * @link http://www.smarty.net/docs/en/api.unregister.object.tpl + * @link https://www.smarty.net/docs/en/api.unregister.object.tpl * * @param \Smarty_Internal_TemplateBase|\Smarty_Internal_Template|\Smarty $obj * @param string $object_name name of object diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_unregisterplugin.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_unregisterplugin.php index f39e31678..2431d5c23 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_unregisterplugin.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_unregisterplugin.php @@ -22,7 +22,7 @@ class Smarty_Internal_Method_UnregisterPlugin * Registers plugin to be used in templates * * @api Smarty::unregisterPlugin() - * @link http://www.smarty.net/docs/en/api.unregister.plugin.tpl + * @link https://www.smarty.net/docs/en/api.unregister.plugin.tpl * * @param \Smarty_Internal_TemplateBase|\Smarty_Internal_Template|\Smarty $obj * @param string $type plugin type diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_unregisterresource.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_unregisterresource.php index a79db4299..bbb6a861d 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_unregisterresource.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_method_unregisterresource.php @@ -22,7 +22,7 @@ class Smarty_Internal_Method_UnregisterResource * Registers a resource to fetch a template * * @api Smarty::unregisterResource() - * @link http://www.smarty.net/docs/en/api.unregister.resource.tpl + * @link https://www.smarty.net/docs/en/api.unregister.resource.tpl * * @param \Smarty_Internal_TemplateBase|\Smarty_Internal_Template|\Smarty $obj * @param string $type name of resource type diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_parsetree_template.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_parsetree_template.php index ab4c3ec3b..829c420fe 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_parsetree_template.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_parsetree_template.php @@ -87,83 +87,83 @@ public function to_smarty_php(Smarty_Internal_Templateparser $parser) $code = ''; foreach ($this->getChunkedSubtrees() as $chunk) { - $text = ''; - switch ($chunk['mode']) { - case 'textstripped': - foreach ($chunk['subtrees'] as $subtree) { - $text .= $subtree->to_smarty_php($parser); - } - $code .= preg_replace( - '/((<%)|(%>)|(<\?php)|(<\?)|(\?>)|(<\/?script))/', - "\n", - $parser->compiler->processText($text) - ); - break; - case 'text': - foreach ($chunk['subtrees'] as $subtree) { - $text .= $subtree->to_smarty_php($parser); - } - $code .= preg_replace( - '/((<%)|(%>)|(<\?php)|(<\?)|(\?>)|(<\/?script))/', - "\n", - $text - ); - break; - case 'tag': - foreach ($chunk['subtrees'] as $subtree) { - $text = $parser->compiler->appendCode($text, $subtree->to_smarty_php($parser)); - } - $code .= $text; - break; - default: - foreach ($chunk['subtrees'] as $subtree) { - $text = $subtree->to_smarty_php($parser); - } - $code .= $text; + $text = ''; + switch ($chunk['mode']) { + case 'textstripped': + foreach ($chunk['subtrees'] as $subtree) { + $text .= $subtree->to_smarty_php($parser); + } + $code .= preg_replace( + '/((<%)|(%>)|(<\?php)|(<\?)|(\?>)|(<\/?script))/', + "\n", + $parser->compiler->processText($text) + ); + break; + case 'text': + foreach ($chunk['subtrees'] as $subtree) { + $text .= $subtree->to_smarty_php($parser); + } + $code .= preg_replace( + '/((<%)|(%>)|(<\?php)|(<\?)|(\?>)|(<\/?script))/', + "\n", + $text + ); + break; + case 'tag': + foreach ($chunk['subtrees'] as $subtree) { + $text = $parser->compiler->appendCode($text, $subtree->to_smarty_php($parser)); + } + $code .= $text; + break; + default: + foreach ($chunk['subtrees'] as $subtree) { + $text = $subtree->to_smarty_php($parser); + } + $code .= $text; - } + } } return $code; } private function getChunkedSubtrees() { - $chunks = array(); - $currentMode = null; - $currentChunk = array(); - for ($key = 0, $cnt = count($this->subtrees); $key < $cnt; $key++) { + $chunks = array(); + $currentMode = null; + $currentChunk = array(); + for ($key = 0, $cnt = count($this->subtrees); $key < $cnt; $key++) { - if ($this->subtrees[ $key ]->data === '' && in_array($currentMode, array('textstripped', 'text', 'tag'))) { - continue; - } + if ($this->subtrees[ $key ]->data === '' && in_array($currentMode, array('textstripped', 'text', 'tag'))) { + continue; + } - if ($this->subtrees[ $key ] instanceof Smarty_Internal_ParseTree_Text - && $this->subtrees[ $key ]->isToBeStripped()) { - $newMode = 'textstripped'; - } elseif ($this->subtrees[ $key ] instanceof Smarty_Internal_ParseTree_Text) { - $newMode = 'text'; - } elseif ($this->subtrees[ $key ] instanceof Smarty_Internal_ParseTree_Tag) { - $newMode = 'tag'; - } else { - $newMode = 'other'; - } + if ($this->subtrees[ $key ] instanceof Smarty_Internal_ParseTree_Text + && $this->subtrees[ $key ]->isToBeStripped()) { + $newMode = 'textstripped'; + } elseif ($this->subtrees[ $key ] instanceof Smarty_Internal_ParseTree_Text) { + $newMode = 'text'; + } elseif ($this->subtrees[ $key ] instanceof Smarty_Internal_ParseTree_Tag) { + $newMode = 'tag'; + } else { + $newMode = 'other'; + } - if ($newMode == $currentMode) { - $currentChunk[] = $this->subtrees[ $key ]; - } else { - $chunks[] = array( - 'mode' => $currentMode, - 'subtrees' => $currentChunk - ); - $currentMode = $newMode; - $currentChunk = array($this->subtrees[ $key ]); - } - } - if ($currentMode && $currentChunk) { - $chunks[] = array( - 'mode' => $currentMode, - 'subtrees' => $currentChunk - ); - } - return $chunks; + if ($newMode == $currentMode) { + $currentChunk[] = $this->subtrees[ $key ]; + } else { + $chunks[] = array( + 'mode' => $currentMode, + 'subtrees' => $currentChunk + ); + $currentMode = $newMode; + $currentChunk = array($this->subtrees[ $key ]); + } + } + if ($currentMode && $currentChunk) { + $chunks[] = array( + 'mode' => $currentMode, + 'subtrees' => $currentChunk + ); + } + return $chunks; } } diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_parsetree_text.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_parsetree_text.php index 399e84941..58116c811 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_parsetree_text.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_parsetree_text.php @@ -17,30 +17,30 @@ class Smarty_Internal_ParseTree_Text extends Smarty_Internal_ParseTree { - /** - * Wether this section should be stripped on output to smarty php - * @var bool - */ - private $toBeStripped = false; + /** + * Wether this section should be stripped on output to smarty php + * @var bool + */ + private $toBeStripped = false; - /** - * Create template text buffer - * - * @param string $data text - * @param bool $toBeStripped wether this section should be stripped on output to smarty php - */ + /** + * Create template text buffer + * + * @param string $data text + * @param bool $toBeStripped wether this section should be stripped on output to smarty php + */ public function __construct($data, $toBeStripped = false) { $this->data = $data; $this->toBeStripped = $toBeStripped; } - /** - * Wether this section should be stripped on output to smarty php - * @return bool - */ - public function isToBeStripped() { - return $this->toBeStripped; + /** + * Wether this section should be stripped on output to smarty php + * @return bool + */ + public function isToBeStripped() { + return $this->toBeStripped; } /** diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_resource_registered.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_resource_registered.php deleted file mode 100644 index df526101f..000000000 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_resource_registered.php +++ /dev/null @@ -1,101 +0,0 @@ -filepath = $source->type . ':' . $source->name; - $source->uid = sha1($source->filepath . $source->smarty->_joined_template_dir); - $source->timestamp = $this->getTemplateTimestamp($source); - $source->exists = !!$source->timestamp; - } - - /** - * populate Source Object with timestamp and exists from Resource - * - * @param Smarty_Template_Source $source source object - * - * @return void - */ - public function populateTimestamp(Smarty_Template_Source $source) - { - $source->timestamp = $this->getTemplateTimestamp($source); - $source->exists = !!$source->timestamp; - } - - /** - * Get timestamp (epoch) the template source was modified - * - * @param Smarty_Template_Source $source source object - * - * @return integer|boolean timestamp (epoch) the template was modified, false if resources has no timestamp - */ - public function getTemplateTimestamp(Smarty_Template_Source $source) - { - // return timestamp - $time_stamp = false; - call_user_func_array( - $source->smarty->registered_resources[ $source->type ][ 0 ][ 1 ], - array($source->name, &$time_stamp, $source->smarty) - ); - return is_numeric($time_stamp) ? (int)$time_stamp : $time_stamp; - } - - /** - * Load template's source by invoking the registered callback into current template object - * - * @param Smarty_Template_Source $source source object - * - * @return string template source - * @throws SmartyException if source cannot be loaded - */ - public function getContent(Smarty_Template_Source $source) - { - // return template string - $content = null; - $t = call_user_func_array( - $source->smarty->registered_resources[ $source->type ][ 0 ][ 0 ], - array($source->name, &$content, $source->smarty) - ); - if (is_bool($t) && !$t) { - throw new SmartyException("Unable to read template {$source->type} '{$source->name}'"); - } - return $content; - } - - /** - * Determine basename for compiled filename - * - * @param Smarty_Template_Source $source source object - * - * @return string resource's basename - */ - public function getBasename(Smarty_Template_Source $source) - { - return basename($source->name); - } -} diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_resource_stream.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_resource_stream.php index 9956bd073..5f0203498 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_resource_stream.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_resource_stream.php @@ -13,7 +13,7 @@ * Smarty Internal Plugin Resource Stream * Implements the streams as resource for Smarty template * - * @link http://php.net/streams + * @link https://php.net/streams * @package Smarty * @subpackage TemplateResources */ diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_runtime_codeframe.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_runtime_codeframe.php index 983ca6180..d0ca751e2 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_runtime_codeframe.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_runtime_codeframe.php @@ -44,9 +44,12 @@ public function create( $properties[ 'file_dependency' ] = $_template->cached->file_dependency; $properties[ 'cache_lifetime' ] = $_template->cache_lifetime; } - $output = "source->filepath) . "' */\n\n"; + $output = sprintf( + "source->filepath) + ); $output .= "/* @var Smarty_Internal_Template \$_smarty_tpl */\n"; $dec = "\$_smarty_tpl->_decodeProperties(\$_smarty_tpl, " . var_export($properties, true) . ',' . ($cache ? 'true' : 'false') . ')'; diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_runtime_make_nocache.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_runtime_make_nocache.php index 53069148d..7994aa048 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_runtime_make_nocache.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_runtime_make_nocache.php @@ -22,7 +22,7 @@ public function save(Smarty_Internal_Template $tpl, $var) { if (isset($tpl->tpl_vars[ $var ])) { $export = - preg_replace('/^Smarty_Variable::__set_state[(]|[)]$/', '', var_export($tpl->tpl_vars[ $var ], true)); + preg_replace('/^\\\\?Smarty_Variable::__set_state[(]|[)]$/', '', var_export($tpl->tpl_vars[ $var ], true)); if (preg_match('/(\w+)::__set_state/', $export, $match)) { throw new SmartyException("{make_nocache \${$var}} in template '{$tpl->source->name}': variable does contain object '{$match[1]}' not implementing method '__set_state'"); } diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_runtime_writefile.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_runtime_writefile.php index 4383e6f38..492d5eb25 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_runtime_writefile.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_runtime_writefile.php @@ -29,12 +29,6 @@ public function writeFile($_filepath, $_contents, Smarty $smarty) { $_error_reporting = error_reporting(); error_reporting($_error_reporting & ~E_NOTICE & ~E_WARNING); - $_file_perms = property_exists($smarty, '_file_perms') ? $smarty->_file_perms : 0644; - $_dir_perms = - property_exists($smarty, '_dir_perms') ? (isset($smarty->_dir_perms) ? $smarty->_dir_perms : 0777) : 0771; - if ($_file_perms !== null) { - $old_umask = umask(0); - } $_dirpath = dirname($_filepath); // if subdirs, create dir structure if ($_dirpath !== '.') { @@ -42,7 +36,7 @@ public function writeFile($_filepath, $_contents, Smarty $smarty) // loop if concurrency problem occurs // see https://bugs.php.net/bug.php?id=35326 while (!is_dir($_dirpath)) { - if (@mkdir($_dirpath, $_dir_perms, true)) { + if (@mkdir($_dirpath, 0777, true)) { break; } clearstatcache(); @@ -89,11 +83,8 @@ public function writeFile($_filepath, $_contents, Smarty $smarty) error_reporting($_error_reporting); throw new SmartyException("unable to write file {$_filepath}"); } - if ($_file_perms !== null) { - // set file permissions - chmod($_filepath, $_file_perms); - umask($old_umask); - } + // set file permissions + @chmod($_filepath, 0666 & ~umask()); error_reporting($_error_reporting); return true; } diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_template.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_template.php index bae22a7d5..72d1d52e0 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_template.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_template.php @@ -24,6 +24,7 @@ * * @method bool mustCompile() */ +#[\AllowDynamicProperties] class Smarty_Internal_Template extends Smarty_Internal_TemplateBase { /** @@ -292,7 +293,7 @@ public function _subTemplateRender( $smarty = &$this->smarty; $_templateId = $smarty->_getTemplateId($template, $cache_id, $compile_id, $caching, $tpl); // recursive call ? - if (isset($tpl->templateId) ? $tpl->templateId : $tpl->_getTemplateId() !== $_templateId) { + if ((isset($tpl->templateId) ? $tpl->templateId : $tpl->_getTemplateId()) !== $_templateId) { // already in template cache? if (isset(self::$tplObjCache[ $_templateId ])) { // copy data from cached object @@ -358,7 +359,7 @@ public function _subTemplateRender( } if ($tpl->caching === 9999) { if (!isset($tpl->compiled)) { - $this->loadCompiled(true); + $tpl->loadCompiled(true); } if ($tpl->compiled->has_nocache_code) { $this->cached->hashes[ $tpl->compiled->nocache_hash ] = true; diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_templatebase.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_templatebase.php index 200c11bb5..918362e91 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_templatebase.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_templatebase.php @@ -138,7 +138,7 @@ public function display($template = null, $cache_id = null, $compile_id = null, * test if cache is valid * * @api Smarty::isCached() - * @link http://www.smarty.net/docs/en/api.is.cached.tpl + * @link https://www.smarty.net/docs/en/api.is.cached.tpl * * @param null|string|\Smarty_Internal_Template $template the resource handle of the template file or template * object @@ -199,6 +199,12 @@ private function _execute($template, $cache_id, $compile_id, $parent, $function) try { $_smarty_old_error_level = isset($smarty->error_reporting) ? error_reporting($smarty->error_reporting) : null; + + if ($smarty->isMutingUndefinedOrNullWarnings()) { + $errorHandler = new Smarty_Internal_ErrorHandler(); + $errorHandler->activate(); + } + if ($this->_objType === 2) { /* @var Smarty_Internal_Template $this */ $template->tplFunctions = $this->tplFunctions; @@ -242,14 +248,23 @@ private function _execute($template, $cache_id, $compile_id, $parent, $function) } } } + + if (isset($errorHandler)) { + $errorHandler->deactivate(); + } + if (isset($_smarty_old_error_level)) { error_reporting($_smarty_old_error_level); } return $result; - } catch (Exception $e) { + } catch (Throwable $e) { while (ob_get_level() > $level) { ob_end_clean(); } + if (isset($errorHandler)) { + $errorHandler->deactivate(); + } + if (isset($_smarty_old_error_level)) { error_reporting($_smarty_old_error_level); } @@ -261,7 +276,7 @@ private function _execute($template, $cache_id, $compile_id, $parent, $function) * Registers plugin to be used in templates * * @api Smarty::registerPlugin() - * @link http://www.smarty.net/docs/en/api.register.plugin.tpl + * @link https://www.smarty.net/docs/en/api.register.plugin.tpl * * @param string $type plugin type * @param string $name name of template tag @@ -281,7 +296,7 @@ public function registerPlugin($type, $name, $callback, $cacheable = true, $cach * load a filter of specified type and name * * @api Smarty::loadFilter() - * @link http://www.smarty.net/docs/en/api.load.filter.tpl + * @link https://www.smarty.net/docs/en/api.load.filter.tpl * * @param string $type filter type * @param string $name filter name @@ -298,7 +313,7 @@ public function loadFilter($type, $name) * Registers a filter function * * @api Smarty::registerFilter() - * @link http://www.smarty.net/docs/en/api.register.filter.tpl + * @link https://www.smarty.net/docs/en/api.register.filter.tpl * * @param string $type filter type * @param callable $callback @@ -316,7 +331,7 @@ public function registerFilter($type, $callback, $name = null) * Registers object to be used in templates * * @api Smarty::registerObject() - * @link http://www.smarty.net/docs/en/api.register.object.tpl + * @link https://www.smarty.net/docs/en/api.register.object.tpl * * @param string $object_name * @param object $object the referenced PHP object to register diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_templatecompilerbase.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_templatecompilerbase.php index 3cc957dec..d5c18d31a 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_templatecompilerbase.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_templatecompilerbase.php @@ -203,13 +203,6 @@ abstract class Smarty_Internal_TemplateCompilerBase */ public $blockOrFunctionCode = ''; - /** - * php_handling setting either from Smarty or security - * - * @var int - */ - public $php_handling = 0; - /** * flags for used modifier plugins * @@ -429,20 +422,12 @@ public function compileTemplateSource( try { // save template object in compiler class $this->template = $template; - if (property_exists($this->template->smarty, 'plugin_search_order')) { - $this->plugin_search_order = $this->template->smarty->plugin_search_order; - } if ($this->smarty->debugging) { if (!isset($this->smarty->_debug)) { $this->smarty->_debug = new Smarty_Internal_Debug(); } $this->smarty->_debug->start_compile($this->template); } - if (isset($this->template->smarty->security_policy)) { - $this->php_handling = $this->template->smarty->security_policy->php_handling; - } else { - $this->php_handling = $this->template->smarty->php_handling; - } $this->parent_compiler = $parent_compiler ? $parent_compiler : $this; $nocache = isset($nocache) ? $nocache : false; if (empty($template->compiled->nocache_hash)) { @@ -620,18 +605,18 @@ public function compilePHPFunctionCall($name, $parameter) if (strcasecmp($name, 'isset') === 0 || strcasecmp($name, 'empty') === 0 || strcasecmp($name, 'array') === 0 || is_callable($name) ) { - $func_name = strtolower($name); + $func_name = smarty_strtolower_ascii($name); if ($func_name === 'isset') { if (count($parameter) === 0) { $this->trigger_template_error('Illegal number of parameter in "isset()"'); } - $pa = array(); - foreach ($parameter as $p) { - $pa[] = $this->syntaxMatchesVariable($p) ? 'isset(' . $p . ')' : '(' . $p . ' !== null )'; - } - return '(' . implode(' && ', $pa) . ')'; + $pa = array(); + foreach ($parameter as $p) { + $pa[] = $this->syntaxMatchesVariable($p) ? 'isset(' . $p . ')' : '(' . $p . ' !== null )'; + } + return '(' . implode(' && ', $pa) . ')'; } elseif (in_array( $func_name, @@ -649,12 +634,8 @@ public function compilePHPFunctionCall($name, $parameter) $this->trigger_template_error("Illegal number of parameter in '{$func_name()}'"); } if ($func_name === 'empty') { - if (!$this->syntaxMatchesVariable($parameter[0]) && version_compare(PHP_VERSION, '5.5.0', '<')) { - return '(' . $parameter[ 0 ] . ' === false )'; - } else { - return $func_name . '(' . - str_replace("')->value", "',null,true,false)->value", $parameter[ 0 ]) . ')'; - } + return $func_name . '(' . + str_replace("')->value", "',null,true,false)->value", $parameter[ 0 ]) . ')'; } else { return $func_name . '(' . $parameter[ 0 ] . ')'; } @@ -667,16 +648,16 @@ public function compilePHPFunctionCall($name, $parameter) } } - /** - * Determines whether the passed string represents a valid (PHP) variable. - * This is important, because `isset()` only works on variables and `empty()` can only be passed - * a variable prior to php5.5 - * @param $string - * @return bool - */ - private function syntaxMatchesVariable($string) { - static $regex_pattern = '/^\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*((->)[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*|\[.*]*\])*$/'; - return 1 === preg_match($regex_pattern, trim($string)); + /** + * Determines whether the passed string represents a valid (PHP) variable. + * This is important, because `isset()` only works on variables and `empty()` can only be passed + * a variable prior to php5.5 + * @param $string + * @return bool + */ + private function syntaxMatchesVariable($string) { + static $regex_pattern = '/^\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*((->)[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*|\[.*]*\])*$/'; + return 1 === preg_match($regex_pattern, trim($string)); } /** @@ -691,11 +672,11 @@ public function processText($text) { if (strpos($text, '<') === false) { - return preg_replace($this->stripRegEx, '', $text); + return preg_replace($this->stripRegEx, '', $text); } - $store = array(); - $_store = 0; + $store = array(); + $_store = 0; // capture html elements not to be messed with $_offset = 0; @@ -784,7 +765,7 @@ public function getTagCompiler($tag) if (!isset(self::$_tag_objects[ $tag ])) { // lazy load internal compiler plugin $_tag = explode('_', $tag); - $_tag = array_map('ucfirst', $_tag); + $_tag = array_map('smarty_ucfirst_ascii', $_tag); $class_name = 'Smarty_Internal_Compile_' . implode('_', $_tag); if (class_exists($class_name) && (!isset($this->smarty->security_policy) || $this->smarty->security_policy->isTrustedTag($tag, $this)) @@ -1150,8 +1131,12 @@ public function trigger_template_error($args = null, $line = null, $tagline = nu echo ob_get_clean(); flush(); } - $e = new SmartyCompilerException($error_text); - $e->line = $line; + $e = new SmartyCompilerException( + $error_text, + 0, + $this->template->source->filepath, + $line + ); $e->source = trim(preg_replace('![\t\r\n]+!', ' ', $match[ $line - 1 ])); $e->desc = $args; $e->template = $this->template->source->filepath; @@ -1455,6 +1440,10 @@ public function compileCheckPlugins($requiredPlugins) */ abstract protected function doCompile($_content, $isTemplateSource = false); + public function cStyleComment($string) { + return '/*' . str_replace('*/', '* /' , $string) . '*/'; + } + /** * Compile Tag * diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_templatelexer.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_templatelexer.php index 867a31d26..5ca482058 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_templatelexer.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_templatelexer.php @@ -18,12 +18,6 @@ */ class Smarty_Internal_Templatelexer { - const TEXT = 1; - const TAG = 2; - const TAGBODY = 3; - const LITERAL = 4; - const DOUBLEQUOTEDSTRING = 5; - /** * Source * @@ -80,7 +74,7 @@ class Smarty_Internal_Templatelexer */ public $phpType = ''; - /** + /** * state number * * @var int @@ -229,10 +223,6 @@ class Smarty_Internal_Templatelexer */ private $yy_global_literal = null; - private $_yy_state = 1; - - private $_yy_stack = array(); - /** * constructor * @@ -245,14 +235,14 @@ public function __construct($source, Smarty_Internal_TemplateCompilerBase $compi $this->dataLength = strlen($this->data); $this->counter = 0; if (preg_match('/^\xEF\xBB\xBF/i', $this->data, $match)) { - $this->counter += strlen($match[ 0 ]); + $this->counter += strlen($match[0]); } $this->line = 1; $this->smarty = $compiler->template->smarty; $this->compiler = $compiler; $this->compiler->initDelimiterPreg(); - $this->smarty_token_names[ 'LDEL' ] = $this->smarty->getLeftDelimiter(); - $this->smarty_token_names[ 'RDEL' ] = $this->smarty->getRightDelimiter(); + $this->smarty_token_names['LDEL'] = $this->smarty->getLeftDelimiter(); + $this->smarty_token_names['RDEL'] = $this->smarty->getRightDelimiter(); } /** @@ -265,17 +255,17 @@ public function PrintTrace() $this->yyTracePrompt = '
'; } - /** + /** * replace placeholders with runtime preg code * * @param string $preg * * @return string */ - public function replace($preg) - { + public function replace($preg) + { return $this->compiler->replaceDelimiter($preg); - } + } /** * check if current value is an autoliteral left delimiter @@ -286,7 +276,11 @@ public function isAutoLiteral() { return $this->smarty->getAutoLiteral() && isset($this->value[ $this->compiler->getLdelLength() ]) ? strpos(" \n\t\r", $this->value[ $this->compiler->getLdelLength() ]) !== false : false; - } // end function + } + + + private $_yy_state = 1; + private $_yy_stack = array(); public function yylex() { @@ -296,62 +290,60 @@ public function yylex() public function yypushstate($state) { if ($this->yyTraceFILE) { - fprintf($this->yyTraceFILE, "%sState push %s\n", $this->yyTracePrompt, - isset($this->state_name[ $this->_yy_state ]) ? $this->state_name[ $this->_yy_state ] : $this->_yy_state); + fprintf($this->yyTraceFILE, "%sState push %s\n", $this->yyTracePrompt, isset($this->state_name[$this->_yy_state]) ? $this->state_name[$this->_yy_state] : $this->_yy_state); } array_push($this->_yy_stack, $this->_yy_state); $this->_yy_state = $state; if ($this->yyTraceFILE) { - fprintf($this->yyTraceFILE, "%snew State %s\n", $this->yyTracePrompt, - isset($this->state_name[ $this->_yy_state ]) ? $this->state_name[ $this->_yy_state ] : $this->_yy_state); + fprintf($this->yyTraceFILE, "%snew State %s\n", $this->yyTracePrompt, isset($this->state_name[$this->_yy_state]) ? $this->state_name[$this->_yy_state] : $this->_yy_state); } } public function yypopstate() { - if ($this->yyTraceFILE) { - fprintf($this->yyTraceFILE, "%sState pop %s\n", $this->yyTracePrompt, - isset($this->state_name[ $this->_yy_state ]) ? $this->state_name[ $this->_yy_state ] : $this->_yy_state); + if ($this->yyTraceFILE) { + fprintf($this->yyTraceFILE, "%sState pop %s\n", $this->yyTracePrompt, isset($this->state_name[$this->_yy_state]) ? $this->state_name[$this->_yy_state] : $this->_yy_state); } - $this->_yy_state = array_pop($this->_yy_stack); + $this->_yy_state = array_pop($this->_yy_stack); if ($this->yyTraceFILE) { - fprintf($this->yyTraceFILE, "%snew State %s\n", $this->yyTracePrompt, - isset($this->state_name[ $this->_yy_state ]) ? $this->state_name[ $this->_yy_state ] : $this->_yy_state); + fprintf($this->yyTraceFILE, "%snew State %s\n", $this->yyTracePrompt, isset($this->state_name[$this->_yy_state]) ? $this->state_name[$this->_yy_state] : $this->_yy_state); } + } public function yybegin($state) { - $this->_yy_state = $state; + $this->_yy_state = $state; if ($this->yyTraceFILE) { - fprintf($this->yyTraceFILE, "%sState set %s\n", $this->yyTracePrompt, - isset($this->state_name[ $this->_yy_state ]) ? $this->state_name[ $this->_yy_state ] : $this->_yy_state); + fprintf($this->yyTraceFILE, "%sState set %s\n", $this->yyTracePrompt, isset($this->state_name[$this->_yy_state]) ? $this->state_name[$this->_yy_state] : $this->_yy_state); } } + + public function yylex1() { if (!isset($this->yy_global_pattern1)) { - $this->yy_global_pattern1 = - $this->replace("/\G([{][}])|\G((SMARTYldel)SMARTYal[*])|\G((SMARTYldel)SMARTYalphp([ ].*?)?SMARTYrdel|(SMARTYldel)SMARTYal[\/]phpSMARTYrdel)|\G((SMARTYldel)SMARTYautoliteral\\s+SMARTYliteral)|\G((SMARTYldel)SMARTYalliteral\\s*SMARTYrdel)|\G((SMARTYldel)SMARTYal[\/]literal\\s*SMARTYrdel)|\G((SMARTYldel)SMARTYal)|\G([<][?]((php\\s+|=)|\\s+)|[<][%]|[<][?]xml\\s+|[<]script\\s+language\\s*=\\s*[\"']?\\s*php\\s*[\"']?\\s*[>]|[?][>]|[%][>])|\G([\S\s])/isS"); + $this->yy_global_pattern1 = $this->replace("/\G([{][}])|\G((SMARTYldel)SMARTYal[*])|\G((SMARTYldel)SMARTYautoliteral\\s+SMARTYliteral)|\G((SMARTYldel)SMARTYalliteral\\s*SMARTYrdel)|\G((SMARTYldel)SMARTYal[\/]literal\\s*SMARTYrdel)|\G((SMARTYldel)SMARTYal)|\G([\S\s])/isS"); } if (!isset($this->dataLength)) { $this->dataLength = strlen($this->data); } - if ($this->counter >= $this->dataLength) { + if ($this->counter >= $this->dataLength) { return false; // end of input } + do { - if (preg_match($this->yy_global_pattern1, $this->data, $yymatches, 0, $this->counter)) { - if (!isset($yymatches[ 0 ][ 1 ])) { - $yymatches = preg_grep("/(.|\s)+/", $yymatches); + if (preg_match($this->yy_global_pattern1,$this->data, $yymatches, 0, $this->counter)) { + if (!isset($yymatches[ 0 ][1])) { + $yymatches = preg_grep("/(.|\s)+/", $yymatches); } else { $yymatches = array_filter($yymatches); } if (empty($yymatches)) { throw new Exception('Error: lexing failed because a rule matched' . - ' an empty string. Input "' . substr($this->data, - $this->counter, 5) . '... state TEXT'); + ' an empty string. Input "' . substr($this->data, + $this->counter, 5) . '... state TEXT'); } next($yymatches); // skip global match $this->token = key($yymatches); // token number @@ -369,110 +361,102 @@ public function yylex1() } elseif ($r === false) { $this->counter += strlen($this->value); $this->line += substr_count($this->value, "\n"); - if ($this->counter >= $this->dataLength) { + if ($this->counter >= $this->dataLength) { return false; // end of input } // skip this token continue; - } - } else { - throw new Exception('Unexpected input at line ' . $this->line . - ': ' . $this->data[ $this->counter ]); + } } else { + throw new Exception('Unexpected input at line' . $this->line . + ': ' . $this->data[$this->counter]); } break; } while (true); - } + } // end function + + + const TEXT = 1; public function yy_r1_1() { - $this->token = Smarty_Internal_Templateparser::TP_TEXT; - } + $this->token = Smarty_Internal_Templateparser::TP_TEXT; + } public function yy_r1_2() { - $to = $this->dataLength; - preg_match("/[*]{$this->compiler->getRdelPreg()}[\n]?/", $this->data, $match, PREG_OFFSET_CAPTURE, - $this->counter); - if (isset($match[ 0 ][ 1 ])) { - $to = $match[ 0 ][ 1 ] + strlen($match[ 0 ][ 0 ]); + + $to = $this->dataLength; + preg_match("/[*]{$this->compiler->getRdelPreg()}[\n]?/",$this->data,$match,PREG_OFFSET_CAPTURE,$this->counter); + if (isset($match[0][1])) { + $to = $match[0][1] + strlen($match[0][0]); } else { - $this->compiler->trigger_template_error("missing or misspelled comment closing tag '{$this->smarty->getRightDelimiter()}'"); + $this->compiler->trigger_template_error ("missing or misspelled comment closing tag '{$this->smarty->getRightDelimiter()}'"); } - $this->value = substr($this->data, $this->counter, $to - $this->counter); + $this->value = substr($this->data,$this->counter,$to-$this->counter); return false; - } - + } public function yy_r1_4() { - $this->compiler->getTagCompiler('private_php')->parsePhp($this); - } - public function yy_r1_8() - { $this->token = Smarty_Internal_Templateparser::TP_TEXT; - } - - public function yy_r1_10() + } + public function yy_r1_6() { + $this->token = Smarty_Internal_Templateparser::TP_LITERALSTART; $this->yypushstate(self::LITERAL); - } - - public function yy_r1_12() + } + public function yy_r1_8() { + $this->token = Smarty_Internal_Templateparser::TP_LITERALEND; $this->yypushstate(self::LITERAL); - } // end function - - public function yy_r1_14() + } + public function yy_r1_10() { + $this->yypushstate(self::TAG); return true; - } - - public function yy_r1_16() + } + public function yy_r1_12() { - $this->compiler->getTagCompiler('private_php')->parsePhp($this); - } - public function yy_r1_19() - { - if (!isset($this->yy_global_text)) { - $this->yy_global_text = - $this->replace('/(SMARTYldel)SMARTYal|[<][?]((php\s+|=)|\s+)|[<][%]|[<][?]xml\s+|[<]script\s+language\s*=\s*["\']?\s*php\s*["\']?\s*[>]|[?][>]|[%][>]SMARTYliteral/isS'); - } - $to = $this->dataLength; - preg_match($this->yy_global_text, $this->data, $match, PREG_OFFSET_CAPTURE, $this->counter); - if (isset($match[ 0 ][ 1 ])) { - $to = $match[ 0 ][ 1 ]; - } - $this->value = substr($this->data, $this->counter, $to - $this->counter); - $this->token = Smarty_Internal_Templateparser::TP_TEXT; - } + if (!isset($this->yy_global_text)) { + $this->yy_global_text = $this->replace('/(SMARTYldel)SMARTYal/isS'); + } + $to = $this->dataLength; + preg_match($this->yy_global_text, $this->data,$match,PREG_OFFSET_CAPTURE,$this->counter); + if (isset($match[0][1])) { + $to = $match[0][1]; + } + $this->value = substr($this->data,$this->counter,$to-$this->counter); + $this->token = Smarty_Internal_Templateparser::TP_TEXT; + } + public function yylex2() { if (!isset($this->yy_global_pattern2)) { - $this->yy_global_pattern2 = - $this->replace("/\G((SMARTYldel)SMARTYal(if|elseif|else if|while)\\s+)|\G((SMARTYldel)SMARTYalfor\\s+)|\G((SMARTYldel)SMARTYalforeach(?![^\s]))|\G((SMARTYldel)SMARTYalsetfilter\\s+)|\G((SMARTYldel)SMARTYalmake_nocache\\s+)|\G((SMARTYldel)SMARTYal[0-9]*[a-zA-Z_]\\w*(\\s+nocache)?\\s*SMARTYrdel)|\G((SMARTYldel)SMARTYal[$]smarty\\.block\\.(child|parent)\\s*SMARTYrdel)|\G((SMARTYldel)SMARTYal[\/][0-9]*[a-zA-Z_]\\w*\\s*SMARTYrdel)|\G((SMARTYldel)SMARTYal[$][0-9]*[a-zA-Z_]\\w*(\\s+nocache)?\\s*SMARTYrdel)|\G((SMARTYldel)SMARTYal[\/])|\G((SMARTYldel)SMARTYal)/isS"); + $this->yy_global_pattern2 = $this->replace("/\G((SMARTYldel)SMARTYal(if|elseif|else if|while)\\s+)|\G((SMARTYldel)SMARTYalfor\\s+)|\G((SMARTYldel)SMARTYalforeach(?![^\s]))|\G((SMARTYldel)SMARTYalsetfilter\\s+)|\G((SMARTYldel)SMARTYalmake_nocache\\s+)|\G((SMARTYldel)SMARTYal[0-9]*[a-zA-Z_]\\w*(\\s+nocache)?\\s*SMARTYrdel)|\G((SMARTYldel)SMARTYal[$]smarty\\.block\\.(child|parent)\\s*SMARTYrdel)|\G((SMARTYldel)SMARTYal[\/][0-9]*[a-zA-Z_]\\w*\\s*SMARTYrdel)|\G((SMARTYldel)SMARTYal[$][0-9]*[a-zA-Z_]\\w*(\\s+nocache)?\\s*SMARTYrdel)|\G((SMARTYldel)SMARTYal[\/])|\G((SMARTYldel)SMARTYal)/isS"); } if (!isset($this->dataLength)) { $this->dataLength = strlen($this->data); } - if ($this->counter >= $this->dataLength) { + if ($this->counter >= $this->dataLength) { return false; // end of input } + do { - if (preg_match($this->yy_global_pattern2, $this->data, $yymatches, 0, $this->counter)) { - if (!isset($yymatches[ 0 ][ 1 ])) { - $yymatches = preg_grep("/(.|\s)+/", $yymatches); + if (preg_match($this->yy_global_pattern2,$this->data, $yymatches, 0, $this->counter)) { + if (!isset($yymatches[ 0 ][1])) { + $yymatches = preg_grep("/(.|\s)+/", $yymatches); } else { $yymatches = array_filter($yymatches); } if (empty($yymatches)) { throw new Exception('Error: lexing failed because a rule matched' . - ' an empty string. Input "' . substr($this->data, - $this->counter, 5) . '... state TAG'); + ' an empty string. Input "' . substr($this->data, + $this->counter, 5) . '... state TAG'); } next($yymatches); // skip global match $this->token = key($yymatches); // token number @@ -490,79 +474,82 @@ public function yylex2() } elseif ($r === false) { $this->counter += strlen($this->value); $this->line += substr_count($this->value, "\n"); - if ($this->counter >= $this->dataLength) { + if ($this->counter >= $this->dataLength) { return false; // end of input } // skip this token continue; - } - } else { - throw new Exception('Unexpected input at line ' . $this->line . - ': ' . $this->data[ $this->counter ]); + } } else { + throw new Exception('Unexpected input at line' . $this->line . + ': ' . $this->data[$this->counter]); } break; } while (true); - } + } // end function + + + const TAG = 2; public function yy_r2_1() { + $this->token = Smarty_Internal_Templateparser::TP_LDELIF; $this->yybegin(self::TAGBODY); $this->taglineno = $this->line; - } - + } public function yy_r2_4() { + $this->token = Smarty_Internal_Templateparser::TP_LDELFOR; $this->yybegin(self::TAGBODY); $this->taglineno = $this->line; - } - + } public function yy_r2_6() { + $this->token = Smarty_Internal_Templateparser::TP_LDELFOREACH; $this->yybegin(self::TAGBODY); $this->taglineno = $this->line; - } - + } public function yy_r2_8() { + $this->token = Smarty_Internal_Templateparser::TP_LDELSETFILTER; $this->yybegin(self::TAGBODY); $this->taglineno = $this->line; - } - + } public function yy_r2_10() { + $this->token = Smarty_Internal_Templateparser::TP_LDELMAKENOCACHE; $this->yybegin(self::TAGBODY); $this->taglineno = $this->line; - } - + } public function yy_r2_12() { + $this->yypopstate(); $this->token = Smarty_Internal_Templateparser::TP_SIMPLETAG; $this->taglineno = $this->line; - } - + } public function yy_r2_15() { - $this->yypopstate(); - $this->token = Smarty_Internal_Templateparser::TP_SMARTYBLOCKCHILDPARENT; - $this->taglineno = $this->line; - } + $this->yypopstate(); + $this->token = Smarty_Internal_Templateparser::TP_SMARTYBLOCKCHILDPARENT; + $this->taglineno = $this->line; + } public function yy_r2_18() { + $this->yypopstate(); $this->token = Smarty_Internal_Templateparser::TP_CLOSETAG; $this->taglineno = $this->line; - } - + } public function yy_r2_20() { - if ($this->_yy_stack[ count($this->_yy_stack) - 1 ] === self::TEXT) { + + if ($this->_yy_stack[count($this->_yy_stack)-1] === self::TEXT) { $this->yypopstate(); $this->token = Smarty_Internal_Templateparser::TP_SIMPELOUTPUT; $this->taglineno = $this->line; @@ -572,45 +559,46 @@ public function yy_r2_20() $this->yybegin(self::TAGBODY); $this->taglineno = $this->line; } - } // end function - + } public function yy_r2_23() { + $this->token = Smarty_Internal_Templateparser::TP_LDELSLASH; $this->yybegin(self::TAGBODY); $this->taglineno = $this->line; - } - + } public function yy_r2_25() { + $this->token = Smarty_Internal_Templateparser::TP_LDEL; $this->yybegin(self::TAGBODY); $this->taglineno = $this->line; - } + } + public function yylex3() { if (!isset($this->yy_global_pattern3)) { - $this->yy_global_pattern3 = - $this->replace("/\G(\\s*SMARTYrdel)|\G((SMARTYldel)SMARTYal)|\G([\"])|\G('[^'\\\\]*(?:\\\\.[^'\\\\]*)*')|\G([$][0-9]*[a-zA-Z_]\\w*)|\G([$])|\G(\\s+is\\s+in\\s+)|\G(\\s+as\\s+)|\G(\\s+to\\s+)|\G(\\s+step\\s+)|\G(\\s+instanceof\\s+)|\G(\\s*([!=][=]{1,2}|[<][=>]?|[>][=]?|[&|]{2})\\s*)|\G(\\s+(eq|ne|neq|gt|ge|gte|lt|le|lte|mod|and|or|xor)\\s+)|\G(\\s+is\\s+(not\\s+)?(odd|even|div)\\s+by\\s+)|\G(\\s+is\\s+(not\\s+)?(odd|even))|\G([!]\\s*|not\\s+)|\G([(](int(eger)?|bool(ean)?|float|double|real|string|binary|array|object)[)]\\s*)|\G(\\s*[(]\\s*)|\G(\\s*[)])|\G(\\[\\s*)|\G(\\s*\\])|\G(\\s*[-][>]\\s*)|\G(\\s*[=][>]\\s*)|\G(\\s*[=]\\s*)|\G(([+]|[-]){2})|\G(\\s*([+]|[-])\\s*)|\G(\\s*([*]{1,2}|[%\/^&]|[<>]{2})\\s*)|\G([@])|\G(array\\s*[(]\\s*)|\G([#])|\G(\\s+[0-9]*[a-zA-Z_][a-zA-Z0-9_\-:]*\\s*[=]\\s*)|\G(([0-9]*[a-zA-Z_]\\w*)?(\\\\[0-9]*[a-zA-Z_]\\w*)+)|\G([0-9]*[a-zA-Z_]\\w*)|\G(\\d+)|\G([`])|\G([|][@]?)|\G([.])|\G(\\s*[,]\\s*)|\G(\\s*[;]\\s*)|\G([:]{2})|\G(\\s*[:]\\s*)|\G(\\s*[?]\\s*)|\G(0[xX][0-9a-fA-F]+)|\G(\\s+)|\G([\S\s])/isS"); + $this->yy_global_pattern3 = $this->replace("/\G(\\s*SMARTYrdel)|\G((SMARTYldel)SMARTYal)|\G([\"])|\G('[^'\\\\]*(?:\\\\.[^'\\\\]*)*')|\G([$][0-9]*[a-zA-Z_]\\w*)|\G([$])|\G(\\s+is\\s+in\\s+)|\G(\\s+as\\s+)|\G(\\s+to\\s+)|\G(\\s+step\\s+)|\G(\\s+instanceof\\s+)|\G(\\s*([!=][=]{1,2}|[<][=>]?|[>][=]?|[&|]{2})\\s*)|\G(\\s+(eq|ne|neq|gt|ge|gte|lt|le|lte|mod|and|or|xor)\\s+)|\G(\\s+is\\s+(not\\s+)?(odd|even|div)\\s+by\\s+)|\G(\\s+is\\s+(not\\s+)?(odd|even))|\G([!]\\s*|not\\s+)|\G([(](int(eger)?|bool(ean)?|float|double|real|string|binary|array|object)[)]\\s*)|\G(\\s*[(]\\s*)|\G(\\s*[)])|\G(\\[\\s*)|\G(\\s*\\])|\G(\\s*[-][>]\\s*)|\G(\\s*[=][>]\\s*)|\G(\\s*[=]\\s*)|\G(([+]|[-]){2})|\G(\\s*([+]|[-])\\s*)|\G(\\s*([*]{1,2}|[%\/^&]|[<>]{2})\\s*)|\G([@])|\G(array\\s*[(]\\s*)|\G([#])|\G(\\s+[0-9]*[a-zA-Z_][a-zA-Z0-9_\-:]*\\s*[=]\\s*)|\G(([0-9]*[a-zA-Z_]\\w*)?(\\\\[0-9]*[a-zA-Z_]\\w*)+)|\G([0-9]*[a-zA-Z_]\\w*)|\G(\\d+)|\G([`])|\G([|][@]?)|\G([.])|\G(\\s*[,]\\s*)|\G(\\s*[;]\\s*)|\G([:]{2})|\G(\\s*[:]\\s*)|\G(\\s*[?]\\s*)|\G(0[xX][0-9a-fA-F]+)|\G(\\s+)|\G([\S\s])/isS"); } if (!isset($this->dataLength)) { $this->dataLength = strlen($this->data); } - if ($this->counter >= $this->dataLength) { + if ($this->counter >= $this->dataLength) { return false; // end of input } + do { - if (preg_match($this->yy_global_pattern3, $this->data, $yymatches, 0, $this->counter)) { - if (!isset($yymatches[ 0 ][ 1 ])) { - $yymatches = preg_grep("/(.|\s)+/", $yymatches); + if (preg_match($this->yy_global_pattern3,$this->data, $yymatches, 0, $this->counter)) { + if (!isset($yymatches[ 0 ][1])) { + $yymatches = preg_grep("/(.|\s)+/", $yymatches); } else { $yymatches = array_filter($yymatches); } if (empty($yymatches)) { throw new Exception('Error: lexing failed because a rule matched' . - ' an empty string. Input "' . substr($this->data, - $this->counter, 5) . '... state TAGBODY'); + ' an empty string. Input "' . substr($this->data, + $this->counter, 5) . '... state TAGBODY'); } next($yymatches); // skip global match $this->token = key($yymatches); // token number @@ -628,281 +616,285 @@ public function yylex3() } elseif ($r === false) { $this->counter += strlen($this->value); $this->line += substr_count($this->value, "\n"); - if ($this->counter >= $this->dataLength) { + if ($this->counter >= $this->dataLength) { return false; // end of input } // skip this token continue; - } - } else { - throw new Exception('Unexpected input at line ' . $this->line . - ': ' . $this->data[ $this->counter ]); + } } else { + throw new Exception('Unexpected input at line' . $this->line . + ': ' . $this->data[$this->counter]); } break; } while (true); - } + } // end function + + + const TAGBODY = 3; public function yy_r3_1() { + $this->token = Smarty_Internal_Templateparser::TP_RDEL; $this->yypopstate(); - } - + } public function yy_r3_2() { + $this->yypushstate(self::TAG); return true; - } - + } public function yy_r3_4() { + $this->token = Smarty_Internal_Templateparser::TP_QUOTE; $this->yypushstate(self::DOUBLEQUOTEDSTRING); $this->compiler->enterDoubleQuote(); - } - + } public function yy_r3_5() { - $this->token = Smarty_Internal_Templateparser::TP_SINGLEQUOTESTRING; - } + $this->token = Smarty_Internal_Templateparser::TP_SINGLEQUOTESTRING; + } public function yy_r3_6() { - $this->token = Smarty_Internal_Templateparser::TP_DOLLARID; - } + $this->token = Smarty_Internal_Templateparser::TP_DOLLARID; + } public function yy_r3_7() { - $this->token = Smarty_Internal_Templateparser::TP_DOLLAR; - } + $this->token = Smarty_Internal_Templateparser::TP_DOLLAR; + } public function yy_r3_8() { - $this->token = Smarty_Internal_Templateparser::TP_ISIN; - } + $this->token = Smarty_Internal_Templateparser::TP_ISIN; + } public function yy_r3_9() { - $this->token = Smarty_Internal_Templateparser::TP_AS; - } + $this->token = Smarty_Internal_Templateparser::TP_AS; + } public function yy_r3_10() { - $this->token = Smarty_Internal_Templateparser::TP_TO; - } + $this->token = Smarty_Internal_Templateparser::TP_TO; + } public function yy_r3_11() { - $this->token = Smarty_Internal_Templateparser::TP_STEP; - } + $this->token = Smarty_Internal_Templateparser::TP_STEP; + } public function yy_r3_12() { - $this->token = Smarty_Internal_Templateparser::TP_INSTANCEOF; - } + $this->token = Smarty_Internal_Templateparser::TP_INSTANCEOF; + } public function yy_r3_13() { - $this->token = Smarty_Internal_Templateparser::TP_LOGOP; - } + $this->token = Smarty_Internal_Templateparser::TP_LOGOP; + } public function yy_r3_15() { - $this->token = Smarty_Internal_Templateparser::TP_SLOGOP; - } + $this->token = Smarty_Internal_Templateparser::TP_SLOGOP; + } public function yy_r3_17() { - $this->token = Smarty_Internal_Templateparser::TP_TLOGOP; - } + $this->token = Smarty_Internal_Templateparser::TP_TLOGOP; + } public function yy_r3_20() { - $this->token = Smarty_Internal_Templateparser::TP_SINGLECOND; - } + $this->token = Smarty_Internal_Templateparser::TP_SINGLECOND; + } public function yy_r3_23() { - $this->token = Smarty_Internal_Templateparser::TP_NOT; - } + $this->token = Smarty_Internal_Templateparser::TP_NOT; + } public function yy_r3_24() { - $this->token = Smarty_Internal_Templateparser::TP_TYPECAST; - } + $this->token = Smarty_Internal_Templateparser::TP_TYPECAST; + } public function yy_r3_28() { - $this->token = Smarty_Internal_Templateparser::TP_OPENP; - } + $this->token = Smarty_Internal_Templateparser::TP_OPENP; + } public function yy_r3_29() { - $this->token = Smarty_Internal_Templateparser::TP_CLOSEP; - } + $this->token = Smarty_Internal_Templateparser::TP_CLOSEP; + } public function yy_r3_30() { - $this->token = Smarty_Internal_Templateparser::TP_OPENB; - } + $this->token = Smarty_Internal_Templateparser::TP_OPENB; + } public function yy_r3_31() { - $this->token = Smarty_Internal_Templateparser::TP_CLOSEB; - } + $this->token = Smarty_Internal_Templateparser::TP_CLOSEB; + } public function yy_r3_32() { - $this->token = Smarty_Internal_Templateparser::TP_PTR; - } + $this->token = Smarty_Internal_Templateparser::TP_PTR; + } public function yy_r3_33() { - $this->token = Smarty_Internal_Templateparser::TP_APTR; - } + $this->token = Smarty_Internal_Templateparser::TP_APTR; + } public function yy_r3_34() { - $this->token = Smarty_Internal_Templateparser::TP_EQUAL; - } + $this->token = Smarty_Internal_Templateparser::TP_EQUAL; + } public function yy_r3_35() { - $this->token = Smarty_Internal_Templateparser::TP_INCDEC; - } + $this->token = Smarty_Internal_Templateparser::TP_INCDEC; + } public function yy_r3_37() { - $this->token = Smarty_Internal_Templateparser::TP_UNIMATH; - } + $this->token = Smarty_Internal_Templateparser::TP_UNIMATH; + } public function yy_r3_39() { - $this->token = Smarty_Internal_Templateparser::TP_MATH; - } + $this->token = Smarty_Internal_Templateparser::TP_MATH; + } public function yy_r3_41() { - $this->token = Smarty_Internal_Templateparser::TP_AT; - } + $this->token = Smarty_Internal_Templateparser::TP_AT; + } public function yy_r3_42() { - $this->token = Smarty_Internal_Templateparser::TP_ARRAYOPEN; - } + $this->token = Smarty_Internal_Templateparser::TP_ARRAYOPEN; + } public function yy_r3_43() { - $this->token = Smarty_Internal_Templateparser::TP_HATCH; - } + $this->token = Smarty_Internal_Templateparser::TP_HATCH; + } public function yy_r3_44() { + // resolve conflicts with shorttag and right_delimiter starting with '=' - if (substr($this->data, $this->counter + strlen($this->value) - 1, $this->compiler->getRdelLength()) === - $this->smarty->getRightDelimiter()) { - preg_match('/\s+/', $this->value, $match); - $this->value = $match[ 0 ]; + if (substr($this->data, $this->counter + strlen($this->value) - 1, $this->compiler->getRdelLength()) === $this->smarty->getRightDelimiter()) { + preg_match('/\s+/',$this->value,$match); + $this->value = $match[0]; $this->token = Smarty_Internal_Templateparser::TP_SPACE; } else { $this->token = Smarty_Internal_Templateparser::TP_ATTR; } - } - + } public function yy_r3_45() { - $this->token = Smarty_Internal_Templateparser::TP_NAMESPACE; - } + $this->token = Smarty_Internal_Templateparser::TP_NAMESPACE; + } public function yy_r3_48() { - $this->token = Smarty_Internal_Templateparser::TP_ID; - } + $this->token = Smarty_Internal_Templateparser::TP_ID; + } public function yy_r3_49() { - $this->token = Smarty_Internal_Templateparser::TP_INTEGER; - } + $this->token = Smarty_Internal_Templateparser::TP_INTEGER; + } public function yy_r3_50() { + $this->token = Smarty_Internal_Templateparser::TP_BACKTICK; $this->yypopstate(); - } - + } public function yy_r3_51() { - $this->token = Smarty_Internal_Templateparser::TP_VERT; - } + $this->token = Smarty_Internal_Templateparser::TP_VERT; + } public function yy_r3_52() { - $this->token = Smarty_Internal_Templateparser::TP_DOT; - } + $this->token = Smarty_Internal_Templateparser::TP_DOT; + } public function yy_r3_53() { - $this->token = Smarty_Internal_Templateparser::TP_COMMA; - } + $this->token = Smarty_Internal_Templateparser::TP_COMMA; + } public function yy_r3_54() { - $this->token = Smarty_Internal_Templateparser::TP_SEMICOLON; - } + $this->token = Smarty_Internal_Templateparser::TP_SEMICOLON; + } public function yy_r3_55() { - $this->token = Smarty_Internal_Templateparser::TP_DOUBLECOLON; - } + $this->token = Smarty_Internal_Templateparser::TP_DOUBLECOLON; + } public function yy_r3_56() { - $this->token = Smarty_Internal_Templateparser::TP_COLON; - } + $this->token = Smarty_Internal_Templateparser::TP_COLON; + } public function yy_r3_57() { - $this->token = Smarty_Internal_Templateparser::TP_QMARK; - } + $this->token = Smarty_Internal_Templateparser::TP_QMARK; + } public function yy_r3_58() { - $this->token = Smarty_Internal_Templateparser::TP_HEX; - } + $this->token = Smarty_Internal_Templateparser::TP_HEX; + } public function yy_r3_59() { - $this->token = Smarty_Internal_Templateparser::TP_SPACE; - } // end function + $this->token = Smarty_Internal_Templateparser::TP_SPACE; + } public function yy_r3_60() { + $this->token = Smarty_Internal_Templateparser::TP_TEXT; - } + } + + public function yylex4() { if (!isset($this->yy_global_pattern4)) { - $this->yy_global_pattern4 = - $this->replace("/\G((SMARTYldel)SMARTYalliteral\\s*SMARTYrdel)|\G((SMARTYldel)SMARTYal[\/]literal\\s*SMARTYrdel)|\G([\S\s])/isS"); + $this->yy_global_pattern4 = $this->replace("/\G((SMARTYldel)SMARTYalliteral\\s*SMARTYrdel)|\G((SMARTYldel)SMARTYal[\/]literal\\s*SMARTYrdel)|\G([\S\s])/isS"); } if (!isset($this->dataLength)) { $this->dataLength = strlen($this->data); } - if ($this->counter >= $this->dataLength) { + if ($this->counter >= $this->dataLength) { return false; // end of input } + do { - if (preg_match($this->yy_global_pattern4, $this->data, $yymatches, 0, $this->counter)) { - if (!isset($yymatches[ 0 ][ 1 ])) { - $yymatches = preg_grep("/(.|\s)+/", $yymatches); + if (preg_match($this->yy_global_pattern4,$this->data, $yymatches, 0, $this->counter)) { + if (!isset($yymatches[ 0 ][1])) { + $yymatches = preg_grep("/(.|\s)+/", $yymatches); } else { $yymatches = array_filter($yymatches); } if (empty($yymatches)) { throw new Exception('Error: lexing failed because a rule matched' . - ' an empty string. Input "' . substr($this->data, - $this->counter, 5) . '... state LITERAL'); + ' an empty string. Input "' . substr($this->data, + $this->counter, 5) . '... state LITERAL'); } next($yymatches); // skip global match $this->token = key($yymatches); // token number @@ -920,76 +912,80 @@ public function yylex4() } elseif ($r === false) { $this->counter += strlen($this->value); $this->line += substr_count($this->value, "\n"); - if ($this->counter >= $this->dataLength) { + if ($this->counter >= $this->dataLength) { return false; // end of input } // skip this token continue; - } - } else { - throw new Exception('Unexpected input at line ' . $this->line . - ': ' . $this->data[ $this->counter ]); + } } else { + throw new Exception('Unexpected input at line' . $this->line . + ': ' . $this->data[$this->counter]); } break; } while (true); - } + } // end function + + + const LITERAL = 4; public function yy_r4_1() { + $this->literal_cnt++; $this->token = Smarty_Internal_Templateparser::TP_LITERAL; - } - + } public function yy_r4_3() { + if ($this->literal_cnt) { - $this->literal_cnt--; + $this->literal_cnt--; $this->token = Smarty_Internal_Templateparser::TP_LITERAL; } else { $this->token = Smarty_Internal_Templateparser::TP_LITERALEND; $this->yypopstate(); } - } - + } public function yy_r4_5() { - if (!isset($this->yy_global_literal)) { - $this->yy_global_literal = $this->replace('/(SMARTYldel)SMARTYal[\/]?literalSMARTYrdel/isS'); - } - $to = $this->dataLength; - preg_match($this->yy_global_literal, $this->data, $match, PREG_OFFSET_CAPTURE, $this->counter); - if (isset($match[ 0 ][ 1 ])) { - $to = $match[ 0 ][ 1 ]; - } else { - $this->compiler->trigger_template_error("missing or misspelled literal closing tag"); - } - $this->value = substr($this->data, $this->counter, $to - $this->counter); - $this->token = Smarty_Internal_Templateparser::TP_LITERAL; - } // end function + if (!isset($this->yy_global_literal)) { + $this->yy_global_literal = $this->replace('/(SMARTYldel)SMARTYal[\/]?literalSMARTYrdel/isS'); + } + $to = $this->dataLength; + preg_match($this->yy_global_literal, $this->data,$match,PREG_OFFSET_CAPTURE,$this->counter); + if (isset($match[0][1])) { + $to = $match[0][1]; + } else { + $this->compiler->trigger_template_error ("missing or misspelled literal closing tag"); + } + $this->value = substr($this->data,$this->counter,$to-$this->counter); + $this->token = Smarty_Internal_Templateparser::TP_LITERAL; + } + + public function yylex5() { if (!isset($this->yy_global_pattern5)) { - $this->yy_global_pattern5 = - $this->replace("/\G((SMARTYldel)SMARTYautoliteral\\s+SMARTYliteral)|\G((SMARTYldel)SMARTYalliteral\\s*SMARTYrdel)|\G((SMARTYldel)SMARTYal[\/]literal\\s*SMARTYrdel)|\G((SMARTYldel)SMARTYal[\/])|\G((SMARTYldel)SMARTYal[0-9]*[a-zA-Z_]\\w*)|\G((SMARTYldel)SMARTYal)|\G([\"])|\G([`][$])|\G([$][0-9]*[a-zA-Z_]\\w*)|\G([$])|\G(([^\"\\\\]*?)((?:\\\\.[^\"\\\\]*?)*?)(?=((SMARTYldel)SMARTYal|\\$|`\\$|\"SMARTYliteral)))|\G([\S\s])/isS"); + $this->yy_global_pattern5 = $this->replace("/\G((SMARTYldel)SMARTYautoliteral\\s+SMARTYliteral)|\G((SMARTYldel)SMARTYalliteral\\s*SMARTYrdel)|\G((SMARTYldel)SMARTYal[\/]literal\\s*SMARTYrdel)|\G((SMARTYldel)SMARTYal[\/])|\G((SMARTYldel)SMARTYal[0-9]*[a-zA-Z_]\\w*)|\G((SMARTYldel)SMARTYal)|\G([\"])|\G([`][$])|\G([$][0-9]*[a-zA-Z_]\\w*)|\G([$])|\G(([^\"\\\\]*?)((?:\\\\.[^\"\\\\]*?)*?)(?=((SMARTYldel)SMARTYal|\\$|`\\$|\"SMARTYliteral)))|\G([\S\s])/isS"); } if (!isset($this->dataLength)) { $this->dataLength = strlen($this->data); } - if ($this->counter >= $this->dataLength) { + if ($this->counter >= $this->dataLength) { return false; // end of input } + do { - if (preg_match($this->yy_global_pattern5, $this->data, $yymatches, 0, $this->counter)) { - if (!isset($yymatches[ 0 ][ 1 ])) { - $yymatches = preg_grep("/(.|\s)+/", $yymatches); + if (preg_match($this->yy_global_pattern5,$this->data, $yymatches, 0, $this->counter)) { + if (!isset($yymatches[ 0 ][1])) { + $yymatches = preg_grep("/(.|\s)+/", $yymatches); } else { $yymatches = array_filter($yymatches); } if (empty($yymatches)) { throw new Exception('Error: lexing failed because a rule matched' . - ' an empty string. Input "' . substr($this->data, - $this->counter, 5) . '... state DOUBLEQUOTEDSTRING'); + ' an empty string. Input "' . substr($this->data, + $this->counter, 5) . '... state DOUBLEQUOTEDSTRING'); } next($yymatches); // skip global match $this->token = key($yymatches); // token number @@ -1007,89 +1003,93 @@ public function yylex5() } elseif ($r === false) { $this->counter += strlen($this->value); $this->line += substr_count($this->value, "\n"); - if ($this->counter >= $this->dataLength) { + if ($this->counter >= $this->dataLength) { return false; // end of input } // skip this token continue; - } - } else { - throw new Exception('Unexpected input at line ' . $this->line . - ': ' . $this->data[ $this->counter ]); + } } else { + throw new Exception('Unexpected input at line' . $this->line . + ': ' . $this->data[$this->counter]); } break; } while (true); - } + } // end function + + + const DOUBLEQUOTEDSTRING = 5; public function yy_r5_1() { - $this->token = Smarty_Internal_Templateparser::TP_TEXT; - } + $this->token = Smarty_Internal_Templateparser::TP_TEXT; + } public function yy_r5_3() { - $this->token = Smarty_Internal_Templateparser::TP_TEXT; - } + $this->token = Smarty_Internal_Templateparser::TP_TEXT; + } public function yy_r5_5() { - $this->token = Smarty_Internal_Templateparser::TP_TEXT; - } + $this->token = Smarty_Internal_Templateparser::TP_TEXT; + } public function yy_r5_7() { + $this->yypushstate(self::TAG); return true; - } - + } public function yy_r5_9() { + $this->yypushstate(self::TAG); return true; - } - + } public function yy_r5_11() { + $this->token = Smarty_Internal_Templateparser::TP_LDEL; $this->taglineno = $this->line; $this->yypushstate(self::TAGBODY); - } - + } public function yy_r5_13() { + $this->token = Smarty_Internal_Templateparser::TP_QUOTE; $this->yypopstate(); - } - + } public function yy_r5_14() { + $this->token = Smarty_Internal_Templateparser::TP_BACKTICK; - $this->value = substr($this->value, 0, -1); + $this->value = substr($this->value,0,-1); $this->yypushstate(self::TAGBODY); $this->taglineno = $this->line; - } - + } public function yy_r5_15() { - $this->token = Smarty_Internal_Templateparser::TP_DOLLARID; - } + $this->token = Smarty_Internal_Templateparser::TP_DOLLARID; + } public function yy_r5_16() { - $this->token = Smarty_Internal_Templateparser::TP_TEXT; - } + $this->token = Smarty_Internal_Templateparser::TP_TEXT; + } public function yy_r5_17() { - $this->token = Smarty_Internal_Templateparser::TP_TEXT; - } + $this->token = Smarty_Internal_Templateparser::TP_TEXT; + } public function yy_r5_22() { + $to = $this->dataLength; - $this->value = substr($this->data, $this->counter, $to - $this->counter); + $this->value = substr($this->data,$this->counter,$to-$this->counter); $this->token = Smarty_Internal_Templateparser::TP_TEXT; - } -} + } - + } + + \ No newline at end of file diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_templateparser.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_templateparser.php index aaeae63b7..a2dd0d6fb 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_templateparser.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_templateparser.php @@ -7,1598 +7,25 @@ class TP_yyStackEntry ** number for the token at this stack level */ public $minor; /* The user-supplied minor token value. This ** is the value of the token */ -} +}; + // line 11 "../smarty/lexer/smarty_internal_templateparser.y" /** - * Smarty Template Parser Class - * - * This is the template parser. - * It is generated from the smarty_internal_templateparser.y file - * - * @author Uwe Tews - */ +* Smarty Template Parser Class +* +* This is the template parser. +* It is generated from the smarty_internal_templateparser.y file +* +* @author Uwe Tews +*/ class Smarty_Internal_Templateparser { - // line 23 "../smarty/lexer/smarty_internal_templateparser.y" - const ERR1 = 'Security error: Call to private object member not allowed'; - const ERR2 = 'Security error: Call to dynamic object member not allowed'; - const ERR3 = 'PHP in template not allowed. Use SmartyBC to enable it'; - const TP_VERT = 1; - const TP_COLON = 2; - const TP_PHP = 3; - const TP_TEXT = 4; - const TP_STRIPON = 5; - const TP_STRIPOFF = 6; - const TP_LITERALSTART = 7; - const TP_LITERALEND = 8; - const TP_LITERAL = 9; - const TP_SIMPELOUTPUT = 10; - const TP_SIMPLETAG = 11; - const TP_SMARTYBLOCKCHILDPARENT = 12; - const TP_LDEL = 13; - const TP_RDEL = 14; - const TP_DOLLARID = 15; - const TP_EQUAL = 16; - const TP_ID = 17; - const TP_PTR = 18; - const TP_LDELMAKENOCACHE = 19; - const TP_LDELIF = 20; - const TP_LDELFOR = 21; - const TP_SEMICOLON = 22; - const TP_INCDEC = 23; - const TP_TO = 24; - const TP_STEP = 25; - const TP_LDELFOREACH = 26; - const TP_SPACE = 27; - const TP_AS = 28; - const TP_APTR = 29; - const TP_LDELSETFILTER = 30; - const TP_CLOSETAG = 31; - const TP_LDELSLASH = 32; - const TP_ATTR = 33; - const TP_INTEGER = 34; - const TP_COMMA = 35; - const TP_OPENP = 36; - const TP_CLOSEP = 37; - const TP_MATH = 38; - const TP_UNIMATH = 39; - const TP_ISIN = 40; - const TP_QMARK = 41; - const TP_NOT = 42; - const TP_TYPECAST = 43; - const TP_HEX = 44; - const TP_DOT = 45; - const TP_INSTANCEOF = 46; - const TP_SINGLEQUOTESTRING = 47; - const TP_DOUBLECOLON = 48; - const TP_NAMESPACE = 49; - const TP_AT = 50; - const TP_HATCH = 51; - const TP_OPENB = 52; - const TP_CLOSEB = 53; - const TP_DOLLAR = 54; - const TP_LOGOP = 55; - const TP_SLOGOP = 56; - const TP_TLOGOP = 57; - const TP_SINGLECOND = 58; - const TP_ARRAYOPEN = 59; - const TP_QUOTE = 60; - const TP_BACKTICK = 61; - const YY_NO_ACTION = 516; - const YY_ACCEPT_ACTION = 515; - const YY_ERROR_ACTION = 514; - const YY_SZ_ACTTAB = 2071; - const YY_SHIFT_USE_DFLT = -31; - const YY_SHIFT_MAX = 230; - const YY_REDUCE_USE_DFLT = -91; - const YY_REDUCE_MAX = 178; - const YYNOCODE = 110; - const YYSTACKDEPTH = 500; - const YYNSTATE = 327; - const YYNRULE = 187; - const YYERRORSYMBOL = 62; - const YYERRSYMDT = 'yy0'; - const YYFALLBACK = 0; - - public static $yy_action = array( - 251, 234, 237, 1, 144, 127, 428, 184, 199, 212, - 10, 54, 19, 175, 282, 215, 109, 389, 428, 428, - 224, 321, 223, 303, 203, 389, 13, 389, 281, 43, - 389, 428, 41, 40, 266, 225, 389, 213, 389, 194, - 389, 52, 4, 308, 301, 383, 34, 209, 222, 3, - 50, 153, 251, 234, 237, 1, 199, 131, 383, 198, - 305, 212, 10, 54, 383, 16, 199, 428, 109, 385, - 132, 18, 224, 321, 223, 222, 221, 12, 32, 428, - 116, 43, 385, 262, 41, 40, 266, 225, 385, 233, - 95, 194, 16, 52, 4, 131, 301, 252, 18, 265, - 164, 3, 50, 324, 251, 234, 237, 1, 23, 130, - 229, 198, 150, 212, 10, 54, 326, 11, 170, 284, - 109, 42, 22, 239, 224, 321, 223, 193, 221, 261, - 13, 52, 157, 43, 301, 286, 41, 40, 266, 225, - 205, 233, 5, 194, 96, 52, 4, 263, 301, 301, - 99, 349, 96, 3, 50, 199, 251, 234, 237, 1, - 238, 130, 241, 181, 349, 212, 10, 54, 382, 240, - 349, 36, 109, 185, 104, 256, 224, 321, 223, 132, - 191, 382, 13, 49, 91, 43, 12, 382, 41, 40, - 266, 225, 257, 233, 152, 194, 457, 52, 4, 457, - 301, 301, 228, 457, 282, 3, 50, 285, 251, 234, - 237, 1, 301, 131, 441, 198, 238, 212, 10, 54, - 349, 441, 325, 175, 109, 30, 349, 273, 224, 321, - 223, 20, 221, 295, 32, 211, 457, 39, 166, 49, - 41, 40, 266, 225, 87, 233, 205, 194, 279, 52, - 4, 24, 301, 204, 200, 280, 99, 3, 50, 199, - 251, 234, 237, 1, 31, 130, 96, 198, 205, 212, - 10, 54, 350, 55, 293, 207, 109, 283, 99, 96, - 224, 321, 223, 199, 180, 350, 13, 134, 230, 43, - 222, 350, 41, 40, 266, 225, 104, 233, 316, 194, - 279, 52, 4, 24, 301, 165, 284, 280, 85, 3, - 50, 25, 251, 234, 237, 1, 131, 129, 210, 198, - 14, 212, 10, 54, 269, 270, 301, 116, 109, 295, - 216, 211, 224, 321, 223, 171, 221, 95, 13, 28, - 219, 43, 323, 9, 41, 40, 266, 225, 151, 233, - 324, 194, 52, 52, 4, 301, 301, 30, 282, 302, - 178, 3, 50, 7, 251, 234, 237, 1, 136, 130, - 304, 179, 238, 212, 10, 54, 279, 175, 282, 24, - 109, 238, 429, 280, 224, 321, 223, 177, 221, 270, - 13, 255, 281, 43, 429, 49, 41, 40, 266, 225, - 275, 233, 318, 194, 49, 52, 4, 276, 301, 163, - 26, 199, 8, 3, 50, 119, 251, 234, 237, 1, - 11, 93, 291, 51, 107, 212, 10, 54, 226, 428, - 206, 201, 109, 148, 178, 322, 224, 321, 223, 441, - 221, 428, 13, 282, 9, 43, 441, 115, 41, 40, - 266, 225, 167, 233, 227, 194, 457, 52, 4, 457, - 301, 96, 158, 457, 101, 3, 50, 271, 251, 234, - 237, 1, 282, 130, 235, 186, 135, 212, 10, 54, - 199, 37, 119, 315, 109, 165, 284, 176, 224, 321, - 223, 104, 221, 149, 13, 281, 146, 43, 281, 300, - 41, 40, 266, 225, 30, 233, 289, 194, 21, 52, - 4, 272, 301, 211, 18, 301, 161, 3, 50, 110, - 251, 234, 237, 1, 137, 128, 282, 198, 268, 212, - 10, 54, 222, 169, 515, 92, 109, 172, 284, 31, - 224, 321, 223, 29, 221, 238, 6, 260, 53, 43, - 232, 139, 41, 40, 266, 225, 154, 233, 178, 194, - 168, 52, 4, 214, 301, 145, 99, 33, 49, 3, - 50, 245, 208, 211, 320, 282, 90, 111, 311, 183, - 98, 70, 309, 297, 236, 178, 95, 319, 142, 258, - 247, 267, 249, 264, 250, 195, 231, 199, 246, 324, - 317, 253, 254, 259, 126, 137, 133, 251, 234, 237, - 1, 326, 290, 105, 143, 156, 212, 10, 54, 88, - 84, 83, 484, 109, 322, 282, 37, 224, 321, 223, - 245, 208, 211, 320, 281, 90, 111, 298, 182, 98, - 56, 245, 298, 211, 178, 95, 103, 147, 258, 197, - 102, 75, 141, 250, 195, 231, 95, 246, 324, 258, - 279, 242, 89, 24, 250, 195, 231, 280, 246, 324, - 298, 298, 298, 298, 298, 298, 298, 16, 298, 192, - 277, 298, 298, 18, 294, 44, 45, 38, 298, 298, - 251, 234, 237, 2, 298, 296, 298, 298, 298, 212, - 10, 54, 310, 312, 313, 314, 109, 162, 298, 298, - 224, 321, 223, 298, 298, 298, 294, 282, 298, 42, - 22, 239, 251, 234, 237, 2, 298, 296, 298, 298, - 298, 212, 10, 54, 298, 159, 298, 298, 109, 298, - 298, 17, 224, 321, 223, 282, 298, 42, 22, 239, - 298, 298, 245, 298, 211, 278, 298, 103, 111, 298, - 183, 98, 70, 298, 298, 298, 298, 95, 298, 298, - 258, 298, 292, 17, 298, 250, 195, 231, 279, 246, - 324, 24, 298, 395, 245, 280, 211, 298, 298, 103, - 298, 298, 197, 102, 75, 16, 298, 140, 298, 95, - 298, 18, 258, 298, 298, 298, 298, 250, 195, 231, - 298, 246, 324, 298, 298, 298, 298, 428, 298, 395, - 395, 395, 202, 277, 298, 245, 298, 211, 298, 428, - 103, 298, 298, 197, 120, 69, 395, 395, 395, 395, - 95, 298, 298, 258, 298, 298, 298, 160, 250, 195, - 231, 86, 246, 324, 245, 16, 211, 282, 298, 103, - 196, 18, 197, 120, 69, 298, 44, 45, 38, 95, - 298, 298, 258, 298, 298, 298, 178, 250, 195, 231, - 298, 246, 324, 310, 312, 313, 314, 298, 298, 190, - 245, 298, 211, 298, 298, 103, 298, 298, 197, 102, - 75, 298, 298, 298, 298, 95, 298, 298, 258, 298, - 298, 298, 298, 250, 195, 231, 298, 246, 324, 298, - 298, 298, 245, 298, 211, 298, 199, 100, 298, 288, - 197, 120, 47, 298, 106, 298, 298, 95, 298, 353, - 258, 155, 298, 218, 298, 250, 195, 231, 298, 246, - 324, 282, 16, 42, 22, 239, 298, 245, 18, 211, - 298, 428, 103, 298, 298, 197, 120, 69, 298, 298, - 298, 298, 95, 428, 298, 258, 298, 298, 298, 298, - 250, 195, 231, 298, 246, 324, 245, 298, 211, 298, - 298, 100, 189, 298, 197, 120, 59, 245, 207, 211, - 298, 95, 103, 298, 258, 197, 120, 81, 298, 250, - 195, 231, 95, 246, 324, 258, 298, 298, 298, 298, - 250, 195, 231, 298, 246, 324, 298, 245, 298, 211, - 298, 298, 103, 298, 298, 197, 120, 80, 298, 298, - 298, 298, 95, 298, 298, 258, 298, 298, 298, 298, - 250, 195, 231, 298, 246, 324, 245, 298, 211, 298, - 298, 103, 298, 298, 197, 120, 67, 245, 298, 211, - 298, 95, 103, 298, 258, 197, 120, 57, 298, 250, - 195, 231, 95, 246, 324, 258, 298, 298, 298, 298, - 250, 195, 231, 298, 246, 324, 298, 245, 298, 211, - 298, 298, 103, 298, 298, 197, 120, 58, 298, 298, - 298, 298, 95, 298, 298, 258, 298, 298, 298, 298, - 250, 195, 231, 298, 246, 324, 245, 298, 211, 298, - 298, 103, 298, 298, 197, 120, 82, 245, 298, 211, - 298, 95, 103, 298, 258, 197, 97, 76, 298, 250, - 195, 231, 95, 246, 324, 258, 298, 298, 298, 298, - 250, 195, 231, 298, 246, 324, 298, 245, 298, 211, - 298, 298, 103, 298, 298, 197, 120, 71, 298, 298, - 298, 298, 95, 298, 298, 258, 298, 298, 298, 298, - 250, 195, 231, 298, 246, 324, 245, 298, 211, 298, - 298, 103, 298, 298, 187, 120, 61, 245, 298, 211, - 298, 95, 103, 298, 258, 197, 120, 63, 298, 250, - 195, 231, 95, 246, 324, 258, 298, 298, 298, 298, - 250, 195, 231, 298, 246, 324, 298, 245, 298, 211, - 298, 298, 103, 298, 298, 197, 94, 79, 298, 298, - 298, 298, 95, 298, 298, 258, 298, 298, 298, 298, - 250, 195, 231, 298, 246, 324, 245, 298, 211, 298, - 298, 103, 298, 298, 197, 120, 59, 245, 298, 211, - 298, 95, 103, 298, 258, 197, 120, 77, 298, 250, - 195, 231, 95, 246, 324, 258, 298, 298, 298, 298, - 250, 195, 231, 298, 246, 324, 298, 245, 298, 211, - 298, 298, 103, 298, 298, 188, 108, 64, 298, 298, - 298, 298, 95, 298, 298, 258, 298, 298, 298, 298, - 250, 195, 231, 298, 246, 324, 245, 298, 211, 298, - 298, 103, 298, 298, 197, 120, 65, 245, 298, 211, - 298, 95, 103, 298, 258, 197, 97, 66, 298, 250, - 195, 231, 95, 246, 324, 258, 298, 298, 298, 298, - 250, 195, 231, 298, 246, 324, 298, 245, 298, 211, - 298, 298, 103, 298, 298, 197, 120, 68, 298, 298, - 298, 298, 95, 298, 298, 258, 298, 298, 298, 298, - 250, 195, 231, 298, 246, 324, 245, 298, 211, 298, - 298, 103, 298, 298, 197, 120, 62, 245, 298, 211, - 298, 95, 103, 298, 258, 197, 120, 60, 298, 250, - 195, 231, 95, 246, 324, 258, 298, 298, 298, 298, - 250, 195, 231, 298, 246, 324, 298, 245, 298, 211, - 298, 298, 103, 298, 298, 197, 120, 74, 298, 298, - 298, 298, 95, 298, 298, 258, 298, 298, 298, 298, - 250, 195, 231, 298, 246, 324, 245, 298, 211, 298, - 298, 103, 298, 298, 197, 120, 72, 245, 298, 211, - 298, 95, 103, 298, 258, 197, 120, 48, 298, 250, - 195, 231, 95, 246, 324, 258, 298, 298, 298, 298, - 250, 195, 231, 298, 246, 324, 298, 245, 298, 211, - 298, 298, 103, 298, 298, 197, 120, 46, 298, 298, - 298, 298, 95, 298, 298, 258, 298, 298, 298, 298, - 250, 195, 231, 298, 246, 324, 245, 298, 211, 298, - 298, 103, 298, 298, 197, 120, 78, 245, 298, 211, - 298, 95, 103, 298, 258, 197, 120, 73, 298, 250, - 195, 231, 95, 246, 324, 258, 298, 298, 298, 298, - 250, 195, 231, 298, 246, 324, 298, 245, 298, 211, - 298, 298, 103, 298, 298, 197, 125, 298, 298, 298, - 298, 298, 95, 298, 298, 298, 298, 298, 298, 244, - 250, 195, 231, 298, 246, 324, 245, 298, 211, 298, - 298, 103, 298, 298, 197, 114, 298, 245, 298, 211, - 298, 95, 103, 298, 298, 197, 122, 298, 243, 250, - 195, 231, 95, 246, 324, 298, 298, 298, 298, 298, - 250, 195, 231, 298, 246, 324, 298, 245, 298, 211, - 298, 298, 103, 298, 298, 197, 117, 298, 298, 298, - 298, 298, 95, 298, 298, 298, 298, 298, 298, 298, - 250, 195, 231, 298, 246, 324, 245, 298, 211, 298, - 298, 103, 298, 298, 197, 121, 298, 245, 298, 211, - 298, 95, 103, 298, 298, 197, 124, 298, 298, 250, - 195, 231, 95, 246, 324, 298, 298, 298, 298, 298, - 250, 195, 231, 298, 246, 324, 298, 245, 298, 211, - 298, 298, 103, 298, 298, 197, 118, 298, 298, 298, - 298, 298, 95, 298, 298, 298, 298, 298, 298, 298, - 250, 195, 231, 298, 246, 324, 245, 298, 211, 298, - 298, 103, 298, 298, 197, 123, 298, 245, 298, 211, - 298, 95, 103, 298, 298, 197, 113, 298, 298, 250, - 195, 231, 95, 246, 324, 298, 298, 298, 298, 298, - 250, 195, 231, 220, 246, 324, 298, 27, 298, 16, - 298, 457, 298, 298, 457, 18, 298, 26, 457, 441, - 44, 45, 38, 217, 44, 45, 38, 298, 298, 298, - 298, 298, 298, 298, 298, 298, 298, 310, 312, 313, - 314, 310, 312, 313, 314, 298, 441, 298, 298, 441, - 298, 457, 220, 441, 457, 298, 298, 457, 298, 298, - 457, 457, 441, 457, 298, 298, 220, 457, 441, 298, - 298, 298, 298, 298, 457, 298, 298, 457, 298, 298, - 5, 457, 441, 298, 298, 298, 298, 298, 298, 441, - 298, 298, 441, 298, 457, 441, 441, 298, 441, 298, - 457, 298, 441, 306, 298, 298, 298, 298, 298, 441, - 298, 298, 441, 298, 457, 220, 441, 298, 298, 298, - 298, 298, 298, 457, 298, 298, 457, 298, 298, 15, - 457, 441, 35, 274, 44, 45, 38, 457, 298, 298, - 457, 298, 298, 298, 457, 441, 298, 298, 298, 298, - 298, 310, 312, 313, 314, 298, 298, 298, 441, 298, - 298, 441, 298, 457, 298, 441, 287, 298, 44, 45, - 38, 298, 441, 298, 298, 441, 298, 457, 298, 441, - 248, 298, 298, 298, 298, 310, 312, 313, 314, 298, - 44, 45, 38, 298, 298, 112, 298, 44, 45, 38, - 298, 173, 298, 298, 44, 45, 38, 310, 312, 313, - 314, 44, 45, 38, 310, 312, 313, 314, 298, 298, - 299, 310, 312, 313, 314, 44, 45, 38, 310, 312, - 313, 314, 174, 298, 298, 298, 138, 298, 298, 298, - 298, 298, 310, 312, 313, 314, 44, 45, 38, 298, - 298, 298, 44, 45, 38, 298, 44, 45, 38, 298, - 44, 45, 38, 310, 312, 313, 314, 307, 298, 310, - 312, 313, 314, 310, 312, 313, 314, 310, 312, 313, - 314, - ); - - public static $yy_lookahead = array( - 10, 11, 12, 13, 74, 15, 36, 17, 1, 19, - 20, 21, 29, 103, 84, 45, 26, 14, 48, 36, - 30, 31, 32, 53, 34, 22, 36, 24, 98, 39, - 27, 48, 42, 43, 44, 45, 33, 47, 35, 49, - 37, 51, 52, 53, 54, 14, 16, 16, 45, 59, - 60, 96, 10, 11, 12, 13, 1, 15, 27, 17, - 53, 19, 20, 21, 33, 27, 1, 36, 26, 14, - 45, 33, 30, 31, 32, 45, 34, 52, 36, 48, - 72, 39, 27, 75, 42, 43, 44, 45, 33, 47, - 82, 49, 27, 51, 52, 15, 54, 17, 33, 91, - 83, 59, 60, 95, 10, 11, 12, 13, 13, 15, - 15, 17, 17, 19, 20, 21, 97, 35, 99, 100, - 26, 86, 87, 88, 30, 31, 32, 66, 34, 49, - 36, 51, 96, 39, 54, 53, 42, 43, 44, 45, - 72, 47, 16, 49, 18, 51, 52, 79, 54, 54, - 82, 14, 18, 59, 60, 1, 10, 11, 12, 13, - 23, 15, 15, 17, 27, 19, 20, 21, 14, 17, - 33, 13, 26, 15, 48, 17, 30, 31, 32, 45, - 34, 27, 36, 46, 83, 39, 52, 33, 42, 43, - 44, 45, 34, 47, 74, 49, 10, 51, 52, 13, - 54, 54, 50, 17, 84, 59, 60, 14, 10, 11, - 12, 13, 54, 15, 45, 17, 23, 19, 20, 21, - 27, 52, 100, 103, 26, 35, 33, 37, 30, 31, - 32, 22, 34, 67, 36, 69, 50, 39, 83, 46, - 42, 43, 44, 45, 35, 47, 72, 49, 10, 51, - 52, 13, 54, 79, 80, 17, 82, 59, 60, 1, - 10, 11, 12, 13, 16, 15, 18, 17, 72, 19, - 20, 21, 14, 107, 108, 79, 26, 71, 82, 18, - 30, 31, 32, 1, 34, 27, 36, 15, 50, 39, - 45, 33, 42, 43, 44, 45, 48, 47, 53, 49, - 10, 51, 52, 13, 54, 99, 100, 17, 36, 59, - 60, 29, 10, 11, 12, 13, 15, 15, 17, 17, - 13, 19, 20, 21, 8, 9, 54, 72, 26, 67, - 75, 69, 30, 31, 32, 78, 34, 82, 36, 24, - 50, 39, 17, 36, 42, 43, 44, 45, 74, 47, - 95, 49, 51, 51, 52, 54, 54, 35, 84, 37, - 103, 59, 60, 36, 10, 11, 12, 13, 74, 15, - 108, 17, 23, 19, 20, 21, 10, 103, 84, 13, - 26, 23, 36, 17, 30, 31, 32, 7, 34, 9, - 36, 17, 98, 39, 48, 46, 42, 43, 44, 45, - 17, 47, 53, 49, 46, 51, 52, 93, 54, 78, - 16, 1, 36, 59, 60, 101, 10, 11, 12, 13, - 35, 15, 37, 17, 48, 19, 20, 21, 18, 36, - 65, 66, 26, 74, 103, 104, 30, 31, 32, 45, - 34, 48, 36, 84, 36, 39, 52, 17, 42, 43, - 44, 45, 15, 47, 17, 49, 10, 51, 52, 13, - 54, 18, 74, 17, 82, 59, 60, 34, 10, 11, - 12, 13, 84, 15, 93, 17, 15, 19, 20, 21, - 1, 2, 101, 101, 26, 99, 100, 17, 30, 31, - 32, 48, 34, 96, 36, 98, 96, 39, 98, 71, - 42, 43, 44, 45, 35, 47, 37, 49, 27, 51, - 52, 67, 54, 69, 33, 54, 74, 59, 60, 17, - 10, 11, 12, 13, 96, 15, 84, 17, 34, 19, - 20, 21, 45, 78, 63, 64, 26, 99, 100, 16, - 30, 31, 32, 16, 34, 23, 36, 17, 17, 39, - 23, 51, 42, 43, 44, 45, 72, 47, 103, 49, - 78, 51, 52, 17, 54, 74, 82, 41, 46, 59, - 60, 67, 68, 69, 70, 84, 72, 73, 53, 75, - 76, 77, 53, 61, 15, 103, 82, 14, 51, 85, - 14, 37, 17, 8, 90, 91, 92, 1, 94, 95, - 3, 4, 5, 6, 7, 96, 82, 10, 11, 12, - 13, 97, 84, 81, 96, 74, 19, 20, 21, 78, - 82, 82, 1, 26, 104, 84, 2, 30, 31, 32, - 67, 68, 69, 70, 98, 72, 73, 109, 75, 76, - 77, 67, 109, 69, 103, 82, 72, 96, 85, 75, - 76, 77, 96, 90, 91, 92, 82, 94, 95, 85, - 10, 14, 96, 13, 90, 91, 92, 17, 94, 95, - 109, 109, 109, 109, 109, 109, 109, 27, 109, 105, - 106, 109, 109, 33, 4, 38, 39, 40, 109, 109, - 10, 11, 12, 13, 109, 15, 109, 109, 109, 19, - 20, 21, 55, 56, 57, 58, 26, 74, 109, 109, - 30, 31, 32, 109, 109, 109, 4, 84, 109, 86, - 87, 88, 10, 11, 12, 13, 109, 15, 109, 109, - 109, 19, 20, 21, 109, 74, 109, 109, 26, 109, - 60, 61, 30, 31, 32, 84, 109, 86, 87, 88, - 109, 109, 67, 109, 69, 70, 109, 72, 73, 109, - 75, 76, 77, 109, 109, 109, 109, 82, 109, 109, - 85, 109, 60, 61, 109, 90, 91, 92, 10, 94, - 95, 13, 109, 2, 67, 17, 69, 109, 109, 72, - 109, 109, 75, 76, 77, 27, 109, 29, 109, 82, - 109, 33, 85, 109, 109, 109, 109, 90, 91, 92, - 109, 94, 95, 109, 109, 109, 109, 36, 109, 38, - 39, 40, 105, 106, 109, 67, 109, 69, 109, 48, - 72, 109, 109, 75, 76, 77, 55, 56, 57, 58, - 82, 109, 109, 85, 109, 109, 109, 74, 90, 91, - 92, 78, 94, 95, 67, 27, 69, 84, 109, 72, - 102, 33, 75, 76, 77, 109, 38, 39, 40, 82, - 109, 109, 85, 109, 109, 109, 103, 90, 91, 92, - 109, 94, 95, 55, 56, 57, 58, 109, 109, 102, - 67, 109, 69, 109, 109, 72, 109, 109, 75, 76, - 77, 109, 109, 109, 109, 82, 109, 109, 85, 109, - 109, 109, 109, 90, 91, 92, 109, 94, 95, 109, - 109, 109, 67, 109, 69, 109, 1, 72, 109, 106, - 75, 76, 77, 109, 79, 109, 109, 82, 109, 14, - 85, 74, 109, 18, 109, 90, 91, 92, 109, 94, - 95, 84, 27, 86, 87, 88, 109, 67, 33, 69, - 109, 36, 72, 109, 109, 75, 76, 77, 109, 109, - 109, 109, 82, 48, 109, 85, 109, 109, 109, 109, - 90, 91, 92, 109, 94, 95, 67, 109, 69, 109, - 109, 72, 102, 109, 75, 76, 77, 67, 79, 69, - 109, 82, 72, 109, 85, 75, 76, 77, 109, 90, - 91, 92, 82, 94, 95, 85, 109, 109, 109, 109, - 90, 91, 92, 109, 94, 95, 109, 67, 109, 69, - 109, 109, 72, 109, 109, 75, 76, 77, 109, 109, - 109, 109, 82, 109, 109, 85, 109, 109, 109, 109, - 90, 91, 92, 109, 94, 95, 67, 109, 69, 109, - 109, 72, 109, 109, 75, 76, 77, 67, 109, 69, - 109, 82, 72, 109, 85, 75, 76, 77, 109, 90, - 91, 92, 82, 94, 95, 85, 109, 109, 109, 109, - 90, 91, 92, 109, 94, 95, 109, 67, 109, 69, - 109, 109, 72, 109, 109, 75, 76, 77, 109, 109, - 109, 109, 82, 109, 109, 85, 109, 109, 109, 109, - 90, 91, 92, 109, 94, 95, 67, 109, 69, 109, - 109, 72, 109, 109, 75, 76, 77, 67, 109, 69, - 109, 82, 72, 109, 85, 75, 76, 77, 109, 90, - 91, 92, 82, 94, 95, 85, 109, 109, 109, 109, - 90, 91, 92, 109, 94, 95, 109, 67, 109, 69, - 109, 109, 72, 109, 109, 75, 76, 77, 109, 109, - 109, 109, 82, 109, 109, 85, 109, 109, 109, 109, - 90, 91, 92, 109, 94, 95, 67, 109, 69, 109, - 109, 72, 109, 109, 75, 76, 77, 67, 109, 69, - 109, 82, 72, 109, 85, 75, 76, 77, 109, 90, - 91, 92, 82, 94, 95, 85, 109, 109, 109, 109, - 90, 91, 92, 109, 94, 95, 109, 67, 109, 69, - 109, 109, 72, 109, 109, 75, 76, 77, 109, 109, - 109, 109, 82, 109, 109, 85, 109, 109, 109, 109, - 90, 91, 92, 109, 94, 95, 67, 109, 69, 109, - 109, 72, 109, 109, 75, 76, 77, 67, 109, 69, - 109, 82, 72, 109, 85, 75, 76, 77, 109, 90, - 91, 92, 82, 94, 95, 85, 109, 109, 109, 109, - 90, 91, 92, 109, 94, 95, 109, 67, 109, 69, - 109, 109, 72, 109, 109, 75, 76, 77, 109, 109, - 109, 109, 82, 109, 109, 85, 109, 109, 109, 109, - 90, 91, 92, 109, 94, 95, 67, 109, 69, 109, - 109, 72, 109, 109, 75, 76, 77, 67, 109, 69, - 109, 82, 72, 109, 85, 75, 76, 77, 109, 90, - 91, 92, 82, 94, 95, 85, 109, 109, 109, 109, - 90, 91, 92, 109, 94, 95, 109, 67, 109, 69, - 109, 109, 72, 109, 109, 75, 76, 77, 109, 109, - 109, 109, 82, 109, 109, 85, 109, 109, 109, 109, - 90, 91, 92, 109, 94, 95, 67, 109, 69, 109, - 109, 72, 109, 109, 75, 76, 77, 67, 109, 69, - 109, 82, 72, 109, 85, 75, 76, 77, 109, 90, - 91, 92, 82, 94, 95, 85, 109, 109, 109, 109, - 90, 91, 92, 109, 94, 95, 109, 67, 109, 69, - 109, 109, 72, 109, 109, 75, 76, 77, 109, 109, - 109, 109, 82, 109, 109, 85, 109, 109, 109, 109, - 90, 91, 92, 109, 94, 95, 67, 109, 69, 109, - 109, 72, 109, 109, 75, 76, 77, 67, 109, 69, - 109, 82, 72, 109, 85, 75, 76, 77, 109, 90, - 91, 92, 82, 94, 95, 85, 109, 109, 109, 109, - 90, 91, 92, 109, 94, 95, 109, 67, 109, 69, - 109, 109, 72, 109, 109, 75, 76, 77, 109, 109, - 109, 109, 82, 109, 109, 85, 109, 109, 109, 109, - 90, 91, 92, 109, 94, 95, 67, 109, 69, 109, - 109, 72, 109, 109, 75, 76, 77, 67, 109, 69, - 109, 82, 72, 109, 85, 75, 76, 77, 109, 90, - 91, 92, 82, 94, 95, 85, 109, 109, 109, 109, - 90, 91, 92, 109, 94, 95, 109, 67, 109, 69, - 109, 109, 72, 109, 109, 75, 76, 109, 109, 109, - 109, 109, 82, 109, 109, 109, 109, 109, 109, 89, - 90, 91, 92, 109, 94, 95, 67, 109, 69, 109, - 109, 72, 109, 109, 75, 76, 109, 67, 109, 69, - 109, 82, 72, 109, 109, 75, 76, 109, 89, 90, - 91, 92, 82, 94, 95, 109, 109, 109, 109, 109, - 90, 91, 92, 109, 94, 95, 109, 67, 109, 69, - 109, 109, 72, 109, 109, 75, 76, 109, 109, 109, - 109, 109, 82, 109, 109, 109, 109, 109, 109, 109, - 90, 91, 92, 109, 94, 95, 67, 109, 69, 109, - 109, 72, 109, 109, 75, 76, 109, 67, 109, 69, - 109, 82, 72, 109, 109, 75, 76, 109, 109, 90, - 91, 92, 82, 94, 95, 109, 109, 109, 109, 109, - 90, 91, 92, 109, 94, 95, 109, 67, 109, 69, - 109, 109, 72, 109, 109, 75, 76, 109, 109, 109, - 109, 109, 82, 109, 109, 109, 109, 109, 109, 109, - 90, 91, 92, 109, 94, 95, 67, 109, 69, 109, - 109, 72, 109, 109, 75, 76, 109, 67, 109, 69, - 109, 82, 72, 109, 109, 75, 76, 109, 109, 90, - 91, 92, 82, 94, 95, 109, 109, 109, 109, 109, - 90, 91, 92, 2, 94, 95, 109, 25, 109, 27, - 109, 10, 109, 109, 13, 33, 109, 16, 17, 18, - 38, 39, 40, 37, 38, 39, 40, 109, 109, 109, - 109, 109, 109, 109, 109, 109, 109, 55, 56, 57, - 58, 55, 56, 57, 58, 109, 45, 109, 109, 48, - 109, 50, 2, 52, 10, 109, 109, 13, 109, 109, - 10, 17, 18, 13, 109, 109, 2, 17, 18, 109, - 109, 109, 109, 109, 10, 109, 109, 13, 109, 109, - 16, 17, 18, 109, 109, 109, 109, 109, 109, 45, - 109, 109, 48, 109, 50, 45, 52, 109, 48, 109, - 50, 109, 52, 53, 109, 109, 109, 109, 109, 45, - 109, 109, 48, 109, 50, 2, 52, 109, 109, 109, - 109, 109, 109, 10, 109, 109, 13, 109, 109, 2, - 17, 18, 2, 37, 38, 39, 40, 10, 109, 109, - 13, 109, 109, 109, 17, 18, 109, 109, 109, 109, - 109, 55, 56, 57, 58, 109, 109, 109, 45, 109, - 109, 48, 109, 50, 109, 52, 14, 109, 38, 39, - 40, 109, 45, 109, 109, 48, 109, 50, 109, 52, - 14, 109, 109, 109, 109, 55, 56, 57, 58, 109, - 38, 39, 40, 109, 109, 22, 109, 38, 39, 40, - 109, 14, 109, 109, 38, 39, 40, 55, 56, 57, - 58, 38, 39, 40, 55, 56, 57, 58, 109, 109, - 61, 55, 56, 57, 58, 38, 39, 40, 55, 56, - 57, 58, 14, 109, 109, 109, 28, 109, 109, 109, - 109, 109, 55, 56, 57, 58, 38, 39, 40, 109, - 109, 109, 38, 39, 40, 109, 38, 39, 40, 109, - 38, 39, 40, 55, 56, 57, 58, 53, 109, 55, - 56, 57, 58, 55, 56, 57, 58, 55, 56, 57, - 58, - ); - - public static $yy_shift_ofst = array( - -31, 406, 406, 458, 458, 94, 510, 94, 94, 94, - 510, 458, -10, 94, 94, 354, 146, 94, 94, 94, - 94, 146, 94, 94, 94, 94, 250, 94, 94, 94, - 94, 94, 94, 302, 94, 94, 94, 198, 42, 42, - 42, 42, 42, 42, 42, 42, 1772, 828, 828, 80, - 712, 925, 301, 65, 272, 680, 1942, 1920, 1886, 1776, - 647, 1949, 1977, 2008, 2004, 1963, 1998, 1956, 2012, 2012, - 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, - 2012, 2012, 2012, 768, 650, 272, 65, 272, 65, 134, - 126, 479, 597, 1854, 154, 290, 95, 55, 258, 366, - 248, 366, 282, 443, 437, 38, 38, 437, 7, 481, - 410, 38, 461, 621, 596, 596, 261, 596, 596, 261, - 596, 596, 596, 596, 596, -31, -31, 1840, 1791, 1917, - 1903, 1834, 158, 238, 394, 446, 38, 25, 147, 169, - 147, 25, 169, 25, 38, 38, 25, 25, 38, 25, - 307, 38, 38, 25, 527, 38, 38, 25, 38, 38, - 38, 38, 38, 596, 624, 261, 624, 327, 596, 596, - 261, 596, 261, -31, -31, -31, -31, -31, -31, 781, - 3, 31, 193, 137, -30, 186, -17, 522, 349, 469, - 322, 30, 82, 316, 346, 376, 190, 358, 393, 152, - 209, 380, 385, 245, 315, 523, 585, 554, 576, 575, - 537, 573, 569, 529, 525, 546, 500, 526, 531, 325, - 530, 487, 494, 502, 470, 433, 430, 408, 383, 327, - 374, - ); - - public static $yy_reduce_ofst = array( - 471, 504, 563, 717, 574, 685, 919, 890, 787, 758, - 855, 823, 1240, 1199, 1140, 1100, 1070, 1129, 1170, 1210, - 1269, 1280, 1310, 1339, 1350, 1380, 1409, 1420, 1450, 1479, - 1490, 1059, 1030, 1000, 930, 960, 989, 1520, 1549, 1700, - 1619, 1689, 1660, 1630, 1590, 1560, 633, 661, 867, 8, - 166, 773, 255, 541, 174, 262, 35, 35, 35, 35, - 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, - 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, - 35, 35, 35, 294, -70, 196, 120, 68, 274, 19, - 206, 331, 444, 428, 257, 400, 382, 257, 257, 400, - 386, 397, 257, 386, 381, 388, 359, 314, 257, 442, - 482, 491, 484, 257, 257, 455, 386, 257, 257, 438, - 257, 257, 257, 257, 257, 257, 365, 509, 509, 509, - 509, 509, 524, 536, 509, 509, 528, 514, 539, 551, - 538, 514, 556, 514, 528, 528, 514, 514, 528, 514, - 518, 528, 528, 514, 532, 528, 528, 514, 528, 528, - 528, 528, 528, -90, 520, 122, 520, 566, -90, -90, - 122, -90, 122, -45, 36, 155, 101, 61, 17, - ); - - public static $yyExpectedTokens = array( - array(), - array( - 10, 11, 12, 13, 15, 17, 19, 20, 21, 26, 30, 31, 32, 34, 36, 39, 42, 43, 44, 45, 47, 49, 51, 52, 54, 59, 60, - ), - array( - 10, 11, 12, 13, 15, 17, 19, 20, 21, 26, 30, 31, 32, 34, 36, 39, 42, 43, 44, 45, 47, 49, 51, 52, 54, 59, 60, - ), - array( - 10, 11, 12, 13, 15, 17, 19, 20, 21, 26, 30, 31, 32, 34, 36, 39, 42, 43, 44, 45, 47, 49, 51, 52, 54, 59, 60, - ), - array( - 10, 11, 12, 13, 15, 17, 19, 20, 21, 26, 30, 31, 32, 34, 36, 39, 42, 43, 44, 45, 47, 49, 51, 52, 54, 59, 60, - ), - array( - 10, 11, 12, 13, 15, 17, 19, 20, 21, 26, 30, 31, 32, 34, 36, 39, 42, 43, 44, 45, 47, 49, 51, 52, 54, 59, 60, - ), - array( - 10, 11, 12, 13, 15, 17, 19, 20, 21, 26, 30, 31, 32, 34, 36, 39, 42, 43, 44, 45, 47, 49, 51, 52, 54, 59, 60, - ), - array( - 10, 11, 12, 13, 15, 17, 19, 20, 21, 26, 30, 31, 32, 34, 36, 39, 42, 43, 44, 45, 47, 49, 51, 52, 54, 59, 60, - ), - array( - 10, 11, 12, 13, 15, 17, 19, 20, 21, 26, 30, 31, 32, 34, 36, 39, 42, 43, 44, 45, 47, 49, 51, 52, 54, 59, 60, - ), - array( - 10, 11, 12, 13, 15, 17, 19, 20, 21, 26, 30, 31, 32, 34, 36, 39, 42, 43, 44, 45, 47, 49, 51, 52, 54, 59, 60, - ), - array( - 10, 11, 12, 13, 15, 17, 19, 20, 21, 26, 30, 31, 32, 34, 36, 39, 42, 43, 44, 45, 47, 49, 51, 52, 54, 59, 60, - ), - array( - 10, 11, 12, 13, 15, 17, 19, 20, 21, 26, 30, 31, 32, 34, 36, 39, 42, 43, 44, 45, 47, 49, 51, 52, 54, 59, 60, - ), - array( - 10, 11, 12, 13, 15, 17, 19, 20, 21, 26, 30, 31, 32, 34, 36, 39, 42, 43, 44, 45, 47, 49, 51, 52, 53, 54, 59, - 60, - ), - array( - 10, 11, 12, 13, 15, 17, 19, 20, 21, 26, 30, 31, 32, 34, 36, 39, 42, 43, 44, 45, 47, 49, 51, 52, 54, 59, 60, - ), - array( - 10, 11, 12, 13, 15, 17, 19, 20, 21, 26, 30, 31, 32, 34, 36, 39, 42, 43, 44, 45, 47, 49, 51, 52, 54, 59, 60, - ), - array( - 10, 11, 12, 13, 15, 17, 19, 20, 21, 26, 30, 31, 32, 34, 36, 39, 42, 43, 44, 45, 47, 49, 51, 52, 54, 59, 60, - ), - array( - 10, 11, 12, 13, 15, 17, 19, 20, 21, 26, 30, 31, 32, 34, 36, 39, 42, 43, 44, 45, 47, 49, 51, 52, 54, 59, 60, - ), - array( - 10, 11, 12, 13, 15, 17, 19, 20, 21, 26, 30, 31, 32, 34, 36, 39, 42, 43, 44, 45, 47, 49, 51, 52, 54, 59, 60, - ), - array( - 10, 11, 12, 13, 15, 17, 19, 20, 21, 26, 30, 31, 32, 34, 36, 39, 42, 43, 44, 45, 47, 49, 51, 52, 54, 59, 60, - ), - array( - 10, 11, 12, 13, 15, 17, 19, 20, 21, 26, 30, 31, 32, 34, 36, 39, 42, 43, 44, 45, 47, 49, 51, 52, 54, 59, 60, - ), - array( - 10, 11, 12, 13, 15, 17, 19, 20, 21, 26, 30, 31, 32, 34, 36, 39, 42, 43, 44, 45, 47, 49, 51, 52, 54, 59, 60, - ), - array( - 10, 11, 12, 13, 15, 17, 19, 20, 21, 26, 30, 31, 32, 34, 36, 39, 42, 43, 44, 45, 47, 49, 51, 52, 54, 59, 60, - ), - array( - 10, 11, 12, 13, 15, 17, 19, 20, 21, 26, 30, 31, 32, 34, 36, 39, 42, 43, 44, 45, 47, 49, 51, 52, 54, 59, 60, - ), - array( - 10, 11, 12, 13, 15, 17, 19, 20, 21, 26, 30, 31, 32, 34, 36, 39, 42, 43, 44, 45, 47, 49, 51, 52, 54, 59, 60, - ), - array( - 10, 11, 12, 13, 15, 17, 19, 20, 21, 26, 30, 31, 32, 34, 36, 39, 42, 43, 44, 45, 47, 49, 51, 52, 54, 59, 60, - ), - array( - 10, 11, 12, 13, 15, 17, 19, 20, 21, 26, 30, 31, 32, 34, 36, 39, 42, 43, 44, 45, 47, 49, 51, 52, 54, 59, 60, - ), - array( - 10, 11, 12, 13, 15, 17, 19, 20, 21, 26, 30, 31, 32, 34, 36, 39, 42, 43, 44, 45, 47, 49, 51, 52, 54, 59, 60, - ), - array( - 10, 11, 12, 13, 15, 17, 19, 20, 21, 26, 30, 31, 32, 34, 36, 39, 42, 43, 44, 45, 47, 49, 51, 52, 54, 59, 60, - ), - array( - 10, 11, 12, 13, 15, 17, 19, 20, 21, 26, 30, 31, 32, 34, 36, 39, 42, 43, 44, 45, 47, 49, 51, 52, 54, 59, 60, - ), - array( - 10, 11, 12, 13, 15, 17, 19, 20, 21, 26, 30, 31, 32, 34, 36, 39, 42, 43, 44, 45, 47, 49, 51, 52, 54, 59, 60, - ), - array( - 10, 11, 12, 13, 15, 17, 19, 20, 21, 26, 30, 31, 32, 34, 36, 39, 42, 43, 44, 45, 47, 49, 51, 52, 54, 59, 60, - ), - array( - 10, 11, 12, 13, 15, 17, 19, 20, 21, 26, 30, 31, 32, 34, 36, 39, 42, 43, 44, 45, 47, 49, 51, 52, 54, 59, 60, - ), - array( - 10, 11, 12, 13, 15, 17, 19, 20, 21, 26, 30, 31, 32, 34, 36, 39, 42, 43, 44, 45, 47, 49, 51, 52, 54, 59, 60, - ), - array( - 10, 11, 12, 13, 15, 17, 19, 20, 21, 26, 30, 31, 32, 34, 36, 39, 42, 43, 44, 45, 47, 49, 51, 52, 54, 59, 60, - ), - array( - 10, 11, 12, 13, 15, 17, 19, 20, 21, 26, 30, 31, 32, 34, 36, 39, 42, 43, 44, 45, 47, 49, 51, 52, 54, 59, 60, - ), - array( - 10, 11, 12, 13, 15, 17, 19, 20, 21, 26, 30, 31, 32, 34, 36, 39, 42, 43, 44, 45, 47, 49, 51, 52, 54, 59, 60, - ), - array( - 10, 11, 12, 13, 15, 17, 19, 20, 21, 26, 30, 31, 32, 34, 36, 39, 42, 43, 44, 45, 47, 49, 51, 52, 54, 59, 60, - ), - array( - 10, 11, 12, 13, 15, 17, 19, 20, 21, 26, 30, 31, 32, 34, 36, 39, 42, 43, 44, 45, 47, 49, 51, 52, 54, 59, 60, - ), - array( - 10, 11, 12, 13, 15, 17, 19, 20, 21, 26, 30, 31, 32, 34, 36, 39, 42, 43, 44, 45, 47, 49, 51, 52, 54, 59, 60, - ), - array( - 10, 11, 12, 13, 15, 17, 19, 20, 21, 26, 30, 31, 32, 34, 36, 39, 42, 43, 44, 45, 47, 49, 51, 52, 54, 59, 60, - ), - array( - 10, 11, 12, 13, 15, 17, 19, 20, 21, 26, 30, 31, 32, 34, 36, 39, 42, 43, 44, 45, 47, 49, 51, 52, 54, 59, 60, - ), - array( - 10, 11, 12, 13, 15, 17, 19, 20, 21, 26, 30, 31, 32, 34, 36, 39, 42, 43, 44, 45, 47, 49, 51, 52, 54, 59, 60, - ), - array( - 10, 11, 12, 13, 15, 17, 19, 20, 21, 26, 30, 31, 32, 34, 36, 39, 42, 43, 44, 45, 47, 49, 51, 52, 54, 59, 60, - ), - array( - 10, 11, 12, 13, 15, 17, 19, 20, 21, 26, 30, 31, 32, 34, 36, 39, 42, 43, 44, 45, 47, 49, 51, 52, 54, 59, 60, - ), - array( - 10, 11, 12, 13, 15, 17, 19, 20, 21, 26, 30, 31, 32, 34, 36, 39, 42, 43, 44, 45, 47, 49, 51, 52, 54, 59, 60, - ), - array( - 10, 11, 12, 13, 15, 17, 19, 20, 21, 26, 30, 31, 32, 34, 36, 39, 42, 43, 44, 45, 47, 49, 51, 52, 54, 59, 60, - ), - array(25, 27, 33, 38, 39, 40, 55, 56, 57, 58,), - array(27, 33, 38, 39, 40, 55, 56, 57, 58,), - array(27, 33, 38, 39, 40, 55, 56, 57, 58,), - array(15, 17, 49, 51, 54,), - array(4, 10, 11, 12, 13, 15, 19, 20, 21, 26, 30, 31, 32, 60, 61,), - array(1, 14, 18, 27, 33, 36, 48,), - array(15, 17, 51, 54,), - array(1, 27, 33,), - array(15, 36, 54,), - array(4, 10, 11, 12, 13, 15, 19, 20, 21, 26, 30, 31, 32, 60, 61,), - array(14, 38, 39, 40, 55, 56, 57, 58,), - array(2, 38, 39, 40, 55, 56, 57, 58,), - array(37, 38, 39, 40, 55, 56, 57, 58,), - array(37, 38, 39, 40, 55, 56, 57, 58,), - array(14, 38, 39, 40, 55, 56, 57, 58,), - array(38, 39, 40, 55, 56, 57, 58, 61,), - array(14, 38, 39, 40, 55, 56, 57, 58,), - array(14, 38, 39, 40, 55, 56, 57, 58,), - array(38, 39, 40, 53, 55, 56, 57, 58,), - array(22, 38, 39, 40, 55, 56, 57, 58,), - array(28, 38, 39, 40, 55, 56, 57, 58,), - array(14, 38, 39, 40, 55, 56, 57, 58,), - array(38, 39, 40, 55, 56, 57, 58,), - array(38, 39, 40, 55, 56, 57, 58,), - array(38, 39, 40, 55, 56, 57, 58,), - array(38, 39, 40, 55, 56, 57, 58,), - array(38, 39, 40, 55, 56, 57, 58,), - array(38, 39, 40, 55, 56, 57, 58,), - array(38, 39, 40, 55, 56, 57, 58,), - array(38, 39, 40, 55, 56, 57, 58,), - array(38, 39, 40, 55, 56, 57, 58,), - array(38, 39, 40, 55, 56, 57, 58,), - array(38, 39, 40, 55, 56, 57, 58,), - array(38, 39, 40, 55, 56, 57, 58,), - array(38, 39, 40, 55, 56, 57, 58,), - array(38, 39, 40, 55, 56, 57, 58,), - array(38, 39, 40, 55, 56, 57, 58,), - array(10, 13, 17, 27, 29, 33,), - array(10, 13, 17, 27, 33,), - array(15, 36, 54,), - array(1, 27, 33,), - array(15, 36, 54,), - array(1, 27, 33,), - array(18, 45, 52,), - array(16, 18, 48,), - array(1, 2,), - array(3, 4, 5, 6, 7, 10, 11, 12, 13, 19, 20, 21, 26, 30, 31, 32,), - array(2, 10, 13, 16, 17, 18, 45, 48, 50, 52,), - array(1, 14, 27, 33,), - array(10, 13, 17, 50,), - array(13, 15, 17, 54,), - array(1, 14, 27, 33,), - array(1, 14, 27, 33,), - array(10, 13, 17,), - array(16, 18, 48,), - array(10, 13, 17,), - array(1, 29,), - array(18, 48,), - array(15, 17,), - array(27, 33,), - array(27, 33,), - array(15, 17,), - array(1, 53,), - array(27, 33,), - array(1, 18,), - array(27, 33,), - array(15, 54,), - array(1,), - array(1,), - array(1,), - array(18,), - array(1,), - array(1,), - array(18,), - array(1,), - array(1,), - array(1,), - array(1,), - array(1,), - array(), - array(), - array(2, 10, 13, 17, 18, 45, 48, 50, 52, 53,), - array(2, 10, 13, 16, 17, 18, 45, 48, 50, 52,), - array(2, 10, 13, 17, 18, 45, 48, 50, 52,), - array(2, 10, 13, 17, 18, 45, 48, 50, 52,), - array(10, 13, 17, 18, 45, 48, 50, 52,), - array(13, 15, 17, 34, 54,), - array(10, 13, 17, 50,), - array(16, 45, 52,), - array(10, 13, 17,), - array(27, 33,), - array(45, 52,), - array(15, 54,), - array(45, 52,), - array(15, 54,), - array(45, 52,), - array(45, 52,), - array(45, 52,), - array(27, 33,), - array(27, 33,), - array(45, 52,), - array(45, 52,), - array(27, 33,), - array(45, 52,), - array(13, 36,), - array(27, 33,), - array(27, 33,), - array(45, 52,), - array(16, 23,), - array(27, 33,), - array(27, 33,), - array(45, 52,), - array(27, 33,), - array(27, 33,), - array(27, 33,), - array(27, 33,), - array(27, 33,), - array(1,), - array(2,), - array(18,), - array(2,), - array(36,), - array(1,), - array(1,), - array(18,), - array(1,), - array(18,), - array(), - array(), - array(), - array(), - array(), - array(), - array(2, 36, 38, 39, 40, 48, 55, 56, 57, 58,), - array(14, 22, 24, 27, 33, 35, 37, 45,), - array(14, 16, 27, 33, 36, 48,), - array(14, 23, 27, 33, 46,), - array(14, 23, 27, 33, 46,), - array(36, 45, 48, 53,), - array(10, 13, 17, 50,), - array(29, 36, 48,), - array(23, 46, 61,), - array(23, 46, 53,), - array(35, 37,), - array(35, 37,), - array(16, 45,), - array(35, 53,), - array(8, 9,), - array(36, 48,), - array(36, 48,), - array(35, 37,), - array(23, 46,), - array(36, 48,), - array(17, 50,), - array(22, 35,), - array(7, 9,), - array(35, 37,), - array(45, 53,), - array(24,), - array(16,), - array(8,), - array(37,), - array(14,), - array(17,), - array(51,), - array(14,), - array(15,), - array(53,), - array(53,), - array(17,), - array(51,), - array(41,), - array(17,), - array(17,), - array(17,), - array(45,), - array(34,), - array(17,), - array(17,), - array(34,), - array(17,), - array(36,), - array(17,), - array(36,), - array(17,), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - array(), - ); - - public static $yy_default = array( - 338, 514, 514, 499, 499, 514, 514, 476, 476, 476, - 514, 514, 514, 514, 514, 514, 514, 514, 514, 514, - 514, 514, 514, 514, 514, 514, 514, 514, 514, 514, - 514, 514, 514, 514, 514, 514, 514, 514, 514, 514, - 514, 514, 514, 514, 514, 514, 379, 358, 379, 514, - 514, 415, 514, 379, 514, 514, 351, 514, 514, 514, - 514, 514, 514, 514, 514, 514, 384, 514, 399, 475, - 351, 403, 390, 474, 500, 502, 384, 501, 363, 381, - 404, 386, 391, 379, 379, 514, 379, 514, 379, 489, - 431, 370, 327, 430, 393, 441, 514, 393, 393, 441, - 431, 441, 393, 431, 514, 379, 360, 514, 393, 379, - 373, 379, 514, 406, 402, 375, 431, 396, 398, 486, - 393, 408, 397, 407, 406, 483, 336, 430, 430, 430, - 430, 430, 514, 443, 457, 441, 367, 438, 514, 436, - 514, 435, 434, 466, 368, 348, 439, 437, 361, 467, - 441, 356, 354, 468, 514, 366, 355, 469, 362, 359, - 352, 369, 365, 371, 478, 463, 477, 441, 374, 376, - 490, 424, 487, 441, 441, 482, 482, 336, 482, 415, - 411, 415, 405, 405, 415, 442, 415, 405, 405, 514, - 514, 411, 514, 514, 425, 514, 514, 405, 415, 514, - 514, 334, 514, 411, 387, 514, 514, 514, 514, 514, - 514, 514, 514, 420, 514, 514, 514, 417, 514, 514, - 514, 411, 413, 514, 514, 514, 514, 488, 514, 457, - 514, 421, 364, 420, 340, 422, 357, 341, 409, 400, - 480, 457, 462, 401, 485, 423, 426, 342, 447, 380, - 416, 339, 428, 329, 330, 444, 445, 446, 394, 331, - 395, 429, 419, 388, 332, 418, 410, 392, 412, 333, - 335, 414, 337, 472, 417, 479, 427, 497, 347, 461, - 460, 459, 378, 346, 464, 510, 495, 511, 498, 473, - 377, 496, 503, 506, 513, 512, 509, 507, 504, 508, - 345, 458, 471, 448, 505, 454, 452, 455, 456, 450, - 491, 449, 492, 493, 494, 470, 451, 328, 453, 343, - 344, 372, 481, 432, 433, 465, 440, - ); - - public static $yyFallback = array(); - - public static $yyRuleName = array( - 'start ::= template', - 'template ::= template PHP', - 'template ::= template TEXT', - 'template ::= template STRIPON', - 'template ::= template STRIPOFF', - 'template ::= template LITERALSTART literal_e2 LITERALEND', - 'literal_e2 ::= literal_e1 LITERALSTART literal_e1 LITERALEND', - 'literal_e2 ::= literal_e1', - 'literal_e1 ::= literal_e1 LITERAL', - 'literal_e1 ::=', - 'template ::= template smartytag', - 'template ::=', - 'smartytag ::= SIMPELOUTPUT', - 'smartytag ::= SIMPLETAG', - 'smartytag ::= SMARTYBLOCKCHILDPARENT', - 'smartytag ::= LDEL tagbody RDEL', - 'smartytag ::= tag RDEL', - 'tagbody ::= outattr', - 'tagbody ::= DOLLARID eqoutattr', - 'tagbody ::= varindexed eqoutattr', - 'eqoutattr ::= EQUAL outattr', - 'outattr ::= output attributes', - 'output ::= variable', - 'output ::= value', - 'output ::= expr', - 'tag ::= LDEL ID attributes', - 'tag ::= LDEL ID', - 'tag ::= LDEL ID modifierlist attributes', - 'tag ::= LDEL ID PTR ID attributes', - 'tag ::= LDEL ID PTR ID modifierlist attributes', - 'tag ::= LDELMAKENOCACHE DOLLARID', - 'tag ::= LDELIF expr', - 'tag ::= LDELIF expr attributes', - 'tag ::= LDELIF statement', - 'tag ::= LDELIF statement attributes', - 'tag ::= LDELFOR statements SEMICOLON expr SEMICOLON varindexed foraction attributes', - 'foraction ::= EQUAL expr', - 'foraction ::= INCDEC', - 'tag ::= LDELFOR statement TO expr attributes', - 'tag ::= LDELFOR statement TO expr STEP expr attributes', - 'tag ::= LDELFOREACH SPACE expr AS varvar attributes', - 'tag ::= LDELFOREACH SPACE expr AS varvar APTR varvar attributes', - 'tag ::= LDELFOREACH attributes', - 'tag ::= LDELSETFILTER ID modparameters', - 'tag ::= LDELSETFILTER ID modparameters modifierlist', - 'smartytag ::= CLOSETAG', - 'tag ::= LDELSLASH ID', - 'tag ::= LDELSLASH ID modifierlist', - 'tag ::= LDELSLASH ID PTR ID', - 'tag ::= LDELSLASH ID PTR ID modifierlist', - 'attributes ::= attributes attribute', - 'attributes ::= attribute', - 'attributes ::=', - 'attribute ::= SPACE ID EQUAL ID', - 'attribute ::= ATTR expr', - 'attribute ::= ATTR value', - 'attribute ::= SPACE ID', - 'attribute ::= SPACE expr', - 'attribute ::= SPACE value', - 'attribute ::= SPACE INTEGER EQUAL expr', - 'statements ::= statement', - 'statements ::= statements COMMA statement', - 'statement ::= DOLLARID EQUAL INTEGER', - 'statement ::= DOLLARID EQUAL expr', - 'statement ::= varindexed EQUAL expr', - 'statement ::= OPENP statement CLOSEP', - 'expr ::= value', - 'expr ::= ternary', - 'expr ::= DOLLARID COLON ID', - 'expr ::= expr MATH value', - 'expr ::= expr UNIMATH value', - 'expr ::= expr tlop value', - 'expr ::= expr lop expr', - 'expr ::= expr scond', - 'expr ::= expr ISIN array', - 'expr ::= expr ISIN value', - 'ternary ::= OPENP expr CLOSEP QMARK DOLLARID COLON expr', - 'ternary ::= OPENP expr CLOSEP QMARK expr COLON expr', - 'value ::= variable', - 'value ::= UNIMATH value', - 'value ::= NOT value', - 'value ::= TYPECAST value', - 'value ::= variable INCDEC', - 'value ::= HEX', - 'value ::= INTEGER', - 'value ::= INTEGER DOT INTEGER', - 'value ::= INTEGER DOT', - 'value ::= DOT INTEGER', - 'value ::= ID', - 'value ::= function', - 'value ::= OPENP expr CLOSEP', - 'value ::= variable INSTANCEOF ns1', - 'value ::= variable INSTANCEOF variable', - 'value ::= SINGLEQUOTESTRING', - 'value ::= doublequoted_with_quotes', - 'value ::= varindexed DOUBLECOLON static_class_access', - 'value ::= smartytag', - 'value ::= value modifierlist', - 'value ::= NAMESPACE', - 'value ::= arraydef', - 'value ::= ns1 DOUBLECOLON static_class_access', - 'ns1 ::= ID', - 'ns1 ::= NAMESPACE', - 'variable ::= DOLLARID', - 'variable ::= varindexed', - 'variable ::= varvar AT ID', - 'variable ::= object', - 'variable ::= HATCH ID HATCH', - 'variable ::= HATCH ID HATCH arrayindex', - 'variable ::= HATCH variable HATCH', - 'variable ::= HATCH variable HATCH arrayindex', - 'varindexed ::= DOLLARID arrayindex', - 'varindexed ::= varvar arrayindex', - 'arrayindex ::= arrayindex indexdef', - 'arrayindex ::=', - 'indexdef ::= DOT DOLLARID', - 'indexdef ::= DOT varvar', - 'indexdef ::= DOT varvar AT ID', - 'indexdef ::= DOT ID', - 'indexdef ::= DOT INTEGER', - 'indexdef ::= DOT LDEL expr RDEL', - 'indexdef ::= OPENB ID CLOSEB', - 'indexdef ::= OPENB ID DOT ID CLOSEB', - 'indexdef ::= OPENB SINGLEQUOTESTRING CLOSEB', - 'indexdef ::= OPENB INTEGER CLOSEB', - 'indexdef ::= OPENB DOLLARID CLOSEB', - 'indexdef ::= OPENB variable CLOSEB', - 'indexdef ::= OPENB value CLOSEB', - 'indexdef ::= OPENB expr CLOSEB', - 'indexdef ::= OPENB CLOSEB', - 'varvar ::= DOLLARID', - 'varvar ::= DOLLAR', - 'varvar ::= varvar varvarele', - 'varvarele ::= ID', - 'varvarele ::= SIMPELOUTPUT', - 'varvarele ::= LDEL expr RDEL', - 'object ::= varindexed objectchain', - 'objectchain ::= objectelement', - 'objectchain ::= objectchain objectelement', - 'objectelement ::= PTR ID arrayindex', - 'objectelement ::= PTR varvar arrayindex', - 'objectelement ::= PTR LDEL expr RDEL arrayindex', - 'objectelement ::= PTR ID LDEL expr RDEL arrayindex', - 'objectelement ::= PTR method', - 'function ::= ns1 OPENP params CLOSEP', - 'method ::= ID OPENP params CLOSEP', - 'method ::= DOLLARID OPENP params CLOSEP', - 'params ::= params COMMA expr', - 'params ::= expr', - 'params ::=', - 'modifierlist ::= modifierlist modifier modparameters', - 'modifierlist ::= modifier modparameters', - 'modifier ::= VERT AT ID', - 'modifier ::= VERT ID', - 'modparameters ::= modparameters modparameter', - 'modparameters ::=', - 'modparameter ::= COLON value', - 'modparameter ::= COLON UNIMATH value', - 'modparameter ::= COLON array', - 'static_class_access ::= method', - 'static_class_access ::= method objectchain', - 'static_class_access ::= ID', - 'static_class_access ::= DOLLARID arrayindex', - 'static_class_access ::= DOLLARID arrayindex objectchain', - 'lop ::= LOGOP', - 'lop ::= SLOGOP', - 'tlop ::= TLOGOP', - 'scond ::= SINGLECOND', - 'arraydef ::= OPENB arrayelements CLOSEB', - 'arraydef ::= ARRAYOPEN arrayelements CLOSEP', - 'arrayelements ::= arrayelement', - 'arrayelements ::= arrayelements COMMA arrayelement', - 'arrayelements ::=', - 'arrayelement ::= value APTR expr', - 'arrayelement ::= ID APTR expr', - 'arrayelement ::= expr', - 'doublequoted_with_quotes ::= QUOTE QUOTE', - 'doublequoted_with_quotes ::= QUOTE doublequoted QUOTE', - 'doublequoted ::= doublequoted doublequotedcontent', - 'doublequoted ::= doublequotedcontent', - 'doublequotedcontent ::= BACKTICK variable BACKTICK', - 'doublequotedcontent ::= BACKTICK expr BACKTICK', - 'doublequotedcontent ::= DOLLARID', - 'doublequotedcontent ::= LDEL variable RDEL', - 'doublequotedcontent ::= LDEL expr RDEL', - 'doublequotedcontent ::= smartytag', - 'doublequotedcontent ::= TEXT', - ); +// line 23 "../smarty/lexer/smarty_internal_templateparser.y" - public static $yyRuleInfo = array( - array(0 => 63, 1 => 1), - array(0 => 64, 1 => 2), - array(0 => 64, 1 => 2), - array(0 => 64, 1 => 2), - array(0 => 64, 1 => 2), - array(0 => 64, 1 => 4), - array(0 => 65, 1 => 4), - array(0 => 65, 1 => 1), - array(0 => 66, 1 => 2), - array(0 => 66, 1 => 0), - array(0 => 64, 1 => 2), - array(0 => 64, 1 => 0), - array(0 => 67, 1 => 1), - array(0 => 67, 1 => 1), - array(0 => 67, 1 => 1), - array(0 => 67, 1 => 3), - array(0 => 67, 1 => 2), - array(0 => 68, 1 => 1), - array(0 => 68, 1 => 2), - array(0 => 68, 1 => 2), - array(0 => 71, 1 => 2), - array(0 => 70, 1 => 2), - array(0 => 73, 1 => 1), - array(0 => 73, 1 => 1), - array(0 => 73, 1 => 1), - array(0 => 69, 1 => 3), - array(0 => 69, 1 => 2), - array(0 => 69, 1 => 4), - array(0 => 69, 1 => 5), - array(0 => 69, 1 => 6), - array(0 => 69, 1 => 2), - array(0 => 69, 1 => 2), - array(0 => 69, 1 => 3), - array(0 => 69, 1 => 2), - array(0 => 69, 1 => 3), - array(0 => 69, 1 => 8), - array(0 => 81, 1 => 2), - array(0 => 81, 1 => 1), - array(0 => 69, 1 => 5), - array(0 => 69, 1 => 7), - array(0 => 69, 1 => 6), - array(0 => 69, 1 => 8), - array(0 => 69, 1 => 2), - array(0 => 69, 1 => 3), - array(0 => 69, 1 => 4), - array(0 => 67, 1 => 1), - array(0 => 69, 1 => 2), - array(0 => 69, 1 => 3), - array(0 => 69, 1 => 4), - array(0 => 69, 1 => 5), - array(0 => 74, 1 => 2), - array(0 => 74, 1 => 1), - array(0 => 74, 1 => 0), - array(0 => 84, 1 => 4), - array(0 => 84, 1 => 2), - array(0 => 84, 1 => 2), - array(0 => 84, 1 => 2), - array(0 => 84, 1 => 2), - array(0 => 84, 1 => 2), - array(0 => 84, 1 => 4), - array(0 => 80, 1 => 1), - array(0 => 80, 1 => 3), - array(0 => 79, 1 => 3), - array(0 => 79, 1 => 3), - array(0 => 79, 1 => 3), - array(0 => 79, 1 => 3), - array(0 => 77, 1 => 1), - array(0 => 77, 1 => 1), - array(0 => 77, 1 => 3), - array(0 => 77, 1 => 3), - array(0 => 77, 1 => 3), - array(0 => 77, 1 => 3), - array(0 => 77, 1 => 3), - array(0 => 77, 1 => 2), - array(0 => 77, 1 => 3), - array(0 => 77, 1 => 3), - array(0 => 85, 1 => 7), - array(0 => 85, 1 => 7), - array(0 => 76, 1 => 1), - array(0 => 76, 1 => 2), - array(0 => 76, 1 => 2), - array(0 => 76, 1 => 2), - array(0 => 76, 1 => 2), - array(0 => 76, 1 => 1), - array(0 => 76, 1 => 1), - array(0 => 76, 1 => 3), - array(0 => 76, 1 => 2), - array(0 => 76, 1 => 2), - array(0 => 76, 1 => 1), - array(0 => 76, 1 => 1), - array(0 => 76, 1 => 3), - array(0 => 76, 1 => 3), - array(0 => 76, 1 => 3), - array(0 => 76, 1 => 1), - array(0 => 76, 1 => 1), - array(0 => 76, 1 => 3), - array(0 => 76, 1 => 1), - array(0 => 76, 1 => 2), - array(0 => 76, 1 => 1), - array(0 => 76, 1 => 1), - array(0 => 76, 1 => 3), - array(0 => 91, 1 => 1), - array(0 => 91, 1 => 1), - array(0 => 75, 1 => 1), - array(0 => 75, 1 => 1), - array(0 => 75, 1 => 3), - array(0 => 75, 1 => 1), - array(0 => 75, 1 => 3), - array(0 => 75, 1 => 4), - array(0 => 75, 1 => 3), - array(0 => 75, 1 => 4), - array(0 => 72, 1 => 2), - array(0 => 72, 1 => 2), - array(0 => 96, 1 => 2), - array(0 => 96, 1 => 0), - array(0 => 97, 1 => 2), - array(0 => 97, 1 => 2), - array(0 => 97, 1 => 4), - array(0 => 97, 1 => 2), - array(0 => 97, 1 => 2), - array(0 => 97, 1 => 4), - array(0 => 97, 1 => 3), - array(0 => 97, 1 => 5), - array(0 => 97, 1 => 3), - array(0 => 97, 1 => 3), - array(0 => 97, 1 => 3), - array(0 => 97, 1 => 3), - array(0 => 97, 1 => 3), - array(0 => 97, 1 => 3), - array(0 => 97, 1 => 2), - array(0 => 82, 1 => 1), - array(0 => 82, 1 => 1), - array(0 => 82, 1 => 2), - array(0 => 98, 1 => 1), - array(0 => 98, 1 => 1), - array(0 => 98, 1 => 3), - array(0 => 95, 1 => 2), - array(0 => 99, 1 => 1), - array(0 => 99, 1 => 2), - array(0 => 100, 1 => 3), - array(0 => 100, 1 => 3), - array(0 => 100, 1 => 5), - array(0 => 100, 1 => 6), - array(0 => 100, 1 => 2), - array(0 => 90, 1 => 4), - array(0 => 101, 1 => 4), - array(0 => 101, 1 => 4), - array(0 => 102, 1 => 3), - array(0 => 102, 1 => 1), - array(0 => 102, 1 => 0), - array(0 => 78, 1 => 3), - array(0 => 78, 1 => 2), - array(0 => 103, 1 => 3), - array(0 => 103, 1 => 2), - array(0 => 83, 1 => 2), - array(0 => 83, 1 => 0), - array(0 => 104, 1 => 2), - array(0 => 104, 1 => 3), - array(0 => 104, 1 => 2), - array(0 => 93, 1 => 1), - array(0 => 93, 1 => 2), - array(0 => 93, 1 => 1), - array(0 => 93, 1 => 2), - array(0 => 93, 1 => 3), - array(0 => 87, 1 => 1), - array(0 => 87, 1 => 1), - array(0 => 86, 1 => 1), - array(0 => 88, 1 => 1), - array(0 => 94, 1 => 3), - array(0 => 94, 1 => 3), - array(0 => 105, 1 => 1), - array(0 => 105, 1 => 3), - array(0 => 105, 1 => 0), - array(0 => 106, 1 => 3), - array(0 => 106, 1 => 3), - array(0 => 106, 1 => 1), - array(0 => 92, 1 => 2), - array(0 => 92, 1 => 3), - array(0 => 107, 1 => 2), - array(0 => 107, 1 => 1), - array(0 => 108, 1 => 3), - array(0 => 108, 1 => 3), - array(0 => 108, 1 => 1), - array(0 => 108, 1 => 3), - array(0 => 108, 1 => 3), - array(0 => 108, 1 => 1), - array(0 => 108, 1 => 1), - ); - - public static $yyReduceMap = array( - 0 => 0, - 1 => 1, - 2 => 2, - 3 => 3, - 4 => 4, - 5 => 5, - 6 => 6, - 7 => 7, - 22 => 7, - 23 => 7, - 24 => 7, - 37 => 7, - 57 => 7, - 58 => 7, - 66 => 7, - 67 => 7, - 78 => 7, - 83 => 7, - 84 => 7, - 89 => 7, - 93 => 7, - 94 => 7, - 98 => 7, - 99 => 7, - 101 => 7, - 106 => 7, - 170 => 7, - 175 => 7, - 8 => 8, - 9 => 9, - 10 => 10, - 12 => 12, - 13 => 13, - 14 => 14, - 15 => 15, - 16 => 16, - 17 => 17, - 18 => 18, - 19 => 19, - 20 => 20, - 21 => 21, - 25 => 25, - 26 => 26, - 27 => 27, - 28 => 28, - 29 => 29, - 30 => 30, - 31 => 31, - 32 => 32, - 34 => 32, - 33 => 33, - 35 => 35, - 36 => 36, - 38 => 38, - 39 => 39, - 40 => 40, - 41 => 41, - 42 => 42, - 43 => 43, - 44 => 44, - 45 => 45, - 46 => 46, - 47 => 47, - 48 => 48, - 49 => 49, - 50 => 50, - 51 => 51, - 60 => 51, - 148 => 51, - 152 => 51, - 156 => 51, - 158 => 51, - 52 => 52, - 149 => 52, - 155 => 52, - 53 => 53, - 54 => 54, - 55 => 54, - 56 => 56, - 133 => 56, - 59 => 59, - 61 => 61, - 62 => 62, - 63 => 62, - 64 => 64, - 65 => 65, - 68 => 68, - 69 => 69, - 70 => 69, - 71 => 71, - 72 => 72, - 73 => 73, - 74 => 74, - 75 => 75, - 76 => 76, - 77 => 77, - 79 => 79, - 81 => 79, - 82 => 79, - 113 => 79, - 80 => 80, - 85 => 85, - 86 => 86, - 87 => 87, - 88 => 88, - 90 => 90, - 91 => 91, - 92 => 91, - 95 => 95, - 96 => 96, - 97 => 97, - 100 => 100, - 102 => 102, - 103 => 103, - 104 => 104, - 105 => 105, - 107 => 107, - 108 => 108, - 109 => 109, - 110 => 110, - 111 => 111, - 112 => 112, - 114 => 114, - 172 => 114, - 115 => 115, - 116 => 116, - 117 => 117, - 118 => 118, - 119 => 119, - 120 => 120, - 128 => 120, - 121 => 121, - 122 => 122, - 123 => 123, - 124 => 123, - 126 => 123, - 127 => 123, - 125 => 125, - 129 => 129, - 130 => 130, - 131 => 131, - 176 => 131, - 132 => 132, - 134 => 134, - 135 => 135, - 136 => 136, - 137 => 137, - 138 => 138, - 139 => 139, - 140 => 140, - 141 => 141, - 142 => 142, - 143 => 143, - 144 => 144, - 145 => 145, - 146 => 146, - 147 => 147, - 150 => 150, - 151 => 151, - 153 => 153, - 154 => 154, - 157 => 157, - 159 => 159, - 160 => 160, - 161 => 161, - 162 => 162, - 163 => 163, - 164 => 164, - 165 => 165, - 166 => 166, - 167 => 167, - 168 => 168, - 169 => 168, - 171 => 171, - 173 => 173, - 174 => 174, - 177 => 177, - 178 => 178, - 179 => 179, - 180 => 180, - 183 => 180, - 181 => 181, - 184 => 181, - 182 => 182, - 185 => 185, - 186 => 186, - ); + const ERR1 = 'Security error: Call to private object member not allowed'; + const ERR2 = 'Security error: Call to dynamic object member not allowed'; /** * result status @@ -1654,13 +81,19 @@ class Smarty_Internal_Templateparser */ public $lex; + /** + * internal error flag + * + * @var bool + */ + private $internalError = false; + /** * {strip} status * * @var bool */ public $strip = false; - /** * compiler object * @@ -1710,54 +143,6 @@ class Smarty_Internal_Templateparser */ public $template_postfix = array(); - public $yyTraceFILE; - - public $yyTracePrompt; - - public $yyidx; - - public $yyerrcnt; - - public $yystack = array(); - - public $yyTokenName = array( - '$', 'VERT', 'COLON', 'PHP', - 'TEXT', 'STRIPON', 'STRIPOFF', 'LITERALSTART', - 'LITERALEND', 'LITERAL', 'SIMPELOUTPUT', 'SIMPLETAG', - 'SMARTYBLOCKCHILDPARENT', 'LDEL', 'RDEL', 'DOLLARID', - 'EQUAL', 'ID', 'PTR', 'LDELMAKENOCACHE', - 'LDELIF', 'LDELFOR', 'SEMICOLON', 'INCDEC', - 'TO', 'STEP', 'LDELFOREACH', 'SPACE', - 'AS', 'APTR', 'LDELSETFILTER', 'CLOSETAG', - 'LDELSLASH', 'ATTR', 'INTEGER', 'COMMA', - 'OPENP', 'CLOSEP', 'MATH', 'UNIMATH', - 'ISIN', 'QMARK', 'NOT', 'TYPECAST', - 'HEX', 'DOT', 'INSTANCEOF', 'SINGLEQUOTESTRING', - 'DOUBLECOLON', 'NAMESPACE', 'AT', 'HATCH', - 'OPENB', 'CLOSEB', 'DOLLAR', 'LOGOP', - 'SLOGOP', 'TLOGOP', 'SINGLECOND', 'ARRAYOPEN', - 'QUOTE', 'BACKTICK', 'error', 'start', - 'template', 'literal_e2', 'literal_e1', 'smartytag', - 'tagbody', 'tag', 'outattr', 'eqoutattr', - 'varindexed', 'output', 'attributes', 'variable', - 'value', 'expr', 'modifierlist', 'statement', - 'statements', 'foraction', 'varvar', 'modparameters', - 'attribute', 'ternary', 'tlop', 'lop', - 'scond', 'array', 'function', 'ns1', - 'doublequoted_with_quotes', 'static_class_access', 'arraydef', 'object', - 'arrayindex', 'indexdef', 'varvarele', 'objectchain', - 'objectelement', 'method', 'params', 'modifier', - 'modparameter', 'arrayelements', 'arrayelement', 'doublequoted', - 'doublequotedcontent', - ); - - /** - * internal error flag - * - * @var bool - */ - private $internalError = false; /* Index of top element in stack */ - private $_retvalue; /* Shifts left before out of the error */ /** * constructor * @@ -1772,16 +157,9 @@ public function __construct(Smarty_Internal_Templatelexer $lex, Smarty_Internal_ $this->smarty = $this->template->smarty; $this->security = isset($this->smarty->security_policy) ? $this->smarty->security_policy : false; $this->current_buffer = $this->root_buffer = new Smarty_Internal_ParseTree_Template(); - } /* The parser's stack */ - public static function yy_destructor($yymajor, $yypminor) - { - switch ($yymajor) { - default: - break; /* If no destructor action specified: do nothing */ - } } - /** + /** * insert PHP code in current buffer * * @param string $code @@ -1823,6 +201,898 @@ public function mergePrefixCode($code) return new Smarty_Internal_ParseTree_Tag($this, $this->compiler->processNocacheCode($tmp, true)); } + + const TP_VERT = 1; + const TP_COLON = 2; + const TP_TEXT = 3; + const TP_STRIPON = 4; + const TP_STRIPOFF = 5; + const TP_LITERALSTART = 6; + const TP_LITERALEND = 7; + const TP_LITERAL = 8; + const TP_SIMPELOUTPUT = 9; + const TP_SIMPLETAG = 10; + const TP_SMARTYBLOCKCHILDPARENT = 11; + const TP_LDEL = 12; + const TP_RDEL = 13; + const TP_DOLLARID = 14; + const TP_EQUAL = 15; + const TP_ID = 16; + const TP_PTR = 17; + const TP_LDELMAKENOCACHE = 18; + const TP_LDELIF = 19; + const TP_LDELFOR = 20; + const TP_SEMICOLON = 21; + const TP_INCDEC = 22; + const TP_TO = 23; + const TP_STEP = 24; + const TP_LDELFOREACH = 25; + const TP_SPACE = 26; + const TP_AS = 27; + const TP_APTR = 28; + const TP_LDELSETFILTER = 29; + const TP_CLOSETAG = 30; + const TP_LDELSLASH = 31; + const TP_ATTR = 32; + const TP_INTEGER = 33; + const TP_COMMA = 34; + const TP_OPENP = 35; + const TP_CLOSEP = 36; + const TP_MATH = 37; + const TP_UNIMATH = 38; + const TP_ISIN = 39; + const TP_QMARK = 40; + const TP_NOT = 41; + const TP_TYPECAST = 42; + const TP_HEX = 43; + const TP_DOT = 44; + const TP_INSTANCEOF = 45; + const TP_SINGLEQUOTESTRING = 46; + const TP_DOUBLECOLON = 47; + const TP_NAMESPACE = 48; + const TP_AT = 49; + const TP_HATCH = 50; + const TP_OPENB = 51; + const TP_CLOSEB = 52; + const TP_DOLLAR = 53; + const TP_LOGOP = 54; + const TP_SLOGOP = 55; + const TP_TLOGOP = 56; + const TP_SINGLECOND = 57; + const TP_ARRAYOPEN = 58; + const TP_QUOTE = 59; + const TP_BACKTICK = 60; + const YY_NO_ACTION = 514; + const YY_ACCEPT_ACTION = 513; + const YY_ERROR_ACTION = 512; + + const YY_SZ_ACTTAB = 1997; +public static $yy_action = array( + 249, 250, 239, 1, 27, 127, 220, 184, 160, 213, + 11, 54, 278, 10, 173, 34, 108, 387, 282, 279, + 223, 321, 221, 8, 194, 387, 18, 387, 85, 41, + 387, 285, 42, 44, 264, 222, 387, 209, 387, 198, + 387, 52, 5, 307, 288, 288, 164, 283, 224, 4, + 50, 249, 250, 239, 1, 232, 131, 381, 189, 205, + 213, 11, 54, 39, 35, 243, 31, 108, 94, 17, + 381, 223, 321, 221, 439, 226, 381, 33, 49, 426, + 41, 439, 89, 42, 44, 264, 222, 9, 235, 163, + 198, 426, 52, 5, 131, 288, 212, 284, 102, 106, + 4, 50, 249, 250, 239, 1, 232, 129, 426, 189, + 347, 213, 11, 54, 175, 324, 347, 208, 108, 22, + 426, 301, 223, 321, 221, 302, 226, 135, 18, 49, + 52, 41, 26, 288, 42, 44, 264, 222, 16, 235, + 294, 198, 204, 52, 5, 170, 288, 32, 90, 267, + 268, 4, 50, 249, 250, 239, 1, 20, 129, 185, + 179, 255, 213, 11, 54, 455, 288, 192, 455, 108, + 175, 167, 455, 223, 321, 221, 439, 226, 256, 18, + 55, 292, 41, 439, 132, 42, 44, 264, 222, 427, + 235, 12, 198, 165, 52, 5, 232, 288, 288, 347, + 153, 427, 4, 50, 249, 250, 239, 1, 232, 129, + 286, 181, 347, 213, 11, 54, 24, 13, 347, 49, + 108, 232, 320, 426, 223, 321, 221, 195, 201, 173, + 18, 49, 139, 41, 296, 426, 42, 44, 264, 222, + 7, 235, 286, 198, 49, 52, 5, 147, 288, 117, + 150, 317, 263, 4, 50, 249, 250, 239, 1, 95, + 130, 173, 189, 155, 213, 11, 54, 22, 244, 271, + 192, 108, 323, 286, 101, 223, 321, 221, 294, 226, + 204, 18, 348, 257, 41, 166, 283, 42, 44, 264, + 222, 28, 235, 300, 198, 348, 52, 5, 247, 288, + 117, 348, 94, 206, 4, 50, 249, 250, 239, 1, + 95, 129, 22, 189, 277, 213, 11, 54, 91, 274, + 224, 426, 108, 323, 216, 156, 223, 321, 221, 132, + 180, 262, 18, 426, 100, 41, 12, 288, 42, 44, + 264, 222, 15, 235, 216, 198, 254, 52, 5, 233, + 288, 210, 190, 192, 100, 4, 50, 249, 250, 239, + 1, 3, 131, 94, 189, 192, 213, 11, 54, 269, + 10, 204, 290, 108, 325, 216, 224, 223, 321, 221, + 23, 226, 211, 33, 315, 100, 45, 513, 92, 42, + 44, 264, 222, 102, 235, 178, 198, 268, 52, 5, + 275, 288, 161, 192, 37, 25, 4, 50, 249, 250, + 239, 1, 286, 129, 172, 187, 305, 213, 11, 54, + 164, 283, 310, 141, 108, 281, 281, 236, 223, 321, + 221, 169, 226, 230, 18, 122, 171, 41, 225, 175, + 42, 44, 264, 222, 144, 235, 303, 198, 134, 52, + 5, 265, 288, 151, 286, 192, 175, 4, 50, 249, + 250, 239, 1, 286, 128, 94, 189, 143, 213, 11, + 54, 219, 152, 207, 193, 108, 149, 281, 31, 223, + 321, 221, 100, 226, 21, 6, 286, 288, 41, 158, + 16, 42, 44, 264, 222, 102, 235, 238, 198, 286, + 52, 5, 157, 288, 281, 122, 168, 283, 4, 50, + 249, 250, 239, 1, 30, 93, 308, 51, 215, 213, + 11, 54, 53, 251, 140, 248, 108, 245, 304, 116, + 223, 321, 221, 111, 226, 176, 18, 270, 266, 41, + 224, 322, 42, 44, 264, 222, 7, 235, 259, 198, + 147, 52, 5, 257, 288, 43, 40, 38, 83, 4, + 50, 241, 214, 204, 319, 280, 88, 107, 138, 182, + 97, 64, 311, 312, 313, 316, 95, 281, 298, 258, + 142, 234, 94, 105, 272, 197, 231, 482, 237, 323, + 37, 133, 324, 241, 214, 204, 319, 314, 88, 107, + 296, 183, 97, 82, 84, 43, 40, 38, 95, 296, + 296, 258, 296, 296, 296, 159, 272, 197, 231, 296, + 237, 323, 311, 312, 313, 316, 241, 296, 204, 296, + 296, 103, 296, 296, 199, 104, 77, 296, 296, 110, + 296, 95, 296, 296, 258, 278, 296, 296, 34, 272, + 197, 231, 279, 237, 323, 43, 40, 38, 296, 296, + 296, 241, 26, 204, 196, 276, 103, 296, 16, 199, + 104, 77, 311, 312, 313, 316, 95, 192, 296, 258, + 146, 296, 296, 296, 272, 197, 231, 296, 237, 323, + 286, 393, 39, 35, 243, 296, 296, 296, 296, 191, + 276, 296, 26, 318, 252, 253, 126, 296, 16, 249, + 250, 239, 1, 296, 296, 131, 296, 261, 213, 11, + 54, 296, 296, 296, 426, 108, 393, 393, 393, 223, + 321, 221, 241, 296, 204, 299, 426, 103, 107, 296, + 183, 97, 82, 393, 393, 393, 393, 95, 296, 260, + 258, 52, 296, 296, 288, 272, 197, 231, 296, 237, + 323, 293, 296, 296, 296, 296, 296, 249, 250, 239, + 2, 296, 295, 296, 296, 296, 213, 11, 54, 296, + 296, 177, 296, 108, 136, 296, 296, 223, 321, 221, + 296, 296, 296, 293, 43, 40, 38, 296, 296, 249, + 250, 239, 2, 296, 295, 43, 40, 38, 213, 11, + 54, 311, 312, 313, 316, 108, 296, 291, 14, 223, + 321, 221, 311, 312, 313, 316, 296, 296, 241, 296, + 204, 296, 192, 103, 296, 296, 199, 104, 77, 296, + 296, 296, 296, 95, 383, 296, 258, 296, 296, 297, + 14, 272, 197, 231, 296, 237, 323, 383, 296, 296, + 241, 296, 204, 383, 296, 99, 296, 287, 199, 120, + 48, 241, 112, 204, 296, 95, 103, 296, 258, 199, + 120, 74, 296, 272, 197, 231, 95, 237, 323, 258, + 455, 296, 296, 455, 272, 197, 231, 455, 237, 323, + 241, 296, 204, 296, 296, 103, 200, 296, 199, 120, + 74, 296, 296, 296, 296, 95, 296, 296, 258, 278, + 296, 296, 34, 272, 197, 231, 279, 237, 323, 241, + 455, 204, 296, 296, 99, 202, 296, 199, 120, 56, + 241, 211, 204, 296, 95, 103, 296, 258, 199, 120, + 74, 296, 272, 197, 231, 95, 237, 323, 258, 227, + 296, 296, 296, 272, 197, 231, 296, 237, 323, 241, + 296, 204, 148, 296, 103, 203, 86, 199, 120, 73, + 296, 296, 286, 296, 95, 296, 296, 258, 278, 296, + 296, 34, 272, 197, 231, 279, 237, 323, 241, 296, + 204, 175, 296, 103, 296, 296, 199, 120, 75, 241, + 296, 204, 296, 95, 103, 296, 258, 199, 120, 63, + 296, 272, 197, 231, 95, 237, 323, 258, 229, 192, + 296, 296, 272, 197, 231, 296, 237, 323, 241, 296, + 204, 380, 296, 103, 296, 296, 199, 120, 58, 296, + 296, 296, 296, 95, 380, 296, 258, 296, 296, 296, + 380, 272, 197, 231, 296, 237, 323, 241, 296, 204, + 296, 296, 103, 296, 296, 199, 120, 71, 241, 296, + 204, 296, 95, 103, 296, 258, 199, 120, 79, 296, + 272, 197, 231, 95, 237, 323, 258, 296, 296, 296, + 154, 272, 197, 231, 87, 237, 323, 241, 296, 204, + 286, 296, 103, 296, 296, 199, 120, 70, 296, 296, + 296, 296, 95, 296, 296, 258, 296, 296, 296, 175, + 272, 197, 231, 296, 237, 323, 241, 296, 204, 296, + 296, 103, 296, 296, 199, 120, 56, 241, 296, 204, + 296, 95, 103, 296, 258, 199, 120, 46, 296, 272, + 197, 231, 95, 237, 323, 258, 296, 296, 296, 296, + 272, 197, 231, 296, 237, 323, 241, 296, 204, 296, + 296, 103, 296, 296, 199, 120, 78, 296, 296, 296, + 296, 95, 296, 296, 258, 296, 296, 296, 296, 272, + 197, 231, 296, 237, 323, 241, 296, 204, 296, 296, + 103, 296, 296, 199, 120, 66, 241, 296, 204, 296, + 95, 103, 296, 258, 199, 120, 59, 296, 272, 197, + 231, 95, 237, 323, 258, 296, 296, 296, 296, 272, + 197, 231, 296, 237, 323, 241, 296, 204, 296, 296, + 103, 296, 296, 186, 109, 57, 296, 296, 296, 296, + 95, 296, 296, 258, 296, 296, 296, 296, 272, 197, + 231, 296, 237, 323, 241, 296, 204, 296, 296, 103, + 296, 296, 188, 120, 67, 241, 296, 204, 296, 95, + 103, 296, 258, 199, 96, 62, 296, 272, 197, 231, + 95, 237, 323, 258, 296, 296, 296, 296, 272, 197, + 231, 296, 237, 323, 241, 296, 204, 296, 296, 103, + 296, 296, 199, 120, 80, 296, 296, 296, 296, 95, + 296, 296, 258, 296, 296, 296, 296, 272, 197, 231, + 296, 237, 323, 241, 296, 204, 296, 296, 103, 296, + 296, 199, 120, 76, 241, 296, 204, 296, 95, 103, + 296, 258, 199, 120, 81, 296, 272, 197, 231, 95, + 237, 323, 258, 296, 296, 296, 296, 272, 197, 231, + 296, 237, 323, 241, 296, 204, 296, 296, 103, 296, + 296, 199, 120, 65, 296, 296, 296, 296, 95, 296, + 296, 258, 296, 296, 296, 296, 272, 197, 231, 296, + 237, 323, 241, 296, 204, 296, 296, 103, 296, 296, + 199, 96, 68, 241, 296, 204, 296, 95, 103, 296, + 258, 199, 120, 61, 296, 272, 197, 231, 95, 237, + 323, 258, 296, 296, 296, 296, 272, 197, 231, 296, + 237, 323, 241, 296, 204, 296, 296, 103, 296, 296, + 199, 98, 69, 296, 296, 296, 296, 95, 296, 296, + 258, 296, 296, 296, 296, 272, 197, 231, 296, 237, + 323, 241, 296, 204, 296, 296, 103, 296, 296, 199, + 120, 72, 241, 296, 204, 296, 95, 103, 296, 258, + 199, 120, 47, 296, 272, 197, 231, 95, 237, 323, + 258, 296, 296, 296, 296, 272, 197, 231, 296, 237, + 323, 241, 192, 204, 296, 296, 103, 296, 296, 199, + 120, 60, 296, 296, 351, 296, 95, 296, 217, 258, + 296, 296, 296, 296, 272, 197, 231, 26, 237, 323, + 241, 296, 204, 16, 296, 103, 426, 296, 199, 125, + 296, 241, 296, 204, 296, 95, 103, 296, 426, 199, + 118, 296, 242, 272, 197, 231, 95, 237, 323, 296, + 296, 296, 296, 246, 272, 197, 231, 296, 237, 323, + 241, 296, 204, 278, 296, 103, 34, 296, 199, 121, + 279, 296, 296, 296, 296, 95, 296, 296, 296, 296, + 26, 296, 162, 272, 197, 231, 16, 237, 323, 241, + 296, 204, 296, 296, 103, 296, 296, 199, 123, 296, + 241, 296, 204, 296, 95, 103, 296, 296, 199, 114, + 296, 296, 272, 197, 231, 95, 237, 323, 296, 296, + 296, 296, 296, 272, 197, 231, 296, 237, 323, 241, + 296, 204, 296, 145, 103, 296, 296, 199, 124, 296, + 296, 296, 296, 286, 95, 39, 35, 243, 296, 296, + 296, 296, 272, 197, 231, 296, 237, 323, 241, 296, + 204, 296, 296, 103, 296, 296, 199, 115, 296, 241, + 296, 204, 296, 95, 103, 296, 296, 199, 113, 296, + 296, 272, 197, 231, 95, 237, 323, 296, 296, 296, + 296, 296, 272, 197, 231, 228, 237, 323, 241, 296, + 204, 296, 455, 103, 296, 455, 199, 119, 3, 455, + 439, 296, 296, 95, 296, 296, 296, 296, 296, 296, + 296, 272, 197, 231, 228, 237, 323, 296, 296, 296, + 296, 455, 296, 296, 455, 296, 296, 439, 455, 439, + 439, 228, 455, 296, 439, 296, 296, 137, 455, 296, + 296, 455, 296, 296, 32, 455, 439, 286, 296, 39, + 35, 243, 29, 296, 26, 296, 439, 296, 296, 439, + 16, 455, 296, 439, 306, 43, 40, 38, 296, 296, + 296, 296, 296, 439, 296, 296, 439, 296, 455, 296, + 439, 26, 311, 312, 313, 316, 296, 16, 228, 296, + 296, 296, 43, 40, 38, 455, 296, 296, 455, 296, + 296, 296, 455, 439, 296, 296, 19, 296, 296, 311, + 312, 313, 316, 455, 296, 296, 455, 296, 296, 296, + 455, 439, 296, 296, 296, 43, 40, 38, 296, 296, + 439, 296, 296, 439, 174, 455, 296, 439, 296, 240, + 309, 296, 311, 312, 313, 316, 296, 289, 439, 296, + 36, 439, 296, 455, 296, 439, 296, 296, 43, 40, + 38, 296, 296, 43, 40, 38, 296, 296, 296, 296, + 296, 43, 40, 38, 296, 311, 312, 313, 316, 296, + 311, 312, 313, 316, 296, 43, 40, 38, 311, 312, + 313, 316, 273, 43, 40, 38, 296, 296, 296, 296, + 296, 296, 311, 312, 313, 316, 296, 296, 296, 296, + 311, 312, 313, 316, 455, 296, 296, 455, 43, 40, + 38, 455, 439, 218, 43, 40, 38, 296, 296, 296, + 296, 296, 296, 296, 296, 311, 312, 313, 316, 296, + 296, 311, 312, 313, 316, 296, 296, 296, 296, 439, + 296, 296, 439, 296, 455, 296, 439, + ); + public static $yy_lookahead = array( + 9, 10, 11, 12, 12, 14, 14, 16, 16, 18, + 19, 20, 9, 34, 102, 12, 25, 13, 70, 16, + 29, 30, 31, 35, 33, 21, 35, 23, 95, 38, + 26, 52, 41, 42, 43, 44, 32, 46, 34, 48, + 36, 50, 51, 52, 53, 53, 98, 99, 44, 58, + 59, 9, 10, 11, 12, 22, 14, 13, 16, 15, + 18, 19, 20, 85, 86, 87, 15, 25, 17, 21, + 26, 29, 30, 31, 44, 33, 32, 35, 45, 35, + 38, 51, 34, 41, 42, 43, 44, 35, 46, 77, + 48, 47, 50, 51, 14, 53, 16, 13, 47, 47, + 58, 59, 9, 10, 11, 12, 22, 14, 35, 16, + 26, 18, 19, 20, 102, 103, 32, 44, 25, 34, + 47, 36, 29, 30, 31, 52, 33, 14, 35, 45, + 50, 38, 26, 53, 41, 42, 43, 44, 32, 46, + 66, 48, 68, 50, 51, 77, 53, 15, 35, 7, + 8, 58, 59, 9, 10, 11, 12, 12, 14, 14, + 16, 16, 18, 19, 20, 9, 53, 1, 12, 25, + 102, 82, 16, 29, 30, 31, 44, 33, 33, 35, + 106, 107, 38, 51, 44, 41, 42, 43, 44, 35, + 46, 51, 48, 82, 50, 51, 22, 53, 53, 13, + 73, 47, 58, 59, 9, 10, 11, 12, 22, 14, + 83, 16, 26, 18, 19, 20, 28, 12, 32, 45, + 25, 22, 70, 35, 29, 30, 31, 65, 33, 102, + 35, 45, 73, 38, 60, 47, 41, 42, 43, 44, + 35, 46, 83, 48, 45, 50, 51, 95, 53, 71, + 95, 52, 74, 58, 59, 9, 10, 11, 12, 81, + 14, 102, 16, 73, 18, 19, 20, 34, 90, 36, + 1, 25, 94, 83, 81, 29, 30, 31, 66, 33, + 68, 35, 13, 96, 38, 98, 99, 41, 42, 43, + 44, 15, 46, 100, 48, 26, 50, 51, 14, 53, + 71, 32, 17, 74, 58, 59, 9, 10, 11, 12, + 81, 14, 34, 16, 36, 18, 19, 20, 82, 107, + 44, 35, 25, 94, 71, 95, 29, 30, 31, 44, + 33, 78, 35, 47, 81, 38, 51, 53, 41, 42, + 43, 44, 15, 46, 71, 48, 16, 50, 51, 22, + 53, 78, 79, 1, 81, 58, 59, 9, 10, 11, + 12, 15, 14, 17, 16, 1, 18, 19, 20, 66, + 34, 68, 36, 25, 16, 71, 44, 29, 30, 31, + 28, 33, 78, 35, 52, 81, 38, 62, 63, 41, + 42, 43, 44, 47, 46, 6, 48, 8, 50, 51, + 16, 53, 73, 1, 2, 40, 58, 59, 9, 10, + 11, 12, 83, 14, 77, 16, 52, 18, 19, 20, + 98, 99, 52, 95, 25, 97, 97, 92, 29, 30, + 31, 77, 33, 49, 35, 100, 14, 38, 16, 102, + 41, 42, 43, 44, 73, 46, 14, 48, 14, 50, + 51, 36, 53, 73, 83, 1, 102, 58, 59, 9, + 10, 11, 12, 83, 14, 17, 16, 50, 18, 19, + 20, 17, 71, 64, 65, 25, 73, 97, 15, 29, + 30, 31, 81, 33, 26, 35, 83, 53, 38, 73, + 32, 41, 42, 43, 44, 47, 46, 92, 48, 83, + 50, 51, 95, 53, 97, 100, 98, 99, 58, 59, + 9, 10, 11, 12, 23, 14, 52, 16, 16, 18, + 19, 20, 16, 7, 50, 16, 25, 13, 13, 16, + 29, 30, 31, 16, 33, 16, 35, 33, 33, 38, + 44, 16, 41, 42, 43, 44, 35, 46, 16, 48, + 95, 50, 51, 96, 53, 37, 38, 39, 81, 58, + 59, 66, 67, 68, 69, 83, 71, 72, 95, 74, + 75, 76, 54, 55, 56, 57, 81, 97, 60, 84, + 95, 13, 17, 80, 89, 90, 91, 1, 93, 94, + 2, 81, 103, 66, 67, 68, 69, 99, 71, 72, + 108, 74, 75, 76, 81, 37, 38, 39, 81, 108, + 108, 84, 108, 108, 108, 95, 89, 90, 91, 108, + 93, 94, 54, 55, 56, 57, 66, 108, 68, 108, + 108, 71, 108, 108, 74, 75, 76, 108, 108, 21, + 108, 81, 108, 108, 84, 9, 108, 108, 12, 89, + 90, 91, 16, 93, 94, 37, 38, 39, 108, 108, + 108, 66, 26, 68, 104, 105, 71, 108, 32, 74, + 75, 76, 54, 55, 56, 57, 81, 1, 108, 84, + 73, 108, 108, 108, 89, 90, 91, 108, 93, 94, + 83, 2, 85, 86, 87, 108, 108, 108, 108, 104, + 105, 108, 26, 3, 4, 5, 6, 108, 32, 9, + 10, 11, 12, 108, 108, 14, 108, 16, 18, 19, + 20, 108, 108, 108, 35, 25, 37, 38, 39, 29, + 30, 31, 66, 108, 68, 69, 47, 71, 72, 108, + 74, 75, 76, 54, 55, 56, 57, 81, 108, 48, + 84, 50, 108, 108, 53, 89, 90, 91, 108, 93, + 94, 3, 108, 108, 108, 108, 108, 9, 10, 11, + 12, 108, 14, 108, 108, 108, 18, 19, 20, 108, + 108, 13, 108, 25, 27, 108, 108, 29, 30, 31, + 108, 108, 108, 3, 37, 38, 39, 108, 108, 9, + 10, 11, 12, 108, 14, 37, 38, 39, 18, 19, + 20, 54, 55, 56, 57, 25, 108, 59, 60, 29, + 30, 31, 54, 55, 56, 57, 108, 108, 66, 108, + 68, 108, 1, 71, 108, 108, 74, 75, 76, 108, + 108, 108, 108, 81, 13, 108, 84, 108, 108, 59, + 60, 89, 90, 91, 108, 93, 94, 26, 108, 108, + 66, 108, 68, 32, 108, 71, 108, 105, 74, 75, + 76, 66, 78, 68, 108, 81, 71, 108, 84, 74, + 75, 76, 108, 89, 90, 91, 81, 93, 94, 84, + 9, 108, 108, 12, 89, 90, 91, 16, 93, 94, + 66, 108, 68, 108, 108, 71, 101, 108, 74, 75, + 76, 108, 108, 108, 108, 81, 108, 108, 84, 9, + 108, 108, 12, 89, 90, 91, 16, 93, 94, 66, + 49, 68, 108, 108, 71, 101, 108, 74, 75, 76, + 66, 78, 68, 108, 81, 71, 108, 84, 74, 75, + 76, 108, 89, 90, 91, 81, 93, 94, 84, 49, + 108, 108, 108, 89, 90, 91, 108, 93, 94, 66, + 108, 68, 73, 108, 71, 101, 77, 74, 75, 76, + 108, 108, 83, 108, 81, 108, 108, 84, 9, 108, + 108, 12, 89, 90, 91, 16, 93, 94, 66, 108, + 68, 102, 108, 71, 108, 108, 74, 75, 76, 66, + 108, 68, 108, 81, 71, 108, 84, 74, 75, 76, + 108, 89, 90, 91, 81, 93, 94, 84, 49, 1, + 108, 108, 89, 90, 91, 108, 93, 94, 66, 108, + 68, 13, 108, 71, 108, 108, 74, 75, 76, 108, + 108, 108, 108, 81, 26, 108, 84, 108, 108, 108, + 32, 89, 90, 91, 108, 93, 94, 66, 108, 68, + 108, 108, 71, 108, 108, 74, 75, 76, 66, 108, + 68, 108, 81, 71, 108, 84, 74, 75, 76, 108, + 89, 90, 91, 81, 93, 94, 84, 108, 108, 108, + 73, 89, 90, 91, 77, 93, 94, 66, 108, 68, + 83, 108, 71, 108, 108, 74, 75, 76, 108, 108, + 108, 108, 81, 108, 108, 84, 108, 108, 108, 102, + 89, 90, 91, 108, 93, 94, 66, 108, 68, 108, + 108, 71, 108, 108, 74, 75, 76, 66, 108, 68, + 108, 81, 71, 108, 84, 74, 75, 76, 108, 89, + 90, 91, 81, 93, 94, 84, 108, 108, 108, 108, + 89, 90, 91, 108, 93, 94, 66, 108, 68, 108, + 108, 71, 108, 108, 74, 75, 76, 108, 108, 108, + 108, 81, 108, 108, 84, 108, 108, 108, 108, 89, + 90, 91, 108, 93, 94, 66, 108, 68, 108, 108, + 71, 108, 108, 74, 75, 76, 66, 108, 68, 108, + 81, 71, 108, 84, 74, 75, 76, 108, 89, 90, + 91, 81, 93, 94, 84, 108, 108, 108, 108, 89, + 90, 91, 108, 93, 94, 66, 108, 68, 108, 108, + 71, 108, 108, 74, 75, 76, 108, 108, 108, 108, + 81, 108, 108, 84, 108, 108, 108, 108, 89, 90, + 91, 108, 93, 94, 66, 108, 68, 108, 108, 71, + 108, 108, 74, 75, 76, 66, 108, 68, 108, 81, + 71, 108, 84, 74, 75, 76, 108, 89, 90, 91, + 81, 93, 94, 84, 108, 108, 108, 108, 89, 90, + 91, 108, 93, 94, 66, 108, 68, 108, 108, 71, + 108, 108, 74, 75, 76, 108, 108, 108, 108, 81, + 108, 108, 84, 108, 108, 108, 108, 89, 90, 91, + 108, 93, 94, 66, 108, 68, 108, 108, 71, 108, + 108, 74, 75, 76, 66, 108, 68, 108, 81, 71, + 108, 84, 74, 75, 76, 108, 89, 90, 91, 81, + 93, 94, 84, 108, 108, 108, 108, 89, 90, 91, + 108, 93, 94, 66, 108, 68, 108, 108, 71, 108, + 108, 74, 75, 76, 108, 108, 108, 108, 81, 108, + 108, 84, 108, 108, 108, 108, 89, 90, 91, 108, + 93, 94, 66, 108, 68, 108, 108, 71, 108, 108, + 74, 75, 76, 66, 108, 68, 108, 81, 71, 108, + 84, 74, 75, 76, 108, 89, 90, 91, 81, 93, + 94, 84, 108, 108, 108, 108, 89, 90, 91, 108, + 93, 94, 66, 108, 68, 108, 108, 71, 108, 108, + 74, 75, 76, 108, 108, 108, 108, 81, 108, 108, + 84, 108, 108, 108, 108, 89, 90, 91, 108, 93, + 94, 66, 108, 68, 108, 108, 71, 108, 108, 74, + 75, 76, 66, 108, 68, 108, 81, 71, 108, 84, + 74, 75, 76, 108, 89, 90, 91, 81, 93, 94, + 84, 108, 108, 108, 108, 89, 90, 91, 108, 93, + 94, 66, 1, 68, 108, 108, 71, 108, 108, 74, + 75, 76, 108, 108, 13, 108, 81, 108, 17, 84, + 108, 108, 108, 108, 89, 90, 91, 26, 93, 94, + 66, 108, 68, 32, 108, 71, 35, 108, 74, 75, + 108, 66, 108, 68, 108, 81, 71, 108, 47, 74, + 75, 108, 88, 89, 90, 91, 81, 93, 94, 108, + 108, 108, 108, 88, 89, 90, 91, 108, 93, 94, + 66, 108, 68, 9, 108, 71, 12, 108, 74, 75, + 16, 108, 108, 108, 108, 81, 108, 108, 108, 108, + 26, 108, 28, 89, 90, 91, 32, 93, 94, 66, + 108, 68, 108, 108, 71, 108, 108, 74, 75, 108, + 66, 108, 68, 108, 81, 71, 108, 108, 74, 75, + 108, 108, 89, 90, 91, 81, 93, 94, 108, 108, + 108, 108, 108, 89, 90, 91, 108, 93, 94, 66, + 108, 68, 108, 73, 71, 108, 108, 74, 75, 108, + 108, 108, 108, 83, 81, 85, 86, 87, 108, 108, + 108, 108, 89, 90, 91, 108, 93, 94, 66, 108, + 68, 108, 108, 71, 108, 108, 74, 75, 108, 66, + 108, 68, 108, 81, 71, 108, 108, 74, 75, 108, + 108, 89, 90, 91, 81, 93, 94, 108, 108, 108, + 108, 108, 89, 90, 91, 2, 93, 94, 66, 108, + 68, 108, 9, 71, 108, 12, 74, 75, 15, 16, + 17, 108, 108, 81, 108, 108, 108, 108, 108, 108, + 108, 89, 90, 91, 2, 93, 94, 108, 108, 108, + 108, 9, 108, 108, 12, 108, 108, 44, 16, 17, + 47, 2, 49, 108, 51, 108, 108, 73, 9, 108, + 108, 12, 108, 108, 15, 16, 17, 83, 108, 85, + 86, 87, 24, 108, 26, 108, 44, 108, 108, 47, + 32, 49, 108, 51, 52, 37, 38, 39, 108, 108, + 108, 108, 108, 44, 108, 108, 47, 108, 49, 108, + 51, 26, 54, 55, 56, 57, 108, 32, 2, 108, + 108, 108, 37, 38, 39, 9, 108, 108, 12, 108, + 108, 108, 16, 17, 108, 108, 2, 108, 108, 54, + 55, 56, 57, 9, 108, 108, 12, 108, 108, 108, + 16, 17, 108, 108, 108, 37, 38, 39, 108, 108, + 44, 108, 108, 47, 13, 49, 108, 51, 108, 13, + 52, 108, 54, 55, 56, 57, 108, 13, 44, 108, + 2, 47, 108, 49, 108, 51, 108, 108, 37, 38, + 39, 108, 108, 37, 38, 39, 108, 108, 108, 108, + 108, 37, 38, 39, 108, 54, 55, 56, 57, 108, + 54, 55, 56, 57, 108, 37, 38, 39, 54, 55, + 56, 57, 36, 37, 38, 39, 108, 108, 108, 108, + 108, 108, 54, 55, 56, 57, 108, 108, 108, 108, + 54, 55, 56, 57, 9, 108, 108, 12, 37, 38, + 39, 16, 17, 36, 37, 38, 39, 108, 108, 108, + 108, 108, 108, 108, 108, 54, 55, 56, 57, 108, + 108, 54, 55, 56, 57, 108, 108, 108, 108, 44, + 108, 108, 47, 108, 49, 108, 51, +); + const YY_SHIFT_USE_DFLT = -22; + const YY_SHIFT_MAX = 230; + public static $yy_shift_ofst = array( + -22, 501, 501, 93, 399, 399, 450, 93, 93, 93, + 399, 450, -9, 93, 93, 93, 93, 93, 93, 144, + 93, 195, 93, 93, 93, 246, 195, 93, 93, 93, + 93, 93, 297, 93, 93, 93, 93, 348, 42, 42, + 42, 42, 42, 42, 42, 42, 1768, 1795, 1795, 701, + 758, 1521, 80, 676, 113, 790, 1927, 1828, 1896, 568, + 768, 1861, 757, 1866, 1874, 1888, 618, 518, 1921, 1921, + 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, + 1921, 1921, 1921, 1584, 636, 285, 676, 676, 346, 113, + 113, 402, 700, 1723, -8, 910, 831, 269, 1028, 51, + 3, 3, 422, 448, 352, 106, 422, 106, 458, 364, + 434, 454, 106, 166, 166, 166, 166, 565, 166, 166, + 166, 586, 565, 166, 166, -22, -22, 1752, 1769, 1826, + 1844, 1945, 145, 979, 156, 132, 284, 106, 140, 106, + 30, 140, 140, 30, 106, 106, 106, 140, 106, 106, + 140, 106, 327, 106, 106, 106, 140, 140, 106, 140, + 205, 106, 284, 166, 565, 588, 565, 588, 565, 166, + 166, -12, 166, -22, -22, -22, -22, -22, -22, 689, + 4, 44, 84, 186, 73, 881, 199, 188, 174, 286, + 48, 336, 384, 389, 332, 142, -21, 52, 154, 33, + 85, 276, 278, 233, 515, 509, 474, 516, 502, 464, + 491, 415, 417, 432, 514, 370, 463, 506, 365, 513, + -12, 517, 504, 519, 505, 511, 496, 525, 532, 330, + 358, +); + const YY_REDUCE_USE_DFLT = -89; + const YY_REDUCE_MAX = 178; + public static $yy_reduce_ofst = array( + 325, 527, 495, 666, 595, 560, 863, 874, 834, 805, + 762, 794, 1179, 1455, 1208, 1012, 1386, 1139, 1070, 1110, + 1150, 1219, 1248, 1277, 1288, 1317, 1346, 1357, 1415, 1426, + 1081, 1041, 1001, 972, 943, 932, 903, 1484, 1495, 1622, + 1633, 1662, 1593, 1564, 1553, 1524, 1704, 607, 1590, 178, + 74, 1027, 229, 899, 273, 212, -22, -22, -22, -22, + -22, -22, -22, -22, -22, -22, -22, -22, -22, -22, + -22, -22, -22, -22, -22, -22, -22, -22, -22, -22, + -22, -22, -22, 380, 329, 187, 159, 127, -52, 253, + 304, 12, 303, 152, 193, 328, 68, 68, 68, 322, + 328, 407, 405, 322, 68, 190, 335, 416, 403, 68, + 401, 354, 371, 68, 68, 68, 337, 322, 68, 68, + 68, 68, 408, 68, 68, 68, 409, 455, 455, 455, + 455, 455, 510, 480, 455, 455, 477, 482, 457, 482, + 473, 457, 457, 485, 482, 482, 482, 457, 482, 482, + 457, 482, 503, 482, 482, 482, 457, 457, 482, 457, + 520, 482, 523, -88, 498, 489, 498, 489, 498, -88, + -88, -67, -88, 111, 155, 89, 236, 230, 162, +); + public static $yyExpectedTokens = array( + array(), + array(9, 10, 11, 12, 14, 16, 18, 19, 20, 25, 29, 30, 31, 33, 35, 38, 41, 42, 43, 44, 46, 48, 50, 51, 53, 58, 59, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 20, 25, 29, 30, 31, 33, 35, 38, 41, 42, 43, 44, 46, 48, 50, 51, 53, 58, 59, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 20, 25, 29, 30, 31, 33, 35, 38, 41, 42, 43, 44, 46, 48, 50, 51, 53, 58, 59, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 20, 25, 29, 30, 31, 33, 35, 38, 41, 42, 43, 44, 46, 48, 50, 51, 53, 58, 59, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 20, 25, 29, 30, 31, 33, 35, 38, 41, 42, 43, 44, 46, 48, 50, 51, 53, 58, 59, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 20, 25, 29, 30, 31, 33, 35, 38, 41, 42, 43, 44, 46, 48, 50, 51, 53, 58, 59, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 20, 25, 29, 30, 31, 33, 35, 38, 41, 42, 43, 44, 46, 48, 50, 51, 53, 58, 59, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 20, 25, 29, 30, 31, 33, 35, 38, 41, 42, 43, 44, 46, 48, 50, 51, 53, 58, 59, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 20, 25, 29, 30, 31, 33, 35, 38, 41, 42, 43, 44, 46, 48, 50, 51, 53, 58, 59, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 20, 25, 29, 30, 31, 33, 35, 38, 41, 42, 43, 44, 46, 48, 50, 51, 53, 58, 59, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 20, 25, 29, 30, 31, 33, 35, 38, 41, 42, 43, 44, 46, 48, 50, 51, 53, 58, 59, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 20, 25, 29, 30, 31, 33, 35, 38, 41, 42, 43, 44, 46, 48, 50, 51, 52, 53, 58, 59, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 20, 25, 29, 30, 31, 33, 35, 38, 41, 42, 43, 44, 46, 48, 50, 51, 53, 58, 59, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 20, 25, 29, 30, 31, 33, 35, 38, 41, 42, 43, 44, 46, 48, 50, 51, 53, 58, 59, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 20, 25, 29, 30, 31, 33, 35, 38, 41, 42, 43, 44, 46, 48, 50, 51, 53, 58, 59, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 20, 25, 29, 30, 31, 33, 35, 38, 41, 42, 43, 44, 46, 48, 50, 51, 53, 58, 59, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 20, 25, 29, 30, 31, 33, 35, 38, 41, 42, 43, 44, 46, 48, 50, 51, 53, 58, 59, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 20, 25, 29, 30, 31, 33, 35, 38, 41, 42, 43, 44, 46, 48, 50, 51, 53, 58, 59, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 20, 25, 29, 30, 31, 33, 35, 38, 41, 42, 43, 44, 46, 48, 50, 51, 53, 58, 59, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 20, 25, 29, 30, 31, 33, 35, 38, 41, 42, 43, 44, 46, 48, 50, 51, 53, 58, 59, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 20, 25, 29, 30, 31, 33, 35, 38, 41, 42, 43, 44, 46, 48, 50, 51, 53, 58, 59, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 20, 25, 29, 30, 31, 33, 35, 38, 41, 42, 43, 44, 46, 48, 50, 51, 53, 58, 59, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 20, 25, 29, 30, 31, 33, 35, 38, 41, 42, 43, 44, 46, 48, 50, 51, 53, 58, 59, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 20, 25, 29, 30, 31, 33, 35, 38, 41, 42, 43, 44, 46, 48, 50, 51, 53, 58, 59, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 20, 25, 29, 30, 31, 33, 35, 38, 41, 42, 43, 44, 46, 48, 50, 51, 53, 58, 59, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 20, 25, 29, 30, 31, 33, 35, 38, 41, 42, 43, 44, 46, 48, 50, 51, 53, 58, 59, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 20, 25, 29, 30, 31, 33, 35, 38, 41, 42, 43, 44, 46, 48, 50, 51, 53, 58, 59, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 20, 25, 29, 30, 31, 33, 35, 38, 41, 42, 43, 44, 46, 48, 50, 51, 53, 58, 59, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 20, 25, 29, 30, 31, 33, 35, 38, 41, 42, 43, 44, 46, 48, 50, 51, 53, 58, 59, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 20, 25, 29, 30, 31, 33, 35, 38, 41, 42, 43, 44, 46, 48, 50, 51, 53, 58, 59, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 20, 25, 29, 30, 31, 33, 35, 38, 41, 42, 43, 44, 46, 48, 50, 51, 53, 58, 59, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 20, 25, 29, 30, 31, 33, 35, 38, 41, 42, 43, 44, 46, 48, 50, 51, 53, 58, 59, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 20, 25, 29, 30, 31, 33, 35, 38, 41, 42, 43, 44, 46, 48, 50, 51, 53, 58, 59, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 20, 25, 29, 30, 31, 33, 35, 38, 41, 42, 43, 44, 46, 48, 50, 51, 53, 58, 59, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 20, 25, 29, 30, 31, 33, 35, 38, 41, 42, 43, 44, 46, 48, 50, 51, 53, 58, 59, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 20, 25, 29, 30, 31, 33, 35, 38, 41, 42, 43, 44, 46, 48, 50, 51, 53, 58, 59, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 20, 25, 29, 30, 31, 33, 35, 38, 41, 42, 43, 44, 46, 48, 50, 51, 53, 58, 59, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 20, 25, 29, 30, 31, 33, 35, 38, 41, 42, 43, 44, 46, 48, 50, 51, 53, 58, 59, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 20, 25, 29, 30, 31, 33, 35, 38, 41, 42, 43, 44, 46, 48, 50, 51, 53, 58, 59, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 20, 25, 29, 30, 31, 33, 35, 38, 41, 42, 43, 44, 46, 48, 50, 51, 53, 58, 59, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 20, 25, 29, 30, 31, 33, 35, 38, 41, 42, 43, 44, 46, 48, 50, 51, 53, 58, 59, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 20, 25, 29, 30, 31, 33, 35, 38, 41, 42, 43, 44, 46, 48, 50, 51, 53, 58, 59, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 20, 25, 29, 30, 31, 33, 35, 38, 41, 42, 43, 44, 46, 48, 50, 51, 53, 58, 59, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 20, 25, 29, 30, 31, 33, 35, 38, 41, 42, 43, 44, 46, 48, 50, 51, 53, 58, 59, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 20, 25, 29, 30, 31, 33, 35, 38, 41, 42, 43, 44, 46, 48, 50, 51, 53, 58, 59, ), + array(24, 26, 32, 37, 38, 39, 54, 55, 56, 57, ), + array(26, 32, 37, 38, 39, 54, 55, 56, 57, ), + array(26, 32, 37, 38, 39, 54, 55, 56, 57, ), + array(14, 16, 48, 50, 53, ), + array(3, 9, 10, 11, 12, 14, 18, 19, 20, 25, 29, 30, 31, 59, 60, ), + array(1, 13, 17, 26, 32, 35, 47, ), + array(14, 16, 50, 53, ), + array(1, 26, 32, ), + array(14, 35, 53, ), + array(3, 9, 10, 11, 12, 14, 18, 19, 20, 25, 29, 30, 31, 59, 60, ), + array(36, 37, 38, 39, 54, 55, 56, 57, ), + array(37, 38, 39, 52, 54, 55, 56, 57, ), + array(36, 37, 38, 39, 54, 55, 56, 57, ), + array(13, 37, 38, 39, 54, 55, 56, 57, ), + array(13, 37, 38, 39, 54, 55, 56, 57, ), + array(13, 37, 38, 39, 54, 55, 56, 57, ), + array(27, 37, 38, 39, 54, 55, 56, 57, ), + array(13, 37, 38, 39, 54, 55, 56, 57, ), + array(13, 37, 38, 39, 54, 55, 56, 57, ), + array(2, 37, 38, 39, 54, 55, 56, 57, ), + array(21, 37, 38, 39, 54, 55, 56, 57, ), + array(37, 38, 39, 54, 55, 56, 57, 60, ), + array(37, 38, 39, 54, 55, 56, 57, ), + array(37, 38, 39, 54, 55, 56, 57, ), + array(37, 38, 39, 54, 55, 56, 57, ), + array(37, 38, 39, 54, 55, 56, 57, ), + array(37, 38, 39, 54, 55, 56, 57, ), + array(37, 38, 39, 54, 55, 56, 57, ), + array(37, 38, 39, 54, 55, 56, 57, ), + array(37, 38, 39, 54, 55, 56, 57, ), + array(37, 38, 39, 54, 55, 56, 57, ), + array(37, 38, 39, 54, 55, 56, 57, ), + array(37, 38, 39, 54, 55, 56, 57, ), + array(37, 38, 39, 54, 55, 56, 57, ), + array(37, 38, 39, 54, 55, 56, 57, ), + array(37, 38, 39, 54, 55, 56, 57, ), + array(37, 38, 39, 54, 55, 56, 57, ), + array(9, 12, 16, 26, 28, 32, ), + array(9, 12, 16, 26, 32, ), + array(17, 44, 51, ), + array(1, 26, 32, ), + array(1, 26, 32, ), + array(15, 17, 47, ), + array(14, 35, 53, ), + array(14, 35, 53, ), + array(1, 2, ), + array(3, 4, 5, 6, 9, 10, 11, 12, 18, 19, 20, 25, 29, 30, 31, ), + array(2, 9, 12, 15, 16, 17, 44, 47, 49, 51, ), + array(12, 14, 16, 53, ), + array(9, 12, 16, 49, ), + array(1, 13, 26, 32, ), + array(1, 13, 26, 32, ), + array(1, 13, 26, 32, ), + array(15, 17, 47, ), + array(9, 12, 16, ), + array(9, 12, 16, ), + array(14, 16, ), + array(17, 47, ), + array(1, 28, ), + array(26, 32, ), + array(14, 16, ), + array(26, 32, ), + array(26, 32, ), + array(1, 52, ), + array(14, 53, ), + array(1, 17, ), + array(26, 32, ), + array(1, ), + array(1, ), + array(1, ), + array(1, ), + array(17, ), + array(1, ), + array(1, ), + array(1, ), + array(1, ), + array(17, ), + array(1, ), + array(1, ), + array(), + array(), + array(2, 9, 12, 16, 17, 44, 47, 49, 51, 52, ), + array(2, 9, 12, 15, 16, 17, 44, 47, 49, 51, ), + array(2, 9, 12, 16, 17, 44, 47, 49, 51, ), + array(2, 9, 12, 16, 17, 44, 47, 49, 51, ), + array(9, 12, 16, 17, 44, 47, 49, 51, ), + array(12, 14, 16, 33, 53, ), + array(9, 12, 16, 49, ), + array(9, 12, 16, ), + array(15, 44, 51, ), + array(14, 53, ), + array(26, 32, ), + array(44, 51, ), + array(26, 32, ), + array(44, 51, ), + array(44, 51, ), + array(44, 51, ), + array(44, 51, ), + array(26, 32, ), + array(26, 32, ), + array(26, 32, ), + array(44, 51, ), + array(26, 32, ), + array(26, 32, ), + array(44, 51, ), + array(26, 32, ), + array(15, 22, ), + array(26, 32, ), + array(26, 32, ), + array(26, 32, ), + array(44, 51, ), + array(44, 51, ), + array(26, 32, ), + array(44, 51, ), + array(12, 35, ), + array(26, 32, ), + array(14, 53, ), + array(1, ), + array(17, ), + array(2, ), + array(17, ), + array(2, ), + array(17, ), + array(1, ), + array(1, ), + array(35, ), + array(1, ), + array(), + array(), + array(), + array(), + array(), + array(), + array(2, 35, 37, 38, 39, 47, 54, 55, 56, 57, ), + array(13, 21, 23, 26, 32, 34, 36, 44, ), + array(13, 15, 26, 32, 35, 47, ), + array(13, 22, 26, 32, 45, ), + array(13, 22, 26, 32, 45, ), + array(35, 44, 47, 52, ), + array(9, 12, 16, 49, ), + array(22, 45, 52, ), + array(28, 35, 47, ), + array(22, 45, 60, ), + array(35, 47, ), + array(21, 34, ), + array(34, 36, ), + array(16, 49, ), + array(6, 8, ), + array(44, 52, ), + array(7, 8, ), + array(34, 52, ), + array(35, 47, ), + array(35, 47, ), + array(22, 45, ), + array(34, 36, ), + array(15, 44, ), + array(34, 36, ), + array(34, 36, ), + array(13, ), + array(16, ), + array(50, ), + array(7, ), + array(16, ), + array(52, ), + array(23, ), + array(36, ), + array(50, ), + array(14, ), + array(13, ), + array(52, ), + array(15, ), + array(16, ), + array(40, ), + array(16, ), + array(35, ), + array(16, ), + array(33, ), + array(16, ), + array(33, ), + array(35, ), + array(44, ), + array(16, ), + array(16, ), + array(16, ), + array(16, ), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), +); + public static $yy_default = array( + 336, 512, 512, 512, 497, 497, 512, 474, 474, 474, + 512, 512, 512, 512, 512, 512, 512, 512, 512, 512, + 512, 512, 512, 512, 512, 512, 512, 512, 512, 512, + 512, 512, 512, 512, 512, 512, 512, 512, 512, 512, + 512, 512, 512, 512, 512, 512, 377, 377, 356, 512, + 512, 413, 512, 377, 512, 512, 512, 512, 512, 512, + 512, 512, 382, 512, 349, 512, 512, 512, 382, 379, + 389, 388, 384, 402, 473, 397, 498, 500, 401, 361, + 472, 499, 349, 377, 377, 487, 377, 377, 429, 512, + 512, 368, 326, 428, 512, 439, 391, 391, 391, 429, + 439, 439, 512, 429, 391, 377, 512, 377, 377, 391, + 512, 371, 358, 395, 394, 396, 373, 429, 400, 404, + 391, 404, 484, 406, 405, 481, 334, 428, 428, 428, + 428, 428, 512, 441, 439, 455, 512, 363, 435, 354, + 434, 437, 433, 432, 359, 357, 364, 436, 353, 367, + 466, 365, 512, 352, 350, 360, 467, 465, 346, 464, + 439, 366, 512, 369, 461, 475, 488, 476, 485, 372, + 422, 439, 374, 480, 439, 480, 480, 439, 334, 413, + 409, 413, 403, 403, 413, 440, 403, 413, 403, 413, + 512, 512, 512, 332, 409, 512, 512, 512, 423, 403, + 512, 409, 512, 512, 512, 512, 512, 512, 512, 418, + 385, 512, 512, 512, 512, 512, 512, 512, 415, 512, + 455, 512, 512, 512, 411, 486, 409, 512, 512, 512, + 512, 419, 407, 362, 445, 418, 425, 424, 420, 339, + 460, 421, 483, 398, 416, 340, 399, 455, 378, 337, + 338, 330, 328, 329, 442, 443, 444, 438, 392, 393, + 427, 426, 386, 417, 408, 390, 410, 331, 333, 335, + 412, 470, 414, 415, 503, 478, 495, 471, 459, 458, + 375, 457, 344, 462, 508, 493, 376, 496, 456, 509, + 494, 501, 504, 511, 510, 507, 505, 502, 506, 345, + 468, 469, 446, 355, 341, 452, 450, 454, 448, 453, + 447, 489, 490, 491, 463, 449, 492, 451, 327, 342, + 343, 370, 430, 431, 479, 477, +); + const YYNOCODE = 109; + const YYSTACKDEPTH = 500; + const YYNSTATE = 326; + const YYNRULE = 186; + const YYERRORSYMBOL = 61; + const YYERRSYMDT = 'yy0'; + const YYFALLBACK = 0; + public static $yyFallback = array( + ); public function Trace($TraceFILE, $zTracePrompt) { if (!$TraceFILE) { @@ -1840,18 +1110,250 @@ public function PrintTrace() $this->yyTracePrompt = '
'; } + public $yyTraceFILE; + public $yyTracePrompt; + public $yyidx; /* Index of top element in stack */ + public $yyerrcnt; /* Shifts left before out of the error */ + public $yystack = array(); /* The parser's stack */ + + public $yyTokenName = array( + '$', 'VERT', 'COLON', 'TEXT', + 'STRIPON', 'STRIPOFF', 'LITERALSTART', 'LITERALEND', + 'LITERAL', 'SIMPELOUTPUT', 'SIMPLETAG', 'SMARTYBLOCKCHILDPARENT', + 'LDEL', 'RDEL', 'DOLLARID', 'EQUAL', + 'ID', 'PTR', 'LDELMAKENOCACHE', 'LDELIF', + 'LDELFOR', 'SEMICOLON', 'INCDEC', 'TO', + 'STEP', 'LDELFOREACH', 'SPACE', 'AS', + 'APTR', 'LDELSETFILTER', 'CLOSETAG', 'LDELSLASH', + 'ATTR', 'INTEGER', 'COMMA', 'OPENP', + 'CLOSEP', 'MATH', 'UNIMATH', 'ISIN', + 'QMARK', 'NOT', 'TYPECAST', 'HEX', + 'DOT', 'INSTANCEOF', 'SINGLEQUOTESTRING', 'DOUBLECOLON', + 'NAMESPACE', 'AT', 'HATCH', 'OPENB', + 'CLOSEB', 'DOLLAR', 'LOGOP', 'SLOGOP', + 'TLOGOP', 'SINGLECOND', 'ARRAYOPEN', 'QUOTE', + 'BACKTICK', 'error', 'start', 'template', + 'literal_e2', 'literal_e1', 'smartytag', 'tagbody', + 'tag', 'outattr', 'eqoutattr', 'varindexed', + 'output', 'attributes', 'variable', 'value', + 'expr', 'modifierlist', 'statement', 'statements', + 'foraction', 'varvar', 'modparameters', 'attribute', + 'ternary', 'tlop', 'lop', 'scond', + 'array', 'function', 'ns1', 'doublequoted_with_quotes', + 'static_class_access', 'arraydef', 'object', 'arrayindex', + 'indexdef', 'varvarele', 'objectchain', 'objectelement', + 'method', 'params', 'modifier', 'modparameter', + 'arrayelements', 'arrayelement', 'doublequoted', 'doublequotedcontent', + ); + + public static $yyRuleName = array( + 'start ::= template', + 'template ::= template TEXT', + 'template ::= template STRIPON', + 'template ::= template STRIPOFF', + 'template ::= template LITERALSTART literal_e2 LITERALEND', + 'literal_e2 ::= literal_e1 LITERALSTART literal_e1 LITERALEND', + 'literal_e2 ::= literal_e1', + 'literal_e1 ::= literal_e1 LITERAL', + 'literal_e1 ::=', + 'template ::= template smartytag', + 'template ::=', + 'smartytag ::= SIMPELOUTPUT', + 'smartytag ::= SIMPLETAG', + 'smartytag ::= SMARTYBLOCKCHILDPARENT', + 'smartytag ::= LDEL tagbody RDEL', + 'smartytag ::= tag RDEL', + 'tagbody ::= outattr', + 'tagbody ::= DOLLARID eqoutattr', + 'tagbody ::= varindexed eqoutattr', + 'eqoutattr ::= EQUAL outattr', + 'outattr ::= output attributes', + 'output ::= variable', + 'output ::= value', + 'output ::= expr', + 'tag ::= LDEL ID attributes', + 'tag ::= LDEL ID', + 'tag ::= LDEL ID modifierlist attributes', + 'tag ::= LDEL ID PTR ID attributes', + 'tag ::= LDEL ID PTR ID modifierlist attributes', + 'tag ::= LDELMAKENOCACHE DOLLARID', + 'tag ::= LDELIF expr', + 'tag ::= LDELIF expr attributes', + 'tag ::= LDELIF statement', + 'tag ::= LDELIF statement attributes', + 'tag ::= LDELFOR statements SEMICOLON expr SEMICOLON varindexed foraction attributes', + 'foraction ::= EQUAL expr', + 'foraction ::= INCDEC', + 'tag ::= LDELFOR statement TO expr attributes', + 'tag ::= LDELFOR statement TO expr STEP expr attributes', + 'tag ::= LDELFOREACH SPACE expr AS varvar attributes', + 'tag ::= LDELFOREACH SPACE expr AS varvar APTR varvar attributes', + 'tag ::= LDELFOREACH attributes', + 'tag ::= LDELSETFILTER ID modparameters', + 'tag ::= LDELSETFILTER ID modparameters modifierlist', + 'smartytag ::= CLOSETAG', + 'tag ::= LDELSLASH ID', + 'tag ::= LDELSLASH ID modifierlist', + 'tag ::= LDELSLASH ID PTR ID', + 'tag ::= LDELSLASH ID PTR ID modifierlist', + 'attributes ::= attributes attribute', + 'attributes ::= attribute', + 'attributes ::=', + 'attribute ::= SPACE ID EQUAL ID', + 'attribute ::= ATTR expr', + 'attribute ::= ATTR value', + 'attribute ::= SPACE ID', + 'attribute ::= SPACE expr', + 'attribute ::= SPACE value', + 'attribute ::= SPACE INTEGER EQUAL expr', + 'statements ::= statement', + 'statements ::= statements COMMA statement', + 'statement ::= DOLLARID EQUAL INTEGER', + 'statement ::= DOLLARID EQUAL expr', + 'statement ::= varindexed EQUAL expr', + 'statement ::= OPENP statement CLOSEP', + 'expr ::= value', + 'expr ::= ternary', + 'expr ::= DOLLARID COLON ID', + 'expr ::= expr MATH value', + 'expr ::= expr UNIMATH value', + 'expr ::= expr tlop value', + 'expr ::= expr lop expr', + 'expr ::= expr scond', + 'expr ::= expr ISIN array', + 'expr ::= expr ISIN value', + 'ternary ::= OPENP expr CLOSEP QMARK DOLLARID COLON expr', + 'ternary ::= OPENP expr CLOSEP QMARK expr COLON expr', + 'value ::= variable', + 'value ::= UNIMATH value', + 'value ::= NOT value', + 'value ::= TYPECAST value', + 'value ::= variable INCDEC', + 'value ::= HEX', + 'value ::= INTEGER', + 'value ::= INTEGER DOT INTEGER', + 'value ::= INTEGER DOT', + 'value ::= DOT INTEGER', + 'value ::= ID', + 'value ::= function', + 'value ::= OPENP expr CLOSEP', + 'value ::= variable INSTANCEOF ns1', + 'value ::= variable INSTANCEOF variable', + 'value ::= SINGLEQUOTESTRING', + 'value ::= doublequoted_with_quotes', + 'value ::= varindexed DOUBLECOLON static_class_access', + 'value ::= smartytag', + 'value ::= value modifierlist', + 'value ::= NAMESPACE', + 'value ::= arraydef', + 'value ::= ns1 DOUBLECOLON static_class_access', + 'ns1 ::= ID', + 'ns1 ::= NAMESPACE', + 'variable ::= DOLLARID', + 'variable ::= varindexed', + 'variable ::= varvar AT ID', + 'variable ::= object', + 'variable ::= HATCH ID HATCH', + 'variable ::= HATCH ID HATCH arrayindex', + 'variable ::= HATCH variable HATCH', + 'variable ::= HATCH variable HATCH arrayindex', + 'varindexed ::= DOLLARID arrayindex', + 'varindexed ::= varvar arrayindex', + 'arrayindex ::= arrayindex indexdef', + 'arrayindex ::=', + 'indexdef ::= DOT DOLLARID', + 'indexdef ::= DOT varvar', + 'indexdef ::= DOT varvar AT ID', + 'indexdef ::= DOT ID', + 'indexdef ::= DOT INTEGER', + 'indexdef ::= DOT LDEL expr RDEL', + 'indexdef ::= OPENB ID CLOSEB', + 'indexdef ::= OPENB ID DOT ID CLOSEB', + 'indexdef ::= OPENB SINGLEQUOTESTRING CLOSEB', + 'indexdef ::= OPENB INTEGER CLOSEB', + 'indexdef ::= OPENB DOLLARID CLOSEB', + 'indexdef ::= OPENB variable CLOSEB', + 'indexdef ::= OPENB value CLOSEB', + 'indexdef ::= OPENB expr CLOSEB', + 'indexdef ::= OPENB CLOSEB', + 'varvar ::= DOLLARID', + 'varvar ::= DOLLAR', + 'varvar ::= varvar varvarele', + 'varvarele ::= ID', + 'varvarele ::= SIMPELOUTPUT', + 'varvarele ::= LDEL expr RDEL', + 'object ::= varindexed objectchain', + 'objectchain ::= objectelement', + 'objectchain ::= objectchain objectelement', + 'objectelement ::= PTR ID arrayindex', + 'objectelement ::= PTR varvar arrayindex', + 'objectelement ::= PTR LDEL expr RDEL arrayindex', + 'objectelement ::= PTR ID LDEL expr RDEL arrayindex', + 'objectelement ::= PTR method', + 'function ::= ns1 OPENP params CLOSEP', + 'method ::= ID OPENP params CLOSEP', + 'method ::= DOLLARID OPENP params CLOSEP', + 'params ::= params COMMA expr', + 'params ::= expr', + 'params ::=', + 'modifierlist ::= modifierlist modifier modparameters', + 'modifierlist ::= modifier modparameters', + 'modifier ::= VERT AT ID', + 'modifier ::= VERT ID', + 'modparameters ::= modparameters modparameter', + 'modparameters ::=', + 'modparameter ::= COLON value', + 'modparameter ::= COLON UNIMATH value', + 'modparameter ::= COLON array', + 'static_class_access ::= method', + 'static_class_access ::= method objectchain', + 'static_class_access ::= ID', + 'static_class_access ::= DOLLARID arrayindex', + 'static_class_access ::= DOLLARID arrayindex objectchain', + 'lop ::= LOGOP', + 'lop ::= SLOGOP', + 'tlop ::= TLOGOP', + 'scond ::= SINGLECOND', + 'arraydef ::= OPENB arrayelements CLOSEB', + 'arraydef ::= ARRAYOPEN arrayelements CLOSEP', + 'arrayelements ::= arrayelement', + 'arrayelements ::= arrayelements COMMA arrayelement', + 'arrayelements ::=', + 'arrayelement ::= value APTR expr', + 'arrayelement ::= ID APTR expr', + 'arrayelement ::= expr', + 'doublequoted_with_quotes ::= QUOTE QUOTE', + 'doublequoted_with_quotes ::= QUOTE doublequoted QUOTE', + 'doublequoted ::= doublequoted doublequotedcontent', + 'doublequoted ::= doublequotedcontent', + 'doublequotedcontent ::= BACKTICK variable BACKTICK', + 'doublequotedcontent ::= BACKTICK expr BACKTICK', + 'doublequotedcontent ::= DOLLARID', + 'doublequotedcontent ::= LDEL variable RDEL', + 'doublequotedcontent ::= LDEL expr RDEL', + 'doublequotedcontent ::= smartytag', + 'doublequotedcontent ::= TEXT', + ); + public function tokenName($tokenType) { if ($tokenType === 0) { return 'End of Input'; } if ($tokenType > 0 && $tokenType < count($this->yyTokenName)) { - return $this->yyTokenName[ $tokenType ]; + return $this->yyTokenName[$tokenType]; } else { return 'Unknown'; } } + public static function yy_destructor($yymajor, $yypminor) + { + switch ($yymajor) { + default: break; /* If no destructor action specified: do nothing */ + } + } + public function yy_pop_parser_stack() { if (empty($this->yystack)) { @@ -1860,18 +1362,19 @@ public function yy_pop_parser_stack() $yytos = array_pop($this->yystack); if ($this->yyTraceFILE && $this->yyidx >= 0) { fwrite($this->yyTraceFILE, - $this->yyTracePrompt . 'Popping ' . $this->yyTokenName[ $yytos->major ] . - "\n"); + $this->yyTracePrompt . 'Popping ' . $this->yyTokenName[$yytos->major] . + "\n"); } $yymajor = $yytos->major; self::yy_destructor($yymajor, $yytos->minor); $this->yyidx--; + return $yymajor; } public function __destruct() { - while ($this->yystack !== array()) { + while ($this->yystack !== Array()) { $this->yy_pop_parser_stack(); } if (is_resource($this->yyTraceFILE)) { @@ -1883,14 +1386,14 @@ public function yy_get_expected_tokens($token) { static $res3 = array(); static $res4 = array(); - $state = $this->yystack[ $this->yyidx ]->stateno; - $expected = self::$yyExpectedTokens[ $state ]; - if (isset($res3[ $state ][ $token ])) { - if ($res3[ $state ][ $token ]) { + $state = $this->yystack[$this->yyidx]->stateno; + $expected = self::$yyExpectedTokens[$state]; + if (isset($res3[$state][$token])) { + if ($res3[$state][$token]) { return $expected; } } else { - if ($res3[ $state ][ $token ] = in_array($token, self::$yyExpectedTokens[ $state ], true)) { + if ($res3[$state][$token] = in_array($token, self::$yyExpectedTokens[$state], true)) { return $expected; } } @@ -1910,21 +1413,20 @@ public function yy_get_expected_tokens($token) return array_unique($expected); } $yyruleno = $yyact - self::YYNSTATE; - $this->yyidx -= self::$yyRuleInfo[ $yyruleno ][ 1 ]; + $this->yyidx -= self::$yyRuleInfo[$yyruleno][1]; $nextstate = $this->yy_find_reduce_action( - $this->yystack[ $this->yyidx ]->stateno, - self::$yyRuleInfo[ $yyruleno ][ 0 ]); - if (isset(self::$yyExpectedTokens[ $nextstate ])) { - $expected = array_merge($expected, self::$yyExpectedTokens[ $nextstate ]); - if (isset($res4[ $nextstate ][ $token ])) { - if ($res4[ $nextstate ][ $token ]) { + $this->yystack[$this->yyidx]->stateno, + self::$yyRuleInfo[$yyruleno][0]); + if (isset(self::$yyExpectedTokens[$nextstate])) { + $expected = array_merge($expected, self::$yyExpectedTokens[$nextstate]); + if (isset($res4[$nextstate][$token])) { + if ($res4[$nextstate][$token]) { $this->yyidx = $yyidx; $this->yystack = $stack; return array_unique($expected); } } else { - if ($res4[ $nextstate ][ $token ] = - in_array($token, self::$yyExpectedTokens[ $nextstate ], true)) { + if ($res4[$nextstate][$token] = in_array($token, self::$yyExpectedTokens[$nextstate], true)) { $this->yyidx = $yyidx; $this->yystack = $stack; return array_unique($expected); @@ -1936,8 +1438,8 @@ public function yy_get_expected_tokens($token) $this->yyidx++; $x = new TP_yyStackEntry; $x->stateno = $nextstate; - $x->major = self::$yyRuleInfo[ $yyruleno ][ 0 ]; - $this->yystack[ $this->yyidx ] = $x; + $x->major = self::$yyRuleInfo[$yyruleno][0]; + $this->yystack[$this->yyidx] = $x; continue 2; } elseif ($nextstate === self::YYNSTATE + self::YYNRULE + 1) { $this->yyidx = $yyidx; @@ -1958,8 +1460,9 @@ public function yy_get_expected_tokens($token) } break; } while (true); - $this->yyidx = $yyidx; - $this->yystack = $stack; + $this->yyidx = $yyidx; + $this->yystack = $stack; + return array_unique($expected); } @@ -1970,16 +1473,16 @@ public function yy_is_expected_token($token) if ($token === 0) { return true; // 0 is not part of this } - $state = $this->yystack[ $this->yyidx ]->stateno; - if (isset($res[ $state ][ $token ])) { - if ($res[ $state ][ $token ]) { + $state = $this->yystack[$this->yyidx]->stateno; + if (isset($res[$state][$token])) { + if ($res[$state][$token]) { return true; } } else { - if ($res[ $state ][ $token ] = in_array($token, self::$yyExpectedTokens[ $state ], true)) { + if ($res[$state][$token] = in_array($token, self::$yyExpectedTokens[$state], true)) { return true; } - } + } $stack = $this->yystack; $yyidx = $this->yyidx; do { @@ -1996,20 +1499,18 @@ public function yy_is_expected_token($token) return true; } $yyruleno = $yyact - self::YYNSTATE; - $this->yyidx -= self::$yyRuleInfo[ $yyruleno ][ 1 ]; + $this->yyidx -= self::$yyRuleInfo[$yyruleno][1]; $nextstate = $this->yy_find_reduce_action( - $this->yystack[ $this->yyidx ]->stateno, - self::$yyRuleInfo[ $yyruleno ][ 0 ]); - if (isset($res2[ $nextstate ][ $token ])) { - if ($res2[ $nextstate ][ $token ]) { + $this->yystack[$this->yyidx]->stateno, + self::$yyRuleInfo[$yyruleno][0]); + if (isset($res2[$nextstate][$token])) { + if ($res2[$nextstate][$token]) { $this->yyidx = $yyidx; $this->yystack = $stack; return true; } } else { - if ($res2[ $nextstate ][ $token ] = - (isset(self::$yyExpectedTokens[ $nextstate ]) && - in_array($token, self::$yyExpectedTokens[ $nextstate ], true))) { + if ($res2[$nextstate][$token] = (isset(self::$yyExpectedTokens[$nextstate]) && in_array($token, self::$yyExpectedTokens[$nextstate], true))) { $this->yyidx = $yyidx; $this->yystack = $stack; return true; @@ -2020,8 +1521,8 @@ public function yy_is_expected_token($token) $this->yyidx++; $x = new TP_yyStackEntry; $x->stateno = $nextstate; - $x->major = self::$yyRuleInfo[ $yyruleno ][ 0 ]; - $this->yystack[ $this->yyidx ] = $x; + $x->major = self::$yyRuleInfo[$yyruleno][0]; + $this->yystack[$this->yyidx] = $x; continue 2; } elseif ($nextstate === self::YYNSTATE + self::YYNRULE + 1) { $this->yyidx = $yyidx; @@ -2048,65 +1549,69 @@ public function yy_is_expected_token($token) } while (true); $this->yyidx = $yyidx; $this->yystack = $stack; + return true; } - public function yy_find_shift_action($iLookAhead) + public function yy_find_shift_action($iLookAhead) { - $stateno = $this->yystack[ $this->yyidx ]->stateno; + $stateno = $this->yystack[$this->yyidx]->stateno; + /* if ($this->yyidx < 0) return self::YY_NO_ACTION; */ - if (!isset(self::$yy_shift_ofst[ $stateno ])) { + if (!isset(self::$yy_shift_ofst[$stateno])) { // no shift actions - return self::$yy_default[ $stateno ]; + return self::$yy_default[$stateno]; } - $i = self::$yy_shift_ofst[ $stateno ]; + $i = self::$yy_shift_ofst[$stateno]; if ($i === self::YY_SHIFT_USE_DFLT) { - return self::$yy_default[ $stateno ]; + return self::$yy_default[$stateno]; } if ($iLookAhead === self::YYNOCODE) { return self::YY_NO_ACTION; } $i += $iLookAhead; if ($i < 0 || $i >= self::YY_SZ_ACTTAB || - self::$yy_lookahead[ $i ] != $iLookAhead) { + self::$yy_lookahead[$i] != $iLookAhead) { if (count(self::$yyFallback) && $iLookAhead < count(self::$yyFallback) - && ($iFallback = self::$yyFallback[ $iLookAhead ]) != 0) { + && ($iFallback = self::$yyFallback[$iLookAhead]) != 0) { if ($this->yyTraceFILE) { fwrite($this->yyTraceFILE, $this->yyTracePrompt . 'FALLBACK ' . - $this->yyTokenName[ $iLookAhead ] . ' => ' . - $this->yyTokenName[ $iFallback ] . "\n"); + $this->yyTokenName[$iLookAhead] . ' => ' . + $this->yyTokenName[$iFallback] . "\n"); } + return $this->yy_find_shift_action($iFallback); } - return self::$yy_default[ $stateno ]; + + return self::$yy_default[$stateno]; } else { - return self::$yy_action[ $i ]; + return self::$yy_action[$i]; } } public function yy_find_reduce_action($stateno, $iLookAhead) { /* $stateno = $this->yystack[$this->yyidx]->stateno; */ - if (!isset(self::$yy_reduce_ofst[ $stateno ])) { - return self::$yy_default[ $stateno ]; + + if (!isset(self::$yy_reduce_ofst[$stateno])) { + return self::$yy_default[$stateno]; } - $i = self::$yy_reduce_ofst[ $stateno ]; + $i = self::$yy_reduce_ofst[$stateno]; if ($i === self::YY_REDUCE_USE_DFLT) { - return self::$yy_default[ $stateno ]; + return self::$yy_default[$stateno]; } if ($iLookAhead === self::YYNOCODE) { return self::YY_NO_ACTION; } $i += $iLookAhead; if ($i < 0 || $i >= self::YY_SZ_ACTTAB || - self::$yy_lookahead[ $i ] != $iLookAhead) { - return self::$yy_default[ $stateno ]; + self::$yy_lookahead[$i] != $iLookAhead) { + return self::$yy_default[$stateno]; } else { - return self::$yy_action[ $i ]; + return self::$yy_action[$i]; } } - // line 234 "../smarty/lexer/smarty_internal_templateparser.y" public function yy_shift($yyNewState, $yyMajor, $yypMinor) { $this->yyidx++; @@ -2118,9 +1623,11 @@ public function yy_shift($yyNewState, $yyMajor, $yypMinor) while ($this->yyidx >= 0) { $this->yy_pop_parser_stack(); } - // line 221 "../smarty/lexer/smarty_internal_templateparser.y" - $this->internalError = true; - $this->compiler->trigger_template_error('Stack overflow in template parser'); +// line 220 "../smarty/lexer/smarty_internal_templateparser.y" + + $this->internalError = true; + $this->compiler->trigger_template_error('Stack overflow in template parser'); + return; } $yytos = new TP_yyStackEntry; @@ -2134,1357 +1641,1150 @@ public function yy_shift($yyNewState, $yyMajor, $yypMinor) fprintf($this->yyTraceFILE, "%sStack:", $this->yyTracePrompt); for ($i = 1; $i <= $this->yyidx; $i++) { fprintf($this->yyTraceFILE, " %s", - $this->yyTokenName[ $this->yystack[ $i ]->major ]); + $this->yyTokenName[$this->yystack[$i]->major]); } - fwrite($this->yyTraceFILE, "\n"); + fwrite($this->yyTraceFILE,"\n"); } } - // line 242 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r0() - { - $this->root_buffer->prepend_array($this, $this->template_prefix); - $this->root_buffer->append_array($this, $this->template_postfix); - $this->_retvalue = $this->root_buffer->to_smarty_php($this); - } + public static $yyRuleInfo = array( + array( 0 => 62, 1 => 1 ), + array( 0 => 63, 1 => 2 ), + array( 0 => 63, 1 => 2 ), + array( 0 => 63, 1 => 2 ), + array( 0 => 63, 1 => 4 ), + array( 0 => 64, 1 => 4 ), + array( 0 => 64, 1 => 1 ), + array( 0 => 65, 1 => 2 ), + array( 0 => 65, 1 => 0 ), + array( 0 => 63, 1 => 2 ), + array( 0 => 63, 1 => 0 ), + array( 0 => 66, 1 => 1 ), + array( 0 => 66, 1 => 1 ), + array( 0 => 66, 1 => 1 ), + array( 0 => 66, 1 => 3 ), + array( 0 => 66, 1 => 2 ), + array( 0 => 67, 1 => 1 ), + array( 0 => 67, 1 => 2 ), + array( 0 => 67, 1 => 2 ), + array( 0 => 70, 1 => 2 ), + array( 0 => 69, 1 => 2 ), + array( 0 => 72, 1 => 1 ), + array( 0 => 72, 1 => 1 ), + array( 0 => 72, 1 => 1 ), + array( 0 => 68, 1 => 3 ), + array( 0 => 68, 1 => 2 ), + array( 0 => 68, 1 => 4 ), + array( 0 => 68, 1 => 5 ), + array( 0 => 68, 1 => 6 ), + array( 0 => 68, 1 => 2 ), + array( 0 => 68, 1 => 2 ), + array( 0 => 68, 1 => 3 ), + array( 0 => 68, 1 => 2 ), + array( 0 => 68, 1 => 3 ), + array( 0 => 68, 1 => 8 ), + array( 0 => 80, 1 => 2 ), + array( 0 => 80, 1 => 1 ), + array( 0 => 68, 1 => 5 ), + array( 0 => 68, 1 => 7 ), + array( 0 => 68, 1 => 6 ), + array( 0 => 68, 1 => 8 ), + array( 0 => 68, 1 => 2 ), + array( 0 => 68, 1 => 3 ), + array( 0 => 68, 1 => 4 ), + array( 0 => 66, 1 => 1 ), + array( 0 => 68, 1 => 2 ), + array( 0 => 68, 1 => 3 ), + array( 0 => 68, 1 => 4 ), + array( 0 => 68, 1 => 5 ), + array( 0 => 73, 1 => 2 ), + array( 0 => 73, 1 => 1 ), + array( 0 => 73, 1 => 0 ), + array( 0 => 83, 1 => 4 ), + array( 0 => 83, 1 => 2 ), + array( 0 => 83, 1 => 2 ), + array( 0 => 83, 1 => 2 ), + array( 0 => 83, 1 => 2 ), + array( 0 => 83, 1 => 2 ), + array( 0 => 83, 1 => 4 ), + array( 0 => 79, 1 => 1 ), + array( 0 => 79, 1 => 3 ), + array( 0 => 78, 1 => 3 ), + array( 0 => 78, 1 => 3 ), + array( 0 => 78, 1 => 3 ), + array( 0 => 78, 1 => 3 ), + array( 0 => 76, 1 => 1 ), + array( 0 => 76, 1 => 1 ), + array( 0 => 76, 1 => 3 ), + array( 0 => 76, 1 => 3 ), + array( 0 => 76, 1 => 3 ), + array( 0 => 76, 1 => 3 ), + array( 0 => 76, 1 => 3 ), + array( 0 => 76, 1 => 2 ), + array( 0 => 76, 1 => 3 ), + array( 0 => 76, 1 => 3 ), + array( 0 => 84, 1 => 7 ), + array( 0 => 84, 1 => 7 ), + array( 0 => 75, 1 => 1 ), + array( 0 => 75, 1 => 2 ), + array( 0 => 75, 1 => 2 ), + array( 0 => 75, 1 => 2 ), + array( 0 => 75, 1 => 2 ), + array( 0 => 75, 1 => 1 ), + array( 0 => 75, 1 => 1 ), + array( 0 => 75, 1 => 3 ), + array( 0 => 75, 1 => 2 ), + array( 0 => 75, 1 => 2 ), + array( 0 => 75, 1 => 1 ), + array( 0 => 75, 1 => 1 ), + array( 0 => 75, 1 => 3 ), + array( 0 => 75, 1 => 3 ), + array( 0 => 75, 1 => 3 ), + array( 0 => 75, 1 => 1 ), + array( 0 => 75, 1 => 1 ), + array( 0 => 75, 1 => 3 ), + array( 0 => 75, 1 => 1 ), + array( 0 => 75, 1 => 2 ), + array( 0 => 75, 1 => 1 ), + array( 0 => 75, 1 => 1 ), + array( 0 => 75, 1 => 3 ), + array( 0 => 90, 1 => 1 ), + array( 0 => 90, 1 => 1 ), + array( 0 => 74, 1 => 1 ), + array( 0 => 74, 1 => 1 ), + array( 0 => 74, 1 => 3 ), + array( 0 => 74, 1 => 1 ), + array( 0 => 74, 1 => 3 ), + array( 0 => 74, 1 => 4 ), + array( 0 => 74, 1 => 3 ), + array( 0 => 74, 1 => 4 ), + array( 0 => 71, 1 => 2 ), + array( 0 => 71, 1 => 2 ), + array( 0 => 95, 1 => 2 ), + array( 0 => 95, 1 => 0 ), + array( 0 => 96, 1 => 2 ), + array( 0 => 96, 1 => 2 ), + array( 0 => 96, 1 => 4 ), + array( 0 => 96, 1 => 2 ), + array( 0 => 96, 1 => 2 ), + array( 0 => 96, 1 => 4 ), + array( 0 => 96, 1 => 3 ), + array( 0 => 96, 1 => 5 ), + array( 0 => 96, 1 => 3 ), + array( 0 => 96, 1 => 3 ), + array( 0 => 96, 1 => 3 ), + array( 0 => 96, 1 => 3 ), + array( 0 => 96, 1 => 3 ), + array( 0 => 96, 1 => 3 ), + array( 0 => 96, 1 => 2 ), + array( 0 => 81, 1 => 1 ), + array( 0 => 81, 1 => 1 ), + array( 0 => 81, 1 => 2 ), + array( 0 => 97, 1 => 1 ), + array( 0 => 97, 1 => 1 ), + array( 0 => 97, 1 => 3 ), + array( 0 => 94, 1 => 2 ), + array( 0 => 98, 1 => 1 ), + array( 0 => 98, 1 => 2 ), + array( 0 => 99, 1 => 3 ), + array( 0 => 99, 1 => 3 ), + array( 0 => 99, 1 => 5 ), + array( 0 => 99, 1 => 6 ), + array( 0 => 99, 1 => 2 ), + array( 0 => 89, 1 => 4 ), + array( 0 => 100, 1 => 4 ), + array( 0 => 100, 1 => 4 ), + array( 0 => 101, 1 => 3 ), + array( 0 => 101, 1 => 1 ), + array( 0 => 101, 1 => 0 ), + array( 0 => 77, 1 => 3 ), + array( 0 => 77, 1 => 2 ), + array( 0 => 102, 1 => 3 ), + array( 0 => 102, 1 => 2 ), + array( 0 => 82, 1 => 2 ), + array( 0 => 82, 1 => 0 ), + array( 0 => 103, 1 => 2 ), + array( 0 => 103, 1 => 3 ), + array( 0 => 103, 1 => 2 ), + array( 0 => 92, 1 => 1 ), + array( 0 => 92, 1 => 2 ), + array( 0 => 92, 1 => 1 ), + array( 0 => 92, 1 => 2 ), + array( 0 => 92, 1 => 3 ), + array( 0 => 86, 1 => 1 ), + array( 0 => 86, 1 => 1 ), + array( 0 => 85, 1 => 1 ), + array( 0 => 87, 1 => 1 ), + array( 0 => 93, 1 => 3 ), + array( 0 => 93, 1 => 3 ), + array( 0 => 104, 1 => 1 ), + array( 0 => 104, 1 => 3 ), + array( 0 => 104, 1 => 0 ), + array( 0 => 105, 1 => 3 ), + array( 0 => 105, 1 => 3 ), + array( 0 => 105, 1 => 1 ), + array( 0 => 91, 1 => 2 ), + array( 0 => 91, 1 => 3 ), + array( 0 => 106, 1 => 2 ), + array( 0 => 106, 1 => 1 ), + array( 0 => 107, 1 => 3 ), + array( 0 => 107, 1 => 3 ), + array( 0 => 107, 1 => 1 ), + array( 0 => 107, 1 => 3 ), + array( 0 => 107, 1 => 3 ), + array( 0 => 107, 1 => 1 ), + array( 0 => 107, 1 => 1 ), + ); - // line 251 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r1() - { - $code = - $this->compiler->compileTag('private_php', - array(array('code' => $this->yystack[ $this->yyidx + 0 ]->minor), array('type' => $this->lex->phpType)), - array()); - if ($this->compiler->has_code && !empty($code)) { - $tmp = ''; - foreach ($this->compiler->prefix_code as $code) { - $tmp .= $code; + public static $yyReduceMap = array( + 0 => 0, + 1 => 1, + 2 => 2, + 3 => 3, + 4 => 4, + 5 => 5, + 6 => 6, + 21 => 6, + 22 => 6, + 23 => 6, + 36 => 6, + 56 => 6, + 57 => 6, + 65 => 6, + 66 => 6, + 77 => 6, + 82 => 6, + 83 => 6, + 88 => 6, + 92 => 6, + 93 => 6, + 97 => 6, + 98 => 6, + 100 => 6, + 105 => 6, + 169 => 6, + 174 => 6, + 7 => 7, + 8 => 8, + 9 => 9, + 11 => 11, + 12 => 12, + 13 => 13, + 14 => 14, + 15 => 15, + 16 => 16, + 17 => 17, + 18 => 18, + 19 => 19, + 20 => 20, + 24 => 24, + 25 => 25, + 26 => 26, + 27 => 27, + 28 => 28, + 29 => 29, + 30 => 30, + 31 => 31, + 33 => 31, + 32 => 32, + 34 => 34, + 35 => 35, + 37 => 37, + 38 => 38, + 39 => 39, + 40 => 40, + 41 => 41, + 42 => 42, + 43 => 43, + 44 => 44, + 45 => 45, + 46 => 46, + 47 => 47, + 48 => 48, + 49 => 49, + 50 => 50, + 59 => 50, + 147 => 50, + 151 => 50, + 155 => 50, + 157 => 50, + 51 => 51, + 148 => 51, + 154 => 51, + 52 => 52, + 53 => 53, + 54 => 53, + 55 => 55, + 132 => 55, + 58 => 58, + 60 => 60, + 61 => 61, + 62 => 61, + 63 => 63, + 64 => 64, + 67 => 67, + 68 => 68, + 69 => 68, + 70 => 70, + 71 => 71, + 72 => 72, + 73 => 73, + 74 => 74, + 75 => 75, + 76 => 76, + 78 => 78, + 80 => 78, + 81 => 78, + 112 => 78, + 79 => 79, + 84 => 84, + 85 => 85, + 86 => 86, + 87 => 87, + 89 => 89, + 90 => 90, + 91 => 90, + 94 => 94, + 95 => 95, + 96 => 96, + 99 => 99, + 101 => 101, + 102 => 102, + 103 => 103, + 104 => 104, + 106 => 106, + 107 => 107, + 108 => 108, + 109 => 109, + 110 => 110, + 111 => 111, + 113 => 113, + 171 => 113, + 114 => 114, + 115 => 115, + 116 => 116, + 117 => 117, + 118 => 118, + 119 => 119, + 127 => 119, + 120 => 120, + 121 => 121, + 122 => 122, + 123 => 122, + 125 => 122, + 126 => 122, + 124 => 124, + 128 => 128, + 129 => 129, + 130 => 130, + 175 => 130, + 131 => 131, + 133 => 133, + 134 => 134, + 135 => 135, + 136 => 136, + 137 => 137, + 138 => 138, + 139 => 139, + 140 => 140, + 141 => 141, + 142 => 142, + 143 => 143, + 144 => 144, + 145 => 145, + 146 => 146, + 149 => 149, + 150 => 150, + 152 => 152, + 153 => 153, + 156 => 156, + 158 => 158, + 159 => 159, + 160 => 160, + 161 => 161, + 162 => 162, + 163 => 163, + 164 => 164, + 165 => 165, + 166 => 166, + 167 => 167, + 168 => 167, + 170 => 170, + 172 => 172, + 173 => 173, + 176 => 176, + 177 => 177, + 178 => 178, + 179 => 179, + 182 => 179, + 180 => 180, + 183 => 180, + 181 => 181, + 184 => 184, + 185 => 185, + ); +// line 233 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r0(){ + $this->root_buffer->prepend_array($this, $this->template_prefix); + $this->root_buffer->append_array($this, $this->template_postfix); + $this->_retvalue = $this->root_buffer->to_smarty_php($this); + } +// line 240 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r1(){ + $text = $this->yystack[ $this->yyidx + 0 ]->minor; + + if ((string)$text == '') { + $this->current_buffer->append_subtree($this, null); + } + + $this->current_buffer->append_subtree($this, new Smarty_Internal_ParseTree_Text($text, $this->strip)); + } +// line 250 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r2(){ + $this->strip = true; + } +// line 254 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r3(){ + $this->strip = false; + } +// line 259 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r4(){ + $this->current_buffer->append_subtree($this, new Smarty_Internal_ParseTree_Text($this->yystack[$this->yyidx + -1]->minor)); + } +// line 264 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r5(){ + $this->_retvalue = $this->yystack[$this->yyidx + -3]->minor.$this->yystack[$this->yyidx + -1]->minor; + } +// line 267 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r6(){ + $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor; + } +// line 271 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r7(){ + $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor.$this->yystack[$this->yyidx + 0]->minor; + + } +// line 276 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r8(){ + $this->_retvalue = ''; + } +// line 280 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r9(){ + if ($this->compiler->has_code) { + $this->current_buffer->append_subtree($this, $this->mergePrefixCode($this->yystack[$this->yyidx + 0]->minor)); + } + $this->compiler->has_variable_string = false; + $this->block_nesting_level = count($this->compiler->_tag_stack); + } +// line 292 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r11(){ + $var = trim(substr($this->yystack[$this->yyidx + 0]->minor, $this->compiler->getLdelLength(), -$this->compiler->getRdelLength()), ' $'); + if (preg_match('/^(.*)(\s+nocache)$/', $var, $match)) { + $this->_retvalue = $this->compiler->compileTag('private_print_expression',array('nocache'),array('value'=>$this->compiler->compileVariable('\''.$match[1].'\''))); + } else { + $this->_retvalue = $this->compiler->compileTag('private_print_expression',array(),array('value'=>$this->compiler->compileVariable('\''.$var.'\''))); + } + } +// line 302 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r12(){ + $tag = trim(substr($this->yystack[$this->yyidx + 0]->minor, $this->compiler->getLdelLength(), -$this->compiler->getRdelLength())); + if ($tag == 'strip') { + $this->strip = true; + $this->_retvalue = null; + } else { + if (defined($tag)) { + if ($this->security) { + $this->security->isTrustedConstant($tag, $this->compiler); + } + $this->_retvalue = $this->compiler->compileTag('private_print_expression',array(),array('value'=>$tag)); + } else { + if (preg_match('/^(.*)(\s+nocache)$/', $tag, $match)) { + $this->_retvalue = $this->compiler->compileTag($match[1],array('\'nocache\'')); + } else { + $this->_retvalue = $this->compiler->compileTag($tag,array()); } - $this->compiler->prefix_code = array(); - $this->current_buffer->append_subtree($this, - new Smarty_Internal_ParseTree_Tag($this, $this->compiler->processNocacheCode($tmp . $code, true))); } } - - // line 255 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r2() - { - $text = $this->yystack[ $this->yyidx + 0 ]->minor; - - if ((string)$text == '') { - $this->current_buffer->append_subtree($this, null); - } - - $this->current_buffer->append_subtree($this, new Smarty_Internal_ParseTree_Text($text, $this->strip)); } - - // line 259 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r3() - { - $this->strip = true; +// line 323 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r13(){ + $j = strrpos($this->yystack[$this->yyidx + 0]->minor,'.'); + if ($this->yystack[$this->yyidx + 0]->minor[$j+1] == 'c') { + // {$smarty.block.child} + $this->_retvalue = $this->compiler->compileTag('child',array(),array($this->yystack[$this->yyidx + 0]->minor)); + } else { + // {$smarty.block.parent} + $this->_retvalue = $this->compiler->compileTag('parent',array(),array($this->yystack[$this->yyidx + 0]->minor)); + } + } +// line 334 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r14(){ + $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor; + } +// line 338 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r15(){ + $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor; + } +// line 342 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r16(){ + $this->_retvalue = $this->compiler->compileTag('private_print_expression',$this->yystack[$this->yyidx + 0]->minor[1],array('value'=>$this->yystack[$this->yyidx + 0]->minor[0])); + } +// line 351 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r17(){ + $this->_retvalue = $this->compiler->compileTag('assign',array_merge(array(array('value'=>$this->yystack[$this->yyidx + 0]->minor[0]),array('var'=>'\''.substr($this->yystack[$this->yyidx + -1]->minor,1).'\'')),$this->yystack[$this->yyidx + 0]->minor[1])); + } +// line 355 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r18(){ + $this->_retvalue = $this->compiler->compileTag('assign',array_merge(array(array('value'=>$this->yystack[$this->yyidx + 0]->minor[0]),array('var'=>$this->yystack[$this->yyidx + -1]->minor['var'])),$this->yystack[$this->yyidx + 0]->minor[1]),array('smarty_internal_index'=>$this->yystack[$this->yyidx + -1]->minor['smarty_internal_index'])); + } +// line 359 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r19(){ + $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor; + } +// line 363 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r20(){ + $this->_retvalue = array($this->yystack[$this->yyidx + -1]->minor,$this->yystack[$this->yyidx + 0]->minor); + } +// line 378 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r24(){ + if (defined($this->yystack[$this->yyidx + -1]->minor)) { + if ($this->security) { + $this->security->isTrustedConstant($this->yystack[$this->yyidx + -1]->minor, $this->compiler); + } + $this->_retvalue = $this->compiler->compileTag('private_print_expression',$this->yystack[$this->yyidx + 0]->minor,array('value'=>$this->yystack[$this->yyidx + -1]->minor)); + } else { + $this->_retvalue = $this->compiler->compileTag($this->yystack[$this->yyidx + -1]->minor,$this->yystack[$this->yyidx + 0]->minor); + } } - - // line 264 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r4() - { +// line 388 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r25(){ + if (defined($this->yystack[$this->yyidx + 0]->minor)) { + if ($this->security) { + $this->security->isTrustedConstant($this->yystack[$this->yyidx + 0]->minor, $this->compiler); + } + $this->_retvalue = $this->compiler->compileTag('private_print_expression',array(),array('value'=>$this->yystack[$this->yyidx + 0]->minor)); + } else { + $this->_retvalue = $this->compiler->compileTag($this->yystack[$this->yyidx + 0]->minor,array()); + } + } +// line 401 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r26(){ + if (defined($this->yystack[$this->yyidx + -2]->minor)) { + if ($this->security) { + $this->security->isTrustedConstant($this->yystack[$this->yyidx + -2]->minor, $this->compiler); + } + $this->_retvalue = $this->compiler->compileTag('private_print_expression',$this->yystack[$this->yyidx + 0]->minor,array('value'=>$this->yystack[$this->yyidx + -2]->minor, 'modifierlist'=>$this->yystack[$this->yyidx + -1]->minor)); + } else { + $this->_retvalue = $this->compiler->compileTag($this->yystack[$this->yyidx + -2]->minor,$this->yystack[$this->yyidx + 0]->minor, array('modifierlist'=>$this->yystack[$this->yyidx + -1]->minor)); + } + } +// line 413 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r27(){ + $this->_retvalue = $this->compiler->compileTag($this->yystack[$this->yyidx + -3]->minor,$this->yystack[$this->yyidx + 0]->minor,array('object_method'=>$this->yystack[$this->yyidx + -1]->minor)); + } +// line 418 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r28(){ + $this->_retvalue = $this->compiler->compileTag($this->yystack[$this->yyidx + -4]->minor,$this->yystack[$this->yyidx + 0]->minor,array('modifierlist'=>$this->yystack[$this->yyidx + -1]->minor, 'object_method'=>$this->yystack[$this->yyidx + -2]->minor)); + } +// line 423 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r29(){ + $this->_retvalue = $this->compiler->compileTag('make_nocache',array(array('var'=>'\''.substr($this->yystack[$this->yyidx + 0]->minor,1).'\''))); + } +// line 428 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r30(){ + $tag = trim(substr($this->yystack[$this->yyidx + -1]->minor,$this->compiler->getLdelLength())); + $this->_retvalue = $this->compiler->compileTag(($tag === 'else if')? 'elseif' : $tag,array(),array('if condition'=>$this->yystack[$this->yyidx + 0]->minor)); + } +// line 433 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r31(){ + $tag = trim(substr($this->yystack[$this->yyidx + -2]->minor,$this->compiler->getLdelLength())); + $this->_retvalue = $this->compiler->compileTag(($tag === 'else if')? 'elseif' : $tag,$this->yystack[$this->yyidx + 0]->minor,array('if condition'=>$this->yystack[$this->yyidx + -1]->minor)); + } +// line 438 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r32(){ + $tag = trim(substr($this->yystack[$this->yyidx + -1]->minor,$this->compiler->getLdelLength())); + $this->_retvalue = $this->compiler->compileTag(($tag === 'else if')? 'elseif' : $tag,array(),array('if condition'=>$this->yystack[$this->yyidx + 0]->minor)); + } +// line 449 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r34(){ + $this->_retvalue = $this->compiler->compileTag('for',array_merge($this->yystack[$this->yyidx + 0]->minor,array(array('start'=>$this->yystack[$this->yyidx + -6]->minor),array('ifexp'=>$this->yystack[$this->yyidx + -4]->minor),array('var'=>$this->yystack[$this->yyidx + -2]->minor),array('step'=>$this->yystack[$this->yyidx + -1]->minor))),1); + } +// line 453 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r35(){ + $this->_retvalue = '='.$this->yystack[$this->yyidx + 0]->minor; + } +// line 461 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r37(){ + $this->_retvalue = $this->compiler->compileTag('for',array_merge($this->yystack[$this->yyidx + 0]->minor,array(array('start'=>$this->yystack[$this->yyidx + -3]->minor),array('to'=>$this->yystack[$this->yyidx + -1]->minor))),0); + } +// line 465 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r38(){ + $this->_retvalue = $this->compiler->compileTag('for',array_merge($this->yystack[$this->yyidx + 0]->minor,array(array('start'=>$this->yystack[$this->yyidx + -5]->minor),array('to'=>$this->yystack[$this->yyidx + -3]->minor),array('step'=>$this->yystack[$this->yyidx + -1]->minor))),0); + } +// line 470 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r39(){ + $this->_retvalue = $this->compiler->compileTag('foreach',array_merge($this->yystack[$this->yyidx + 0]->minor,array(array('from'=>$this->yystack[$this->yyidx + -3]->minor),array('item'=>$this->yystack[$this->yyidx + -1]->minor)))); + } +// line 474 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r40(){ + $this->_retvalue = $this->compiler->compileTag('foreach',array_merge($this->yystack[$this->yyidx + 0]->minor,array(array('from'=>$this->yystack[$this->yyidx + -5]->minor),array('item'=>$this->yystack[$this->yyidx + -1]->minor),array('key'=>$this->yystack[$this->yyidx + -3]->minor)))); + } +// line 477 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r41(){ + $this->_retvalue = $this->compiler->compileTag('foreach',$this->yystack[$this->yyidx + 0]->minor); + } +// line 482 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r42(){ + $this->_retvalue = $this->compiler->compileTag('setfilter',array(),array('modifier_list'=>array(array_merge(array($this->yystack[$this->yyidx + -1]->minor),$this->yystack[$this->yyidx + 0]->minor)))); + } +// line 486 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r43(){ + $this->_retvalue = $this->compiler->compileTag('setfilter',array(),array('modifier_list'=>array_merge(array(array_merge(array($this->yystack[$this->yyidx + -2]->minor),$this->yystack[$this->yyidx + -1]->minor)),$this->yystack[$this->yyidx + 0]->minor))); + } +// line 492 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r44(){ + $tag = trim(substr($this->yystack[$this->yyidx + 0]->minor, $this->compiler->getLdelLength(), -$this->compiler->getRdelLength()), ' /'); + if ($tag === 'strip') { $this->strip = false; + $this->_retvalue = null; + } else { + $this->_retvalue = $this->compiler->compileTag($tag.'close',array()); + } + } +// line 501 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r45(){ + $this->_retvalue = $this->compiler->compileTag($this->yystack[$this->yyidx + 0]->minor.'close',array()); + } +// line 505 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r46(){ + $this->_retvalue = $this->compiler->compileTag($this->yystack[$this->yyidx + -1]->minor.'close',array(),array('modifier_list'=>$this->yystack[$this->yyidx + 0]->minor)); + } +// line 510 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r47(){ + $this->_retvalue = $this->compiler->compileTag($this->yystack[$this->yyidx + -2]->minor.'close',array(),array('object_method'=>$this->yystack[$this->yyidx + 0]->minor)); + } +// line 514 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r48(){ + $this->_retvalue = $this->compiler->compileTag($this->yystack[$this->yyidx + -3]->minor.'close',array(),array('object_method'=>$this->yystack[$this->yyidx + -1]->minor, 'modifier_list'=>$this->yystack[$this->yyidx + 0]->minor)); + } +// line 522 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r49(){ + $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor; + $this->_retvalue[] = $this->yystack[$this->yyidx + 0]->minor; + } +// line 528 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r50(){ + $this->_retvalue = array($this->yystack[$this->yyidx + 0]->minor); + } +// line 533 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r51(){ + $this->_retvalue = array(); + } +// line 538 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r52(){ + if (defined($this->yystack[$this->yyidx + 0]->minor)) { + if ($this->security) { + $this->security->isTrustedConstant($this->yystack[$this->yyidx + 0]->minor, $this->compiler); + } + $this->_retvalue = array($this->yystack[$this->yyidx + -2]->minor=>$this->yystack[$this->yyidx + 0]->minor); + } else { + $this->_retvalue = array($this->yystack[$this->yyidx + -2]->minor=>'\''.$this->yystack[$this->yyidx + 0]->minor.'\''); } - - // line 269 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r5() - { - $this->current_buffer->append_subtree($this, - new Smarty_Internal_ParseTree_Text($this->yystack[ $this->yyidx + -1 ]->minor)); } - - // line 272 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r6() - { - $this->_retvalue = $this->yystack[ $this->yyidx + -3 ]->minor . $this->yystack[ $this->yyidx + -1 ]->minor; +// line 549 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r53(){ + $this->_retvalue = array(trim($this->yystack[$this->yyidx + -1]->minor," =\n\r\t")=>$this->yystack[$this->yyidx + 0]->minor); } - - // line 276 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r7() - { - $this->_retvalue = $this->yystack[ $this->yyidx + 0 ]->minor; +// line 557 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r55(){ + $this->_retvalue = '\''.$this->yystack[$this->yyidx + 0]->minor.'\''; } - - // line 281 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r8() - { - $this->_retvalue = $this->yystack[ $this->yyidx + -1 ]->minor . $this->yystack[ $this->yyidx + 0 ]->minor; +// line 569 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r58(){ + $this->_retvalue = array($this->yystack[$this->yyidx + -2]->minor=>$this->yystack[$this->yyidx + 0]->minor); } - - // line 285 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r9() - { - $this->_retvalue = ''; +// line 582 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r60(){ + $this->yystack[$this->yyidx + -2]->minor[]=$this->yystack[$this->yyidx + 0]->minor; + $this->_retvalue = $this->yystack[$this->yyidx + -2]->minor; } - - // line 297 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r10() - { - if ($this->compiler->has_code) { - $this->current_buffer->append_subtree($this, - $this->mergePrefixCode($this->yystack[ $this->yyidx + 0 ]->minor)); - } - $this->compiler->has_variable_string = false; - $this->block_nesting_level = count($this->compiler->_tag_stack); +// line 587 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r61(){ + $this->_retvalue = array('var' => '\''.substr($this->yystack[$this->yyidx + -2]->minor,1).'\'', 'value'=>$this->yystack[$this->yyidx + 0]->minor); } - - // line 307 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r12() - { - $var = - trim(substr($this->yystack[ $this->yyidx + 0 ]->minor, $this->compiler->getLdelLength(), - -$this->compiler->getRdelLength()), ' $'); - if (preg_match('/^(.*)(\s+nocache)$/', $var, $match)) { - $this->_retvalue = - $this->compiler->compileTag('private_print_expression', array('nocache'), - array('value' => $this->compiler->compileVariable('\'' . $match[ 1 ] . '\''))); - } else { - $this->_retvalue = - $this->compiler->compileTag('private_print_expression', array(), - array('value' => $this->compiler->compileVariable('\'' . $var . '\''))); - } +// line 594 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r63(){ + $this->_retvalue = array('var' => $this->yystack[$this->yyidx + -2]->minor, 'value'=>$this->yystack[$this->yyidx + 0]->minor); } - - // line 328 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r13() - { - $tag = - trim(substr($this->yystack[ $this->yyidx + 0 ]->minor, $this->compiler->getLdelLength(), - -$this->compiler->getRdelLength())); - if ($tag == 'strip') { - $this->strip = true; - $this->_retvalue = null; - } else { - if (defined($tag)) { - if ($this->security) { - $this->security->isTrustedConstant($tag, $this->compiler); - } - $this->_retvalue = - $this->compiler->compileTag('private_print_expression', array(), array('value' => $tag)); - } else { - if (preg_match('/^(.*)(\s+nocache)$/', $tag, $match)) { - $this->_retvalue = $this->compiler->compileTag($match[ 1 ], array('\'nocache\'')); - } else { - $this->_retvalue = $this->compiler->compileTag($tag, array()); - } - } - } +// line 598 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r64(){ + $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor; } - - // line 339 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r14() - { - $j = strrpos($this->yystack[ $this->yyidx + 0 ]->minor, '.'); - if ($this->yystack[ $this->yyidx + 0 ]->minor[ $j + 1 ] == 'c') { - // {$smarty.block.child} - $this->_retvalue = - $this->compiler->compileTag('child', array(), array($this->yystack[ $this->yyidx + 0 ]->minor)); - } else { - // {$smarty.block.parent} - $this->_retvalue = - $this->compiler->compileTag('parent', array(), array($this->yystack[ $this->yyidx + 0 ]->minor)); - } +// line 618 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r67(){ + $this->_retvalue = '$_smarty_tpl->getStreamVariable(\''.substr($this->yystack[$this->yyidx + -2]->minor,1).'://' . $this->yystack[$this->yyidx + 0]->minor . '\')'; } - - // line 343 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r15() - { - $this->_retvalue = $this->yystack[ $this->yyidx + -1 ]->minor; +// line 623 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r68(){ + $this->_retvalue = $this->yystack[$this->yyidx + -2]->minor . trim($this->yystack[$this->yyidx + -1]->minor) . $this->yystack[$this->yyidx + 0]->minor; } - - // line 347 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r16() - { - $this->_retvalue = $this->yystack[ $this->yyidx + -1 ]->minor; +// line 633 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r70(){ + $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor['pre']. $this->yystack[$this->yyidx + -2]->minor.$this->yystack[$this->yyidx + -1]->minor['op'].$this->yystack[$this->yyidx + 0]->minor .')'; } - - // line 356 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r17() - { - $this->_retvalue = - $this->compiler->compileTag('private_print_expression', $this->yystack[ $this->yyidx + 0 ]->minor[ 1 ], - array('value' => $this->yystack[ $this->yyidx + 0 ]->minor[ 0 ])); +// line 637 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r71(){ + $this->_retvalue = $this->yystack[$this->yyidx + -2]->minor.$this->yystack[$this->yyidx + -1]->minor.$this->yystack[$this->yyidx + 0]->minor; } - - // line 360 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r18() - { - $this->_retvalue = - $this->compiler->compileTag('assign', array_merge(array( - array('value' => $this->yystack[ $this->yyidx + 0 ]->minor[ 0 ]), - array('var' => '\'' . substr($this->yystack[ $this->yyidx + -1 ]->minor, 1) . '\'') - ), $this->yystack[ $this->yyidx + 0 ]->minor[ 1 ])); +// line 641 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r72(){ + $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor . $this->yystack[$this->yyidx + -1]->minor . ')'; } - - // line 364 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r19() - { - $this->_retvalue = - $this->compiler->compileTag('assign', array_merge(array( - array('value' => $this->yystack[ $this->yyidx + 0 ]->minor[ 0 ]), - array('var' => $this->yystack[ $this->yyidx + -1 ]->minor[ 'var' ]) - ), $this->yystack[ $this->yyidx + 0 ]->minor[ 1 ]), array( - 'smarty_internal_index' => $this->yystack[ $this->yyidx + - -1 ]->minor[ 'smarty_internal_index' ] - )); - } - - // line 368 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r20() - { - $this->_retvalue = $this->yystack[ $this->yyidx + 0 ]->minor; +// line 645 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r73(){ + $this->_retvalue = 'in_array('.$this->yystack[$this->yyidx + -2]->minor.','.$this->yystack[$this->yyidx + 0]->minor.')'; } - - // line 383 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r21() - { - $this->_retvalue = array($this->yystack[ $this->yyidx + -1 ]->minor, $this->yystack[ $this->yyidx + 0 ]->minor); - } - - // line 393 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r25() - { - if (defined($this->yystack[ $this->yyidx + -1 ]->minor)) { - if ($this->security) { - $this->security->isTrustedConstant($this->yystack[ $this->yyidx + -1 ]->minor, $this->compiler); - } - $this->_retvalue = - $this->compiler->compileTag('private_print_expression', $this->yystack[ $this->yyidx + 0 ]->minor, - array('value' => $this->yystack[ $this->yyidx + -1 ]->minor)); - } else { - $this->_retvalue = - $this->compiler->compileTag($this->yystack[ $this->yyidx + -1 ]->minor, - $this->yystack[ $this->yyidx + 0 ]->minor); - } - } - - // line 406 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r26() - { - if (defined($this->yystack[ $this->yyidx + 0 ]->minor)) { - if ($this->security) { - $this->security->isTrustedConstant($this->yystack[ $this->yyidx + 0 ]->minor, $this->compiler); - } - $this->_retvalue = - $this->compiler->compileTag('private_print_expression', array(), - array('value' => $this->yystack[ $this->yyidx + 0 ]->minor)); - } else { - $this->_retvalue = $this->compiler->compileTag($this->yystack[ $this->yyidx + 0 ]->minor, array()); - } - } - - // line 418 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r27() - { - if (defined($this->yystack[ $this->yyidx + -2 ]->minor)) { - if ($this->security) { - $this->security->isTrustedConstant($this->yystack[ $this->yyidx + -2 ]->minor, $this->compiler); - } - $this->_retvalue = - $this->compiler->compileTag('private_print_expression', $this->yystack[ $this->yyidx + 0 ]->minor, - array( - 'value' => $this->yystack[ $this->yyidx + -2 ]->minor, - 'modifierlist' => $this->yystack[ $this->yyidx + -1 ]->minor - )); - } else { - $this->_retvalue = - $this->compiler->compileTag($this->yystack[ $this->yyidx + -2 ]->minor, - $this->yystack[ $this->yyidx + 0 ]->minor, - array('modifierlist' => $this->yystack[ $this->yyidx + -1 ]->minor)); - } - } - - // line 423 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r28() - { - $this->_retvalue = - $this->compiler->compileTag($this->yystack[ $this->yyidx + -3 ]->minor, - $this->yystack[ $this->yyidx + 0 ]->minor, - array('object_method' => $this->yystack[ $this->yyidx + -1 ]->minor)); - } - - // line 428 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r29() - { - $this->_retvalue = - $this->compiler->compileTag($this->yystack[ $this->yyidx + -4 ]->minor, - $this->yystack[ $this->yyidx + 0 ]->minor, array( - 'modifierlist' => $this->yystack[ $this->yyidx + -1 ]->minor, - 'object_method' => $this->yystack[ $this->yyidx + -2 ]->minor - )); - } - - // line 433 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r30() - { - $this->_retvalue = - $this->compiler->compileTag('make_nocache', - array(array('var' => '\'' . substr($this->yystack[ $this->yyidx + 0 ]->minor, 1) . '\''))); - } - - // line 438 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r31() - { - $tag = trim(substr($this->yystack[ $this->yyidx + -1 ]->minor, $this->compiler->getLdelLength())); - $this->_retvalue = - $this->compiler->compileTag(($tag === 'else if') ? 'elseif' : $tag, array(), - array('if condition' => $this->yystack[ $this->yyidx + 0 ]->minor)); - } - - // line 443 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r32() - { - $tag = trim(substr($this->yystack[ $this->yyidx + -2 ]->minor, $this->compiler->getLdelLength())); - $this->_retvalue = - $this->compiler->compileTag(($tag === 'else if') ? 'elseif' : $tag, - $this->yystack[ $this->yyidx + 0 ]->minor, - array('if condition' => $this->yystack[ $this->yyidx + -1 ]->minor)); - } - - // line 454 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r33() - { - $tag = trim(substr($this->yystack[ $this->yyidx + -1 ]->minor, $this->compiler->getLdelLength())); - $this->_retvalue = - $this->compiler->compileTag(($tag === 'else if') ? 'elseif' : $tag, array(), - array('if condition' => $this->yystack[ $this->yyidx + 0 ]->minor)); - } - - // line 458 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r35() - { - $this->_retvalue = - $this->compiler->compileTag('for', array_merge($this->yystack[ $this->yyidx + 0 ]->minor, array( - array('start' => $this->yystack[ $this->yyidx + -6 ]->minor), - array('ifexp' => $this->yystack[ $this->yyidx + -4 ]->minor), - array('var' => $this->yystack[ $this->yyidx + -2 ]->minor), - array('step' => $this->yystack[ $this->yyidx + -1 ]->minor) - )), 1); - } - - // line 466 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r36() - { - $this->_retvalue = '=' . $this->yystack[ $this->yyidx + 0 ]->minor; - } - - // line 470 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r38() - { - $this->_retvalue = - $this->compiler->compileTag('for', array_merge($this->yystack[ $this->yyidx + 0 ]->minor, array( - array('start' => $this->yystack[ $this->yyidx + -3 ]->minor), - array('to' => $this->yystack[ $this->yyidx + -1 ]->minor) - )), 0); - } - - // line 475 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r39() - { - $this->_retvalue = - $this->compiler->compileTag('for', array_merge($this->yystack[ $this->yyidx + 0 ]->minor, array( - array('start' => $this->yystack[ $this->yyidx + -5 ]->minor), - array('to' => $this->yystack[ $this->yyidx + -3 ]->minor), - array('step' => $this->yystack[ $this->yyidx + -1 ]->minor) - )), 0); - } - - // line 479 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r40() - { - $this->_retvalue = - $this->compiler->compileTag('foreach', array_merge($this->yystack[ $this->yyidx + 0 ]->minor, array( - array('from' => $this->yystack[ $this->yyidx + -3 ]->minor), - array('item' => $this->yystack[ $this->yyidx + -1 ]->minor) - ))); - } - - // line 482 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r41() - { - $this->_retvalue = - $this->compiler->compileTag('foreach', array_merge($this->yystack[ $this->yyidx + 0 ]->minor, array( - array('from' => $this->yystack[ $this->yyidx + -5 ]->minor), - array('item' => $this->yystack[ $this->yyidx + -1 ]->minor), - array('key' => $this->yystack[ $this->yyidx + -3 ]->minor) - ))); - } - - // line 487 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r42() - { - $this->_retvalue = $this->compiler->compileTag('foreach', $this->yystack[ $this->yyidx + 0 ]->minor); - } - - // line 491 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r43() - { - $this->_retvalue = - $this->compiler->compileTag('setfilter', array(), array( - 'modifier_list' => array( - array_merge(array($this->yystack[ $this->yyidx + -1 ]->minor), - $this->yystack[ $this->yyidx + 0 ]->minor) - ) - )); - } - - // line 497 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r44() - { - $this->_retvalue = - $this->compiler->compileTag('setfilter', array(), array( - 'modifier_list' => array_merge(array( - array_merge(array( - $this->yystack[ $this->yyidx + - -2 ]->minor - ), $this->yystack[ $this->yyidx + -1 ]->minor) - ), $this->yystack[ $this->yyidx + 0 ]->minor) - )); - } - - // line 506 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r45() - { - $tag = - trim(substr($this->yystack[ $this->yyidx + 0 ]->minor, $this->compiler->getLdelLength(), - -$this->compiler->getRdelLength()), ' /'); - if ($tag === 'strip') { - $this->strip = false; - $this->_retvalue = null; - } else { - $this->_retvalue = $this->compiler->compileTag($tag . 'close', array()); - } - } - - // line 510 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r46() - { - $this->_retvalue = $this->compiler->compileTag($this->yystack[ $this->yyidx + 0 ]->minor . 'close', array()); - } - - // line 515 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r47() - { - $this->_retvalue = - $this->compiler->compileTag($this->yystack[ $this->yyidx + -1 ]->minor . 'close', array(), - array('modifier_list' => $this->yystack[ $this->yyidx + 0 ]->minor)); - } - - // line 519 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r48() - { - $this->_retvalue = - $this->compiler->compileTag($this->yystack[ $this->yyidx + -2 ]->minor . 'close', array(), - array('object_method' => $this->yystack[ $this->yyidx + 0 ]->minor)); - } - - // line 527 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r49() - { - $this->_retvalue = - $this->compiler->compileTag($this->yystack[ $this->yyidx + -3 ]->minor . 'close', array(), array( - 'object_method' => $this->yystack[ $this->yyidx + -1 ]->minor, - 'modifier_list' => $this->yystack[ $this->yyidx + 0 ]->minor - )); - } - - // line 533 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r50() - { - $this->_retvalue = $this->yystack[ $this->yyidx + -1 ]->minor; - $this->_retvalue[] = $this->yystack[ $this->yyidx + 0 ]->minor; - } - - // line 538 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r51() - { - $this->_retvalue = array($this->yystack[ $this->yyidx + 0 ]->minor); - } - - // line 543 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r52() - { - $this->_retvalue = array(); - } - - // line 554 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r53() - { - if (defined($this->yystack[ $this->yyidx + 0 ]->minor)) { - if ($this->security) { - $this->security->isTrustedConstant($this->yystack[ $this->yyidx + 0 ]->minor, $this->compiler); - } - $this->_retvalue = - array($this->yystack[ $this->yyidx + -2 ]->minor => $this->yystack[ $this->yyidx + 0 ]->minor); - } else { - $this->_retvalue = - array( - $this->yystack[ $this->yyidx + -2 ]->minor => '\'' . - $this->yystack[ $this->yyidx + 0 ]->minor . - '\'' - ); - } - } - - // line 562 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r54() - { - $this->_retvalue = - array( - trim($this->yystack[ $this->yyidx + -1 ]->minor, " =\n\r\t") => $this->yystack[ $this->yyidx + - 0 ]->minor - ); - } - - // line 574 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r56() - { - $this->_retvalue = '\'' . $this->yystack[ $this->yyidx + 0 ]->minor . '\''; - } - - // line 587 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r59() - { - $this->_retvalue = - array($this->yystack[ $this->yyidx + -2 ]->minor => $this->yystack[ $this->yyidx + 0 ]->minor); - } - - // line 592 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r61() - { - $this->yystack[ $this->yyidx + -2 ]->minor[] = $this->yystack[ $this->yyidx + 0 ]->minor; - $this->_retvalue = $this->yystack[ $this->yyidx + -2 ]->minor; - } - - // line 599 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r62() - { - $this->_retvalue = - array( - 'var' => '\'' . substr($this->yystack[ $this->yyidx + -2 ]->minor, 1) . '\'', - 'value' => $this->yystack[ $this->yyidx + 0 ]->minor - ); - } - - // line 603 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r64() - { - $this->_retvalue = - array( - 'var' => $this->yystack[ $this->yyidx + -2 ]->minor, - 'value' => $this->yystack[ $this->yyidx + 0 ]->minor - ); - } - - // line 623 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r65() - { - $this->_retvalue = $this->yystack[ $this->yyidx + -1 ]->minor; - } - - // line 628 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r68() - { - $this->_retvalue = - '$_smarty_tpl->getStreamVariable(\'' . - substr($this->yystack[ $this->yyidx + -2 ]->minor, 1) . - '://' . - $this->yystack[ $this->yyidx + 0 ]->minor . - '\')'; - } - - // line 638 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r69() - { - $this->_retvalue = - $this->yystack[ $this->yyidx + -2 ]->minor . - trim($this->yystack[ $this->yyidx + -1 ]->minor) . - $this->yystack[ $this->yyidx + 0 ]->minor; - } - - // line 642 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r71() - { - $this->_retvalue = - $this->yystack[ $this->yyidx + -1 ]->minor[ 'pre' ] . - $this->yystack[ $this->yyidx + -2 ]->minor . - $this->yystack[ $this->yyidx + -1 ]->minor[ 'op' ] . - $this->yystack[ $this->yyidx + 0 ]->minor . - ')'; - } - - // line 646 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r72() - { - $this->_retvalue = - $this->yystack[ $this->yyidx + -2 ]->minor . - $this->yystack[ $this->yyidx + -1 ]->minor . - $this->yystack[ $this->yyidx + 0 ]->minor; - } - - // line 650 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r73() - { - $this->_retvalue = $this->yystack[ $this->yyidx + 0 ]->minor . $this->yystack[ $this->yyidx + -1 ]->minor . ')'; - } - - // line 654 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r74() - { - $this->_retvalue = - 'in_array(' . - $this->yystack[ $this->yyidx + -2 ]->minor . - ',' . - $this->yystack[ $this->yyidx + 0 ]->minor . - ')'; - } - - // line 662 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r75() - { - $this->_retvalue = - 'in_array(' . - $this->yystack[ $this->yyidx + -2 ]->minor . - ',(array)' . - $this->yystack[ $this->yyidx + 0 ]->minor . - ')'; - } - - // line 666 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r76() - { - $this->_retvalue = - $this->yystack[ $this->yyidx + -5 ]->minor . - ' ? ' . - $this->compiler->compileVariable('\'' . substr($this->yystack[ $this->yyidx + -2 ]->minor, 1) . '\'') . - ' : ' . - $this->yystack[ $this->yyidx + 0 ]->minor; - } - - // line 676 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r77() - { - $this->_retvalue = - $this->yystack[ $this->yyidx + -5 ]->minor . - ' ? ' . - $this->yystack[ $this->yyidx + -2 ]->minor . - ' : ' . - $this->yystack[ $this->yyidx + 0 ]->minor; - } - - // line 681 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r79() - { - $this->_retvalue = $this->yystack[ $this->yyidx + -1 ]->minor . $this->yystack[ $this->yyidx + 0 ]->minor; - } - - // line 702 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r80() - { - $this->_retvalue = '!' . $this->yystack[ $this->yyidx + 0 ]->minor; - } - - // line 706 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r85() - { - $this->_retvalue = $this->yystack[ $this->yyidx + -2 ]->minor . '.' . $this->yystack[ $this->yyidx + 0 ]->minor; - } - - // line 710 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r86() - { - $this->_retvalue = $this->yystack[ $this->yyidx + -1 ]->minor . '.'; - } - - // line 715 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r87() - { - $this->_retvalue = '.' . $this->yystack[ $this->yyidx + 0 ]->minor; - } - - // line 732 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r88() - { - if (defined($this->yystack[ $this->yyidx + 0 ]->minor)) { - if ($this->security) { - $this->security->isTrustedConstant($this->yystack[ $this->yyidx + 0 ]->minor, $this->compiler); - } - $this->_retvalue = $this->yystack[ $this->yyidx + 0 ]->minor; - } else { - $this->_retvalue = '\'' . $this->yystack[ $this->yyidx + 0 ]->minor . '\''; - } - } - - // line 736 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r90() - { - $this->_retvalue = '(' . $this->yystack[ $this->yyidx + -1 ]->minor . ')'; - } - - // line 754 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r91() - { - $this->_retvalue = - $this->yystack[ $this->yyidx + -2 ]->minor . - $this->yystack[ $this->yyidx + -1 ]->minor . - $this->yystack[ $this->yyidx + 0 ]->minor; - } - - // line 765 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r95() - { - $prefixVar = $this->compiler->getNewPrefixVariable(); - if ($this->yystack[ $this->yyidx + -2 ]->minor[ 'var' ] === '\'smarty\'') { - $this->compiler->appendPrefixCode("compiler->compileTag('private_special_variable', array(), - $this->yystack[ $this->yyidx + - -2 ]->minor[ 'smarty_internal_index' ]) . - ';?>'); - } else { - $this->compiler->appendPrefixCode("compiler->compileVariable($this->yystack[ $this->yyidx + - -2 ]->minor[ 'var' ]) . - $this->yystack[ $this->yyidx + -2 ]->minor[ 'smarty_internal_index' ] . - ';?>'); - } - $this->_retvalue = - $prefixVar . - '::' . - $this->yystack[ $this->yyidx + 0 ]->minor[ 0 ] . - $this->yystack[ $this->yyidx + 0 ]->minor[ 1 ]; - } - - // line 772 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r96() - { - $prefixVar = $this->compiler->getNewPrefixVariable(); - $tmp = $this->compiler->appendCode('', $this->yystack[ $this->yyidx + 0 ]->minor); - $this->compiler->appendPrefixCode($this->compiler->appendCode($tmp, "")); - $this->_retvalue = $prefixVar; - } - - // line 785 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r97() - { - $this->_retvalue = - $this->compiler->compileTag('private_modifier', array(), array( - 'value' => $this->yystack[ $this->yyidx + -1 ]->minor, - 'modifierlist' => $this->yystack[ $this->yyidx + 0 ]->minor - )); +// line 649 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r74(){ + $this->_retvalue = 'in_array('.$this->yystack[$this->yyidx + -2]->minor.',(array)'.$this->yystack[$this->yyidx + 0]->minor.')'; } - - // line 804 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r100() - { - if (!in_array(strtolower($this->yystack[ $this->yyidx + -2 ]->minor), array('self', 'parent')) && - (!$this->security || - $this->security->isTrustedStaticClassAccess($this->yystack[ $this->yyidx + -2 ]->minor, - $this->yystack[ $this->yyidx + 0 ]->minor, $this->compiler))) { - if (isset($this->smarty->registered_classes[ $this->yystack[ $this->yyidx + -2 ]->minor ])) { - $this->_retvalue = - $this->smarty->registered_classes[ $this->yystack[ $this->yyidx + -2 ]->minor ] . - '::' . - $this->yystack[ $this->yyidx + 0 ]->minor[ 0 ] . - $this->yystack[ $this->yyidx + 0 ]->minor[ 1 ]; - } else { - $this->_retvalue = - $this->yystack[ $this->yyidx + -2 ]->minor . - '::' . - $this->yystack[ $this->yyidx + 0 ]->minor[ 0 ] . - $this->yystack[ $this->yyidx + 0 ]->minor[ 1 ]; - } - } else { - $this->compiler->trigger_template_error('static class \'' . - $this->yystack[ $this->yyidx + -2 ]->minor . - '\' is undefined or not allowed by security setting'); - } - } - - // line 815 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r102() - { - $this->_retvalue = $this->yystack[ $this->yyidx + 0 ]->minor; - } - - // line 818 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r103() - { - $this->_retvalue = - $this->compiler->compileVariable('\'' . substr($this->yystack[ $this->yyidx + 0 ]->minor, 1) . '\''); +// line 657 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r75(){ + $this->_retvalue = $this->yystack[$this->yyidx + -5]->minor.' ? '. $this->compiler->compileVariable('\''.substr($this->yystack[$this->yyidx + -2]->minor,1).'\'') . ' : '.$this->yystack[$this->yyidx + 0]->minor; } - - // line 831 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r104() - { - if ($this->yystack[ $this->yyidx + 0 ]->minor[ 'var' ] === '\'smarty\'') { - $smarty_var = - $this->compiler->compileTag('private_special_variable', array(), - $this->yystack[ $this->yyidx + 0 ]->minor[ 'smarty_internal_index' ]); - $this->_retvalue = $smarty_var; - } else { - // used for array reset,next,prev,end,current - $this->last_variable = $this->yystack[ $this->yyidx + 0 ]->minor[ 'var' ]; - $this->last_index = $this->yystack[ $this->yyidx + 0 ]->minor[ 'smarty_internal_index' ]; - $this->_retvalue = - $this->compiler->compileVariable($this->yystack[ $this->yyidx + 0 ]->minor[ 'var' ]) . - $this->yystack[ $this->yyidx + 0 ]->minor[ 'smarty_internal_index' ]; - } +// line 661 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r76(){ + $this->_retvalue = $this->yystack[$this->yyidx + -5]->minor.' ? '.$this->yystack[$this->yyidx + -2]->minor.' : '.$this->yystack[$this->yyidx + 0]->minor; } - - // line 841 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r105() - { - $this->_retvalue = - '$_smarty_tpl->tpl_vars[' . - $this->yystack[ $this->yyidx + -2 ]->minor . - ']->' . - $this->yystack[ $this->yyidx + 0 ]->minor; +// line 671 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r78(){ + $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor.$this->yystack[$this->yyidx + 0]->minor; } - - // line 845 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r107() - { - $this->_retvalue = - $this->compiler->compileConfigVariable('\'' . $this->yystack[ $this->yyidx + -1 ]->minor . '\''); +// line 676 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r79(){ + $this->_retvalue = '!'.$this->yystack[$this->yyidx + 0]->minor; } - - // line 849 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r108() - { - $this->_retvalue = - '(is_array($tmp = ' . - $this->compiler->compileConfigVariable('\'' . $this->yystack[ $this->yyidx + -2 ]->minor . '\'') . - ') ? $tmp' . - $this->yystack[ $this->yyidx + 0 ]->minor . - ' :null)'; +// line 697 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r84(){ + $this->_retvalue = $this->yystack[$this->yyidx + -2]->minor.'.'.$this->yystack[$this->yyidx + 0]->minor; } - - // line 853 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r109() - { - $this->_retvalue = $this->compiler->compileConfigVariable($this->yystack[ $this->yyidx + -1 ]->minor); - } - - // line 857 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r110() - { - $this->_retvalue = - '(is_array($tmp = ' . - $this->compiler->compileConfigVariable($this->yystack[ $this->yyidx + -2 ]->minor) . - ') ? $tmp' . - $this->yystack[ $this->yyidx + 0 ]->minor . - ' : null)'; - } - - // line 860 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r111() - { - $this->_retvalue = - array( - 'var' => '\'' . substr($this->yystack[ $this->yyidx + -1 ]->minor, 1) . '\'', - 'smarty_internal_index' => $this->yystack[ $this->yyidx + 0 ]->minor - ); - } - - // line 873 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r112() - { - $this->_retvalue = - array( - 'var' => $this->yystack[ $this->yyidx + -1 ]->minor, - 'smarty_internal_index' => $this->yystack[ $this->yyidx + 0 ]->minor - ); - } - - // line 879 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r114() - { - return; - } - - // line 882 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r115() - { - $this->_retvalue = - '[' . - $this->compiler->compileVariable('\'' . substr($this->yystack[ $this->yyidx + 0 ]->minor, 1) . '\'') . - ']'; - } - - // line 886 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r116() - { - $this->_retvalue = '[' . $this->compiler->compileVariable($this->yystack[ $this->yyidx + 0 ]->minor) . ']'; - } - - // line 890 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r117() - { - $this->_retvalue = - '[' . - $this->compiler->compileVariable($this->yystack[ $this->yyidx + -2 ]->minor) . - '->' . - $this->yystack[ $this->yyidx + 0 ]->minor . - ']'; - } - - // line 894 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r118() - { - $this->_retvalue = '[\'' . $this->yystack[ $this->yyidx + 0 ]->minor . '\']'; - } - - // line 899 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r119() - { - $this->_retvalue = '[' . $this->yystack[ $this->yyidx + 0 ]->minor . ']'; - } - - // line 904 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r120() - { - $this->_retvalue = '[' . $this->yystack[ $this->yyidx + -1 ]->minor . ']'; - } - - // line 908 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r121() - { - $this->_retvalue = - '[' . - $this->compiler->compileTag('private_special_variable', array(), - '[\'section\'][\'' . $this->yystack[ $this->yyidx + -1 ]->minor . '\'][\'index\']') . - ']'; - } - - // line 911 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r122() - { - $this->_retvalue = - '[' . - $this->compiler->compileTag('private_special_variable', array(), '[\'section\'][\'' . - $this->yystack[ $this->yyidx + - -3 ]->minor . - '\'][\'' . - $this->yystack[ $this->yyidx + - -1 ]->minor . - '\']') . - ']'; - } - - // line 917 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r123() - { - $this->_retvalue = '[' . $this->yystack[ $this->yyidx + -1 ]->minor . ']'; - } - - // line 933 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r125() - { - $this->_retvalue = - '[' . - $this->compiler->compileVariable('\'' . substr($this->yystack[ $this->yyidx + -1 ]->minor, 1) . '\'') . - ']'; - } - - // line 943 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r129() - { - $this->_retvalue = '[]'; - } - - // line 947 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r130() - { - $this->_retvalue = '\'' . substr($this->yystack[ $this->yyidx + 0 ]->minor, 1) . '\''; - } - - // line 952 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r131() - { - $this->_retvalue = '\'\''; - } - - // line 960 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r132() - { - $this->_retvalue = $this->yystack[ $this->yyidx + -1 ]->minor . '.' . $this->yystack[ $this->yyidx + 0 ]->minor; - } - - // line 966 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r134() - { - $var = - trim(substr($this->yystack[ $this->yyidx + 0 ]->minor, $this->compiler->getLdelLength(), - -$this->compiler->getRdelLength()), ' $'); - $this->_retvalue = $this->compiler->compileVariable('\'' . $var . '\''); - } - - // line 973 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r135() - { - $this->_retvalue = '(' . $this->yystack[ $this->yyidx + -1 ]->minor . ')'; - } - - // line 982 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r136() - { - if ($this->yystack[ $this->yyidx + -1 ]->minor[ 'var' ] === '\'smarty\'') { - $this->_retvalue = - $this->compiler->compileTag('private_special_variable', array(), - $this->yystack[ $this->yyidx + -1 ]->minor[ 'smarty_internal_index' ]) . - $this->yystack[ $this->yyidx + 0 ]->minor; - } else { - $this->_retvalue = - $this->compiler->compileVariable($this->yystack[ $this->yyidx + -1 ]->minor[ 'var' ]) . - $this->yystack[ $this->yyidx + -1 ]->minor[ 'smarty_internal_index' ] . - $this->yystack[ $this->yyidx + 0 ]->minor; - } - } - - // line 987 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r137() - { - $this->_retvalue = $this->yystack[ $this->yyidx + 0 ]->minor; - } - - // line 992 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r138() - { - $this->_retvalue = $this->yystack[ $this->yyidx + -1 ]->minor . $this->yystack[ $this->yyidx + 0 ]->minor; +// line 701 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r85(){ + $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor.'.'; } - - // line 999 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r139() - { - if ($this->security && substr($this->yystack[ $this->yyidx + -1 ]->minor, 0, 1) === '_') { - $this->compiler->trigger_template_error(self::ERR1); - } - $this->_retvalue = - '->' . $this->yystack[ $this->yyidx + -1 ]->minor . $this->yystack[ $this->yyidx + 0 ]->minor; +// line 705 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r86(){ + $this->_retvalue = '.'.$this->yystack[$this->yyidx + 0]->minor; } - - // line 1006 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r140() - { +// line 710 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r87(){ + if (defined($this->yystack[$this->yyidx + 0]->minor)) { if ($this->security) { - $this->compiler->trigger_template_error(self::ERR2); + $this->security->isTrustedConstant($this->yystack[$this->yyidx + 0]->minor, $this->compiler); } - $this->_retvalue = - '->{' . - $this->compiler->compileVariable($this->yystack[ $this->yyidx + -1 ]->minor) . - $this->yystack[ $this->yyidx + 0 ]->minor . - '}'; + $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor; + } else { + $this->_retvalue = '\''.$this->yystack[$this->yyidx + 0]->minor.'\''; } - - // line 1013 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r141() - { - if ($this->security) { - $this->compiler->trigger_template_error(self::ERR2); - } - $this->_retvalue = - '->{' . $this->yystack[ $this->yyidx + -2 ]->minor . $this->yystack[ $this->yyidx + 0 ]->minor . '}'; } - - // line 1021 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r142() - { - if ($this->security) { - $this->compiler->trigger_template_error(self::ERR2); - } - $this->_retvalue = - '->{\'' . - $this->yystack[ $this->yyidx + -4 ]->minor . - '\'.' . - $this->yystack[ $this->yyidx + -2 ]->minor . - $this->yystack[ $this->yyidx + 0 ]->minor . - '}'; +// line 727 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r89(){ + $this->_retvalue = '('. $this->yystack[$this->yyidx + -1]->minor .')'; } - - // line 1029 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r143() - { - $this->_retvalue = '->' . $this->yystack[ $this->yyidx + 0 ]->minor; +// line 731 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r90(){ + $this->_retvalue = $this->yystack[$this->yyidx + -2]->minor.$this->yystack[$this->yyidx + -1]->minor.$this->yystack[$this->yyidx + 0]->minor; } - - // line 1037 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r144() - { - $this->_retvalue = - $this->compiler->compilePHPFunctionCall($this->yystack[ $this->yyidx + -3 ]->minor, - $this->yystack[ $this->yyidx + -1 ]->minor); +// line 749 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r94(){ + if ($this->security && $this->security->static_classes !== array()) { + $this->compiler->trigger_template_error('dynamic static class not allowed by security setting'); + } + $prefixVar = $this->compiler->getNewPrefixVariable(); + if ($this->yystack[$this->yyidx + -2]->minor['var'] === '\'smarty\'') { + $this->compiler->appendPrefixCode("compiler->compileTag('private_special_variable',array(),$this->yystack[$this->yyidx + -2]->minor['smarty_internal_index']).';?>'); + } else { + $this->compiler->appendPrefixCode("compiler->compileVariable($this->yystack[$this->yyidx + -2]->minor['var']).$this->yystack[$this->yyidx + -2]->minor['smarty_internal_index'].';?>'); + } + $this->_retvalue = $prefixVar .'::'.$this->yystack[$this->yyidx + 0]->minor[0].$this->yystack[$this->yyidx + 0]->minor[1]; + } +// line 760 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r95(){ + $prefixVar = $this->compiler->getNewPrefixVariable(); + $tmp = $this->compiler->appendCode('', $this->yystack[$this->yyidx + 0]->minor); + $this->compiler->appendPrefixCode($this->compiler->appendCode($tmp, "")); + $this->_retvalue = $prefixVar; + } +// line 767 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r96(){ + $this->_retvalue = $this->compiler->compileTag('private_modifier',array(),array('value'=>$this->yystack[$this->yyidx + -1]->minor,'modifierlist'=>$this->yystack[$this->yyidx + 0]->minor)); + } +// line 780 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r99(){ + if (!in_array(strtolower($this->yystack[$this->yyidx + -2]->minor), array('self', 'parent')) && (!$this->security || $this->security->isTrustedStaticClassAccess($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor, $this->compiler))) { + if (isset($this->smarty->registered_classes[$this->yystack[$this->yyidx + -2]->minor])) { + $this->_retvalue = $this->smarty->registered_classes[$this->yystack[$this->yyidx + -2]->minor].'::'.$this->yystack[$this->yyidx + 0]->minor[0].$this->yystack[$this->yyidx + 0]->minor[1]; + } else { + $this->_retvalue = $this->yystack[$this->yyidx + -2]->minor.'::'.$this->yystack[$this->yyidx + 0]->minor[0].$this->yystack[$this->yyidx + 0]->minor[1]; + } + } else { + $this->compiler->trigger_template_error ('static class \''.$this->yystack[$this->yyidx + -2]->minor.'\' is undefined or not allowed by security setting'); } - - // line 1044 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r145() - { - if ($this->security && substr($this->yystack[ $this->yyidx + -3 ]->minor, 0, 1) === '_') { - $this->compiler->trigger_template_error(self::ERR1); - } - $this->_retvalue = - $this->yystack[ $this->yyidx + -3 ]->minor . - '(' . - implode(',', $this->yystack[ $this->yyidx + -1 ]->minor) . - ')'; } - - // line 1055 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r146() - { - if ($this->security) { - $this->compiler->trigger_template_error(self::ERR2); +// line 799 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r101(){ + $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor; } - $prefixVar = $this->compiler->getNewPrefixVariable(); - $this->compiler->appendPrefixCode("compiler->compileVariable('\'' . - substr($this->yystack[ $this->yyidx + - -3 ]->minor, 1) . - '\'') . - ';?>'); - $this->_retvalue = $prefixVar . '(' . implode(',', $this->yystack[ $this->yyidx + -1 ]->minor) . ')'; - } - - // line 1072 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r147() - { - $this->_retvalue = - array_merge($this->yystack[ $this->yyidx + -2 ]->minor, array($this->yystack[ $this->yyidx + 0 ]->minor)); +// line 810 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r102(){ + $this->_retvalue = $this->compiler->compileVariable('\''.substr($this->yystack[$this->yyidx + 0]->minor,1).'\''); + } +// line 813 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r103(){ + if ($this->yystack[$this->yyidx + 0]->minor['var'] === '\'smarty\'') { + $smarty_var = $this->compiler->compileTag('private_special_variable',array(),$this->yystack[$this->yyidx + 0]->minor['smarty_internal_index']); + $this->_retvalue = $smarty_var; + } else { + // used for array reset,next,prev,end,current + $this->last_variable = $this->yystack[$this->yyidx + 0]->minor['var']; + $this->last_index = $this->yystack[$this->yyidx + 0]->minor['smarty_internal_index']; + $this->_retvalue = $this->compiler->compileVariable($this->yystack[$this->yyidx + 0]->minor['var']).$this->yystack[$this->yyidx + 0]->minor['smarty_internal_index']; + } + } +// line 826 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r104(){ + $this->_retvalue = '$_smarty_tpl->tpl_vars['. $this->yystack[$this->yyidx + -2]->minor .']->'.$this->yystack[$this->yyidx + 0]->minor; + } +// line 836 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r106(){ + $this->_retvalue = $this->compiler->compileConfigVariable('\'' . $this->yystack[$this->yyidx + -1]->minor . '\''); + } +// line 840 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r107(){ + $this->_retvalue = '(is_array($tmp = ' . $this->compiler->compileConfigVariable('\'' . $this->yystack[$this->yyidx + -2]->minor . '\'') . ') ? $tmp'.$this->yystack[$this->yyidx + 0]->minor.' :null)'; + } +// line 844 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r108(){ + $this->_retvalue = $this->compiler->compileConfigVariable($this->yystack[$this->yyidx + -1]->minor); + } +// line 848 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r109(){ + $this->_retvalue = '(is_array($tmp = ' . $this->compiler->compileConfigVariable($this->yystack[$this->yyidx + -2]->minor) . ') ? $tmp'.$this->yystack[$this->yyidx + 0]->minor.' : null)'; + } +// line 852 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r110(){ + $this->_retvalue = array('var'=>'\''.substr($this->yystack[$this->yyidx + -1]->minor,1).'\'', 'smarty_internal_index'=>$this->yystack[$this->yyidx + 0]->minor); + } +// line 855 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r111(){ + $this->_retvalue = array('var'=>$this->yystack[$this->yyidx + -1]->minor, 'smarty_internal_index'=>$this->yystack[$this->yyidx + 0]->minor); + } +// line 868 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r113(){ + return; + } +// line 874 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r114(){ + $this->_retvalue = '['.$this->compiler->compileVariable('\''.substr($this->yystack[$this->yyidx + 0]->minor,1).'\'').']'; + } +// line 877 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r115(){ + $this->_retvalue = '['.$this->compiler->compileVariable($this->yystack[$this->yyidx + 0]->minor).']'; + } +// line 881 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r116(){ + $this->_retvalue = '['.$this->compiler->compileVariable($this->yystack[$this->yyidx + -2]->minor).'->'.$this->yystack[$this->yyidx + 0]->minor.']'; + } +// line 885 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r117(){ + $this->_retvalue = '[\''. $this->yystack[$this->yyidx + 0]->minor .'\']'; + } +// line 889 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r118(){ + $this->_retvalue = '['. $this->yystack[$this->yyidx + 0]->minor .']'; + } +// line 894 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r119(){ + $this->_retvalue = '['. $this->yystack[$this->yyidx + -1]->minor .']'; } - - // line 1076 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r150() - { - $this->_retvalue = - array_merge($this->yystack[ $this->yyidx + -2 ]->minor, array( - array_merge($this->yystack[ $this->yyidx + -1 ]->minor, $this->yystack[ $this->yyidx + 0 ]->minor) - )); +// line 899 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r120(){ + $this->_retvalue = '['.$this->compiler->compileTag('private_special_variable',array(),'[\'section\'][\''.$this->yystack[$this->yyidx + -1]->minor.'\'][\'index\']').']'; + } +// line 903 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r121(){ + $this->_retvalue = '['.$this->compiler->compileTag('private_special_variable',array(),'[\'section\'][\''.$this->yystack[$this->yyidx + -3]->minor.'\'][\''.$this->yystack[$this->yyidx + -1]->minor.'\']').']'; + } +// line 906 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r122(){ + $this->_retvalue = '['.$this->yystack[$this->yyidx + -1]->minor.']'; + } +// line 912 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r124(){ + $this->_retvalue = '['.$this->compiler->compileVariable('\''.substr($this->yystack[$this->yyidx + -1]->minor,1).'\'').']'; + } +// line 928 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r128(){ + $this->_retvalue = '[]'; + } +// line 938 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r129(){ + $this->_retvalue = '\''.substr($this->yystack[$this->yyidx + 0]->minor,1).'\''; + } +// line 942 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r130(){ + $this->_retvalue = '\'\''; + } +// line 947 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r131(){ + $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor.'.'.$this->yystack[$this->yyidx + 0]->minor; } - - // line 1084 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r151() - { - $this->_retvalue = - array(array_merge($this->yystack[ $this->yyidx + -1 ]->minor, $this->yystack[ $this->yyidx + 0 ]->minor)); +// line 955 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r133(){ + $var = trim(substr($this->yystack[$this->yyidx + 0]->minor, $this->compiler->getLdelLength(), -$this->compiler->getRdelLength()), ' $'); + $this->_retvalue = $this->compiler->compileVariable('\''.$var.'\''); } - - // line 1092 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r153() - { - $this->_retvalue = array($this->yystack[ $this->yyidx + 0 ]->minor); +// line 961 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r134(){ + $this->_retvalue = '('.$this->yystack[$this->yyidx + -1]->minor.')'; } - - // line 1105 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r154() - { - $this->_retvalue = - array_merge($this->yystack[ $this->yyidx + -1 ]->minor, $this->yystack[ $this->yyidx + 0 ]->minor); +// line 968 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r135(){ + if ($this->yystack[$this->yyidx + -1]->minor['var'] === '\'smarty\'') { + $this->_retvalue = $this->compiler->compileTag('private_special_variable',array(),$this->yystack[$this->yyidx + -1]->minor['smarty_internal_index']).$this->yystack[$this->yyidx + 0]->minor; + } else { + $this->_retvalue = $this->compiler->compileVariable($this->yystack[$this->yyidx + -1]->minor['var']).$this->yystack[$this->yyidx + -1]->minor['smarty_internal_index'].$this->yystack[$this->yyidx + 0]->minor; } - - // line 1114 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r157() - { - $this->_retvalue = - array(trim($this->yystack[ $this->yyidx + -1 ]->minor) . $this->yystack[ $this->yyidx + 0 ]->minor); } - - // line 1119 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r159() - { - $this->_retvalue = array($this->yystack[ $this->yyidx + 0 ]->minor, '', 'method'); +// line 977 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r136(){ + $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor; } - - // line 1124 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r160() - { - $this->_retvalue = - array($this->yystack[ $this->yyidx + -1 ]->minor, $this->yystack[ $this->yyidx + 0 ]->minor, 'method'); +// line 982 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r137(){ + $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor.$this->yystack[$this->yyidx + 0]->minor; } - - // line 1129 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r161() - { - $this->_retvalue = array($this->yystack[ $this->yyidx + 0 ]->minor, ''); +// line 987 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r138(){ + if ($this->security && substr($this->yystack[$this->yyidx + -1]->minor,0,1) === '_') { + $this->compiler->trigger_template_error (self::ERR1); } - - // line 1134 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r162() - { - $this->_retvalue = - array($this->yystack[ $this->yyidx + -1 ]->minor, $this->yystack[ $this->yyidx + 0 ]->minor, 'property'); - } - - // line 1140 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r163() - { - $this->_retvalue = - array( - $this->yystack[ $this->yyidx + -2 ]->minor, - $this->yystack[ $this->yyidx + -1 ]->minor . $this->yystack[ $this->yyidx + 0 ]->minor, 'property' - ); + $this->_retvalue = '->'.$this->yystack[$this->yyidx + -1]->minor.$this->yystack[$this->yyidx + 0]->minor; } - - // line 1144 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r164() - { - $this->_retvalue = ' ' . trim($this->yystack[ $this->yyidx + 0 ]->minor) . ' '; - } - - // line 1163 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r165() - { - static $lops = array( - 'eq' => ' == ', - 'ne' => ' != ', - 'neq' => ' != ', - 'gt' => ' > ', - 'ge' => ' >= ', - 'gte' => ' >= ', - 'lt' => ' < ', - 'le' => ' <= ', - 'lte' => ' <= ', - 'mod' => ' % ', - 'and' => ' && ', - 'or' => ' || ', - 'xor' => ' xor ', - ); - $op = strtolower(preg_replace('/\s*/', '', $this->yystack[ $this->yyidx + 0 ]->minor)); - $this->_retvalue = $lops[ $op ]; - } - - // line 1176 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r166() - { - static $tlops = array( - 'isdivby' => array('op' => ' % ', 'pre' => '!('), - 'isnotdivby' => array('op' => ' % ', 'pre' => '('), - 'isevenby' => array('op' => ' / ', 'pre' => '!(1 & '), - 'isnotevenby' => array('op' => ' / ', 'pre' => '(1 & '), - 'isoddby' => array('op' => ' / ', 'pre' => '(1 & '), - 'isnotoddby' => array('op' => ' / ', 'pre' => '!(1 & '), - ); - $op = strtolower(preg_replace('/\s*/', '', $this->yystack[ $this->yyidx + 0 ]->minor)); - $this->_retvalue = $tlops[ $op ]; - } - - // line 1190 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r167() - { - static $scond = array( - 'iseven' => '!(1 & ', +// line 994 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r139(){ + if ($this->security) { + $this->compiler->trigger_template_error (self::ERR2); + } + $this->_retvalue = '->{'.$this->compiler->compileVariable($this->yystack[$this->yyidx + -1]->minor).$this->yystack[$this->yyidx + 0]->minor.'}'; + } +// line 1001 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r140(){ + if ($this->security) { + $this->compiler->trigger_template_error (self::ERR2); + } + $this->_retvalue = '->{'.$this->yystack[$this->yyidx + -2]->minor.$this->yystack[$this->yyidx + 0]->minor.'}'; + } +// line 1008 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r141(){ + if ($this->security) { + $this->compiler->trigger_template_error (self::ERR2); + } + $this->_retvalue = '->{\''.$this->yystack[$this->yyidx + -4]->minor.'\'.'.$this->yystack[$this->yyidx + -2]->minor.$this->yystack[$this->yyidx + 0]->minor.'}'; + } +// line 1016 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r142(){ + $this->_retvalue = '->'.$this->yystack[$this->yyidx + 0]->minor; + } +// line 1024 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r143(){ + $this->_retvalue = $this->compiler->compilePHPFunctionCall($this->yystack[$this->yyidx + -3]->minor, $this->yystack[$this->yyidx + -1]->minor); + } +// line 1032 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r144(){ + if ($this->security && substr($this->yystack[$this->yyidx + -3]->minor,0,1) === '_') { + $this->compiler->trigger_template_error (self::ERR1); + } + $this->_retvalue = $this->yystack[$this->yyidx + -3]->minor . '('. implode(',',$this->yystack[$this->yyidx + -1]->minor) .')'; + } +// line 1039 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r145(){ + if ($this->security) { + $this->compiler->trigger_template_error (self::ERR2); + } + $prefixVar = $this->compiler->getNewPrefixVariable(); + $this->compiler->appendPrefixCode("compiler->compileVariable('\''.substr($this->yystack[$this->yyidx + -3]->minor,1).'\'').';?>'); + $this->_retvalue = $prefixVar .'('. implode(',',$this->yystack[$this->yyidx + -1]->minor) .')'; + } +// line 1050 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r146(){ + $this->_retvalue = array_merge($this->yystack[$this->yyidx + -2]->minor,array($this->yystack[$this->yyidx + 0]->minor)); + } +// line 1067 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r149(){ + $this->_retvalue = array_merge($this->yystack[$this->yyidx + -2]->minor,array(array_merge($this->yystack[$this->yyidx + -1]->minor,$this->yystack[$this->yyidx + 0]->minor))); + } +// line 1071 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r150(){ + $this->_retvalue = array(array_merge($this->yystack[$this->yyidx + -1]->minor,$this->yystack[$this->yyidx + 0]->minor)); + } +// line 1079 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r152(){ + $this->_retvalue = array($this->yystack[$this->yyidx + 0]->minor); + } +// line 1087 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r153(){ + $this->_retvalue = array_merge($this->yystack[$this->yyidx + -1]->minor,$this->yystack[$this->yyidx + 0]->minor); + } +// line 1100 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r156(){ + $this->_retvalue = array(trim($this->yystack[$this->yyidx + -1]->minor).$this->yystack[$this->yyidx + 0]->minor); + } +// line 1109 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r158(){ + $this->_retvalue = array($this->yystack[$this->yyidx + 0]->minor, '', 'method'); + } +// line 1114 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r159(){ + $this->_retvalue = array($this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + 0]->minor, 'method'); + } +// line 1119 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r160(){ + $this->_retvalue = array($this->yystack[$this->yyidx + 0]->minor, ''); + } +// line 1124 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r161(){ + $this->_retvalue = array($this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + 0]->minor, 'property'); + } +// line 1129 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r162(){ + $this->_retvalue = array($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + -1]->minor.$this->yystack[$this->yyidx + 0]->minor, 'property'); + } +// line 1135 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r163(){ + $this->_retvalue = ' '. trim($this->yystack[$this->yyidx + 0]->minor) . ' '; + } +// line 1139 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r164(){ + static $lops = array( + 'eq' => ' == ', + 'ne' => ' != ', + 'neq' => ' != ', + 'gt' => ' > ', + 'ge' => ' >= ', + 'gte' => ' >= ', + 'lt' => ' < ', + 'le' => ' <= ', + 'lte' => ' <= ', + 'mod' => ' % ', + 'and' => ' && ', + 'or' => ' || ', + 'xor' => ' xor ', + ); + $op = strtolower(preg_replace('/\s*/', '', $this->yystack[$this->yyidx + 0]->minor)); + $this->_retvalue = $lops[$op]; + } +// line 1158 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r165(){ + static $tlops = array( + 'isdivby' => array('op' => ' % ', 'pre' => '!('), + 'isnotdivby' => array('op' => ' % ', 'pre' => '('), + 'isevenby' => array('op' => ' / ', 'pre' => '!(1 & '), + 'isnotevenby' => array('op' => ' / ', 'pre' => '(1 & '), + 'isoddby' => array('op' => ' / ', 'pre' => '(1 & '), + 'isnotoddby' => array('op' => ' / ', 'pre' => '!(1 & '), + ); + $op = strtolower(preg_replace('/\s*/', '', $this->yystack[$this->yyidx + 0]->minor)); + $this->_retvalue = $tlops[$op]; + } +// line 1171 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r166(){ + static $scond = array ( + 'iseven' => '!(1 & ', 'isnoteven' => '(1 & ', - 'isodd' => '(1 & ', - 'isnotodd' => '!(1 & ', + 'isodd' => '(1 & ', + 'isnotodd' => '!(1 & ', ); - $op = strtolower(str_replace(' ', '', $this->yystack[ $this->yyidx + 0 ]->minor)); - $this->_retvalue = $scond[ $op ]; + $op = strtolower(str_replace(' ', '', $this->yystack[$this->yyidx + 0]->minor)); + $this->_retvalue = $scond[$op]; } - - // line 1201 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r168() - { - $this->_retvalue = 'array(' . $this->yystack[ $this->yyidx + -1 ]->minor . ')'; +// line 1185 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r167(){ + $this->_retvalue = 'array('.$this->yystack[$this->yyidx + -1]->minor.')'; } - - // line 1209 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r171() - { - $this->_retvalue = $this->yystack[ $this->yyidx + -2 ]->minor . ',' . $this->yystack[ $this->yyidx + 0 ]->minor; +// line 1196 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r170(){ + $this->_retvalue = $this->yystack[$this->yyidx + -2]->minor.','.$this->yystack[$this->yyidx + 0]->minor; } - - // line 1213 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r173() - { - $this->_retvalue = - $this->yystack[ $this->yyidx + -2 ]->minor . '=>' . $this->yystack[ $this->yyidx + 0 ]->minor; +// line 1204 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r172(){ + $this->_retvalue = $this->yystack[$this->yyidx + -2]->minor.'=>'.$this->yystack[$this->yyidx + 0]->minor; } - - // line 1229 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r174() - { - $this->_retvalue = - '\'' . $this->yystack[ $this->yyidx + -2 ]->minor . '\'=>' . $this->yystack[ $this->yyidx + 0 ]->minor; +// line 1208 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r173(){ + $this->_retvalue = '\''.$this->yystack[$this->yyidx + -2]->minor.'\'=>'.$this->yystack[$this->yyidx + 0]->minor; } - - // line 1235 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r177() - { - $this->compiler->leaveDoubleQuote(); - $this->_retvalue = $this->yystack[ $this->yyidx + -1 ]->minor->to_smarty_php($this); +// line 1224 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r176(){ + $this->compiler->leaveDoubleQuote(); + $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor->to_smarty_php($this); } - - // line 1240 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r178() - { - $this->yystack[ $this->yyidx + -1 ]->minor->append_subtree($this, $this->yystack[ $this->yyidx + 0 ]->minor); - $this->_retvalue = $this->yystack[ $this->yyidx + -1 ]->minor; +// line 1230 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r177(){ + $this->yystack[$this->yyidx + -1]->minor->append_subtree($this, $this->yystack[$this->yyidx + 0]->minor); + $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor; } - - // line 1244 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r179() - { - $this->_retvalue = new Smarty_Internal_ParseTree_Dq($this, $this->yystack[ $this->yyidx + 0 ]->minor); +// line 1235 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r178(){ + $this->_retvalue = new Smarty_Internal_ParseTree_Dq($this, $this->yystack[$this->yyidx + 0]->minor); } - - // line 1248 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r180() - { - $this->_retvalue = new Smarty_Internal_ParseTree_Code('(string)' . $this->yystack[ $this->yyidx + -1 ]->minor); +// line 1239 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r179(){ + $this->_retvalue = new Smarty_Internal_ParseTree_Code('(string)'.$this->yystack[$this->yyidx + -1]->minor); } - - // line 1252 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r181() - { - $this->_retvalue = - new Smarty_Internal_ParseTree_Code('(string)(' . $this->yystack[ $this->yyidx + -1 ]->minor . ')'); +// line 1243 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r180(){ + $this->_retvalue = new Smarty_Internal_ParseTree_Code('(string)('.$this->yystack[$this->yyidx + -1]->minor.')'); } - - // line 1264 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r182() - { - $this->_retvalue = - new Smarty_Internal_ParseTree_Code('(string)$_smarty_tpl->tpl_vars[\'' . - substr($this->yystack[ $this->yyidx + 0 ]->minor, 1) . - '\']->value'); +// line 1247 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r181(){ + $this->_retvalue = new Smarty_Internal_ParseTree_Code('(string)$_smarty_tpl->tpl_vars[\''. substr($this->yystack[$this->yyidx + 0]->minor,1) .'\']->value'); } - - // line 1268 "../smarty/lexer/smarty_internal_templateparser.y" - public function yy_r185() - { - $this->_retvalue = new Smarty_Internal_ParseTree_Tag($this, $this->yystack[ $this->yyidx + 0 ]->minor); +// line 1259 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r184(){ + $this->_retvalue = new Smarty_Internal_ParseTree_Tag($this, $this->yystack[$this->yyidx + 0]->minor); } - - public function yy_r186() - { - $this->_retvalue = new Smarty_Internal_ParseTree_DqContent($this->yystack[ $this->yyidx + 0 ]->minor); +// line 1263 "../smarty/lexer/smarty_internal_templateparser.y" + public function yy_r185(){ + $this->_retvalue = new Smarty_Internal_ParseTree_DqContent($this->yystack[$this->yyidx + 0]->minor); } + private $_retvalue; + public function yy_reduce($yyruleno) { if ($this->yyTraceFILE && $yyruleno >= 0 - && $yyruleno < count(self::$yyRuleName)) { + && $yyruleno < count(self::$yyRuleName)) { fprintf($this->yyTraceFILE, "%sReduce (%d) [%s].\n", $this->yyTracePrompt, $yyruleno, - self::$yyRuleName[ $yyruleno ]); + self::$yyRuleName[$yyruleno]); } + $this->_retvalue = $yy_lefthand_side = null; - if (isset(self::$yyReduceMap[ $yyruleno ])) { + if (isset(self::$yyReduceMap[$yyruleno])) { // call the action $this->_retvalue = null; - $this->{'yy_r' . self::$yyReduceMap[ $yyruleno ]}(); + $this->{'yy_r' . self::$yyReduceMap[$yyruleno]}(); $yy_lefthand_side = $this->_retvalue; } - $yygoto = self::$yyRuleInfo[ $yyruleno ][ 0 ]; - $yysize = self::$yyRuleInfo[ $yyruleno ][ 1 ]; + $yygoto = self::$yyRuleInfo[$yyruleno][0]; + $yysize = self::$yyRuleInfo[$yyruleno][1]; $this->yyidx -= $yysize; for ($i = $yysize; $i; $i--) { // pop all of the right-hand side parameters array_pop($this->yystack); } - $yyact = $this->yy_find_reduce_action($this->yystack[ $this->yyidx ]->stateno, $yygoto); + $yyact = $this->yy_find_reduce_action($this->yystack[$this->yyidx]->stateno, $yygoto); if ($yyact < self::YYNSTATE) { if (!$this->yyTraceFILE && $yysize) { $this->yyidx++; @@ -3492,7 +2792,7 @@ public function yy_reduce($yyruleno) $x->stateno = $yyact; $x->major = $yygoto; $x->minor = $yy_lefthand_side; - $this->yystack[ $this->yyidx ] = $x; + $this->yystack[$this->yyidx] = $x; } else { $this->yy_shift($yyact, $yygoto, $yy_lefthand_side); } @@ -3505,37 +2805,38 @@ public function yy_parse_failed() { if ($this->yyTraceFILE) { fprintf($this->yyTraceFILE, "%sFail!\n", $this->yyTracePrompt); - } - while ($this->yyidx >= 0) { + } while ($this->yyidx >= 0) { $this->yy_pop_parser_stack(); } } public function yy_syntax_error($yymajor, $TOKEN) { - // line 214 "../smarty/lexer/smarty_internal_templateparser.y" - $this->internalError = true; - $this->yymajor = $yymajor; - $this->compiler->trigger_template_error(); +// line 213 "../smarty/lexer/smarty_internal_templateparser.y" + + $this->internalError = true; + $this->yymajor = $yymajor; + $this->compiler->trigger_template_error(); } public function yy_accept() { if ($this->yyTraceFILE) { fprintf($this->yyTraceFILE, "%sAccept!\n", $this->yyTracePrompt); - } - while ($this->yyidx >= 0) { + } while ($this->yyidx >= 0) { $this->yy_pop_parser_stack(); } - // line 207 "../smarty/lexer/smarty_internal_templateparser.y" - $this->successful = !$this->internalError; - $this->internalError = false; - $this->retvalue = $this->_retvalue; +// line 206 "../smarty/lexer/smarty_internal_templateparser.y" + + $this->successful = !$this->internalError; + $this->internalError = false; + $this->retvalue = $this->_retvalue; } public function doParse($yymajor, $yytokenvalue) { $yyerrorhit = 0; /* True if yymajor has invoked an error */ + if ($this->yyidx === null || $this->yyidx < 0) { $this->yyidx = 0; $this->yyerrcnt = -1; @@ -3545,15 +2846,17 @@ public function doParse($yymajor, $yytokenvalue) $this->yystack = array(); $this->yystack[] = $x; } - $yyendofinput = ($yymajor == 0); + $yyendofinput = ($yymajor==0); + if ($this->yyTraceFILE) { fprintf($this->yyTraceFILE, "%sInput %s\n", - $this->yyTracePrompt, $this->yyTokenName[ $yymajor ]); + $this->yyTracePrompt, $this->yyTokenName[$yymajor]); } + do { $yyact = $this->yy_find_shift_action($yymajor); if ($yymajor < self::YYERRORSYMBOL && - !$this->yy_is_expected_token($yymajor)) { + !$this->yy_is_expected_token($yymajor)) { // force a syntax error $yyact = self::YY_ERROR_ACTION; } @@ -3576,22 +2879,22 @@ public function doParse($yymajor, $yytokenvalue) if ($this->yyerrcnt < 0) { $this->yy_syntax_error($yymajor, $yytokenvalue); } - $yymx = $this->yystack[ $this->yyidx ]->major; + $yymx = $this->yystack[$this->yyidx]->major; if ($yymx === self::YYERRORSYMBOL || $yyerrorhit) { if ($this->yyTraceFILE) { fprintf($this->yyTraceFILE, "%sDiscard input token %s\n", - $this->yyTracePrompt, $this->yyTokenName[ $yymajor ]); + $this->yyTracePrompt, $this->yyTokenName[$yymajor]); } $this->yy_destructor($yymajor, $yytokenvalue); $yymajor = self::YYNOCODE; } else { while ($this->yyidx >= 0 && - $yymx !== self::YYERRORSYMBOL && - ($yyact = $this->yy_find_shift_action(self::YYERRORSYMBOL)) >= self::YYNSTATE - ) { + $yymx !== self::YYERRORSYMBOL && + ($yyact = $this->yy_find_shift_action(self::YYERRORSYMBOL)) >= self::YYNSTATE + ){ $this->yy_pop_parser_stack(); } - if ($this->yyidx < 0 || $yymajor == 0) { + if ($this->yyidx < 0 || $yymajor==0) { $this->yy_destructor($yymajor, $yytokenvalue); $this->yy_parse_failed(); $yymajor = self::YYNOCODE; diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_testinstall.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_testinstall.php index 504a4582c..c8ffd4cc6 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_testinstall.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_testinstall.php @@ -144,7 +144,7 @@ public static function testInstall(Smarty $smarty, &$errors = null) } // test if all registered plugins_dir are accessible // and if core plugins directory is still registered - $_core_plugins_dir = realpath(dirname(__FILE__) . '/../plugins'); + $_core_plugins_dir = realpath(__DIR__ . '/../plugins'); $_core_plugins_available = false; foreach ($smarty->getPluginsDir() as $plugin_dir) { $_plugin_dir = $plugin_dir; @@ -362,7 +362,6 @@ public static function testInstall(Smarty $smarty, &$errors = null) 'smarty_internal_compile_function.php' => true, 'smarty_internal_compile_if.php' => true, 'smarty_internal_compile_include.php' => true, - 'smarty_internal_compile_include_php.php' => true, 'smarty_internal_compile_insert.php' => true, 'smarty_internal_compile_ldelim.php' => true, 'smarty_internal_compile_make_nocache.php' => true, @@ -373,7 +372,6 @@ public static function testInstall(Smarty $smarty, &$errors = null) 'smarty_internal_compile_private_modifier.php' => true, 'smarty_internal_compile_private_object_block_function.php' => true, 'smarty_internal_compile_private_object_function.php' => true, - 'smarty_internal_compile_private_php.php' => true, 'smarty_internal_compile_private_print_expression.php' => true, 'smarty_internal_compile_private_registered_block.php' => true, 'smarty_internal_compile_private_registered_function.php' => true, @@ -388,7 +386,6 @@ public static function testInstall(Smarty $smarty, &$errors = null) 'smarty_internal_config_file_compiler.php' => true, 'smarty_internal_data.php' => true, 'smarty_internal_debug.php' => true, - 'smarty_internal_errorhandler.php' => true, 'smarty_internal_extension_handler.php' => true, 'smarty_internal_method_addautoloadfilters.php' => true, 'smarty_internal_method_adddefaultmodifiers.php' => true, @@ -450,7 +447,6 @@ public static function testInstall(Smarty $smarty, &$errors = null) 'smarty_internal_resource_extends.php' => true, 'smarty_internal_resource_file.php' => true, 'smarty_internal_resource_php.php' => true, - 'smarty_internal_resource_registered.php' => true, 'smarty_internal_resource_stream.php' => true, 'smarty_internal_resource_string.php' => true, 'smarty_internal_runtime_cachemodify.php' => true, diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_resource.php b/WEB-INF/lib/smarty/sysplugins/smarty_resource.php index aae7e42f7..3c43a9f46 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_resource.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_resource.php @@ -72,17 +72,15 @@ public static function load(Smarty $smarty, $type) } // try registered resource if (isset($smarty->registered_resources[ $type ])) { - return $smarty->_cache[ 'resource_handlers' ][ $type ] = - $smarty->registered_resources[ $type ] instanceof Smarty_Resource ? - $smarty->registered_resources[ $type ] : new Smarty_Internal_Resource_Registered(); + return $smarty->_cache[ 'resource_handlers' ][ $type ] = $smarty->registered_resources[ $type ]; } // try sysplugins dir if (isset(self::$sysplugins[ $type ])) { - $_resource_class = 'Smarty_Internal_Resource_' . ucfirst($type); + $_resource_class = 'Smarty_Internal_Resource_' . smarty_ucfirst_ascii($type); return $smarty->_cache[ 'resource_handlers' ][ $type ] = new $_resource_class(); } // try plugins dir - $_resource_class = 'Smarty_Resource_' . ucfirst($type); + $_resource_class = 'Smarty_Resource_' . smarty_ucfirst_ascii($type); if ($smarty->loadPlugin($_resource_class)) { if (class_exists($_resource_class, false)) { return $smarty->_cache[ 'resource_handlers' ][ $type ] = new $_resource_class(); diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_resource_custom.php b/WEB-INF/lib/smarty/sysplugins/smarty_resource_custom.php index 8d66be3ae..191fa7c90 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_resource_custom.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_resource_custom.php @@ -47,7 +47,7 @@ protected function fetchTimestamp($name) */ public function populate(Smarty_Template_Source $source, Smarty_Internal_Template $_template = null) { - $source->filepath = $source->type . ':' . substr(preg_replace('/[^A-Za-z0-9.]/', '', $source->name), 0, 25); + $source->filepath = $source->type . ':' . $this->generateSafeName($source->name); $source->uid = sha1($source->type . ':' . $source->name); $mtime = $this->fetchTimestamp($source->name); if ($mtime !== null) { @@ -88,6 +88,17 @@ public function getContent(Smarty_Template_Source $source) */ public function getBasename(Smarty_Template_Source $source) { - return basename(substr(preg_replace('/[^A-Za-z0-9.]/', '', $source->name), 0, 25)); + return basename($this->generateSafeName($source->name)); + } + + /** + * Removes special characters from $name and limits its length to 127 characters. + * + * @param $name + * + * @return string + */ + private function generateSafeName($name): string { + return substr(preg_replace('/[^A-Za-z0-9._]/', '', (string) $name), 0, 127); } } diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_security.php b/WEB-INF/lib/smarty/sysplugins/smarty_security.php index 441a7e284..97cd0521d 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_security.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_security.php @@ -19,21 +19,9 @@ /** * This class does contain the security settings */ +#[\AllowDynamicProperties] class Smarty_Security { - /** - * This determines how Smarty handles "" tags in templates. - * possible values: - *
    - *
  • Smarty::PHP_PASSTHRU -> echo PHP tags as they are
  • - *
  • Smarty::PHP_QUOTE -> escape tags as entities
  • - *
  • Smarty::PHP_REMOVE -> remove php tags
  • - *
  • Smarty::PHP_ALLOW -> execute php tags
  • - *
- * - * @var integer - */ - public $php_handling = Smarty::PHP_PASSTHRU; /** * This is the list of template directories that are considered secure. @@ -118,7 +106,7 @@ class Smarty_Security * * @var array */ - public $php_modifiers = array('escape', 'count', 'nl2br',); + public $php_modifiers = array('escape', 'count', 'sizeof', 'nl2br',); /** * This is an array of allowed tags. @@ -341,7 +329,7 @@ public function isTrustedStaticClassAccess($class_name, $params, $compiler) * * @param string $modifier_name * @param object $compiler compiler object - * + * @deprecated * @return boolean true if modifier is trusted */ public function isTrustedPhpModifier($modifier_name, $compiler) @@ -568,35 +556,6 @@ public function isTrustedUri($uri) throw new SmartyException("URI '{$uri}' not allowed by security setting"); } - /** - * Check if directory of file resource is trusted. - * - * @param string $filepath - * - * @return boolean true if directory is trusted - * @throws SmartyException if PHP directory is not trusted - */ - public function isTrustedPHPDir($filepath) - { - if (empty($this->trusted_dir)) { - throw new SmartyException("directory '{$filepath}' not allowed by security setting (no trusted_dir specified)"); - } - // check if index is outdated - if (!$this->_trusted_dir || $this->_trusted_dir !== $this->trusted_dir) { - $this->_php_resource_dir = array(); - $this->_trusted_dir = $this->trusted_dir; - foreach ((array)$this->trusted_dir as $directory) { - $directory = $this->smarty->_realpath($directory . '/', true); - $this->_php_resource_dir[ $directory ] = true; - } - } - $addPath = $this->_checkDir($filepath, $this->_php_resource_dir); - if ($addPath !== false) { - $this->_php_resource_dir = array_merge($this->_php_resource_dir, $addPath); - } - return true; - } - /** * Remove old directories and its sub folders, add new directories * diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_variable.php b/WEB-INF/lib/smarty/sysplugins/smarty_variable.php index 914d99bd7..6a534228b 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_variable.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_variable.php @@ -7,6 +7,7 @@ * @package Smarty * @subpackage Template */ +#[\AllowDynamicProperties] class Smarty_Variable { /** diff --git a/WEB-INF/lib/smarty/sysplugins/smartycompilerexception.php b/WEB-INF/lib/smarty/sysplugins/smartycompilerexception.php index f7ad39b93..0a0a32351 100644 --- a/WEB-INF/lib/smarty/sysplugins/smartycompilerexception.php +++ b/WEB-INF/lib/smarty/sysplugins/smartycompilerexception.php @@ -7,6 +7,33 @@ */ class SmartyCompilerException extends SmartyException { + /** + * The constructor of the exception + * + * @param string $message The Exception message to throw. + * @param int $code The Exception code. + * @param string|null $filename The filename where the exception is thrown. + * @param int|null $line The line number where the exception is thrown. + * @param Throwable|null $previous The previous exception used for the exception chaining. + */ + public function __construct( + string $message = "", + int $code = 0, + ?string $filename = null, + ?int $line = null, + Throwable $previous = null + ) { + parent::__construct($message, $code, $previous); + + // These are optional parameters, should be be overridden only when present! + if ($filename) { + $this->file = $filename; + } + if ($line) { + $this->line = $line; + } + } + /** * @return string */ @@ -16,11 +43,12 @@ public function __toString() } /** - * The line number of the template error - * - * @type int|null + * @param int $line */ - public $line = null; + public function setLine($line) + { + $this->line = $line; + } /** * The template source snippet relating to the error diff --git a/WEB-INF/lib/ttAdmin.class.php b/WEB-INF/lib/ttAdmin.class.php index 35a26afbe..4a631d5e3 100644 --- a/WEB-INF/lib/ttAdmin.class.php +++ b/WEB-INF/lib/ttAdmin.class.php @@ -138,7 +138,8 @@ static function updateSelf($fields) { // Update self. $user_id = $user->id; $login_part = 'login = '.$mdb2->quote($fields['login']); - if ($fields['password1']) + $password_part = ''; + if (isset($fields['password1'])) $password_part = ', password = md5('.$mdb2->quote($fields['password1']).')'; $name_part = ', name = '.$mdb2->quote($fields['name']); $email_part = ', email = '.$mdb2->quote($fields['email']); @@ -150,6 +151,8 @@ static function updateSelf($fields) { // getGroupName obtains group name. static function getGroupName($group_id) { + $group_id = (int) $group_id; // Just in case because it is used in sql. + $result = array(); $mdb2 = getConnection(); @@ -158,10 +161,11 @@ static function getGroupName($group_id) { $res = $mdb2->query($sql); if (!is_a($res, 'PEAR_Error')) { $val = $res->fetchRow(); - return $val['name']; + if (isset($val['name'])) + return $val['name']; } - return false; + return null; } // getOrgDetails obtains group name and its top manager details. diff --git a/WEB-INF/lib/ttAdminWorkHelper.class.php b/WEB-INF/lib/ttAdminWorkHelper.class.php deleted file mode 100644 index 22098b277..000000000 --- a/WEB-INF/lib/ttAdminWorkHelper.class.php +++ /dev/null @@ -1,1028 +0,0 @@ -errors = &$errors; - - $this->work_server_uri = defined('WORK_SERVER_URI') ? WORK_SERVER_URI : "https://www.anuko.com/work/"; - $this->register_uri = $this->work_server_uri.'register'; - $this->get_work_item_uri = $this->work_server_uri.'admin_getworkitem'; - $this->update_work_item_uri = $this->work_server_uri.'admin_updateworkitem'; - $this->delete_work_item_uri = $this->work_server_uri.'admin_deleteworkitem'; - $this->approve_work_item_uri = $this->work_server_uri.'admin_approveworkitem'; - $this->approve_work_item_on_offer_uri = $this->work_server_uri.'admin_approveworkitemonoffer'; - $this->disapprove_work_item_uri = $this->work_server_uri.'admin_disapproveworkitem'; - $this->disapprove_work_item_on_offer_uri = $this->work_server_uri.'admin_disapproveworkitemonoffer'; - $this->get_offer_uri = $this->work_server_uri.'admin_getoffer'; - $this->update_offer_uri = $this->work_server_uri.'admin_updateoffer'; - $this->delete_offer_uri = $this->work_server_uri.'admin_deleteoffer'; - $this->approve_offer_uri = $this->work_server_uri.'admin_approveoffer'; - $this->disapprove_offer_uri = $this->work_server_uri.'admin_disapproveoffer'; - $this->get_pending_work_items_uri = $this->work_server_uri.'admin_getpendingworkitems'; - $this->get_pending_offers_uri = $this->work_server_uri.'admin_getpendingoffers'; - $this->get_items_uri = $this->work_server_uri.'admin_getitems'; - $this->checkSiteRegistration(); - } - - // checkSiteRegistration - obtains site id and key from local database. - // If not found, it tries to register with remote work server. - function checkSiteRegistration() { - global $i18n; - global $user; - $mdb2 = getConnection(); - - // Obtain site id. - $sql = "select param_value as id from tt_site_config where param_name = 'worksite_id'"; - $res = $mdb2->query($sql); - $val = $res->fetchRow(); - if (!$val) { - // No site id found, need to register. - $fields = array('lang' => urlencode($user->lang), - 'name' => urlencode('time tracker'), - 'origin' => urlencode('time tracker source')); - - // Urlify the data for the POST. - foreach($fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; } - $fields_string = rtrim($fields_string, '&'); - - // Open connection. - $ch = curl_init(); - - // Set the url, number of POST vars, POST data. - curl_setopt($ch, CURLOPT_URL, $this->register_uri); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - - // Execute a post request. - $result = curl_exec($ch); - - // Close connection. - curl_close($ch); - - if (!$result) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - - $result_array = json_decode($result, true); - - // Check for errors. - $call_status = $result_array['call_status']; - if (!$call_status) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - if ($call_status['code'] != TT_CURL_SUCCESS) { - $this->errors->add($call_status['error']); - return false; - } - - $reg_status = $result_array['reg_status']; // Registration status. - if ($reg_status['site_id'] && $reg_status['site_key']) { - $this->site_id = $reg_status['site_id']; - $this->site_key = $reg_status['site_key']; - - // Registration successful. Store id and key locally for future use. - $sql = "insert into tt_site_config values('worksite_id', $this->site_id, now(), null)"; - $mdb2->exec($sql); - $sql = "insert into tt_site_config values('worksite_key', ".$mdb2->quote($this->site_key).", now(), null)"; - $mdb2->exec($sql); - } else { - $this->errors->add($i18n->get('error.remote_work')); - } - } else { - // Site id found. - $this->site_id = $val['id']; - - // Obtain site key. - $sql = "select param_value as site_key from tt_site_config where param_name = 'worksite_key'"; - $res = $mdb2->query($sql); - $val = $res->fetchRow(); - $this->site_key = $val['site_key']; - } - } - - // getPendingOffers - obtains a list of offers pending approval. - function getPendingOffers() { - global $i18n; - global $user; - $mdb2 = getConnection(); - - $curl_fields = array('lang' => urlencode($user->lang), - 'site_id' => urlencode($this->site_id), - 'site_key' => urlencode($this->site_key), - 'user_id' => urlencode($user->id) - ); - - // url-ify the data for the POST. - foreach($curl_fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; } - $fields_string = rtrim($fields_string, '&'); - - // Open connection. - $ch = curl_init(); - - // Set the url, number of POST vars, POST data. - curl_setopt($ch, CURLOPT_URL, $this->get_pending_offers_uri); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - - // Execute a post request. - $result = curl_exec($ch); - - // Close connection. - curl_close($ch); - - if (!$result) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - - $result_array = json_decode($result, true); - - // Check for errors. - $call_status = $result_array['call_status']; - if (!$call_status) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - if ($call_status['code'] != TT_CURL_SUCCESS) { - $this->errors->add($call_status['error']); - return false; - } - - $pending_offers = $result_array['pending_offers']; - return $pending_offers; - } - - // getPendingWorkItems - obtains a list of work items pending approval. - function getPendingWorkItems() { - global $i18n; - global $user; - $mdb2 = getConnection(); - - $curl_fields = array('lang' => urlencode($user->lang), - 'site_id' => urlencode($this->site_id), - 'site_key' => urlencode($this->site_key), - 'user_id' => urlencode($user->id) - ); - - // url-ify the data for the POST. - foreach($curl_fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; } - $fields_string = rtrim($fields_string, '&'); - - // Open connection. - $ch = curl_init(); - - // Set the url, number of POST vars, POST data. - curl_setopt($ch, CURLOPT_URL, $this->get_pending_work_items_uri); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - - // Execute a post request. - $result = curl_exec($ch); - - // Close connection. - curl_close($ch); - - if (!$result) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - - $result_array = json_decode($result, true); - - // Check for errors. - $call_status = $result_array['call_status']; - if (!$call_status) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - if ($call_status['code'] != TT_CURL_SUCCESS) { - $this->errors->add($call_status['error']); - return false; - } - - $pending_work = $result_array['pending_work']; - return $pending_work; - } - - // getWorkItem - gets work item details from remote work server. - function getWorkItem($work_id) { - global $i18n; - global $user; - $mdb2 = getConnection(); - - $curl_fields = array('lang' => urlencode($user->lang), - 'site_id' => urlencode($this->site_id), - 'site_key' => urlencode($this->site_key), - 'user_id' => urlencode($user->id), - 'work_id' => urlencode($work_id)); - - // url-ify the data for the POST. - foreach($curl_fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; } - $fields_string = rtrim($fields_string, '&'); - - // Open connection. - $ch = curl_init(); - - // Set the url, number of POST vars, POST data. - curl_setopt($ch, CURLOPT_URL, $this->get_work_item_uri); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - - // Execute a post request. - $result = curl_exec($ch); - - // Close connection. - curl_close($ch); - - if (!$result) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - - $result_array = json_decode($result, true); - - // Check for errors. - $call_status = $result_array['call_status']; - if (!$call_status) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - if ($call_status['code'] != TT_CURL_SUCCESS) { - $this->errors->add($call_status['error']); - return false; - } - - $work_item = $result_array['work_item']; - return $work_item; - } - - // deleteWorkItem - deletes work item from remote work server. - function deleteWorkItem($work_id) { - global $i18n; - global $user; - $mdb2 = getConnection(); - - $curl_fields = array('lang' => urlencode($user->lang), - 'site_id' => urlencode($this->site_id), - 'site_key' => urlencode($this->site_key), - 'user_id' => urlencode($user->id), - 'work_id' => urlencode($work_id), - 'modified_ip' => urlencode($_SERVER['REMOTE_ADDR']), - 'modified_by' => urlencode($user->getUser()), - 'modified_by_name' => urlencode(base64_encode($user->getName())), - 'modified_by_email' => urlencode(base64_encode($user->getEmail()))); - - // url-ify the data for the POST. - foreach($curl_fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; } - $fields_string = rtrim($fields_string, '&'); - - // Open connection. - $ch = curl_init(); - - // Set the url, number of POST vars, POST data. - curl_setopt($ch, CURLOPT_URL, $this->delete_work_item_uri); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - - // Execute a post request. - $result = curl_exec($ch); - - // Close connection. - curl_close($ch); - - if (!$result) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - - $result_array = json_decode($result, true); - - // Check for errors. - $call_status = $result_array['call_status']; - if (!$call_status) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - if ($call_status['code'] != TT_CURL_SUCCESS) { - $this->errors->add($call_status['error']); - return false; - } - - return true; - } - - // deleteOffer - deletes an offer from remote work server. - function deleteOffer($offer_id) { - global $i18n; - global $user; - $mdb2 = getConnection(); - - $curl_fields = array('lang' => urlencode($user->lang), - 'site_id' => urlencode($this->site_id), - 'site_key' => urlencode($this->site_key), - 'user_id' => urlencode($user->id), - 'offer_id' => urlencode($offer_id), - 'modified_ip' => urlencode($_SERVER['REMOTE_ADDR']), - 'modified_by' => urlencode($user->getUser()), - 'modified_by_name' => urlencode(base64_encode($user->getName())), - 'modified_by_email' => urlencode(base64_encode($user->getEmail()))); - - // url-ify the data for the POST. - foreach($curl_fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; } - $fields_string = rtrim($fields_string, '&'); - - // Open connection. - $ch = curl_init(); - - // Set the url, number of POST vars, POST data. - curl_setopt($ch, CURLOPT_URL, $this->delete_offer_uri); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - - // Execute a post request. - $result = curl_exec($ch); - - // Close connection. - curl_close($ch); - - if (!$result) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - - $result_array = json_decode($result, true); - - // Check for errors. - $call_status = $result_array['call_status']; - if (!$call_status) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - if ($call_status['code'] != TT_CURL_SUCCESS) { - $this->errors->add($call_status['error']); - return false; - } - - return true; - } - - // getOffer - gets offer details from remote work server. - function getOffer($offer_id) { - global $i18n; - global $user; - $mdb2 = getConnection(); - - $curl_fields = array('lang' => urlencode($user->lang), - 'site_id' => urlencode($this->site_id), - 'site_key' => urlencode($this->site_key), - 'user_id' => urlencode($user->id), - 'offer_id' => urlencode($offer_id)); - - // url-ify the data for the POST. - foreach($curl_fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; } - $fields_string = rtrim($fields_string, '&'); - - // Open connection. - $ch = curl_init(); - - // Set the url, number of POST vars, POST data. - curl_setopt($ch, CURLOPT_URL, $this->get_offer_uri); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - - // Execute a post request. - $result = curl_exec($ch); - - // Close connection. - curl_close($ch); - - if (!$result) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - - $result_array = json_decode($result, true); - - // Check for errors. - $call_status = $result_array['call_status']; - if (!$call_status) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - if ($call_status['code'] != TT_CURL_SUCCESS) { - $this->errors->add($call_status['error']); - return false; - } - - $offer = $result_array['offer']; - return $offer; - } - - // updateWorkItem - updates a work item in remote work server. - function updateWorkItem($fields) { - global $i18n; - global $user; - $mdb2 = getConnection(); - - $curl_fields = array('lang' => urlencode($user->lang), - 'site_id' => urlencode($this->site_id), - 'site_key' => urlencode($this->site_key), - 'user_id' => urlencode($user->id), - 'work_id' => urlencode($fields['work_id']), - 'subject' => urlencode(base64_encode($fields['subject'])), - 'descr_short' => urlencode(base64_encode($fields['descr_short'])), - 'descr_long' => urlencode(base64_encode($fields['descr_long'])), - 'currency' => urlencode($fields['currency']), - 'amount' => urlencode($fields['amount']), - 'moderator_comment' => urlencode(base64_encode($fields['moderator_comment'])), - 'modified_ip' => urlencode($_SERVER['REMOTE_ADDR']), - 'modified_by' => urlencode($user->getUser()), - 'modified_by_name' => urlencode(base64_encode($user->getName())), - 'modified_by_email' => urlencode(base64_encode($user->getEmail())) - ); - - // url-ify the data for the POST. - foreach($curl_fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; } - $fields_string = rtrim($fields_string, '&'); - - // Open connection. - $ch = curl_init(); - - // Set the url, number of POST vars, POST data. - curl_setopt($ch, CURLOPT_URL, $this->update_work_item_uri); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - - // Execute a post request. - $result = curl_exec($ch); - - // Close connection. - curl_close($ch); - - if (!$result) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - - $result_array = json_decode($result, true); - - // Check for errors. - $call_status = $result_array['call_status']; - if (!$call_status) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - if ($call_status['code'] != TT_CURL_SUCCESS) { - $this->errors->add($call_status['error']); - return false; - } - - return true; - } - - // approveWorkItem - approves work item in remote work server. - function approveWorkItem($fields) { - global $i18n; - global $user; - $mdb2 = getConnection(); - - $curl_fields = array('lang' => urlencode($user->lang), - 'site_id' => urlencode($this->site_id), - 'site_key' => urlencode($this->site_key), - 'user_id' => urlencode($user->id), - 'work_id' => urlencode($fields['work_id']), - 'subject' => urlencode(base64_encode($fields['subject'])), - 'descr_short' => urlencode(base64_encode($fields['descr_short'])), - 'descr_long' => urlencode(base64_encode($fields['descr_long'])), - 'currency' => urlencode($fields['currency']), - 'amount' => urlencode($fields['amount']), - 'moderator_comment' => urlencode(base64_encode($fields['moderator_comment'])), - 'modified_ip' => urlencode($_SERVER['REMOTE_ADDR']), - 'modified_by' => urlencode($user->getUser()), - 'modified_by_name' => urlencode(base64_encode($user->getName())), - 'modified_by_email' => urlencode(base64_encode($user->getEmail()))); - - // url-ify the data for the POST. - foreach($curl_fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; } - $fields_string = rtrim($fields_string, '&'); - - // Open connection. - $ch = curl_init(); - - // Set the url, number of POST vars, POST data. - curl_setopt($ch, CURLOPT_URL, $this->approve_work_item_uri); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - - // Execute a post request. - $result = curl_exec($ch); - - // Close connection. - curl_close($ch); - - if (!$result) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - - $result_array = json_decode($result, true); - - // Check for errors. - $call_status = $result_array['call_status']; - if (!$call_status) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - if ($call_status['code'] != TT_CURL_SUCCESS) { - $this->errors->add($call_status['error']); - return false; - } - - return true; - } - - // disapproveWorkItem - disapproves work item in remote work server. - function disapproveWorkItem($fields) { - global $i18n; - global $user; - $mdb2 = getConnection(); - - $curl_fields = array('lang' => urlencode($user->lang), - 'site_id' => urlencode($this->site_id), - 'site_key' => urlencode($this->site_key), - 'user_id' => urlencode($user->id), - 'work_id' => urlencode($fields['work_id']), - 'subject' => urlencode(base64_encode($fields['subject'])), - 'descr_short' => urlencode(base64_encode($fields['descr_short'])), - 'descr_long' => urlencode(base64_encode($fields['descr_long'])), - 'currency' => urlencode($fields['currency']), - 'amount' => urlencode($fields['amount']), - 'moderator_comment' => urlencode(base64_encode($fields['moderator_comment'])), - 'modified_ip' => urlencode($_SERVER['REMOTE_ADDR']), - 'modified_by' => urlencode($user->getUser()), - 'modified_by_name' => urlencode(base64_encode($user->getName())), - 'modified_by_email' => urlencode(base64_encode($user->getEmail()))); - - // url-ify the data for the POST. - foreach($curl_fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; } - $fields_string = rtrim($fields_string, '&'); - // Open connection. - $ch = curl_init(); - - // Set the url, number of POST vars, POST data. - curl_setopt($ch, CURLOPT_URL, $this->disapprove_work_item_uri); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - - // Execute a post request. - $result = curl_exec($ch); - - // Close connection. - curl_close($ch); - - if (!$result) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - - $result_array = json_decode($result, true); - - // Check for errors. - $call_status = $result_array['call_status']; - if (!$call_status) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - if ($call_status['code'] != TT_CURL_SUCCESS) { - $this->errors->add($call_status['error']); - return false; - } - - return true; - } - - // disapproveWorkItemOnOffer - disapproves work item posted on offer in remote work server. - function disapproveWorkItemOnOffer($fields) { - global $i18n; - global $user; - $mdb2 = getConnection(); - - $curl_fields = array('lang' => urlencode($user->lang), - 'site_id' => urlencode($this->site_id), - 'site_key' => urlencode($this->site_key), - 'user_id' => urlencode($user->id), - 'work_id' => urlencode($fields['work_id']), - 'descr_short' => urlencode(base64_encode($fields['descr_short'])), - 'descr_long' => urlencode(base64_encode($fields['descr_long'])), - 'moderator_comment' => urlencode(base64_encode($fields['moderator_comment'])), - 'modified_ip' => urlencode($_SERVER['REMOTE_ADDR']), - 'modified_by' => urlencode($user->getUser()), - 'modified_by_name' => urlencode(base64_encode($user->getName())), - 'modified_by_email' => urlencode(base64_encode($user->getEmail()))); - - // url-ify the data for the POST. - foreach($curl_fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; } - $fields_string = rtrim($fields_string, '&'); - // Open connection. - $ch = curl_init(); - - // Set the url, number of POST vars, POST data. - curl_setopt($ch, CURLOPT_URL, $this->disapprove_work_item_on_offer_uri); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - - // Execute a post request. - $result = curl_exec($ch); - - // Close connection. - curl_close($ch); - - if (!$result) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - - $result_array = json_decode($result, true); - - // Check for errors. - $call_status = $result_array['call_status']; - if (!$call_status) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - if ($call_status['code'] != TT_CURL_SUCCESS) { - $this->errors->add($call_status['error']); - return false; - } - - return true; - } - - // approveWorkItemOnOffer - approves work item posted on offer in remote work server. - function approveWorkItemOnOffer($fields) { - global $i18n; - global $user; - $mdb2 = getConnection(); - - $curl_fields = array('lang' => urlencode($user->lang), - 'site_id' => urlencode($this->site_id), - 'site_key' => urlencode($this->site_key), - 'user_id' => urlencode($user->id), - 'work_id' => urlencode($fields['work_id']), - 'descr_short' => urlencode(base64_encode($fields['descr_short'])), - 'descr_long' => urlencode(base64_encode($fields['descr_long'])), - 'moderator_comment' => urlencode(base64_encode($fields['moderator_comment'])), - 'modified_ip' => urlencode($_SERVER['REMOTE_ADDR']), - 'modified_by' => urlencode($user->getUser()), - 'modified_by_name' => urlencode(base64_encode($user->getName())), - 'modified_by_email' => urlencode(base64_encode($user->getEmail()))); - - // url-ify the data for the POST. - foreach($curl_fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; } - $fields_string = rtrim($fields_string, '&'); - // Open connection. - $ch = curl_init(); - - // Set the url, number of POST vars, POST data. - curl_setopt($ch, CURLOPT_URL, $this->approve_work_item_on_offer_uri); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - - // Execute a post request. - $result = curl_exec($ch); - - // Close connection. - curl_close($ch); - - if (!$result) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - - $result_array = json_decode($result, true); - - // Check for errors. - $call_status = $result_array['call_status']; - if (!$call_status) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - if ($call_status['code'] != TT_CURL_SUCCESS) { - $this->errors->add($call_status['error']); - return false; - } - - return true; - } - - // updateOffer - updates an offer in remote work server. - function updateOffer($fields) { - global $i18n; - global $user; - $mdb2 = getConnection(); - - $curl_fields = array('lang' => urlencode($user->lang), - 'site_id' => urlencode($this->site_id), - 'site_key' => urlencode($this->site_key), - 'user_id' => urlencode($user->id), - 'offer_id' => urlencode($fields['offer_id']), - 'subject' => urlencode(base64_encode($fields['subject'])), - 'descr_short' => urlencode(base64_encode($fields['descr_short'])), - 'descr_long' => urlencode(base64_encode($fields['descr_long'])), - 'currency' => urlencode($fields['currency']), - 'amount' => urlencode($fields['amount']), - 'payment_info' => urlencode(base64_encode($fields['payment_info'])), - 'moderator_comment' => urlencode(base64_encode($fields['moderator_comment'])), - 'modified_ip' => urlencode($_SERVER['REMOTE_ADDR']), - 'modified_by' => urlencode($user->getUser()), - 'modified_by_name' => urlencode(base64_encode($user->getName())), - 'modified_by_email' => urlencode(base64_encode($user->getEmail())) - ); - - // url-ify the data for the POST. - foreach($curl_fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; } - $fields_string = rtrim($fields_string, '&'); - - // Open connection. - $ch = curl_init(); - - // Set the url, number of POST vars, POST data. - curl_setopt($ch, CURLOPT_URL, $this->update_offer_uri); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - - // Execute a post request. - $result = curl_exec($ch); - - // Close connection. - curl_close($ch); - - if (!$result) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - - $result_array = json_decode($result, true); - - // Check for errors. - $call_status = $result_array['call_status']; - if (!$call_status) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - if ($call_status['code'] != TT_CURL_SUCCESS) { - $this->errors->add($call_status['error']); - return false; - } - - return true; - } - - // approveOffer - approves offer in remote work server. - function approveOffer($fields) { - global $i18n; - global $user; - $mdb2 = getConnection(); - - $curl_fields = array('lang' => urlencode($user->lang), - 'site_id' => urlencode($this->site_id), - 'site_key' => urlencode($this->site_key), - 'user_id' => urlencode($user->id), - 'offer_id' => urlencode($fields['offer_id']), - 'subject' => urlencode(base64_encode($fields['subject'])), - 'descr_short' => urlencode(base64_encode($fields['descr_short'])), - 'descr_long' => urlencode(base64_encode($fields['descr_long'])), - 'currency' => urlencode($fields['currency']), - 'amount' => urlencode($fields['amount']), - 'moderator_comment' => urlencode(base64_encode($fields['moderator_comment'])), - 'modified_ip' => urlencode($_SERVER['REMOTE_ADDR']), - 'modified_by' => urlencode($user->getUser()), - 'modified_by_name' => urlencode(base64_encode($user->getName())), - 'modified_by_email' => urlencode(base64_encode($user->getEmail()))); - - // url-ify the data for the POST. - foreach($curl_fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; } - $fields_string = rtrim($fields_string, '&'); - - // Open connection. - $ch = curl_init(); - - // Set the url, number of POST vars, POST data. - curl_setopt($ch, CURLOPT_URL, $this->approve_offer_uri); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - - // Execute a post request. - $result = curl_exec($ch); - - // Close connection. - curl_close($ch); - - if (!$result) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - - $result_array = json_decode($result, true); - - // Check for errors. - $call_status = $result_array['call_status']; - if (!$call_status) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - if ($call_status['code'] != TT_CURL_SUCCESS) { - $this->errors->add($call_status['error']); - return false; - } - - return true; - } - - - // disapproveOffer - disapproves offer in remote work server. - function disapproveOffer($fields) { - global $i18n; - global $user; - $mdb2 = getConnection(); - - $curl_fields = array('lang' => urlencode($user->lang), - 'site_id' => urlencode($this->site_id), - 'site_key' => urlencode($this->site_key), - 'user_id' => urlencode($user->id), - 'offer_id' => urlencode($fields['offer_id']), - 'subject' => urlencode(base64_encode($fields['subject'])), - 'descr_short' => urlencode(base64_encode($fields['descr_short'])), - 'descr_long' => urlencode(base64_encode($fields['descr_long'])), - 'currency' => urlencode($fields['currency']), - 'amount' => urlencode($fields['amount']), - 'moderator_comment' => urlencode(base64_encode($fields['moderator_comment'])), - 'modified_ip' => urlencode($_SERVER['REMOTE_ADDR']), - 'modified_by' => urlencode($user->getUser()), - 'modified_by_name' => urlencode(base64_encode($user->getName())), - 'modified_by_email' => urlencode(base64_encode($user->getEmail()))); - - // url-ify the data for the POST. - foreach($curl_fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; } - $fields_string = rtrim($fields_string, '&'); - - // Open connection. - $ch = curl_init(); - - // Set the url, number of POST vars, POST data. - curl_setopt($ch, CURLOPT_URL, $this->disapprove_offer_uri); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - - // Execute a post request. - $result = curl_exec($ch); - - // Close connection. - curl_close($ch); - - if (!$result) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - - $result_array = json_decode($result, true); - - // Check for errors. - $call_status = $result_array['call_status']; - if (!$call_status) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - if ($call_status['code'] != TT_CURL_SUCCESS) { - $this->errors->add($call_status['error']); - return false; - } - - return true; - } - - // getItems - obtains a list of all items relevant to admin in one API call to Remote Work Server. - function getItems() { - global $i18n; - global $user; - $mdb2 = getConnection(); - - $group_id = $user->getGroup(); - $org_id = $user->org_id; - - $curl_fields = array('lang' => urlencode($user->lang), - 'site_id' => urlencode($this->site_id), - 'site_key' => urlencode($this->site_key), - 'user_id' => urlencode($user->id) - ); - - // url-ify the data for the POST. - foreach($curl_fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; } - $fields_string = rtrim($fields_string, '&'); - - // Open connection. - $ch = curl_init(); - - // Set the url, number of POST vars, POST data. - curl_setopt($ch, CURLOPT_URL, $this->get_items_uri); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - - // Execute a post request. - $result = curl_exec($ch); - - // Close connection. - curl_close($ch); - - if (!$result) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - - $result_array = json_decode($result, true); - - // Check for errors. - $call_status = $result_array['call_status']; - if (!$call_status) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - if ($call_status['code'] != TT_CURL_SUCCESS) { - $this->errors->add($call_status['error']); - return false; - } - - unset($result_array['call_status']); // Remove call_status element. - return $result_array; - } -} diff --git a/WEB-INF/lib/ttBehalfUser.class.php b/WEB-INF/lib/ttBehalfUser.class.php index 408602a10..e85770c1b 100644 --- a/WEB-INF/lib/ttBehalfUser.class.php +++ b/WEB-INF/lib/ttBehalfUser.class.php @@ -23,7 +23,7 @@ // | // +----------------------------------------------------------------------+ // | Contributors: -// | https://www.anuko.com/time_tracker/credits.htm +// | https://www.anuko.com/time-tracker/credits.htm // +----------------------------------------------------------------------+ import('ttConfigHelper'); diff --git a/WEB-INF/lib/ttChartHelper.class.php b/WEB-INF/lib/ttChartHelper.class.php index 60c9897cb..6550f5fa2 100644 --- a/WEB-INF/lib/ttChartHelper.class.php +++ b/WEB-INF/lib/ttChartHelper.class.php @@ -1,33 +1,11 @@ getGroup(); + $org_id = $user->org_id; + $period = null; switch ($interval_type) { case INTERVAL_THIS_DAY: - $period = new Period(INTERVAL_THIS_DAY, new DateAndTime(DB_DATEFORMAT, $selected_date)); + $period = new ttPeriod(new ttDate($selected_date), INTERVAL_THIS_DAY); break; case INTERVAL_THIS_WEEK: - $period = new Period(INTERVAL_THIS_WEEK, new DateAndTime(DB_DATEFORMAT, $selected_date)); + $period = new ttPeriod(new ttDate($selected_date), INTERVAL_THIS_WEEK); break; case INTERVAL_THIS_MONTH: - $period = new Period(INTERVAL_THIS_MONTH, new DateAndTime(DB_DATEFORMAT, $selected_date)); + $period = new ttPeriod(new ttDate($selected_date), INTERVAL_THIS_MONTH); + break; + + case INTERVAL_PREVIOUS_MONTH: + $period = new ttPeriod(new ttDate($selected_date), INTERVAL_PREVIOUS_MONTH); break; case INTERVAL_THIS_YEAR: - $period = new Period(INTERVAL_THIS_YEAR, new DateAndTime(DB_DATEFORMAT, $selected_date)); + $period = new ttPeriod(new ttDate($selected_date), INTERVAL_THIS_YEAR); break; } $result = array(); $mdb2 = getConnection(); + $userIdPart = ''; + if ($user_id > 0) { + // -1 here means "all users in group" both active and inactive. + // Therefore, we will not be using user id. + $userIdPart = "and l.user_id = $user_id"; + } + $q_period = ''; if ($period != null) { - $q_period = " and date >= '".$period->getStartDate(DB_DATEFORMAT)."' and date <= '".$period->getEndDate(DB_DATEFORMAT)."'"; + $q_period = "and date >= '".$period->getStartDate(DB_DATEFORMAT)."' and date <= '".$period->getEndDate(DB_DATEFORMAT)."'"; + } + if (CHART_PROJECTS == $chart_type) { + // Data for projects. + $sql = "select p.name as name, sum(time_to_sec(l.duration)) as time from tt_log l + left join tt_projects p on (p.id = l.project_id) + where l.status = 1 $userIdPart and l.group_id = $group_id and l.org_id = $org_id $q_period group by l.project_id"; + } elseif (CHART_TASKS == $chart_type) { + // Data for tasks. + $sql = "select t.name as name, sum(time_to_sec(l.duration)) as time from tt_log l + left join tt_tasks t on (t.id = l.task_id) + where l.status = 1 $userIdPart and l.group_id = $group_id and l.org_id = $org_id $q_period group by l.task_id"; + } elseif (CHART_CLIENTS == $chart_type) { + // Data for clients. + $sql = "select c.name as name, sum(time_to_sec(l.duration)) as time from tt_log l + left join tt_clients c on (c.id = l.client_id) + where l.status = 1 $userIdPart and l.group_id = $group_id and l.org_id = $org_id $q_period group by l.client_id"; + } + + $res = $mdb2->query($sql); + if (!is_a($res, 'PEAR_Error')) { + while ($val = $res->fetchRow()) { + if ($val['time'] > 0) // Only positive totals make sense in pie charts. Skip negatives entirely. + $result[] = array('name'=>$val['name'],'time'=>$val['time']); // name - entity name, time - total for entity in seconds. + } + } + + // Get total time. We'll need it to calculate percentages (for labels to the right of diagram). + $total = 0; + foreach ($result as $one_val) { + $total += $one_val['time']; + } + // Add a string representation of time + percentage to names. Example: "Time Tracker (1:15 - 6%)". + foreach ($result as &$one_val) { + $percent = round(100*$one_val['time']/$total).'%'; + $one_val['name'] .= ' ('.ttTimeHelper::minutesToDuration($one_val['time'] / 60).' - '.$percent.')'; + } + + // Note: the remaining code here is needed to display labels on the side of the diagram. + // We print labels ourselves (not using libchart.php) because quality of libchart labels is not good. + + // Note: Optimize this sorting and reversing. + $result = mu_sort($result, 'time'); + $result = array_reverse($result); // This is to assign correct colors to labels. + + // Add color to array items. This is used in labels on the side of a chart. + $colors = array( + array(2, 78, 0), + array(148, 170, 36), + array(233, 191, 49), + array(240, 127, 41), + array(243, 63, 34), + array(190, 71, 47), + array(135, 81, 60), + array(128, 78, 162), + array(121, 75, 255), + array(142, 165, 250), + array(162, 254, 239), + array(137, 240, 166), + array(104, 221, 71), + array(98, 174, 35), + array(93, 129, 1) + ); + for ($i = 0; $i < count($result); $i++) { + $color = $colors[$i%count($colors)]; + $result[$i]['color_html'] = sprintf('#%02x%02x%02x', $color[0], $color[1], $color[2]); } + + return $result; + } + + + // getTotals - returns total times by project, task, or client for a selected fav report. + static function getTotalsForFavReport($fav_report_id, $chart_type) { + + global $user; + + // Use custom fields plugin if it is enabled. + if ($user->isPluginEnabled('cf')) { + require_once(APP_DIR.'/plugins/CustomFields.class.php'); + $custom_fields = new CustomFields(); + } + + $result = array(); + $mdb2 = getConnection(); + + // Get favorite report details. + $options = ttFavReportHelper::getReportOptions($fav_report_id); + if (!$options) + return $result; // Return empty array if something went wrong. + + $options = ttFavReportHelper::adjustOptions($options); + + // Obtain the where clause for fav report. + $where = ttReportHelper::getWhere($options); + + // Build sql query according to selected chart type. if (CHART_PROJECTS == $chart_type) { // Data for projects. $sql = "select p.name as name, sum(time_to_sec(l.duration)) as time from tt_log l left join tt_projects p on (p.id = l.project_id) - where l.status = 1 and l.user_id = $user_id $q_period group by l.project_id"; + $where group by l.project_id"; } elseif (CHART_TASKS == $chart_type) { // Data for tasks. $sql = "select t.name as name, sum(time_to_sec(l.duration)) as time from tt_log l left join tt_tasks t on (t.id = l.task_id) - where l.status = 1 and l.user_id = $user_id $q_period group by l.task_id"; + $where group by l.task_id"; } elseif (CHART_CLIENTS == $chart_type) { // Data for clients. $sql = "select c.name as name, sum(time_to_sec(l.duration)) as time from tt_log l left join tt_clients c on (c.id = l.client_id) - where l.status = 1 and l.user_id = $user_id $q_period group by l.client_id"; + $where group by l.client_id"; } $res = $mdb2->query($sql); @@ -135,6 +224,7 @@ static function getTotals($user_id, $chart_type, $selected_date, $interval_type) return $result; } + // adjustType - adjust chart type to something that is available for a group. static function adjustType($requested_type) { global $user; diff --git a/WEB-INF/lib/ttClientHelper.class.php b/WEB-INF/lib/ttClientHelper.class.php index ba62d0f33..5c9dff349 100644 --- a/WEB-INF/lib/ttClientHelper.class.php +++ b/WEB-INF/lib/ttClientHelper.class.php @@ -358,4 +358,49 @@ static function deleteProjectFromClient($project_id, $client_id) { $affected = $mdb2->exec($sql); return (!is_a($affected, 'PEAR_Error')); } + + // unassignProjectFromAllClients - removes a project reference from all clients in tt_clients table + // and also from tt_client_project_binds. + static function unassignProjectFromAllClients($project_id) { + global $user; + $mdb2 = getConnection(); + + $group_id = $user->getGroup(); + $org_id = $user->org_id; + + $project_id = (int) $project_id; // Cast for sql injection protection, just in case. + + // Start with cleaning up tt_client_project_binds table. + $sql = "delete from tt_client_project_binds". + " where project_id = $project_id and group_id = $group_id and org_id = $org_id"; + $affected = $mdb2->exec($sql); + if (is_a($affected, 'PEAR_Error')) + return false; + + // Continue with tt_clients table. + $sql = "select id, projects from tt_clients where projects like '%$project_id%' and group_id = $group_id and org_id = $org_id"; + $res = $mdb2->query($sql); + while ($val = $res->fetchRow()) { + + $client_id = (int) $val['id']; + $projectListed = false; + + $projects = explode(',', $val['projects']); + if (($key = array_search($project_id, $projects)) !== false) { + unset($projects[$key]); + $projectListed = true; + } + if (!$projectListed) + continue; // Project not listed, continue iterating. + + // If we are here, project is listed and we need to remove it. + $comma_separated = implode(',', $projects); + $sql = "update tt_clients set projects = ".$mdb2->quote($comma_separated). + " where id = $client_id and group_id = $group_id and org_id = $org_id"; + $affected = $mdb2->exec($sql); + if (is_a($affected, 'PEAR_Error')) + return false; + } + return true; + } } diff --git a/WEB-INF/lib/ttConfigHelper.class.php b/WEB-INF/lib/ttConfigHelper.class.php index 33e94e513..95bc1e0f1 100644 --- a/WEB-INF/lib/ttConfigHelper.class.php +++ b/WEB-INF/lib/ttConfigHelper.class.php @@ -1,30 +1,6 @@ config = trim($config, ' ,'); + if (!is_null($config)) + $this->config = trim($config, ' ,'); if ($this->config) $this->config_array = explode(',', $this->config); } @@ -120,6 +97,6 @@ function setIntValue($name, $value) { // The getConfig returns the config string. function getConfig() { - return trim($this->config, ' ,'); + return (is_null($this->config) ? null : trim($this->config, ' ,')); } } diff --git a/WEB-INF/lib/ttCronJobHelper.class.php b/WEB-INF/lib/ttCronJobHelper.class.php index 8c44756c7..153938951 100644 --- a/WEB-INF/lib/ttCronJobHelper.class.php +++ b/WEB-INF/lib/ttCronJobHelper.class.php @@ -23,7 +23,7 @@ // | // +----------------------------------------------------------------------+ // | Contributors: -// | https://www.anuko.com/time_tracker/credits.htm +// | https://www.anuko.com/time-tracker/credits.htm // +----------------------------------------------------------------------+ import('ttRoleHelper'); diff --git a/WEB-INF/lib/ttDate.class.php b/WEB-INF/lib/ttDate.class.php new file mode 100644 index 000000000..25dc4874a --- /dev/null +++ b/WEB-INF/lib/ttDate.class.php @@ -0,0 +1,196 @@ +dateInDbDateFormat = $dateInDbDateFormat; + } else { + $today = date_create(); + $this->dateInDbDateFormat = date_format($today, 'Y-m-d'); + } + + $this->unixTimestamp = strtotime($this->dateInDbDateFormat); + $this->year = date('Y', $this->unixTimestamp); + $this->month = date('m', $this->unixTimestamp); + $this->day = date('d', $this->unixTimestamp); + $this->dayOfWeek = date('w', $this->unixTimestamp); + } + + + // Returns unix timestamp. + function getTimestamp() { + return $this->unixTimestamp; + } + + + // Resets the object properties from a passed in unix timestamp. + function setFromUnixTimestamp($unixTimestamp) { + $this->unixTimestamp = $unixTimestamp; + + $this->year = date('Y', $this->unixTimestamp); + $this->month = date('m', $this->unixTimestamp); + $this->day = date('d', $this->unixTimestamp); + $this->dayOfWeek = date('w', $this->unixTimestamp); + + $this->dateInDbDateFormat = $this->year.'-'.$this->month.'-'.$this->day; + } + + + // toString returns a date in specified format. + function toString($format = null) { + if ($format == null || $format == DB_DATEFORMAT) + return $this->dateInDbDateFormat; + else { + return $this->formatDate($format); + } + } + + + // formatDate returns a date string in specified format. + function formatDate($format) { + global $i18n; + + $formattedDate = $format; // Start with unmodified format string. + + // Replace all found elements with data. + $formattedDate = str_replace('%Y', $this->year, $formattedDate); + $formattedDate = str_replace('%m', $this->month, $formattedDate); + $formattedDate = str_replace('%d', $this->day, $formattedDate); + // Replace locale-dependent days of week. + $formattedDate = str_replace('%a', mb_substr($i18n->getWeekDayName($this->dayOfWeek), 0, 3, 'utf-8'), $formattedDate); + return $formattedDate; + } + + + function before(/*ttDate*/ $obj) { + if ($this->getTimestamp() < $obj->getTimestamp()) return true; + return false; + } + + + function after(/*ttDate*/ $obj) { + if ($this->getTimestamp() > $obj->getTimestamp()) return true; + return false; + } + + + function compare(/*ttDate*/ $obj) { + $ts1 = $this->getTimestamp(); + $ts2 = $obj->getTimestamp(); + if ($ts1 < $ts2) return -1; + if ($ts1 == $ts2) return 0; + if ($ts1 > $ts2) return 1; + } + + + // Getters. + function getYear() { return $this->year; } + function getMonth() { return $this->month; } + function getDay() { return $this->day; } + function getDayOfWeek() { return $this->dayOfWeek; } + + + // incrementDay increments our date by a number of days. + function incrementDay(/*int*/ $days = 1) { + $this->setFromUnixTimestamp(@mktime(0, 0, 0, $this->month, $this->day + $days, $this->year)); + } + + + // decrementDay decrements our date by a number of days. + function decrementDay(/*int*/ $days = 1) { + $this->setFromUnixTimestamp(@mktime(0, 0, 0, $this->month, $this->day - $days, $this->year)); + } + + + // A static function to obtain a date in DB_DATEFORMAT from a Unix timestamp. + static function dateFromUnixTimestamp($unixTimestamp = null) { + if ($unixTimestamp == null) { + $today = date_create(); + return date_format($today, 'Y-m-d'); + } + + $year = date('Y', $unixTimestamp); + $month = date('m', $unixTimestamp); + $day = date('d', $unixTimestamp); + $dateInDbFormat = $year.'-'.$month.'-'.$day; + return $dateInDbFormat; + } +} diff --git a/WEB-INF/lib/ttDebugTracer.class.php b/WEB-INF/lib/ttDebugTracer.class.php index 46469a908..33921e9dd 100644 --- a/WEB-INF/lib/ttDebugTracer.class.php +++ b/WEB-INF/lib/ttDebugTracer.class.php @@ -23,7 +23,7 @@ // | // +----------------------------------------------------------------------+ // | Contributors: -// | https://www.anuko.com/time_tracker/credits.htm +// | https://www.anuko.com/time-tracker/credits.htm // +----------------------------------------------------------------------+ import('ttConfigHelper'); diff --git a/WEB-INF/lib/ttFavReportHelper.class.php b/WEB-INF/lib/ttFavReportHelper.class.php index be23f6a39..2b2470439 100644 --- a/WEB-INF/lib/ttFavReportHelper.class.php +++ b/WEB-INF/lib/ttFavReportHelper.class.php @@ -3,6 +3,7 @@ License: See license.txt */ import('ttGroupHelper'); +import('ttDate'); // Class ttFavReportHelper is used to help with favorite report related tasks. class ttFavReportHelper { @@ -96,8 +97,8 @@ static function insertReport($fields) { $org_id = $user->org_id; $sql = "insert into tt_fav_reports". - " (name, user_id, group_id, org_id, report_spec, client_id, project_id, task_id,". - " billable, approved, invoice, timesheet, paid_status, users, period, period_start,". + " (name, user_id, group_id, org_id, report_spec, client_id, task_id,". + " billable, approved, invoice, timesheet, paid_status, note_containing, users, period, period_start,". " period_end, show_client, show_invoice, show_paid, show_ip,". " show_project, show_timesheet, show_start, show_duration, show_cost,". " show_task, show_end, show_note, show_approved, show_work_units,". @@ -106,10 +107,10 @@ static function insertReport($fields) { $mdb2->quote($fields['name']).", $user_id, $group_id, $org_id, ". $mdb2->quote($fields['report_spec']).", ". $mdb2->quote($fields['client']).", ". - $mdb2->quote($fields['project']).", ".$mdb2->quote($fields['task']).", ". + $mdb2->quote($fields['task']).", ". $mdb2->quote($fields['billable']).", ".$mdb2->quote($fields['approved']).", ". $mdb2->quote($fields['invoice']).", ".$mdb2->quote($fields['timesheet']).", ". - $mdb2->quote($fields['paid_status']).", ". + $mdb2->quote($fields['paid_status']).", ".$mdb2->quote($fields['note_containing']).",". $mdb2->quote($fields['users']).", ".$mdb2->quote($fields['period']).", ". $mdb2->quote($fields['from']).", ".$mdb2->quote($fields['to']).", ". $fields['chclient'].", ".$fields['chinvoice'].", ".$fields['chpaid'].", ".$fields['chip'].", ". @@ -138,13 +139,14 @@ static function updateReport($fields) { "name = ".$mdb2->quote($fields['name']).", ". "report_spec = ".$mdb2->quote($fields['report_spec']).", ". "client_id = ".$mdb2->quote($fields['client']).", ". - "project_id = ".$mdb2->quote($fields['project']).", ". + //"project_id = ".$mdb2->quote($fields['project']).", ". "task_id = ".$mdb2->quote($fields['task']).", ". "billable = ".$mdb2->quote($fields['billable']).", ". "approved = ".$mdb2->quote($fields['approved']).", ". "invoice = ".$mdb2->quote($fields['invoice']).", ". "timesheet = ".$mdb2->quote($fields['timesheet']).", ". "paid_status = ".$mdb2->quote($fields['paid_status']).", ". + "note_containing = ".$mdb2->quote($fields['note_containing']).", ". "users = ".$mdb2->quote($fields['users']).", ". "period = ".$mdb2->quote($fields['period']).", ". "period_start = ".$mdb2->quote($fields['from']).", ". @@ -212,12 +214,12 @@ static function saveReport($bean) { } if ($bean->getAttribute('start_date')) { - $dt = new DateAndTime($user->getDateFormat(), $bean->getAttribute('start_date')); - $from = $dt->toString(DB_DATEFORMAT); + $dt = new ttDate($bean->getAttribute('start_date'), $user->getDateFormat()); + $from = $dt->toString(); } if ($bean->getAttribute('end_date')) { - $dt = new DateAndTime($user->getDateFormat(), $bean->getAttribute('end_date')); - $to = $dt->toString(DB_DATEFORMAT); + $dt = new ttDate($bean->getAttribute('end_date'), $user->getDateFormat()); + $to = $dt->toString(); } $fields = array( @@ -225,13 +227,14 @@ static function saveReport($bean) { 'report_spec'=>ttFavReportHelper::makeReportSpec($bean), 'client'=>$bean->getAttribute('client'), 'option'=>$bean->getAttribute('option'), - 'project'=>$bean->getAttribute('project'), + //'project'=>$bean->getAttribute('project'), 'task'=>$bean->getAttribute('task'), 'billable'=>$bean->getAttribute('include_records'), 'approved'=>$bean->getAttribute('approved'), 'paid_status'=>$bean->getAttribute('paid_status'), 'invoice'=>$bean->getAttribute('invoice'), 'timesheet'=>$bean->getAttribute('timesheet'), + 'note_containing'=>$bean->getAttribute('note_containing'), 'users'=>$users, 'period'=>$bean->getAttribute('period'), 'from'=>$from, @@ -325,25 +328,44 @@ static function loadReport(&$bean) { $bean->setAttribute($checkbox_field_name, $checkbox_value); } } + // Project custom field settings. + if (isset($custom_fields) && $custom_fields->projectFields) { + foreach ($custom_fields->projectFields as $projectField) { + $field_name = 'project_field_'.$projectField['id']; + $checkbox_field_name = 'show_'.$field_name; + $field_value = ttFavReportHelper::getFieldSettingFromReportSpec($field_name, $report_spec); + $bean->setAttribute($field_name, $field_value); + $checkbox_value = ttFavReportHelper::getFieldSettingFromReportSpec($checkbox_field_name, $report_spec); + $bean->setAttribute($checkbox_field_name, $checkbox_value); + } + } + + // Project ids. + $project_ids = ttFavReportHelper::getFieldSettingFromReportSpec('project_ids', $report_spec); + if (isset($project_ids)) { + $projects = explode('&', $project_ids); + $bean->setAttribute('project', $projects); + } } $bean->setAttribute('client', $val['client_id']); - $bean->setAttribute('project', $val['project_id']); + // $bean->setAttribute('project', $val['project_id']); $bean->setAttribute('task', $val['task_id']); $bean->setAttribute('include_records', $val['billable']); $bean->setAttribute('approved', $val['approved']); $bean->setAttribute('invoice', $val['invoice']); $bean->setAttribute('paid_status', $val['paid_status']); $bean->setAttribute('timesheet', $val['timesheet']); + $bean->setAttribute('note_containing', $val['note_containing']); $bean->setAttribute('users_active', explode(',', $val['users'])); $bean->setAttribute('users_inactive', explode(',', $val['users'])); $bean->setAttribute('period', $val['period']); if ($val['period_start']) { - $dt = new DateAndTime(DB_DATEFORMAT, $val['period_start']); + $dt = new ttDate($val['period_start']); $bean->setAttribute('start_date', $dt->toString($user->getDateFormat())); } if ($val['period_end']) { - $dt = new DateAndTime(DB_DATEFORMAT, $val['period_end']); + $dt = new ttDate($val['period_end']); $bean->setAttribute('end_date', $dt->toString($user->getDateFormat())); } $bean->setAttribute('chclient', $val['show_client']); @@ -439,6 +461,47 @@ static function getReportOptions($id) { unset($options['id']); unset($options['status']); + // Cast to int some values similar to ttReportHelper::getReportOptions + // where we do it for extra protection against SQL injections. + // This step is redundant, though, as data validation is supposed to work. + // However, in case we missed something, casting to int reduces our risks. + $options['client_id'] = isset($options['client_id']) ? (int)$options['client_id'] : 0; + + // Obtain project ids from report_spec. + $project_ids = ttFavReportHelper::getFieldSettingFromReportSpec('project_ids', $options['report_spec']); + if (isset($project_ids)) { + $projects = explode('&', $project_ids); + $options['project_ids'] = join(',', $projects); + } + + $options['task_id'] = isset($options['task_id']) ? (int)$options['task_id'] : 0; + $options['billable'] = isset($options['billable']) ? (int)$options['billable'] : 0; + $options['invoice'] = isset($options['invoice']) ? (int)$options['invoice'] : 0; + $options['paid_status'] = isset($options['paid_status']) ? (int)$options['paid_status'] : 0; + $options['approved'] = isset($options['approved']) ? (int)$options['approved'] : 0; + $options['timesheet'] = isset($options['timesheet']) ? (int)$options['timesheet'] : 0; + $options['period'] = isset($options['period']) ? (int)$options['period'] : 0; + + $options['show_client'] = isset($options['show_client']) ? (int)$options['show_client'] : 0; + $options['show_invoice'] = isset($options['show_invoice']) ? (int)$options['show_invoice'] : 0; + $options['show_approved'] = isset($options['show_approved']) ? (int)$options['show_approved'] : 0; + $options['show_paid'] = isset($options['show_paid']) ? (int)$options['show_paid'] : 0; + $options['show_ip'] = isset($options['show_ip']) ? (int)$options['show_ip'] : 0; + $options['show_project'] = isset($options['show_project']) ? (int)$options['show_project'] : 0; + $options['show_start'] = isset($options['show_start']) ? (int)$options['show_start'] : 0; + $options['show_duration'] = isset($options['show_duration']) ? (int)$options['show_duration'] : 0; + $options['show_cost'] = isset($options['show_cost']) ? (int)$options['show_cost'] : 0; + $options['show_task'] = isset($options['show_task']) ? (int)$options['show_task'] : 0; + $options['show_end'] = isset($options['show_end']) ? (int)$options['show_end'] : 0; + $options['show_note'] = isset($options['show_note']) ? (int)$options['show_note'] : 0; + $options['show_work_units'] = isset($options['show_work_units']) ? (int)$options['show_work_units'] : 0; + $options['show_timesheet'] = isset($options['show_timesheet']) ? (int)$options['show_timesheet'] : 0; + $options['show_files'] = isset($options['show_files']) ? (int)$options['show_files'] : 0; + $options['show_totals_only'] = isset($options['show_totals_only']) ? (int)$options['show_totals_only'] : 0; + + // Note: We can't cast anything to int for custom fields because global $user object is not recycled + // yet for user id. See cron.php. Because of this we do it in adjustOptions instead. + // Note: special handling for NULL users field is done in cron.php // $options now is a subset of db fields from tt_fav_reports table. @@ -473,6 +536,7 @@ static function adjustOptions($options) { } } else { $users_to_adjust = explode(',', $options['users']); // Users to adjust. + // Adjust user list for a client. if ($user->isClient()) { $users = ttGroupHelper::getUsersForClient(); // Active and inactive users for clients. foreach ($users as $single_user) { @@ -485,7 +549,15 @@ static function adjustOptions($options) { } $options['users'] = implode(',', $adjusted_user_ids); } - // TODO: add checking the existing user list for potentially changed access rights for user. + // Reset user list for a role that no longer has view_reports or view_all_reports rights. + if (!($user->can('view_reports') || $user->can('view_all_reports'))) { + $options['users'] = null; + // Also remove grouping by user if we can't do it. + if (isset($options['group_by1']) && $options['group_by1'] == 'user') unset($options['group_by1']); + if (isset($options['group_by2']) && $options['group_by2'] == 'user') unset($options['group_by2']); + if (isset($options['group_by3']) && $options['group_by3'] == 'user') unset($options['group_by3']); + } + // TODO: improve checking the existing user list for potentially changed access rights for user. } if ($user->isPluginEnabled('ap') && $user->isClient() && !$user->can('view_client_unapproved')) @@ -501,9 +573,15 @@ static function adjustOptions($options) { $field_name = 'time_field_'.$timeField['id']; $checkbox_field_name = 'show_'.$field_name; $field_value = ttFavReportHelper::getFieldSettingFromReportSpec($field_name, $report_spec); - $options[$field_name] = $field_value; + if ($timeField['type'] == CustomFields::TYPE_DROPDOWN) { + // Cast to int for a dropdown for extra security. + $options[$field_name] = (int)$field_value; + } elseif ($timeField['type'] == CustomFields::TYPE_TEXT) { + // No need to cast to int for a text field. + $options[$field_name] = $field_value; + } $checkbox_value = ttFavReportHelper::getFieldSettingFromReportSpec($checkbox_field_name, $report_spec); - $options[$checkbox_field_name] = $checkbox_value; + $options[$checkbox_field_name] = (int)$checkbox_value; } } // User fields. @@ -512,12 +590,34 @@ static function adjustOptions($options) { $field_name = 'user_field_'.$userField['id']; $checkbox_field_name = 'show_'.$field_name; $field_value = ttFavReportHelper::getFieldSettingFromReportSpec($field_name, $report_spec); - $options[$field_name] = $field_value; + if ($userField['type'] == CustomFields::TYPE_DROPDOWN) { + // Cast to int for a dropdown for extra security. + $options[$field_name] = (int)$field_value; + } elseif ($userField['type'] == CustomFields::TYPE_TEXT) { + // No need to cast to int for a text field. + $options[$field_name] = $field_value; + } + $checkbox_value = ttFavReportHelper::getFieldSettingFromReportSpec($checkbox_field_name, $report_spec); + $options[$checkbox_field_name] = (int)$checkbox_value; + } + } + // Project fields. + if ($custom_fields && $custom_fields->projectFields) { + foreach ($custom_fields->projectFields as $projectField) { + $field_name = 'project_field_'.$projectField['id']; + $checkbox_field_name = 'show_'.$field_name; + $field_value = ttFavReportHelper::getFieldSettingFromReportSpec($field_name, $report_spec); + if ($projectField['type'] == CustomFields::TYPE_DROPDOWN) { + // Cast to int for a dropdown for extra security. + $options[$field_name] = (int)$field_value; + } elseif ($projectField['type'] == CustomFields::TYPE_TEXT) { + // No need to cast to int for a text field. + $options[$field_name] = $field_value; + } $checkbox_value = ttFavReportHelper::getFieldSettingFromReportSpec($checkbox_field_name, $report_spec); - $options[$checkbox_field_name] = $checkbox_value; + $options[$checkbox_field_name] = (int)$checkbox_value; } } - // TODO: add project fields here. } // Adjust period_start and period_end to user date format. @@ -566,6 +666,28 @@ static function makeReportSpec($bean) { } } + // Add project custom field settings. + if (isset($custom_fields) && $custom_fields->projectFields) { + foreach ($custom_fields->projectFields as $projectField) { + $field_name = 'project_field_'.$projectField['id']; + $field_value = str_replace(',',',',$bean->getAttribute($field_name)); + $checkbox_field_name = 'show_'.$field_name; + $checkbox_field_value = (int) $bean->getAttribute($checkbox_field_name); + if ($field_value) $reportSpecArray[] = $field_name.':'.$field_value; + if ($checkbox_field_value) $reportSpecArray[] = $checkbox_field_name.':1'; + } + } + + // Add projects ids. Format: project_ids:25&756&567&1023. Use the & sign to join multiple project ids. + $selected_projects_in_bean = $bean->getAttribute('project'); + if (!is_array($selected_projects_in_bean)) { + $selectedProjects = $bean->getAttribute('project[]'); + } + if (is_array($selected_projects_in_bean) && !empty($selected_projects_in_bean[0])) { + $selectedProjectsSpec = join('&', $selected_projects_in_bean); + } + if (isset($selectedProjectsSpec)) $reportSpecArray[] = 'project_ids:'.$selectedProjectsSpec; + $reportSpec = null; if (isset($reportSpecArray)) $reportSpec = implode(',', $reportSpecArray); diff --git a/WEB-INF/lib/ttGroupExportHelper.class.php b/WEB-INF/lib/ttGroupExportHelper.class.php index a464f2e25..ecd4f80ac 100644 --- a/WEB-INF/lib/ttGroupExportHelper.class.php +++ b/WEB-INF/lib/ttGroupExportHelper.class.php @@ -137,7 +137,8 @@ function writeData() { $group_part .= " workday_minutes=\"".$group['workday_minutes']."\""; $group_part .= " custom_logo=\"".$group['custom_logo']."\""; $group_part .= " config=\"".$group['config']."\""; - $group_part .= " custom_css=\"".$group['custom_css']."\""; + $group_part .= " custom_css=\"".htmlspecialchars($group['custom_css'])."\""; + $group_part .= " custom_translation=\"".htmlspecialchars($group['custom_translation'])."\""; $group_part .= ">\n"; // Write group info. @@ -424,6 +425,7 @@ function writeData() { $custom_field_option_part = $this->indentation.' '."customFieldOptionMap[$option['id']]."\""; $custom_field_option_part .= " field_id=\"".$this->customFieldMap[$option['field_id']]."\""; $custom_field_option_part .= " value=\"".htmlspecialchars($option['value'])."\""; + $custom_field_option_part .= " status=\"".$option['status']."\""; $custom_field_option_part .= ">\n"; fwrite($this->file, $custom_field_option_part); } @@ -566,6 +568,7 @@ function writeData() { $fav_report_part .= " invoice=\"".$fav_report['invoice']."\""; $fav_report_part .= " timesheet=\"".$fav_report['timesheet']."\""; $fav_report_part .= " paid_status=\"".$fav_report['paid_status']."\""; + $fav_report_part .= " note_containing=\"".$fav_report['note_containing']."\""; $fav_report_part .= " users=\"".$user_list."\""; $fav_report_part .= " period=\"".$fav_report['period']."\""; $fav_report_part .= " period_start=\"".$fav_report['period_start']."\""; diff --git a/WEB-INF/lib/ttGroupHelper.class.php b/WEB-INF/lib/ttGroupHelper.class.php index aa75a571e..81d1af696 100644 --- a/WEB-INF/lib/ttGroupHelper.class.php +++ b/WEB-INF/lib/ttGroupHelper.class.php @@ -27,9 +27,13 @@ static function getGroupName($group_id) { static function getParentGroup($group_id) { global $user; + // Checking parameters for sanity is normally done in access check blocks on pages. + // This cast below is just in case we forgot to check $group_id to be an integer. + $groupId = (int) $group_id; // Protection against sql injection. + $mdb2 = getConnection(); - $sql = "select parent_id from tt_groups where id = $group_id and org_id = $user->org_id and status = 1"; + $sql = "select parent_id from tt_groups where id = $groupId and org_id = $user->org_id and status = 1"; $res = $mdb2->query($sql); if (!is_a($res, 'PEAR_Error')) { @@ -427,10 +431,9 @@ static function getActiveInvoices($sort_options = false) $res = $mdb2->query($sql); $result = array(); if (!is_a($res, 'PEAR_Error')) { - $dt = new DateAndTime(DB_DATEFORMAT); while ($val = $res->fetchRow()) { // Localize date. - $dt->parseVal($val['date']); + $dt = new ttDate($val['date']); $val['date'] = $dt->toString($user->getDateFormat()); if ($addPaidStatus) $val['paid'] = ttInvoiceHelper::isPaid($val['id']); @@ -675,7 +678,6 @@ static function getRecentInvoices($client_id) { $res = $mdb2->query($sql); $result = array(); if (!is_a($res, 'PEAR_Error')) { - $dt = new DateAndTime(DB_DATEFORMAT); while ($val = $res->fetchRow()) { $result[] = $val; } diff --git a/WEB-INF/lib/ttInvoiceHelper.class.php b/WEB-INF/lib/ttInvoiceHelper.class.php index 9a20730ae..44206fa0e 100644 --- a/WEB-INF/lib/ttInvoiceHelper.class.php +++ b/WEB-INF/lib/ttInvoiceHelper.class.php @@ -3,7 +3,7 @@ License: See license.txt */ import('ttClientHelper'); -import('DateAndTime'); +import('ttDate'); // Class ttInvoiceHelper is used for help with invoices. class ttInvoiceHelper { @@ -159,9 +159,8 @@ static function getInvoiceItems($invoice_id) { $res = $mdb2->query($sql); if (!is_a($res, 'PEAR_Error')) { - $dt = new DateAndTime(DB_DATEFORMAT); while ($val = $res->fetchRow()) { - $dt->parseVal($val['date']); + $dt = new ttDate($val['date']); $val['date'] = $dt->toString($user->getDateFormat()); $result[] = $val; } @@ -226,11 +225,11 @@ static function invoiceableItemsExist($fields) { $client_id = (int) $fields['client_id']; - $start_date = new DateAndTime($user->date_format, $fields['start_date']); - $start = $start_date->toString(DB_DATEFORMAT); + $start_date = new ttDate($fields['start_date'], $user->getDateFormat()); + $start = $start_date->toString(); - $end_date = new DateAndTime($user->date_format, $fields['end_date']); - $end = $end_date->toString(DB_DATEFORMAT); + $end_date = new ttDate($fields['end_date'], $user->getDateFormat()); + $end = $end_date->toString(); $project_id = null; $project_part = ''; @@ -299,19 +298,18 @@ static function createInvoice($fields) { $group_id = $user->getGroup(); $org_id = $user->org_id; - $name = $fields['name']; - if (!$name) return false; + $name = isset($fields['name']) ? $fields['name'] : null; $client_id = (int) $fields['client_id']; - $invoice_date = new DateAndTime($user->date_format, $fields['date']); - $date = $invoice_date->toString(DB_DATEFORMAT); + $invoice_date = new ttDate($fields['date'], $user->getDateFormat()); + $date = $invoice_date->toString(); - $start_date = new DateAndTime($user->date_format, $fields['start_date']); - $start = $start_date->toString(DB_DATEFORMAT); + $start_date = new ttDate($fields['start_date'], $user->getDateFormat()); + $start = $start_date->toString(); - $end_date = new DateAndTime($user->date_format, $fields['end_date']); - $end = $end_date->toString(DB_DATEFORMAT); + $end_date = new ttDate($fields['end_date'], $user->getDateFormat()); + $end = $end_date->toString(); $project_id = null; $project_part = ''; diff --git a/WEB-INF/lib/ttNotificationHelper.class.php b/WEB-INF/lib/ttNotificationHelper.class.php index 35c309040..8189e5a9b 100644 --- a/WEB-INF/lib/ttNotificationHelper.class.php +++ b/WEB-INF/lib/ttNotificationHelper.class.php @@ -23,7 +23,7 @@ // | // +----------------------------------------------------------------------+ // | Contributors: -// | https://www.anuko.com/time_tracker/credits.htm +// | https://www.anuko.com/time-tracker/credits.htm // +----------------------------------------------------------------------+ // Class ttNotificationHelper is used to help with notification related tasks. diff --git a/WEB-INF/lib/ttOrgExportHelper.class.php b/WEB-INF/lib/ttOrgExportHelper.class.php index 2dd54f1b3..9380231e4 100644 --- a/WEB-INF/lib/ttOrgExportHelper.class.php +++ b/WEB-INF/lib/ttOrgExportHelper.class.php @@ -1,30 +1,6 @@ $attrs['WORKDAY_MINUTES'], 'custom_logo' => $attrs['CUSTOM_LOGO'], 'config' => $attrs['CONFIG'], - 'custom_css' => $attrs['CUSTOM_CSS'])); + 'custom_css' => $attrs['CUSTOM_CSS'], + 'custom_translation' => $attrs['CUSTOM_TRANSLATION'])); // Special handling for top group. if (!$this->org_id && $this->current_group_id) { @@ -167,6 +168,7 @@ function startElement($parser, $name, $attrs) { // We get here when processing tags for the current group. // Prepare a list of task ids. + $mapped_tasks = null; if ($attrs['TASKS']) { $tasks = explode(',', $attrs['TASKS']); foreach ($tasks as $id) @@ -193,6 +195,7 @@ function startElement($parser, $name, $attrs) { // We get here when processing tags for the current group. // Prepare a list of project ids. + $mapped_projects = array(); if ($attrs['PROJECTS']) { $projects = explode(',', $attrs['PROJECTS']); foreach ($projects as $id) @@ -218,7 +221,6 @@ function startElement($parser, $name, $attrs) { if ($name == 'USER') { // We get here when processing tags for the current group. - $role_id = $attrs['ROLE_ID'] === '0' ? $this->top_role_id : $this->currentGroupRoleMap[$attrs['ROLE_ID']]; // 0 (not null) means top manager role. $user_id = $this->insertUser(array( @@ -348,7 +350,8 @@ function startElement($parser, $name, $attrs) { 'group_id' => $this->current_group_id, 'org_id' => $this->org_id, 'field_id' => $this->currentGroupCustomFieldMap[$attrs['FIELD_ID']], - 'value' => $attrs['VALUE'])); + 'value' => $attrs['VALUE'], + 'status' => $attrs['STATUS'])); if ($custom_field_option_id) { // Add a mapping. $this->currentGroupCustomFieldOptionMap[$attrs['ID']] = $custom_field_option_id; @@ -461,6 +464,7 @@ function startElement($parser, $name, $attrs) { 'invoice' => $attrs['INVOICE'], 'timesheet' => $attrs['TIMESHEET'], 'paid_status' => $attrs['PAID_STATUS'], + 'note_containing' => $attrs['NOTE_CONTAINING'], 'users' => $user_list, 'period' => $attrs['PERIOD'], 'from' => $attrs['PERIOD_START'], @@ -682,7 +686,7 @@ private function createGroup($fields) { $columns = '(parent_id, org_id, group_key, name, description, currency, decimal_mark, lang, date_format, time_format,'. ' week_start, tracking_mode, project_required, record_type, bcc_email,'. ' allow_ip, password_complexity, plugins, lock_spec,'. - ' workday_minutes, config, custom_css, created, created_ip, created_by)'; + ' workday_minutes, config, custom_css, custom_translation, created, created_ip, created_by)'; $values = ' values ('; $values .= $mdb2->quote($fields['parent_id']); @@ -707,6 +711,7 @@ private function createGroup($fields) { $values .= ', '.(int)$fields['workday_minutes']; $values .= ', '.$mdb2->quote($fields['config']); $values .= ', '.$mdb2->quote($fields['custom_css']); + $values .= ', '.$mdb2->quote($fields['custom_translation']); $values .= ', now(), '.$mdb2->quote($_SERVER['REMOTE_ADDR']).', '.$user->id; $values .= ')'; @@ -898,7 +903,7 @@ private function insertProject($fields) $org_id = (int) $fields['org_id']; $name = $fields['name']; $description = $fields['description']; - $tasks = $fields['tasks']; + $tasks = isset($fields['tasks']) ? $fields['tasks'] : array(); $comma_separated = implode(',', $tasks); // This is a comma-separated list of associated task ids. $status = $fields['status']; @@ -1013,6 +1018,7 @@ private function insertClient($fields) $address = $fields['address']; $tax = $fields['tax']; $projects = $fields['projects']; + $comma_separated = null; if ($projects) $comma_separated = implode(',', $projects); // This is a comma-separated list of associated projects ids. $status = $fields['status']; @@ -1049,7 +1055,7 @@ private function insertFavReport($fields) { $sql = "insert into tt_fav_reports". " (name, user_id, group_id, org_id, report_spec, client_id, project_id, task_id,". - " billable, approved, invoice, timesheet, paid_status, users, period, period_start, period_end,". + " billable, approved, invoice, timesheet, paid_status, note_containing, users, period, period_start, period_end,". " show_client, show_invoice, show_paid, show_ip,". " show_project, show_timesheet, show_start, show_duration, show_cost,". " show_task, show_end, show_note, show_approved, show_work_units,". @@ -1060,7 +1066,7 @@ private function insertFavReport($fields) { $mdb2->quote($fields['project']).", ".$mdb2->quote($fields['task']).", ". $mdb2->quote($fields['billable']).", ".$mdb2->quote($fields['approved']).", ". $mdb2->quote($fields['invoice']).", ".$mdb2->quote($fields['timesheet']).", ". - $mdb2->quote($fields['paid_status']).", ". + $mdb2->quote($fields['paid_status']).", ".$mdb2->quote($fields['note_containing']).", ". $mdb2->quote($fields['users']).", ".$mdb2->quote($fields['period']).", ". $mdb2->quote($fields['from']).", ".$mdb2->quote($fields['to']).", ". $fields['chclient'].", ".$fields['chinvoice'].", ".$fields['chpaid'].", ".$fields['chip'].", ". @@ -1150,9 +1156,10 @@ private function insertCustomFieldOption($fields) { $org_id = (int) $fields['org_id']; $field_id = (int) $fields['field_id']; $value = $fields['value']; + $status = $fields['status']; - $sql = "insert into tt_custom_field_options (group_id, org_id, field_id, value)". - " values ($group_id, $org_id, $field_id, ".$mdb2->quote($value).")"; + $sql = "insert into tt_custom_field_options (group_id, org_id, field_id, value, status)". + " values ($group_id, $org_id, $field_id, ".$mdb2->quote($value).", ".$mdb2->quote($status).")"; $affected = $mdb2->exec($sql); if (is_a($affected, 'PEAR_Error')) return false; diff --git a/WEB-INF/lib/ttPeriod.class.php b/WEB-INF/lib/ttPeriod.class.php new file mode 100644 index 000000000..0dcbf71c1 --- /dev/null +++ b/WEB-INF/lib/ttPeriod.class.php @@ -0,0 +1,85 @@ +getWeekStart(); + + $t_arr = localtime($ttDateInstance->getTimestamp()); + $t_arr[5] = $t_arr[5] + 1900; + $startWeekBias = ($t_arr[6] < $weekStartDay) ? $weekStartDay - 7 : $weekStartDay; + + $this->startDate = new ttDate(); + $this->endDate = new ttDate(); + + switch ($period_type) { + case INTERVAL_THIS_DAY: + $this->startDate->setFromUnixTimestamp($ttDateInstance->getTimestamp()); + $this->endDate->setFromUnixTimestamp($ttDateInstance->getTimestamp()); + break; + + case INTERVAL_PREVIOUS_DAY: + $this->startDate->setFromUnixTimestamp(mktime(0,0,0,$t_arr[4]+1,$t_arr[3]-1,$t_arr[5])); + $this->endDate->setFromUnixTimestamp(mktime(0,0,0,$t_arr[4]+1,$t_arr[3]-1,$t_arr[5])); + break; + + case INTERVAL_THIS_WEEK: + $this->startDate->setFromUnixTimestamp(mktime(0,0,0,$t_arr[4]+1,$t_arr[3]-$t_arr[6]+$startWeekBias,$t_arr[5])); + $this->endDate->setFromUnixTimestamp(mktime(0,0,0,$t_arr[4]+1,$t_arr[3]-$t_arr[6]+6+$startWeekBias,$t_arr[5])); + break; + + case INTERVAL_PREVIOUS_WEEK: + $this->startDate->setFromUnixTimestamp(mktime(0,0,0,$t_arr[4]+1,$t_arr[3]-$t_arr[6]-7+$startWeekBias,$t_arr[5])); + $this->endDate->setFromUnixTimestamp(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->setFromUnixTimestamp(mktime(0,0,0,$t_arr[4]+1,1,$t_arr[5])); + $this->endDate->setFromUnixTimestamp(mktime(0,0,0,$t_arr[4]+2,0,$t_arr[5])); + break; + + case INTERVAL_PREVIOUS_MONTH: + $this->startDate->setFromUnixTimestamp(mktime(0,0,0,$t_arr[4],1,$t_arr[5])); + $this->endDate->setFromUnixTimestamp(mktime(0,0,0,$t_arr[4]+1,0,$t_arr[5])); + break; + + case INTERVAL_THIS_YEAR: + $this->startDate->setFromUnixTimestamp(mktime(0,0,0,1,1,$t_arr[5])); + $this->endDate->setFromUnixTimestamp(mktime(0,0,0,12,31,$t_arr[5])); + break; + } + } + + // Sets period to designated start and and dates. + function setPeriod($start_date, $end_date) { + $this->startDate = $start_date; + $this->endDate = $end_date; + } + + // Returns start date in specified format. + function getStartDate($format = null) { + return $this->startDate->toString($format); + } + + + // Returns end date in specified format. + function getEndDate($format = null) { + return $this->endDate->toString($format); + } +} \ No newline at end of file diff --git a/WEB-INF/lib/ttPredefinedExpenseHelper.class.php b/WEB-INF/lib/ttPredefinedExpenseHelper.class.php index c6bc3f356..6d60d533e 100644 --- a/WEB-INF/lib/ttPredefinedExpenseHelper.class.php +++ b/WEB-INF/lib/ttPredefinedExpenseHelper.class.php @@ -23,7 +23,7 @@ // | // +----------------------------------------------------------------------+ // | Contributors: -// | https://www.anuko.com/time_tracker/credits.htm +// | https://www.anuko.com/time-tracker/credits.htm // +----------------------------------------------------------------------+ diff --git a/WEB-INF/lib/ttProjectHelper.class.php b/WEB-INF/lib/ttProjectHelper.class.php index dd6e35cb8..303289688 100644 --- a/WEB-INF/lib/ttProjectHelper.class.php +++ b/WEB-INF/lib/ttProjectHelper.class.php @@ -23,7 +23,7 @@ // | // +----------------------------------------------------------------------+ // | Contributors: -// | https://www.anuko.com/time_tracker/credits.htm +// | https://www.anuko.com/time-tracker/credits.htm // +----------------------------------------------------------------------+ import('ttUserHelper'); @@ -166,7 +166,7 @@ static function getProjectByName($name) { } - // delete - deletes things associated with a project and marks the project as deleted. + // delete - deletes things associated with a project and marks the project and its custom fields as deleted. static function delete($id) { global $user; $mdb2 = getConnection(); @@ -220,6 +220,17 @@ static function delete($id) { if (!ttClientHelper::deleteProject($id)) return false; + // Mark project custom fields as deleted. + require_once(APP_DIR.'/plugins/CustomFields.class.php'); + $entity_type = CustomFields::ENTITY_PROJECT; + $modified_part = ', modified = now(), modified_ip = '.$mdb2->quote($_SERVER['REMOTE_ADDR']).', modified_by = '.$mdb2->quote($user->id); + $sql = "update tt_entity_custom_fields set status = null $modified_part". + " where entity_type = $entity_type and entity_id = $id". + " and group_id = $group_id and org_id = $org_id"; + $affected = $mdb2->exec($sql); + if (is_a($affected, 'PEAR_Error')) + return false; + // Update entities_modified, too. if (!ttGroupHelper::updateEntitiesModified()) return false; @@ -357,7 +368,12 @@ static function update($fields) { return false; } // End of updating tt_project_task_binds table. - + + // If we are making the project inactive, unassign it from all clients. + if (constant('INACTIVE') == $status) { + ttClientHelper::unassignProjectFromAllClients($project_id); + } + // Update project name, description, tasks and status in tt_projects table. $comma_separated = implode(",", $tasks_to_bind); // This is a comma-separated list of associated task ids. $sql = "update tt_projects set name = ".$mdb2->quote($name).", description = ".$mdb2->quote($description). diff --git a/WEB-INF/lib/ttRegistrator.class.php b/WEB-INF/lib/ttRegistrator.class.php index ae499a9df..8afbf665b 100644 --- a/WEB-INF/lib/ttRegistrator.class.php +++ b/WEB-INF/lib/ttRegistrator.class.php @@ -41,13 +41,13 @@ function __construct($fields, &$err) { function validate() { global $i18n; - if (!ttValidString($this->group_name)) + if (!ttValidString($this->group_name, false, MAX_NAME_CHARS)) $this->err->add($i18n->get('error.field'), $i18n->get('label.group_name')); - if (!ttValidString($this->currency, true)) + if (!ttValidString($this->currency, true, MAX_CURRENCY_CHARS)) $this->err->add($i18n->get('error.field'), $i18n->get('label.currency')); - if (!ttValidString($this->user_name)) + if (!ttValidString($this->user_name, false, MAX_NAME_CHARS)) $this->err->add($i18n->get('error.field'), $i18n->get('label.manager_name')); - if (!ttValidString($this->login)) + if (!ttValidString($this->login, false, MAX_NAME_CHARS)) $this->err->add($i18n->get('error.field'), $i18n->get('label.manager_login')); if (!ttValidString($this->password1)) $this->err->add($i18n->get('error.field'), $i18n->get('label.password')); diff --git a/WEB-INF/lib/ttReportHelper.class.php b/WEB-INF/lib/ttReportHelper.class.php index 68fc85b2d..fb802f95e 100644 --- a/WEB-INF/lib/ttReportHelper.class.php +++ b/WEB-INF/lib/ttReportHelper.class.php @@ -3,12 +3,12 @@ License: See license.txt */ import('ttClientHelper'); -import('DateAndTime'); -import('Period'); +import('ttDate'); +import('ttPeriod'); import('ttTimeHelper'); import('ttConfigHelper'); -require_once(dirname(__FILE__).'/../../plugins/CustomFields.class.php'); +require_once(APP_DIR.'/plugins/CustomFields.class.php'); // Definitions of types for timesheet dropdown. define('TIMESHEET_ALL', 0); // Include all records. @@ -46,18 +46,22 @@ static function getWhere($options) { $dropdown_parts .= ' and l.client_id = '.$options['client_id']; elseif ($user->isClient() && $user->client_id) $dropdown_parts .= ' and l.client_id = '.$user->client_id; - if ($options['project_id']) $dropdown_parts .= ' and l.project_id = '.$options['project_id']; + + if (isset($options['project_ids'])) + $dropdown_parts .= ' and l.project_id in ('.$options['project_ids'].')'; + // if ($options['project_id']) $dropdown_parts .= ' and l.project_id = '.$options['project_id']; // This was here for a single select. + if ($options['task_id']) $dropdown_parts .= ' and l.task_id = '.$options['task_id']; - if ($options['billable']=='1') $dropdown_parts .= ' and l.billable = 1'; - if ($options['billable']=='2') $dropdown_parts .= ' and l.billable = 0'; - if ($options['invoice']=='1') $dropdown_parts .= ' and l.invoice_id is not null'; - if ($options['invoice']=='2') $dropdown_parts .= ' and l.invoice_id is null'; + if ($options['billable']==1) $dropdown_parts .= ' and l.billable = 1'; + if ($options['billable']==2) $dropdown_parts .= ' and l.billable = 0'; + if ($options['invoice']==1) $dropdown_parts .= ' and l.invoice_id is not null'; + if ($options['invoice']==2) $dropdown_parts .= ' and l.invoice_id is null'; if ($options['timesheet']==TIMESHEET_NOT_ASSIGNED) $dropdown_parts .= ' and l.timesheet_id is null'; if ($options['timesheet']==TIMESHEET_ASSIGNED) $dropdown_parts .= ' and l.timesheet_id is not null'; - if ($options['approved']=='1') $dropdown_parts .= ' and l.approved = 1'; - if ($options['approved']=='2') $dropdown_parts .= ' and l.approved = 0'; - if ($options['paid_status']=='1') $dropdown_parts .= ' and l.paid = 1'; - if ($options['paid_status']=='2') $dropdown_parts .= ' and l.paid = 0'; + if ($options['approved']==1) $dropdown_parts .= ' and l.approved = 1'; + if ($options['approved']==2) $dropdown_parts .= ' and l.approved = 0'; + if ($options['paid_status']==1) $dropdown_parts .= ' and l.paid = 1'; + if ($options['paid_status']==2) $dropdown_parts .= ' and l.paid = 0'; // Add time custom fields. if (isset($custom_fields) && $custom_fields->timeFields) { @@ -108,26 +112,56 @@ static function getWhere($options) { } } + // Add project custom fields. + if (isset($custom_fields) && $custom_fields->projectFields) { + foreach ($custom_fields->projectFields as $projectField) { + $field_name = 'project_field_'.$projectField['id']; + $field_value = $options[$field_name]; + if ($projectField['type'] == CustomFields::TYPE_DROPDOWN && $field_value) { + $cfoTable = 'cfo'.$projectField['id']; + $dropdown_parts .= " and $cfoTable.id = $field_value"; + } + } + } + + // Continue preparing part for text custom fields using LIKE operator. + if (isset($custom_fields) && $custom_fields->projectFields) { + foreach ($custom_fields->projectFields as $projectField) { + $field_name = 'project_field_'.$projectField['id']; + $field_value = $options[$field_name]; + if ($projectField['type'] == CustomFields::TYPE_TEXT && $field_value) { + $ecfTableName = 'ecf'.$projectField['id']; + $cf_text_parts .= " and $ecfTableName.value like ".$mdb2->quote("%$field_value%"); + } + } + } + // Prepare sql query part for user list. - $userlist = $options['users'] ? $options['users'] : '-1'; + $userlist = isset($options['users']) ? $options['users'] : '-1'; if ($user->can('view_reports') || $user->can('view_all_reports') || $user->isClient()) $user_list_part = " and l.user_id in ($userlist)"; else $user_list_part = " and l.user_id = ".$user->getUser(); $user_list_part .= " and l.group_id = $group_id and l.org_id = $org_id"; + // Prepare part for note_containing using LIKE operator. + $note_containing_part = ''; + if (isset($options['note_containing']) && !empty($options['note_containing'])) { + $note_containing_part = " and l.comment like ".$mdb2->quote('%'.$options['note_containing'].'%'); + } + // Prepare sql query part for where. $dateFormat = $user->getDateFormat(); - if ($options['period']) - $period = new Period($options['period'], new DateAndTime($dateFormat)); + if (isset($options['period']) && $options['period']) + $period = new ttPeriod(new ttDate(), $options['period']); else { - $period = new Period(); + $period = new ttPeriod(new ttDate()); $period->setPeriod( - new DateAndTime($dateFormat, $options['period_start']), - new DateAndTime($dateFormat, $options['period_end'])); + new ttDate($options['period_start'], $dateFormat), + new ttDate($options['period_end'], $dateFormat)); } - $where = " where l.status = 1 and l.date >= '".$period->getStartDate(DB_DATEFORMAT)."' and l.date <= '".$period->getEndDate(DB_DATEFORMAT)."'". - " $user_list_part $dropdown_parts $cf_text_parts"; + $where = " where l.status = 1 and l.date >= '".$period->getStartDate()."' and l.date <= '".$period->getEndDate()."'". + $user_list_part.$dropdown_parts.$cf_text_parts.$note_containing_part; return $where; } @@ -150,16 +184,20 @@ static function getExpenseWhere($options) { $dropdown_parts .= ' and ei.client_id = '.$options['client_id']; elseif ($user->isClient() && $user->client_id) $dropdown_parts .= ' and ei.client_id = '.$user->client_id; - if ($options['project_id']) $dropdown_parts .= ' and ei.project_id = '.$options['project_id']; - if ($options['invoice']=='1') $dropdown_parts .= ' and ei.invoice_id is not null'; - if ($options['invoice']=='2') $dropdown_parts .= ' and ei.invoice_id is null'; + + if (isset($options['project_ids'])) + $dropdown_parts .= ' and ei.project_id in ('.$options['project_ids'].')'; + // if ($options['project_id']) $dropdown_parts .= ' and l.project_id = '.$options['project_id']; // This was here for a single select. + + if ($options['invoice']==1) $dropdown_parts .= ' and ei.invoice_id is not null'; + if ($options['invoice']==2) $dropdown_parts .= ' and ei.invoice_id is null'; if (isset($options['timesheet']) && ($options['timesheet']!=TIMESHEET_ALL && $options['timesheet']!=TIMESHEET_NOT_ASSIGNED)) { $dropdown_parts .= ' and 0 = 1'; // Expense items do not have a timesheet_id. } - if ($options['approved']=='1') $dropdown_parts .= ' and ei.approved = 1'; - if ($options['approved']=='2') $dropdown_parts .= ' and ei.approved = 0'; - if ($options['paid_status']=='1') $dropdown_parts .= ' and ei.paid = 1'; - if ($options['paid_status']=='2') $dropdown_parts .= ' and ei.paid = 0'; + if ($options['approved']==1) $dropdown_parts .= ' and ei.approved = 1'; + if ($options['approved']==2) $dropdown_parts .= ' and ei.approved = 0'; + if ($options['paid_status']==1) $dropdown_parts .= ' and ei.paid = 1'; + if ($options['paid_status']==2) $dropdown_parts .= ' and ei.paid = 0'; // Not adding conditions for time custom fields by design because expenses are not associated with them. // Whether or not this is proper, we'll know eventually if users complain. @@ -190,26 +228,56 @@ static function getExpenseWhere($options) { } } + // Add project custom fields. + if (isset($custom_fields) && $custom_fields->projectFields) { + foreach ($custom_fields->projectFields as $projectField) { + $field_name = 'project_field_'.$projectField['id']; + $field_value = $options[$field_name]; + if ($projectField['type'] == CustomFields::TYPE_DROPDOWN && $field_value) { + $cfoTable = 'cfo'.$projectField['id']; + $dropdown_parts .= " and $cfoTable.id = $field_value"; + } + } + } + + // Continue preparing parts for text custom fields using LIKE operator. + if (isset($custom_fields) && $custom_fields->projectFields) { + foreach ($custom_fields->projectFields as $projectField) { + $field_name = 'project_field_'.$projectField['id']; + $field_value = $options[$field_name]; + if ($projectField['type'] == CustomFields::TYPE_TEXT && $field_value) { + $ecfTableName = 'ecf'.$projectField['id']; + $cf_text_parts .= " and $ecfTableName.value like ".$mdb2->quote("%$field_value%"); + } + } + } + // Prepare sql query part for user list. - $userlist = $options['users'] ? $options['users'] : '-1'; + $userlist = isset($options['users']) ? $options['users'] : '-1'; if ($user->can('view_reports') || $user->can('view_all_reports') || $user->isClient()) $user_list_part = " and ei.user_id in ($userlist)"; else $user_list_part = " and ei.user_id = ".$user->getUser(); $user_list_part .= " and ei.group_id = $group_id and ei.org_id = $org_id"; + // Prepare part for note_containing using LIKE operator. + $note_containing_part = ''; + if (isset($options['note_containing']) && !empty($options['note_containing'])) { + $note_containing_part = " and ei.name like ".$mdb2->quote('%'.$options['note_containing'].'%'); + } + // Prepare sql query part for where. $dateFormat = $user->getDateFormat(); - if ($options['period']) - $period = new Period($options['period'], new DateAndTime($dateFormat)); + if (isset($options['period']) && $options['period']) + $period = new ttPeriod(new ttDate(), $options['period']); else { - $period = new Period(); + $period = new ttPeriod(new ttDate()); $period->setPeriod( - new DateAndTime($dateFormat, $options['period_start']), - new DateAndTime($dateFormat, $options['period_end'])); + new ttDate($options['period_start'], $dateFormat), + new ttDate($options['period_end'], $dateFormat)); } - $where = " where ei.status = 1 and ei.date >= '".$period->getStartDate(DB_DATEFORMAT)."' and ei.date <= '".$period->getEndDate(DB_DATEFORMAT)."'". - " $user_list_part $dropdown_parts $cf_text_parts"; + $where = " where ei.status = 1 and ei.date >= '".$period->getStartDate()."' and ei.date <= '".$period->getEndDate()."'". + $user_list_part.$dropdown_parts.$cf_text_parts.$note_containing_part; return $where; } @@ -294,6 +362,22 @@ static function getItems($options) { } } } + // Add project custom fields. + if (isset($custom_fields) && $custom_fields->projectFields) { + foreach ($custom_fields->projectFields as $projectField) { + $field_name = 'project_field_'.$projectField['id']; + $checkbox_field_name = 'show_'.$field_name; + if ($options[$checkbox_field_name] || ttReportHelper::groupingBy($field_name, $options)) { + if ($projectField['type'] == CustomFields::TYPE_TEXT) { + $ecfTableName = 'ecf'.$projectField['id']; + array_push($fields, "$ecfTableName.value as $field_name"); + } elseif ($projectField['type'] == CustomFields::TYPE_DROPDOWN) { + $cfoTableName = 'cfo'.$projectField['id']; + array_push($fields, "$cfoTableName.value as $field_name"); + } + } + } + } // Add start time. if ($options['show_start']) { array_push($fields, "l.start as unformatted_start"); @@ -321,12 +405,19 @@ static function getItems($options) { // Handle cost. $includeCost = $options['show_cost']; if ($includeCost) { - if (MODE_TIME == $trackingMode) + $includeCostPerHour = $user->getConfigOption('report_cost_per_hour'); + if (MODE_TIME == $trackingMode) { + if ($includeCostPerHour) + array_push($fields, "cast(l.billable * coalesce(u.rate, 0) as decimal(10,2)) as cost_per_hour"); // Use default user rate. array_push($fields, "cast(l.billable * coalesce(u.rate, 0) * time_to_sec(l.duration)/3600 as decimal(10,2)) as cost"); // Use default user rate. - else + } else { + if ($includeCostPerHour) + array_push($fields, "cast(l.billable * coalesce(upb.rate, 0) as decimal(10,2)) as cost_per_hour"); // Use project rate for user. array_push($fields, "cast(l.billable * coalesce(upb.rate, 0) * time_to_sec(l.duration)/3600 as decimal(10,2)) as cost"); // Use project rate for user. + } array_push($fields, "null as expense"); } + // Add the fields used to determine if we show an edit icon for record. array_push($fields, 'l.approved'); array_push($fields, 'l.timesheet_id'); @@ -353,8 +444,7 @@ static function getItems($options) { // Prepare sql query part for left joins. $left_joins = null; - // Left joins for custom fields. - // 1 join is required for each text field, 2 joins for each dropdown. + // Left joins for user custom fields. if (isset($custom_fields) && $custom_fields->userFields) { foreach ($custom_fields->userFields as $userField) { $field_name = 'user_field_'.$userField['id']; @@ -374,6 +464,26 @@ static function getItems($options) { } } } + // Left joins for project custom fields. + if (isset($custom_fields) && $custom_fields->projectFields) { + foreach ($custom_fields->projectFields as $projectField) { + $field_name = 'project_field_'.$projectField['id']; + $checkbox_field_name = 'show_'.$field_name; + $entity_type = CustomFields::ENTITY_PROJECT; + if ($options[$field_name] || $options[$checkbox_field_name] || ttReportHelper::groupingBy($field_name, $options)) { + $ecfTable = 'ecf'.$projectField['id']; + if ($projectField['type'] == CustomFields::TYPE_TEXT) { + // Add one join for each text field. + $left_joins .= " left join tt_entity_custom_fields $ecfTable on ($ecfTable.entity_type = $entity_type and $ecfTable.entity_id = l.project_id and $ecfTable.field_id = ".$projectField['id'].")"; + } elseif ($projectField['type'] == CustomFields::TYPE_DROPDOWN) { + $cfoTable = 'cfo'.$projectField['id']; + // Add two joins for each dropdown field. + $left_joins .= " left join tt_entity_custom_fields $ecfTable on ($ecfTable.entity_type = $entity_type and $ecfTable.entity_id = l.project_id and $ecfTable.field_id = ".$projectField['id'].")"; + $left_joins .= " left join tt_custom_field_options $cfoTable on ($cfoTable.field_id = $ecfTable.field_id and $cfoTable.id = $ecfTable.option_id)"; + } + } + } + } if ($options['show_client'] || $grouping_by_client) $left_joins .= " left join tt_clients c on (c.id = l.client_id)"; if (($canViewReports || $isClient) && $options['show_invoice']) @@ -476,6 +586,22 @@ static function getItems($options) { } } } + // Add project custom fields. + if (isset($custom_fields) && $custom_fields->projectFields) { + foreach ($custom_fields->projectFields as $projectField) { + $field_name = 'project_field_'.$projectField['id']; + $checkbox_field_name = 'show_'.$field_name; + if ($options[$checkbox_field_name] || ttReportHelper::groupingBy($field_name, $options)) { + if ($projectField['type'] == CustomFields::TYPE_TEXT) { + $ecfTableName = 'ecf'.$projectField['id']; + array_push($fields, "$ecfTableName.value as $field_name"); + } elseif ($projectField['type'] == CustomFields::TYPE_DROPDOWN) { + $cfoTableName = 'cfo'.$projectField['id']; + array_push($fields, "$cfoTableName.value as $field_name"); + } + } + } + } if ($options['show_start']) { array_push($fields, 'null'); // null for unformatted_start. array_push($fields, 'null'); // null for start. @@ -489,6 +615,8 @@ static function getItems($options) { // Use the note field to print item name. if ($options['show_note']) array_push($fields, 'ei.name as note'); + if ($user->getConfigOption('report_cost_per_hour')) + array_push($fields, 'null as cost_per_hour'); // null for cost_per_hour. array_push($fields, 'ei.cost as cost'); array_push($fields, 'ei.cost as expense'); // Add the fields used to determine if we show an edit icon for record. @@ -536,6 +664,26 @@ static function getItems($options) { } } } + // Left joins for project custom fields. + if (isset($custom_fields) && $custom_fields->projectFields) { + foreach ($custom_fields->projectFields as $projectField) { + $field_name = 'project_field_'.$projectField['id']; + $checkbox_field_name = 'show_'.$field_name; + $entity_type = CustomFields::ENTITY_PROJECT; + if ($options[$field_name] || $options[$checkbox_field_name] || ttReportHelper::groupingBy($field_name, $options)) { + $ecfTable = 'ecf'.$projectField['id']; + if ($projectField['type'] == CustomFields::TYPE_TEXT) { + // Add one join for each text field. + $left_joins .= " left join tt_entity_custom_fields $ecfTable on ($ecfTable.entity_type = $entity_type and $ecfTable.entity_id = ei.project_id and $ecfTable.field_id = ".$projectField['id'].")"; + } elseif ($projectField['type'] == CustomFields::TYPE_DROPDOWN) { + $cfoTable = 'cfo'.$projectField['id']; + // Add two joins for each dropdown field. + $left_joins .= " left join tt_entity_custom_fields $ecfTable on ($ecfTable.entity_type = $entity_type and $ecfTable.entity_id = ei.project_id and $ecfTable.field_id = ".$projectField['id'].")"; + $left_joins .= " left join tt_custom_field_options $cfoTable on ($cfoTable.field_id = $ecfTable.field_id and $cfoTable.id = $ecfTable.option_id)"; + } + } + } + } if ($canViewReports || $isClient) $left_joins .= " left join tt_users u on (u.id = ei.user_id)"; if ($options['show_client'] || $grouping_by_client) @@ -571,7 +719,7 @@ static function getItems($options) { } else { $sort_part .= 'date'; } - if (($canViewReports || $isClient) && $options['users'] && !$grouping_by_user) + if (($canViewReports || $isClient) && isset($options['users']) && !$grouping_by_user) $sort_part .= ', user, type'; if ($options['show_start']) $sort_part .= ', unformatted_start'; @@ -584,6 +732,7 @@ static function getItems($options) { $res = $mdb2->query($sql); if (is_a($res, 'PEAR_Error')) die($res->getMessage()); + $report_items = array(); while ($val = $res->fetchRow()) { if ($convertTo12Hour) { if($val['start'] != '') @@ -591,6 +740,10 @@ static function getItems($options) { if($val['finish'] != '') $val['finish'] = ttTimeHelper::to12HourFormat($val['finish']); } + if (isset($val['cost_per_hour'])) { + if ('.' != $decimalMark) + $val['cost_per_hour'] = str_replace('.', $decimalMark, $val['cost_per_hour']); + } if (isset($val['cost'])) { if ('.' != $decimalMark) $val['cost'] = str_replace('.', $decimalMark, $val['cost']); @@ -692,6 +845,8 @@ static function getSubtotals($options) { // Execute query. $res = $mdb2->query($sql); if (is_a($res, 'PEAR_Error')) die($res->getMessage()); + + $subtotals = array(); while ($val = $res->fetchRow()) { $time = ttTimeHelper::minutesToDuration($val['time'] / 60); $rowLabel = ttReportHelper::makeGroupByLabel($val['group_field'], $options); @@ -798,6 +953,26 @@ static function getTotals($options) } } } + if (isset($custom_fields) && $custom_fields->projectFields) { + foreach ($custom_fields->projectFields as $projectField) { + $field_name = 'project_field_'.$projectField['id']; + $field_value = $options[$field_name]; + $entity_type = CustomFields::ENTITY_PROJECT; + if ($field_value) { + // We need to add left joins when input is not null. + $ecfTable = 'ecf'.$projectField['id']; + if ($projectField['type'] == CustomFields::TYPE_TEXT) { + // Add one join for each text field. + $left_joins .= " left join tt_entity_custom_fields $ecfTable on ($ecfTable.entity_type = $entity_type and $ecfTable.entity_id = l.project_id and $ecfTable.field_id = ".$projectField['id'].")"; + } elseif ($projectField['type'] == CustomFields::TYPE_DROPDOWN) { + $cfoTable = 'cfo'.$projectField['id']; + // Add two joins for each dropdown field. + $left_joins .= " left join tt_entity_custom_fields $ecfTable on ($ecfTable.entity_type = $entity_type and $ecfTable.entity_id = l.project_id and $ecfTable.field_id = ".$projectField['id'].")"; + $left_joins .= " left join tt_custom_field_options $cfoTable on ($cfoTable.field_id = $ecfTable.field_id and $cfoTable.id = $ecfTable.option_id)"; + } + } + } + } if (isset($options['show_cost']) && $options['show_cost']) { if (MODE_TIME == $trackingMode) { $left_joins .= " left join tt_users u on (l.user_id = u.id)"; @@ -848,6 +1023,26 @@ static function getTotals($options) } } } + if (isset($custom_fields) && $custom_fields->projectFields) { + foreach ($custom_fields->projectFields as $projectField) { + $field_name = 'project_field_'.$projectField['id']; + $field_value = $options[$field_name]; + $entity_type = CustomFields::ENTITY_PROJECT; + if ($field_value) { + // We need to add left joins when input is not null. + $ecfTable = 'ecf'.$projectField['id']; + if ($projectField['type'] == CustomFields::TYPE_TEXT) { + // Add one join for each text field. + $left_joins .= " left join tt_entity_custom_fields $ecfTable on ($ecfTable.entity_type = $entity_type and $ecfTable.entity_id = ei.project_id and $ecfTable.field_id = ".$projectField['id'].")"; + } elseif ($projectField['type'] == CustomFields::TYPE_DROPDOWN) { + $cfoTable = 'cfo'.$projectField['id']; + // Add two joins for each dropdown field. + $left_joins .= " left join tt_entity_custom_fields $ecfTable on ($ecfTable.entity_type = $entity_type and $ecfTable.entity_id = ei.project_id and $ecfTable.field_id = ".$projectField['id'].")"; + $left_joins .= " left join tt_custom_field_options $cfoTable on ($cfoTable.field_id = $ecfTable.field_id and $cfoTable.id = $ecfTable.option_id)"; + } + } + } + } $sql_for_expenses .= ", sum(cost) as cost, sum(cost) as expenses from tt_expense_items ei $left_joins $where"; @@ -878,17 +1073,16 @@ static function getTotals($options) $dateFormat = $user->getDateFormat(); if (isset($options['period']) && $options['period']) - $period = new Period($options['period'], new DateAndTime($dateFormat)); + $period = new ttPeriod(new ttDate(), $options['period']); else { - $period = new Period(); - if (isset($options['period_start']) && isset($options['period_end'])) - $period->setPeriod( - new DateAndTime($dateFormat, $options['period_start']), - new DateAndTime($dateFormat, $options['period_end'])); + $period = new ttPeriod(new ttDate()); + $period->setPeriod( + new ttDate($options['period_start'], $dateFormat), + new ttDate($options['period_end'], $dateFormat)); } - $totals['start_date'] = $period->getStartDate(); - $totals['end_date'] = $period->getEndDate(); + $totals['start_date'] = $period->getStartDate($user->getDateFormat()); + $totals['end_date'] = $period->getEndDate($user->getDateFormat()); $totals['time'] = $total_time; $totals['minutes'] = $val['time'] / 60; $totals['units'] = isset($val['units']) ? $val['units'] : null; @@ -1010,11 +1204,11 @@ static function prepareReportBody($options, $comment = null) $config = new ttConfigHelper($user->getConfig()); $show_note_column = $options['show_note'] && !$config->getDefinedValue('report_note_on_separate_row'); $show_note_row = $options['show_note'] && $config->getDefinedValue('report_note_on_separate_row'); + $show_cost_per_hour = $options['show_cost'] && $config->getDefinedValue('report_cost_per_hour') && ($user->can('manage_invoices') || $user->isClient()); $items = ttReportHelper::getItems($options); $grouping = ttReportHelper::grouping($options); - if ($grouping) - $subtotals = ttReportHelper::getSubtotals($options); + $subtotals = $grouping ? ttReportHelper::getSubtotals($options) : array(); $totals = ttReportHelper::getTotals($options); // Use custom fields plugin if it is enabled. @@ -1046,6 +1240,14 @@ static function prepareReportBody($options, $comment = null) } if ($options['show_client']) $colspan++; if ($options['show_project']) $colspan++; + // Project custom fields. + if ($custom_fields && $custom_fields->projectFields) { + foreach ($custom_fields->projectFields as $projectField) { + $field_name = 'project_field_'.$projectField['id']; + $checkbox_control_name = 'show_'.$field_name; + if ($options[$checkbox_control_name]) $colspan++; + } + } if ($options['show_task']) $colspan++; // Time custom fields. if ($custom_fields && $custom_fields->timeFields) { @@ -1059,6 +1261,7 @@ static function prepareReportBody($options, $comment = null) if ($options['show_end']) $colspan++; if ($options['show_duration']) $colspan++; if ($options['show_work_units']) $colspan++; + if ($show_cost_per_hour) $colspan++; if ($options['show_cost']) $colspan++; if ($options['show_approved']) $colspan++; if ($options['show_paid']) $colspan++; @@ -1155,6 +1358,14 @@ static function prepareReportBody($options, $comment = null) $body .= ''.$i18n->get('label.client').''; if ($options['show_project']) $body .= ''.$i18n->get('label.project').''; + // Project custom fields. + if ($custom_fields && $custom_fields->projectFields) { + foreach ($custom_fields->projectFields as $projectField) { + $field_name = 'project_field_'.$projectField['id']; + $checkbox_control_name = 'show_'.$field_name; + if ($options[$checkbox_control_name]) $body .= ''.htmlspecialchars($projectField['label']).''; + } + } if ($options['show_task']) $body .= ''.$i18n->get('label.task').''; // Time custom fields. @@ -1175,6 +1386,8 @@ static function prepareReportBody($options, $comment = null) $body .= ''.$i18n->get('label.work_units_short').''; if ($show_note_column) $body .= ''.$i18n->get('label.note').''; + if ($show_cost_per_hour) + $body .= ''.$i18n->get('form.report.per_hour').''; if ($options['show_cost']) $body .= ''.$i18n->get('label.cost').''; if ($options['show_approved']) @@ -1223,6 +1436,14 @@ static function prepareReportBody($options, $comment = null) } if ($options['show_client']) $body .= ''.$subtotals[$prev_grouped_by]['client'].''; if ($options['show_project']) $body .= ''.$subtotals[$prev_grouped_by]['project'].''; + // Project custom fields. + if ($custom_fields && $custom_fields->projectFields) { + foreach ($custom_fields->projectFields as $projectField) { + $field_name = 'project_field_'.$projectField['id']; + $checkbox_control_name = 'show_'.$field_name; + if ($options[$checkbox_control_name]) $body .= ''; + } + } if ($options['show_task']) $body .= ''.$subtotals[$prev_grouped_by]['task'].''; // Time custom fields. if ($custom_fields && $custom_fields->timeFields) { @@ -1237,6 +1458,7 @@ static function prepareReportBody($options, $comment = null) if ($options['show_duration']) $body .= ''.$subtotals[$prev_grouped_by]['time'].''; if ($options['show_work_units']) $body .= ''.$subtotals[$prev_grouped_by]['units'].''; if ($show_note_column) $body .= ''; + if ($show_cost_per_hour) $body .= ''; if ($options['show_cost']) { $body .= ''; $body .= ($canViewReports || $isClient) ? $subtotals[$prev_grouped_by]['cost'] : $subtotals[$prev_grouped_by]['expenses']; @@ -1272,6 +1494,14 @@ static function prepareReportBody($options, $comment = null) $body .= ''.htmlspecialchars($record['client']).''; if ($options['show_project']) $body .= ''.htmlspecialchars($record['project']).''; + // Project custom fields. + if ($custom_fields && $custom_fields->projectFields) { + foreach ($custom_fields->projectFields as $projectField) { + $field_name = 'project_field_'.$projectField['id']; + $checkbox_control_name = 'show_'.$field_name; + if ($options[$checkbox_control_name]) $body .= ''.htmlspecialchars($record[$field_name]).''; + } + } if ($options['show_task']) $body .= ''.htmlspecialchars($record['task']).''; // Time custom fields. @@ -1292,6 +1522,8 @@ static function prepareReportBody($options, $comment = null) $body .= ''.$record['units'].''; if ($show_note_column) $body .= ''.htmlspecialchars($record['note']).''; + if ($show_cost_per_hour) + $body .= ''.$record['cost_per_hour'].''; if ($options['show_cost']) $body .= ''.$record['cost'].''; if ($options['show_approved']) { @@ -1312,7 +1544,7 @@ static function prepareReportBody($options, $comment = null) if ($options['show_invoice']) $body .= ''.htmlspecialchars($record['invoice']).''; if ($options['show_timesheet']) - $body .= ''.htmlspecialchars($record['timesheet']).''; + $body .= ''.htmlspecialchars($record['timesheet_name']).''; $body .= ''; if ($show_note_row && $record['note']) { $body .= ''; @@ -1342,6 +1574,14 @@ static function prepareReportBody($options, $comment = null) } if ($options['show_client']) $body .= ''.$subtotals[$prev_grouped_by]['client'].''; if ($options['show_project']) $body .= ''.$subtotals[$prev_grouped_by]['project'].''; + // Project custom fields. + if ($custom_fields && $custom_fields->projectFields) { + foreach ($custom_fields->projectFields as $projectField) { + $field_name = 'project_field_'.$projectField['id']; + $checkbox_control_name = 'show_'.$field_name; + if ($options[$checkbox_control_name]) $body .= ''; + } + } if ($options['show_task']) $body .= ''.$subtotals[$prev_grouped_by]['task'].''; // Time custom fields. if ($custom_fields && $custom_fields->timeFields) { @@ -1356,6 +1596,7 @@ static function prepareReportBody($options, $comment = null) if ($options['show_duration']) $body .= ''.$subtotals[$cur_grouped_by]['time'].''; if ($options['show_work_units']) $body .= ''.$subtotals[$cur_grouped_by]['units'].''; if ($show_note_column) $body .= ''; + if ($show_cost_per_hour) $body .= ''; if ($options['show_cost']) { $body .= ''; $body .= ($canViewReports || $isClient) ? $subtotals[$cur_grouped_by]['cost'] : $subtotals[$cur_grouped_by]['expenses']; @@ -1384,6 +1625,14 @@ static function prepareReportBody($options, $comment = null) } if ($options['show_client']) $body .= ''; if ($options['show_project']) $body .= ''; + // Project custom fields. + if ($custom_fields && $custom_fields->projectFields) { + foreach ($custom_fields->projectFields as $projectField) { + $field_name = 'project_field_'.$projectField['id']; + $checkbox_control_name = 'show_'.$field_name; + if ($options[$checkbox_control_name]) $body .= ''; + } + } if ($options['show_task']) $body .= ''; // Time custom fields. if ($custom_fields && $custom_fields->timeFields) { @@ -1398,6 +1647,7 @@ static function prepareReportBody($options, $comment = null) if ($options['show_duration']) $body .= ''.$totals['time'].''; if ($options['show_work_units']) $body .= ''.$totals['units'].''; if ($show_note_column) $body .= ''; + if ($show_cost_per_hour) $body .= ''; if ($options['show_cost']) { $body .= ''.htmlspecialchars($user->currency).' '; $body .= ($canViewReports || $isClient) ? $totals['cost'] : $totals['expenses']; @@ -1513,19 +1763,31 @@ static function getReportOptions($bean) { // Construct one by one. $options['name'] = null; // No name required. - $options['user_id'] = $user->id; // Not sure if we need user_id here. Fav reports use it to recycle $user object in cron.php. - $options['client_id'] = $bean->getAttribute('client'); - $options['project_id'] = $bean->getAttribute('project'); - $options['task_id'] = $bean->getAttribute('task'); - $options['billable'] = $bean->getAttribute('include_records'); - $options['invoice'] = $bean->getAttribute('invoice'); - $options['paid_status'] = $bean->getAttribute('paid_status'); - $options['approved'] = $bean->getAttribute('approved'); + // $options['user_id'] = $user->id; // We don't use user_id in regular reports. But fav reports use it to recycle $user object in cron.php. + $options['client_id'] = (int)$bean->getAttribute('client'); + + // Handle selected projects. Just in case check both "project[]" and "project". + $selected_projects_in_bean = $bean->getAttribute('project[]'); + if (is_array($selected_projects_in_bean) && !empty($selected_projects_in_bean[0])) { + $options['project_ids'] = join(',', $selected_projects_in_bean); + } + $selected_projects_in_bean = $bean->getAttribute('project'); + if (is_array($selected_projects_in_bean) && !empty($selected_projects_in_bean[0])) { + $options['project_ids'] = join(',', $selected_projects_in_bean); + } + // $options['project_id'] = (int)$bean->getAttribute('project'); // This was here for a single project select. + + $options['task_id'] = (int)$bean->getAttribute('task'); + $options['billable'] = (int)$bean->getAttribute('include_records'); + $options['invoice'] = (int)$bean->getAttribute('invoice'); + $options['paid_status'] = (int)$bean->getAttribute('paid_status'); + $options['approved'] = (int)$bean->getAttribute('approved'); if ($user->isPluginEnabled('ap') && $user->isClient() && !$user->can('view_client_unapproved')) $options['approved'] = 1; // Restrict clients to approved records only. - $options['timesheet'] = $bean->getAttribute('timesheet'); + $options['timesheet'] = (int)$bean->getAttribute('timesheet'); $active_users_in_bean = $bean->getAttribute('users_active'); + $users = ''; if ($active_users_in_bean && is_array($active_users_in_bean)) { $users = join(',', $active_users_in_bean); } @@ -1536,24 +1798,25 @@ static function getReportOptions($bean) { } if ($users) $options['users'] = $users; - $options['period'] = $bean->getAttribute('period'); + $options['period'] = (int)$bean->getAttribute('period'); $options['period_start'] = $bean->getAttribute('start_date'); $options['period_end'] = $bean->getAttribute('end_date'); - $options['show_client'] = $bean->getAttribute('chclient'); - $options['show_invoice'] = $bean->getAttribute('chinvoice'); - $options['show_approved'] = $bean->getAttribute('chapproved'); - $options['show_paid'] = $bean->getAttribute('chpaid'); - $options['show_ip'] = $bean->getAttribute('chip'); - $options['show_project'] = $bean->getAttribute('chproject'); - $options['show_start'] = $bean->getAttribute('chstart'); - $options['show_duration'] = $bean->getAttribute('chduration'); - $options['show_cost'] = $bean->getAttribute('chcost'); - $options['show_task'] = $bean->getAttribute('chtask'); - $options['show_end'] = $bean->getAttribute('chfinish'); - $options['show_note'] = $bean->getAttribute('chnote'); - $options['show_work_units'] = $bean->getAttribute('chunits'); - $options['show_timesheet'] = $bean->getAttribute('chtimesheet'); - $options['show_files'] = $bean->getAttribute('chfiles'); + $options['note_containing'] = $bean->getAttribute('note_containing'); + $options['show_client'] = (int)$bean->getAttribute('chclient'); + $options['show_invoice'] = (int)$bean->getAttribute('chinvoice'); + $options['show_approved'] = (int)$bean->getAttribute('chapproved'); + $options['show_paid'] = (int)$bean->getAttribute('chpaid'); + $options['show_ip'] = (int)$bean->getAttribute('chip'); + $options['show_project'] = (int)$bean->getAttribute('chproject'); + $options['show_start'] = (int)$bean->getAttribute('chstart'); + $options['show_duration'] = (int)$bean->getAttribute('chduration'); + $options['show_cost'] = (int)$bean->getAttribute('chcost'); + $options['show_task'] = (int)$bean->getAttribute('chtask'); + $options['show_end'] = (int)$bean->getAttribute('chfinish'); + $options['show_note'] = (int)$bean->getAttribute('chnote'); + $options['show_work_units'] = (int)$bean->getAttribute('chunits'); + $options['show_timesheet'] = (int)$bean->getAttribute('chtimesheet'); + $options['show_files'] = (int)$bean->getAttribute('chfiles'); // Prepare custom field options. if ($user->isPluginEnabled('cf')) { @@ -1565,8 +1828,14 @@ static function getReportOptions($bean) { foreach ($custom_fields->timeFields as $timeField) { $control_name = 'time_field_'.$timeField['id']; $checkbox_control_name = 'show_'.$control_name; - $options[$control_name] = $bean->getAttribute($control_name); - $options[$checkbox_control_name] = $bean->getAttribute($checkbox_control_name); + if ($timeField['type'] == CustomFields::TYPE_DROPDOWN) { + // Cast to int for a dropdown for extra security. + $options[$control_name] = (int)$bean->getAttribute($control_name); + } elseif ($timeField['type'] == CustomFields::TYPE_TEXT) { + // No need to cast to int for a text field. + $options[$control_name] = $bean->getAttribute($control_name); + } + $options[$checkbox_control_name] = (int)$bean->getAttribute($checkbox_control_name); } } @@ -1575,18 +1844,38 @@ static function getReportOptions($bean) { foreach ($custom_fields->userFields as $userField) { $control_name = 'user_field_'.$userField['id']; $checkbox_control_name = 'show_'.$control_name; - $options[$control_name] = $bean->getAttribute($control_name); - $options[$checkbox_control_name] = $bean->getAttribute($checkbox_control_name); + if ($userField['type'] == CustomFields::TYPE_DROPDOWN) { + // Cast to int for a dropdown for extra security. + $options[$control_name] = (int)$bean->getAttribute($control_name); + } elseif ($userField['type'] == CustomFields::TYPE_TEXT) { + // No need to cast to int for a text field. + $options[$control_name] = $bean->getAttribute($control_name); + } + $options[$checkbox_control_name] = (int)$bean->getAttribute($checkbox_control_name); } } - // TODO: add project fields here. + // Project fields. + if ($custom_fields->projectFields) { + foreach ($custom_fields->projectFields as $projectField) { + $control_name = 'project_field_'.$projectField['id']; + $checkbox_control_name = 'show_'.$control_name; + if ($projectField['type'] == CustomFields::TYPE_DROPDOWN) { + // Cast to int for a dropdown for extra security. + $options[$control_name] = (int)$bean->getAttribute($control_name); + } elseif ($projectField['type'] == CustomFields::TYPE_TEXT) { + // No need to cast to int for a text field. + $options[$control_name] = $bean->getAttribute($control_name); + } + $options[$checkbox_control_name] = (int)$bean->getAttribute($checkbox_control_name); + } + } } $options['group_by1'] = $bean->getAttribute('group_by1'); $options['group_by2'] = $bean->getAttribute('group_by2'); $options['group_by3'] = $bean->getAttribute('group_by3'); - $options['show_totals_only'] = $bean->getAttribute('chtotalsonly'); + $options['show_totals_only'] = (int)$bean->getAttribute('chtotalsonly'); return $options; } @@ -1594,6 +1883,9 @@ static function getReportOptions($bean) { static function verifyBean($bean) { global $user; + // Check period control. + if (!ttValidInteger($bean->getAttribute('period'), true)) return false; + // Check users. $active_users_in_bean = (array) $bean->getAttribute('users_active'); $inactive_users_in_bean = (array) $bean->getAttribute('users_inactive'); @@ -1614,10 +1906,162 @@ static function verifyBean($bean) { } } - // TODO: add additional checks here. Perhaps do it before saving the bean for consistency. + // Check fav report id. + $fav_report_id = $bean->getAttribute('favorite_report'); + if (!($fav_report_id == -1 || (ttValidInteger($fav_report_id) && ttFavReportHelper::get($fav_report_id)))) return false; + + // Check client id. + if (!ttValidInteger($bean->getAttribute('client'), true)) return false; + + // Check billable control. + if (!ttValidInteger($bean->getAttribute('include_records'), true)) return false; + + // Check invoiced / not invoiced control. + if (!ttValidInteger($bean->getAttribute('invoice'), true)) return false; + + // Check selected project ids. Just in case check both project[] and project. + $selectedProjects = $bean->getAttribute('project'); + if (is_array($selectedProjects)) { + foreach ($selectedProjects as $singleProjectId) { + if (!ttValidInteger($singleProjectId, true)) return false; + } + } + $selectedProjects = $bean->getAttribute('project[]'); + if (is_array($selectedProjects)) { + foreach ($selectedProjects as $singleProjectId) { + if (!ttValidInteger($singleProjectId, true)) return false; + } + } + + // Check task id. + if (!ttValidInteger($bean->getAttribute('task'), true)) return false; + + // Check paid status control. + if (!ttValidInteger($bean->getAttribute('paid_status'), true)) return false; + + // Check approved status control. + if (!ttValidInteger($bean->getAttribute('approved'), true)) return false; + + // Check timesheet status control. + if (!ttValidInteger($bean->getAttribute('timesheet'), true)) return false; + + // Validate checkboxes. + if (!ttValidCheckbox($bean->getAttribute('chclient'))) return false; + if (!ttValidCheckbox($bean->getAttribute('chproject'))) return false; + if (!ttValidCheckbox($bean->getAttribute('chtask'))) return false; + if (!ttValidCheckbox($bean->getAttribute('chstart'))) return false; + if (!ttValidCheckbox($bean->getAttribute('chfinish'))) return false; + if (!ttValidCheckbox($bean->getAttribute('chduration'))) return false; + if (!ttValidCheckbox($bean->getAttribute('chnote'))) return false; + if (!ttValidCheckbox($bean->getAttribute('chunits'))) return false; + if (!ttValidCheckbox($bean->getAttribute('chcost'))) return false; + if (!ttValidCheckbox($bean->getAttribute('chapproved'))) return false; + if (!ttValidCheckbox($bean->getAttribute('chpaid'))) return false; + if (!ttValidCheckbox($bean->getAttribute('chip'))) return false; + if (!ttValidCheckbox($bean->getAttribute('chinvoice'))) return false; + if (!ttValidCheckbox($bean->getAttribute('chtimesheet'))) return false; + if (!ttValidCheckbox($bean->getAttribute('chfiles'))) return false; + if (!ttValidCheckbox($bean->getAttribute('chtotalsonly'))) return false; + + // Verify custom field controls. + if ($user->isPluginEnabled('cf')) { + global $custom_fields; + if (!$custom_fields) $custom_fields = new CustomFields(); + + // Time fields. + if ($custom_fields->timeFields) { + foreach ($custom_fields->timeFields as $timeField) { + $control_name = 'time_field_'.$timeField['id']; + $checkbox_control_name = 'show_'.$control_name; + if ($timeField['type'] == CustomFields::TYPE_DROPDOWN) { + if (!ttValidInteger($bean->getAttribute($control_name), true)) + return false; + } + if (!ttValidCheckbox($bean->getAttribute($checkbox_control_name), true)) + return false; + } + } + + // User fields. + if ($custom_fields->userFields) { + foreach ($custom_fields->userFields as $userField) { + $control_name = 'user_field_'.$userField['id']; + $checkbox_control_name = 'show_'.$control_name; + if ($userField['type'] == CustomFields::TYPE_DROPDOWN) { + if (!ttValidInteger($bean->getAttribute($control_name), true)) + return false; + } + if (!ttValidCheckbox($bean->getAttribute($checkbox_control_name), true)) + return false; + } + } + + // Project fields. + if ($custom_fields->projectFields) { + foreach ($custom_fields->projectFields as $projectField) { + $control_name = 'project_field_'.$projectField['id']; + $checkbox_control_name = 'show_'.$control_name; + if ($projectField['type'] == CustomFields::TYPE_DROPDOWN) { + if (!ttValidInteger($bean->getAttribute($control_name), true)) + return false; + } + if (!ttValidCheckbox($bean->getAttribute($checkbox_control_name), true)) + return false; + } + } + } + + if (!ttReportHelper::validGroupByOption($bean->getAttribute('group_by1'))) return false; + if (!ttReportHelper::validGroupByOption($bean->getAttribute('group_by2'))) return false; + if (!ttReportHelper::validGroupByOption($bean->getAttribute('group_by3'))) return false; + return true; } + // validGroupByOption is a security function to verify selection options for group by controls. + static function validGroupByOption($groupByOption) { + + global $user; + + if ($groupByOption == 'no_grouping' || $groupByOption == 'date' || $groupByOption == 'user' || $groupByOption == 'client' || $groupByOption == 'project' || $groupByOption == 'task' || $groupByOption == null) + return true; + + // Verify custom field group by options. + if ($user->isPluginEnabled('cf')) { + global $custom_fields; + if (!$custom_fields) $custom_fields = new CustomFields(); + + // Time fields. + if ($custom_fields->timeFields) { + foreach ($custom_fields->timeFields as $timeField) { + $control_name = 'time_field_'.$timeField['id']; + if ($groupByOption == $control_name) + return true; + } + } + + // User fields. + if ($custom_fields->userFields) { + foreach ($custom_fields->userFields as $userField) { + $control_name = 'user_field_'.$userField['id']; + if ($groupByOption == $control_name) + return true; + } + } + + // Project fields. + if ($custom_fields->projectFields) { + foreach ($custom_fields->projectFields as $projectField) { + $control_name = 'project_field_'.$projectField['id']; + if ($groupByOption == $control_name) + return true; + } + } + } // if ($user->isPluginEnabled('cf')) { + + return false; + } + // makeGroupByKey builds a combined group by key from group_by1, group_by2 and group_by3 values // (passed in $options) and a row of data ($row obtained from a db query). static function makeGroupByKey($options, $row) { @@ -1758,6 +2202,19 @@ static function makeSingleDropdownConcatCustomFieldPart($dropdown_value) { } } } + // Iterate through project custom fields. + if ($custom_fields && $custom_fields->projectFields) { + foreach ($custom_fields->projectFields as $projectField) { + $field_name = 'project_field_'.$projectField['id']; + if ($dropdown_value == $field_name) { + if ($projectField['type'] == CustomFields::TYPE_TEXT) { + return (", ' - ', coalesce(ecf".$projectField['id'].".value, 'Null')"); + } elseif ($projectField['type'] == CustomFields::TYPE_DROPDOWN) { + return (", ' - ', coalesce(cfo".$projectField['id'].".value, 'Null')"); + } + } + } + } // Iterate through time custom fields. if ($custom_fields && $custom_fields->timeFields) { foreach ($custom_fields->timeFields as $timeField) { @@ -1792,6 +2249,19 @@ static function makeSingleDropdownConcatCustomFieldExpensesPart($dropdown_value) } } } + // Iterate through project custom fields. + if ($custom_fields && $custom_fields->projectFields) { + foreach ($custom_fields->projectFields as $projectField) { + $field_name = 'project_field_'.$projectField['id']; + if ($dropdown_value == $field_name) { + if ($projectField['type'] == CustomFields::TYPE_TEXT) { + return (", ' - ', coalesce(ecf".$projectField['id'].".value, 'Null')"); + } elseif ($projectField['type'] == CustomFields::TYPE_DROPDOWN) { + return (", ' - ', coalesce(cfo".$projectField['id'].".value, 'Null')"); + } + } + } + } // Iterate through time custom fields. if ($custom_fields && $custom_fields->timeFields) { foreach ($custom_fields->timeFields as $timeField) { @@ -1845,6 +2315,19 @@ static function makeSingleDropdownGroupByCustomFieldPart($dropdown_value) { } } } + // Iterate through project custom fields. + if ($custom_fields && $custom_fields->projectFields) { + foreach ($custom_fields->projectFields as $projectField) { + $field_name = 'project_field_'.$projectField['id']; + if ($dropdown_value == $field_name) { + if ($projectField['type'] == CustomFields::TYPE_TEXT) { + return (", ecf".$projectField['id'].".value as $field_name"); + } elseif ($projectField['type'] == CustomFields::TYPE_DROPDOWN) { + return (", cfo".$projectField['id'].".value as $field_name"); + } + } + } + } // Iterate through time custom fields. if ($custom_fields && $custom_fields->timeFields) { foreach ($custom_fields->timeFields as $timeField) { @@ -1879,6 +2362,19 @@ static function makeSingleDropdownGroupByCustomFieldExpensesPart($dropdown_value } } } + // Iterate through project custom fields. + if ($custom_fields && $custom_fields->projectFields) { + foreach ($custom_fields->projectFields as $projectField) { + $field_name = 'project_field_'.$projectField['id']; + if ($dropdown_value == $field_name) { + if ($projectField['type'] == CustomFields::TYPE_TEXT) { + return (", ecf".$projectField['id'].".value as $field_name"); + } elseif ($projectField['type'] == CustomFields::TYPE_DROPDOWN) { + return (", cfo".$projectField['id'].".value as $field_name"); + } + } + } + } // Iterate through time custom fields. if ($custom_fields && $custom_fields->timeFields) { foreach ($custom_fields->timeFields as $timeField) { @@ -2114,6 +2610,27 @@ static function makeJoinPart($options) { } } } + // Left joins for project custom fields. + if (isset($custom_fields) && $custom_fields->projectFields) { + foreach ($custom_fields->projectFields as $projectField) { + $field_name = 'project_field_'.$projectField['id']; + $field_value = $options[$field_name]; + $entity_type = CustomFields::ENTITY_PROJECT; + if ($field_value || ttReportHelper::groupingBy($field_name, $options)) { + // We need to add left joins when input is not null. + $ecfTable = 'ecf'.$projectField['id']; + if ($projectField['type'] == CustomFields::TYPE_TEXT) { + // Add one join for each text field. + $left_joins .= " left join tt_entity_custom_fields $ecfTable on ($ecfTable.entity_type = $entity_type and $ecfTable.entity_id = l.project_id and $ecfTable.field_id = ".$projectField['id'].")"; + } elseif ($projectField['type'] == CustomFields::TYPE_DROPDOWN) { + $cfoTable = 'cfo'.$projectField['id']; + // Add two joins for each dropdown field. + $left_joins .= " left join tt_entity_custom_fields $ecfTable on ($ecfTable.entity_type = $entity_type and $ecfTable.entity_id = l.project_id and $ecfTable.field_id = ".$projectField['id'].")"; + $left_joins .= " left join tt_custom_field_options $cfoTable on ($cfoTable.field_id = $ecfTable.field_id and $cfoTable.id = $ecfTable.option_id)"; + } + } + } + } // Prepare inner joins. $inner_joins = null; @@ -2203,6 +2720,27 @@ static function makeJoinExpensesPart($options) { } } } + // Left joins for project custom fields. + if (isset($custom_fields) && $custom_fields->projectFields) { + foreach ($custom_fields->projectFields as $projectField) { + $field_name = 'project_field_'.$projectField['id']; + $field_value = $options[$field_name]; + $entity_type = CustomFields::ENTITY_PROJECT; + if ($field_value || ttReportHelper::groupingBy($field_name, $options)) { + // We need to add left joins when input is not null. + $ecfTable = 'ecf'.$projectField['id']; + if ($projectField['type'] == CustomFields::TYPE_TEXT) { + // Add one join for each text field. + $left_joins .= " left join tt_entity_custom_fields $ecfTable on ($ecfTable.entity_type = $entity_type and $ecfTable.entity_id = ei.project_id and $ecfTable.field_id = ".$projectField['id'].")"; + } elseif ($projectField['type'] == CustomFields::TYPE_DROPDOWN) { + $cfoTable = 'cfo'.$projectField['id']; + // Add two joins for each dropdown field. + $left_joins .= " left join tt_entity_custom_fields $ecfTable on ($ecfTable.entity_type = $entity_type and $ecfTable.entity_id = ei.project_id and $ecfTable.field_id = ".$projectField['id'].")"; + $left_joins .= " left join tt_custom_field_options $cfoTable on ($cfoTable.field_id = $ecfTable.field_id and $cfoTable.id = $ecfTable.option_id)"; + } + } + } + } return $left_joins; } @@ -2235,7 +2773,7 @@ static function makeGroupByHeaderPart($dropdown_value) { // First, try to get a label from a translation file, which is the most likely scenario // such as grouping by date, user, project, or task. - if (!ttStartsWith($dropdown_value, 'time_field_') && !ttStartsWith($dropdown_value, 'user_field_')) { + if (!ttContains($dropdown_value, '_field_')) { $key = 'label.'.$dropdown_value; $part = $i18n->get($key); if ($part) return $part; @@ -2262,6 +2800,15 @@ static function makeGroupByHeaderPart($dropdown_value) { } } } + // Process project custom fields. + if ($custom_fields && $custom_fields->projectFields) { + foreach ($custom_fields->projectFields as $projectField) { + $field_name = 'project_field_'.$projectField['id']; + if ($dropdown_value == $field_name) { + return $projectField['label']; + } + } + } // Return null if nothing is found. return null; @@ -2298,6 +2845,13 @@ static function makeGroupByHeader($options) { // makeGroupByXmlTag creates an xml tag for a totals only report using group_by1, // group_by2, and group_by3 values passed in $options. static function makeGroupByXmlTag($options) { + // Note: there is a problem with this function for custom fields. + // Returned xml tags are like "project_field_1134" and not their user entered labels. + // This is on purpose because we are trying to avoid potential XML parsing problems. + // See https://stackoverflow.com/questions/1478486/using-in-xml-element-name + // Perhaps review this if someone complains, but there are W3C standard (section 2.3) rules + // on names (including starting character). + $tag = ''; if ($options['group_by1'] != null && $options['group_by1'] != 'no_grouping') { // We have group_by1. diff --git a/WEB-INF/lib/ttRoleHelper.class.php b/WEB-INF/lib/ttRoleHelper.class.php index 683f7ebaf..fa6739a5b 100644 --- a/WEB-INF/lib/ttRoleHelper.class.php +++ b/WEB-INF/lib/ttRoleHelper.class.php @@ -168,8 +168,8 @@ static function createPredefinedRoles($group_id, $lang) $rights_client = 'view_client_reports,view_client_invoices,manage_own_settings'; $rights_user = 'track_own_time,track_own_expenses,view_own_reports,view_own_charts,view_own_projects,view_own_tasks,manage_own_settings,view_users'; - $rights_supervisor = $rights_user.',track_time,track_expenses,view_reports,approve_reports,approve_timesheets,view_charts,view_own_clients,override_punch_mode,override_date_lock,override_own_date_lock,swap_roles,update_work'; - $rights_comanager = $rights_supervisor.',manage_own_account,manage_users,manage_projects,manage_tasks,manage_custom_fields,manage_clients,manage_invoices,override_allow_ip,manage_basic_settings,view_all_reports,manage_work,bid_on_work'; + $rights_supervisor = $rights_user.',track_time,track_expenses,view_reports,approve_reports,approve_timesheets,view_charts,view_own_clients,override_punch_mode,override_date_lock,override_own_date_lock,swap_roles'; + $rights_comanager = $rights_supervisor.',manage_own_account,manage_users,manage_projects,manage_tasks,manage_custom_fields,manage_clients,manage_invoices,override_allow_ip,manage_basic_settings,view_all_charts,view_all_reports'; $rights_manager = $rights_comanager.',manage_features,manage_advanced_settings,manage_roles,export_data,approve_all_reports,approve_own_timesheets,manage_subgroups'; // Active roles. diff --git a/WEB-INF/lib/ttTeamHelper.class.php b/WEB-INF/lib/ttTeamHelper.class.php index 9bb538912..e12c2575b 100644 --- a/WEB-INF/lib/ttTeamHelper.class.php +++ b/WEB-INF/lib/ttTeamHelper.class.php @@ -1,33 +1,8 @@ query($sql); $result = array(); if (!is_a($res, 'PEAR_Error')) { - $dt = new DateAndTime(DB_DATEFORMAT); while ($val = $res->fetchRow()) { $result[] = $val; } diff --git a/WEB-INF/lib/ttTimeHelper.class.php b/WEB-INF/lib/ttTimeHelper.class.php index 031ccfb61..6019b2f12 100644 --- a/WEB-INF/lib/ttTimeHelper.class.php +++ b/WEB-INF/lib/ttTimeHelper.class.php @@ -2,8 +2,6 @@ /* Copyright (c) Anuko International Ltd. https://www.anuko.com License: See license.txt */ -import('DateAndTime'); - // The ttTimeHelper is a class to help with time-related values. class ttTimeHelper { @@ -119,13 +117,13 @@ static function isValidDuration($value) { } // postedDurationToMinutes - converts a value representing a duration - // (usually enetered in a form by a user) to an integer number of minutes. + // (usually entered in a form by a user) to an integer number of minutes. // // Parameters: // $duration - user entered duration string. Valid strings are: // 3 or 3h - means 3 hours. Note: h and m letters are not localized. // 0.25 or 0.25h or .25 or .25h - means a quarter of hour. - // 0,25 or 0,25h or ,25 or ,25h - same as above for users with comma ad decimal mark. + // 0,25 or 0,25h or ,25 or ,25h - same as above for users with comma as decimal mark. // 1:30 - means 1 hour 30 minutes. // 25m - means 25 minutes. // $max - maximum number of minutes that is valid. @@ -200,6 +198,9 @@ static function minutesToDuration($minutes, $abbreviate = false) { // toMinutes - converts a time string in format 00:00 to a number of minutes. static function toMinutes($value) { + if (is_null($value)) + return 0; + $signMultiplier = ttStartsWith($value, '-') ? -1 : 1; if ($signMultiplier == -1) $value = ltrim($value, '-'); @@ -439,8 +440,7 @@ static function insert($fields) if (!$billable) $billable = 0; if (!$paid) $paid = 0; - - if ($duration) { + if (!is_null($duration)) { $sql = "insert into tt_log (user_id, group_id, org_id, date, duration, client_id, project_id, task_id, invoice_id, comment, billable, paid, created, created_ip, created_by) ". "values ($user_id, $group_id, $org_id, ".$mdb2->quote($date).", '$duration', ".$mdb2->quote($client).", ".$mdb2->quote($project).", ".$mdb2->quote($task).", ".$mdb2->quote($invoice).", ".$mdb2->quote($note).", $billable, $paid $created_v)"; $affected = $mdb2->exec($sql); @@ -449,7 +449,6 @@ static function insert($fields) } else { $duration = ttTimeHelper::toDuration($start, $finish); if ($duration === false) $duration = 0; - if (!$duration && ttTimeHelper::getUncompleted($user_id)) return false; $sql = "insert into tt_log (user_id, group_id, org_id, date, start, duration, client_id, project_id, task_id, invoice_id, comment, billable, paid, created, created_ip, created_by) ". "values ($user_id, $group_id, $org_id, ".$mdb2->quote($date).", '$start', '$duration', ".$mdb2->quote($client).", ".$mdb2->quote($project).", ".$mdb2->quote($task).", ".$mdb2->quote($invoice).", ".$mdb2->quote($note).", $billable, $paid $created_v)"; @@ -500,9 +499,9 @@ static function update($fields) $finish = ttTimeHelper::to24HourFormat($finish); if ('00:00' == $finish) $finish = '24:00'; - if ($start) $duration = ''; + if ($start) $duration = null; - if ($duration) { + if (!is_null($duration)) { $sql = "UPDATE tt_log set start = NULL, duration = '$duration', client_id = ".$mdb2->quote($client).", project_id = ".$mdb2->quote($project).", task_id = ".$mdb2->quote($task).", ". "comment = ".$mdb2->quote($note)."$billable_part $paid_part $modified_part, date = '$date' WHERE id = $id and user_id = $user_id and group_id = $group_id and org_id = $org_id"; $affected = $mdb2->exec($sql); @@ -512,9 +511,6 @@ static function update($fields) $duration = ttTimeHelper::toDuration($start, $finish); if ($duration === false) $duration = 0; - $uncompleted = ttTimeHelper::getUncompleted($user_id); - if (!$duration && $uncompleted && ($uncompleted['id'] != $id)) - return false; $sql = "UPDATE tt_log SET start = '$start', duration = '$duration', client_id = ".$mdb2->quote($client).", project_id = ".$mdb2->quote($project).", task_id = ".$mdb2->quote($task).", ". "comment = ".$mdb2->quote($note)."$billable_part $paid_part $modified_part, date = '$date' WHERE id = $id and user_id = $user_id and group_id = $group_id and org_id = $org_id"; @@ -570,7 +566,8 @@ static function getTimeForDay($date) { $org_id = $user->org_id; $sql = "select sum(time_to_sec(duration)) as sm from tt_log". - " where user_id = $user_id and group_id = $group_id and org_id = $org_id and date = '$date' and status = 1"; + " where user_id = $user_id and group_id = $group_id and org_id = $org_id". + " and date = ".$mdb2->quote($date)." and status = 1"; $res = $mdb2->query($sql); if (!is_a($res, 'PEAR_Error')) { $val = $res->fetchRow(); @@ -582,17 +579,17 @@ static function getTimeForDay($date) { // getTimeForWeek - gets total time for a user for a given week. static function getTimeForWeek($date) { global $user; - import('Period'); + import('ttPeriod'); $mdb2 = getConnection(); $user_id = $user->getUser(); $group_id = $user->getGroup(); $org_id = $user->org_id; - $period = new Period(INTERVAL_THIS_WEEK, $date); + $period = new ttPeriod($date, INTERVAL_THIS_WEEK); $sql = "select sum(time_to_sec(duration)) as sm from tt_log". " where user_id = $user_id and group_id = $group_id and org_id = $org_id". - " and date >= '".$period->getStartDate(DB_DATEFORMAT)."' and date <= '".$period->getEndDate(DB_DATEFORMAT)."' and status = 1"; + " and date >= '".$period->getStartDate()."' and date <= '".$period->getEndDate()."' and status = 1"; $res = $mdb2->query($sql); if (!is_a($res, 'PEAR_Error')) { $val = $res->fetchRow(); @@ -604,17 +601,17 @@ static function getTimeForWeek($date) { // getTimeForMonth - gets total time for a user for a given month. static function getTimeForMonth($date) { global $user; - import('Period'); + import('ttPeriod'); $mdb2 = getConnection(); $user_id = $user->getUser(); $group_id = $user->getGroup(); $org_id = $user->org_id; - $period = new Period(INTERVAL_THIS_MONTH, $date); + $period = new ttPeriod($date, INTERVAL_THIS_MONTH); $sql = "select sum(time_to_sec(duration)) as sm from tt_log". " where user_id = $user_id and group_id = $group_id and org_id = $org_id". - " and date >= '".$period->getStartDate(DB_DATEFORMAT)."' and date <= '".$period->getEndDate(DB_DATEFORMAT)."' and status = 1"; + " and date >= '".$period->getStartDate()."' and date <= '".$period->getEndDate()."' and status = 1"; $res = $mdb2->query($sql); if (!is_a($res, 'PEAR_Error')) { $val = $res->fetchRow(); @@ -625,10 +622,45 @@ static function getTimeForMonth($date) { // getUncompleted - retrieves an uncompleted record for user, if one exists. static function getUncompleted($user_id) { + + $user_id = (int) $user_id; // Protection against sql injection. + + global $user; + $group_id = $user->getGroup(); + $org_id = $user->org_id; + $mdb2 = getConnection(); $sql = "select id, start, date from tt_log". - " where user_id = $user_id and start is not null and time_to_sec(duration) = 0 and status = 1"; + " where user_id = $user_id and start is not null and time_to_sec(duration) = 0 and status = 1". + " and group_id = $group_id and org_id = $org_id"; + $res = $mdb2->query($sql); + if (!is_a($res, 'PEAR_Error')) { + if (!$res->numRows()) { + return false; + } + if ($val = $res->fetchRow()) { + return $val; + } + } + return false; + } + + // getFirstUncompletedForDate - retrieves first found uncompleted record for user for a specific date, if one exists. + static function getFirstUncompletedForDate($user_id, $date) { + + $user_id = (int) $user_id; // Protection against sql injection. + + global $user; + $group_id = $user->getGroup(); + $org_id = $user->org_id; + + $mdb2 = getConnection(); + + $sql = "select id, start, date from tt_log". + " where user_id = $user_id and start is not null and time_to_sec(duration) = 0 and status = 1". + " and group_id = $group_id and org_id = $org_id and date = ".$mdb2->quote($date). + " order by start"; // Ordering by start time to get the earliest uncompleted for date. $res = $mdb2->query($sql); if (!is_a($res, 'PEAR_Error')) { if (!$res->numRows()) { @@ -692,6 +724,8 @@ static function overlaps($user_id, $date, $start, $finish, $record_id = null) { // getRecord - retrieves a time record identified by its id. static function getRecord($id) { global $user; + + $id = (int) $id; // Protection against sql injections. $user_id = $user->getUser(); $group_id = $user->getGroup(); diff --git a/WEB-INF/lib/ttTimesheetHelper.class.php b/WEB-INF/lib/ttTimesheetHelper.class.php index 5ca3abe66..16d740291 100644 --- a/WEB-INF/lib/ttTimesheetHelper.class.php +++ b/WEB-INF/lib/ttTimesheetHelper.class.php @@ -4,6 +4,8 @@ import('ttUserHelper'); import('ttFileHelper'); +import('ttPeriod'); +import('ttDate'); // Class ttTimesheetHelper is used to help with project related tasks. class ttTimesheetHelper { @@ -45,11 +47,11 @@ static function createTimesheet($fields) $name = $fields['name']; $comment = $fields['comment']; - $start_date = new DateAndTime($user->date_format, $fields['start_date']); - $start = $start_date->toString(DB_DATEFORMAT); + $start_date = new ttDate($fields['start_date'], $user->getDateFormat()); + $start = $start_date->toString(); - $end_date = new DateAndTime($user->date_format, $fields['end_date']); - $end = $end_date->toString(DB_DATEFORMAT); + $end_date = new ttDate($fields['end_date'], $user->getDateFormat()); + $end = $end_date->toString(); $created_part = ', now(), '.$mdb2->quote($_SERVER['REMOTE_ADDR']).', '.$user->id; @@ -446,11 +448,11 @@ static function timesheetItemsExist($fields) { if (isset($fields['client_id'])) $client_id = (int) $fields['client_id']; if (isset($fields['project_id'])) $project_id = (int) $fields['project_id']; - $start_date = new DateAndTime($user->date_format, $fields['start_date']); - $start = $start_date->toString(DB_DATEFORMAT); + $start_date = new ttDate($fields['start_date'], $user->getDateFormat()); + $start = $start_date->toString(); - $end_date = new DateAndTime($user->date_format, $fields['end_date']); - $end = $end_date->toString(DB_DATEFORMAT); + $end_date = new ttDate($fields['end_date'], $user->getDateFormat()); + $end = $end_date->toString(); // sql parts. $client_part = $project_part = ''; @@ -486,12 +488,12 @@ static function overlaps($fields) { if (isset($fields['client_id'])) $client_id = (int) $fields['client_id']; if (isset($fields['project_id'])) $project_id = (int) $fields['project_id']; - $start_date = new DateAndTime($user->date_format, $fields['start_date']); - $start = $start_date->toString(DB_DATEFORMAT); + $start_date = new ttDate($fields['start_date'], $user->getDateFormat()); + $start = $start_date->toString(); $quoted_start = $mdb2->quote($start); - $end_date = new DateAndTime($user->date_format, $fields['end_date']); - $end = $end_date->toString(DB_DATEFORMAT); + $end_date = new ttDate($fields['end_date'], $user->getDateFormat()); + $end = $end_date->toString(); $quoted_end = $mdb2->quote($end); // sql parts. @@ -544,16 +546,16 @@ static function getMatchingTimesheets($options) { // Determine start and end dates. $dateFormat = $user->getDateFormat(); - if ($options['period']) - $period = new Period($options['period'], new DateAndTime($dateFormat)); + if (isset($options['period']) && $options['period']) + $period = new ttPeriod(new ttDate(), $options['period']); else { - $period = new Period(); + $period = new ttPeriod(new ttDate()); $period->setPeriod( - new DateAndTime($dateFormat, $options['period_start']), - new DateAndTime($dateFormat, $options['period_end'])); + new ttDate($options['period_start'], $dateFormat), + new ttDate($options['period_end'], $dateFormat)); } - $start = $period->getStartDate(DB_DATEFORMAT); - $end = $period->getEndDate(DB_DATEFORMAT); + $start = $period->getStartDate(); + $end = $period->getEndDate(); $result = false; $sql = "select id, name from tt_timesheets". diff --git a/WEB-INF/lib/ttUser.class.php b/WEB-INF/lib/ttUser.class.php index 320fa623d..fedf3dafb 100644 --- a/WEB-INF/lib/ttUser.class.php +++ b/WEB-INF/lib/ttUser.class.php @@ -9,6 +9,7 @@ import('form.Form'); import('form.ActionForm'); import('ttTemplateHelper'); +import('ttDate'); class ttUser { var $login = null; // User login. @@ -49,6 +50,7 @@ class ttUser { var $config = null; // Comma-separated list of miscellaneous config options. var $configHelper = null; // An instance of ttConfigHelper class. var $custom_css = null; // Custom css. + var $custom_translation = null; // Custom translation. var $custom_logo = 0; // Whether to use a custom logo for group. var $lock_spec = null; // Cron specification for record locking. @@ -60,6 +62,9 @@ class ttUser { var $behalfUser = null; // A ttBehalfUser instance with on behalf user attributes. var $behalfGroup = null; // A ttGroup instance with on behalf group attributes. + var $initialized = false; // Set to true when an object is initialized. + + // Constructor. function __construct($login, $id = null) { if (!$login && !$id) { @@ -67,90 +72,99 @@ function __construct($login, $id = null) { return; } - $mdb2 = getConnection(); - - $sql = "SELECT u.id, u.login, u.name, u.group_id, u.role_id, r.rank, r.name as role_name, r.rights, u.client_id,". - " u.quota_percent, u.email, g.org_id, g.group_key, g.name as group_name, g.currency, g.lang, g.decimal_mark, g.date_format,". - " g.time_format, g.week_start, g.tracking_mode, g.project_required, g.record_type,". - " g.bcc_email, g.allow_ip, g.password_complexity, g.plugins, g.config, g.lock_spec, g.custom_css, g.holidays, g.workday_minutes, g.custom_logo". - " FROM tt_users u LEFT JOIN tt_groups g ON (u.group_id = g.id) LEFT JOIN tt_roles r on (r.id = u.role_id) WHERE "; - if ($id) - $sql .= "u.id = $id"; - else - $sql .= "u.login = ".$mdb2->quote($login); - $sql .= " AND u.status = 1"; + try { + $mdb2 = getConnection(); + + $sql = "SELECT u.id, u.login, u.name, u.group_id, u.role_id, r.rank, r.name as role_name, r.rights, u.client_id,". + " u.quota_percent, u.email, g.org_id, g.group_key, g.name as group_name, g.currency, g.lang, g.decimal_mark, g.date_format,". + " g.time_format, g.week_start, g.tracking_mode, g.project_required, g.record_type,". + " g.bcc_email, g.allow_ip, g.password_complexity, g.plugins, g.config, g.lock_spec, g.custom_css, g.custom_translation,". + " g.holidays, g.workday_minutes, g.custom_logo". + " FROM tt_users u LEFT JOIN tt_groups g ON (u.group_id = g.id) LEFT JOIN tt_roles r on (r.id = u.role_id) WHERE "; + if ($id) + $sql .= "u.id = $id"; + else + $sql .= "u.login = ".$mdb2->quote($login); + $sql .= " AND u.status = 1"; + + $res = $mdb2->query($sql); + if (is_a($res, 'PEAR_Error')) { + return; + } - $res = $mdb2->query($sql); - if (is_a($res, 'PEAR_Error')) { - return; - } + $val = $res->fetchRow(); + if ($val['id'] > 0) { + $this->login = $val['login']; + $this->name = $val['name']; + $this->id = $val['id']; + $this->org_id = $val['org_id']; + $this->group_id = $val['group_id']; + $this->group_key = $val['group_key']; + if ($this->org_id == $this->group_key) $this->org_key = $val['group_key']; + $this->role_id = $val['role_id']; + $this->role_name = $val['role_name']; + $this->rights = explode(',', $val['rights']); + $this->rank = $val['rank']; + $this->client_id = $val['client_id']; + $this->is_client = $this->client_id && !in_array('track_own_time', $this->rights); + if ($val['quota_percent']) $this->quota_percent = $val['quota_percent']; + $this->email = $val['email']; + if ($val['lang']) $this->lang = $val['lang']; + if ($val['decimal_mark']) $this->decimal_mark = $val['decimal_mark']; + $this->date_format = $val['date_format']; + $this->time_format = $val['time_format']; + $this->week_start = $val['week_start']; + $this->tracking_mode = $val['tracking_mode']; + $this->project_required = $val['project_required']; + $this->record_type = $val['record_type']; + $this->bcc_email = $val['bcc_email']; + $this->allow_ip = $val['allow_ip']; + $this->password_complexity = $val['password_complexity']; + $this->group_name = $val['group_name']; + $this->currency = $val['currency']; + $this->plugins = $val['plugins']; + $this->lock_spec = $val['lock_spec']; + $this->holidays = $val['holidays']; + $this->workday_minutes = $val['workday_minutes']; + $this->custom_logo = $val['custom_logo']; + + // TODO: refactor this. + $this->config = $val['config']; + $this->configHelper = new ttConfigHelper($val['config']); + + // Set user config options. + $this->punch_mode = $this->configHelper->getDefinedValue('punch_mode'); + $this->allow_overlap = $this->configHelper->getDefinedValue('allow_overlap'); + + $this->custom_css = $val['custom_css']; + $this->custom_translation = $val['custom_translation']; + + // Set "on behalf" id and name (user). + if (isset($_SESSION['behalf_id'])) { + $this->behalf_id = $_SESSION['behalf_id']; + $this->behalf_name = $_SESSION['behalf_name']; + + $this->behalfUser = new ttBehalfUser($this->behalf_id, $this->org_id); + } + // Set "on behalf" id and name (group). + if (isset($_SESSION['behalf_group_id'])) { + $this->behalf_group_id = $_SESSION['behalf_group_id']; + $this->behalf_group_name = $_SESSION['behalf_group_name']; - $val = $res->fetchRow(); - if ($val['id'] > 0) { - $this->login = $val['login']; - $this->name = $val['name']; - $this->id = $val['id']; - $this->org_id = $val['org_id']; - $this->group_id = $val['group_id']; - $this->group_key = $val['group_key']; - if ($this->org_id == $this->group_key) $this->org_key = $val['group_key']; - $this->role_id = $val['role_id']; - $this->role_name = $val['role_name']; - $this->rights = explode(',', $val['rights']); - $this->rank = $val['rank']; - $this->client_id = $val['client_id']; - $this->is_client = $this->client_id && !in_array('track_own_time', $this->rights); - if ($val['quota_percent']) $this->quota_percent = $val['quota_percent']; - $this->email = $val['email']; - if ($val['lang']) $this->lang = $val['lang']; - if ($val['decimal_mark']) $this->decimal_mark = $val['decimal_mark']; - $this->date_format = $val['date_format']; - $this->time_format = $val['time_format']; - $this->week_start = $val['week_start']; - $this->tracking_mode = $val['tracking_mode']; - $this->project_required = $val['project_required']; - $this->record_type = $val['record_type']; - $this->bcc_email = $val['bcc_email']; - $this->allow_ip = $val['allow_ip']; - $this->password_complexity = $val['password_complexity']; - $this->group_name = $val['group_name']; - $this->currency = $val['currency']; - $this->plugins = $val['plugins']; - $this->lock_spec = $val['lock_spec']; - $this->holidays = $val['holidays']; - $this->workday_minutes = $val['workday_minutes']; - $this->custom_logo = $val['custom_logo']; - - // TODO: refactor this. - $this->config = $val['config']; - $this->configHelper = new ttConfigHelper($val['config']); - - // Set user config options. - $this->punch_mode = $this->configHelper->getDefinedValue('punch_mode'); - $this->allow_overlap = $this->configHelper->getDefinedValue('allow_overlap'); - - $this->custom_css = $val['custom_css']; - - // Set "on behalf" id and name (user). - if (isset($_SESSION['behalf_id'])) { - $this->behalf_id = $_SESSION['behalf_id']; - $this->behalf_name = $_SESSION['behalf_name']; - - $this->behalfUser = new ttBehalfUser($this->behalf_id, $this->org_id); + $this->behalfGroup = new ttGroup($this->behalf_group_id, $this->org_id); + } } - // Set "on behalf" id and name (group). - if (isset($_SESSION['behalf_group_id'])) { - $this->behalf_group_id = $_SESSION['behalf_group_id']; - $this->behalf_group_name = $_SESSION['behalf_group_name']; - $this->behalfGroup = new ttGroup($this->behalf_group_id, $this->org_id); - } + $this->initialized = true; + } + catch (Exception $e) { + error_log("Exception raised in ttUser constructor: ".$e->getMessage()); } } // getUser returns user id on behalf of whom the current user is operating. function getUser() { - return ($this->behalfUser ? $this->behalfUser->id : $this->id); + return (isset($this->behalfUser) ? $this->behalfUser->id : $this->id); } // getName returns user name on behalf of whom the current user is operating. @@ -168,6 +182,11 @@ function getEmail() { return ($this->behalfUser ? $this->behalfUser->email : $this->email); } + // getPasswordComplexity returns password complexity for active user. + function getPasswordComplexity() { + return ($this->behalfUser ? $this->behalfUser->password_complexity : $this->password_complexity); + } + // The getGroup returns group id on behalf of which the current user is operating. function getGroup() { return ($this->behalfGroup ? $this->behalfGroup->id : $this->group_id); @@ -284,6 +303,11 @@ function getCustomCss() { return ($this->behalfGroup ? $this->behalfGroup->custom_css : $this->custom_css); } + // getCustomTranslation returns custom translation for active group. + function getCustomTranslation() { + return ($this->behalfGroup ? $this->behalfGroup->custom_translation : $this->custom_translation); + } + // can - determines whether user has a right to do something. function can($do_something) { return in_array($do_something, $this->rights); @@ -297,7 +321,7 @@ function isClient() { // isPluginEnabled checks whether a plugin is enabled for user. function isPluginEnabled($plugin) { - return in_array($plugin, explode(',', $this->getPlugins())); + return in_array($plugin, explode(',', $this->getPlugins() ? $this->getPlugins() : '')); } // isOptionEnabled checks whether a config option is enabled for user. @@ -335,7 +359,7 @@ function getAssignedProjects($options = null) // Do a query with inner join to get assigned projects. $sql = "select p.id, p.name, p.description, p.tasks, upb.rate $filePart from tt_projects p $fileJoin". " inner join tt_user_project_binds upb on (upb.user_id = $user_id and upb.project_id = p.id and upb.status = 1)". - " where p.group_id = $group_id and p.org_id = $org_id and p.status = 1 order by p.name"; + " where p.group_id = $group_id and p.org_id = $org_id and p.status = 1 order by upper(p.name)"; $res = $mdb2->query($sql); if (!is_a($res, 'PEAR_Error')) { $bindTemplatesWithProjects = isset($options['include_templates']) && $options['include_templates']; @@ -436,7 +460,7 @@ function isDateLocked($date) // Calculate the last occurrence of a lock. $last = tdCron::getLastOccurrence($this->getLockSpec(), time()); - $lockdate = new DateAndTime(DB_DATEFORMAT, strftime('%Y-%m-%d', $last)); + $lockdate = new ttDate(strftime('%Y-%m-%d', $last)); if ($date->before($lockdate)) return true; @@ -554,9 +578,10 @@ function getGroupsForDropdown() { // addGroupToDropdown is a recursive function to populate a tree of groups, used with getGroupsForDropdown(). function addGroupToDropdown(&$groups, $group_id, $subgroup_level) { $name = ''; - // Add indentation markup to indicate subdirectory level. + // Add indentation markup to indicate a subdirectory level. for ($i = 0; $i < $subgroup_level; $i++) { - $name .= '🛑'; // Unicode stop sign. + $name .= '*'; + // $name .= '🛑'; // Unicode stop sign. Does not display properly in Chrome 98. } if ($subgroup_level) $name .= ' '; // Add an extra space. $name .= ttGroupHelper::getGroupName($group_id); @@ -596,7 +621,7 @@ function getUserDetails($user_id) { // Determine max rank. If we are searching in on behalf group // then rank restriction does not apply. - $max_rank = $this->behalfGroup ? MAX_RANK : $this->rank; + $max_rank = $this->behalfGroup ? MAX_RANK + 1 : $this->rank; $sql = "select u.id, u.name, u.login, u.role_id, u.client_id, u.status, u.rate, u.quota_percent, u.email from tt_users u". " left join tt_roles r on (u.role_id = r.id)". @@ -675,7 +700,8 @@ function updateGroup($fields) { $name_part = $description_part = $currency_part = $lang_part = $decimal_mark_part = $date_format_part = $time_format_part = $week_start_part = $tracking_mode_part = $project_required_part = $record_type_part = $bcc_email_part = $allow_ip_part = - $plugins_part = $config_part = $custom_css_part = $lock_spec_part = $holidays_part = $workday_minutes_part = ''; + $password_complexity_part = $plugins_part = $config_part = $custom_css_part = $custom_translation_part = $lock_spec_part = + $holidays_part = $workday_minutes_part = ''; if (isset($fields['name'])) $name_part = ', name = '.$mdb2->quote($fields['name']); if (isset($fields['description'])) $description_part = ', description = '.$mdb2->quote($fields['description']); if (isset($fields['currency'])) $currency_part = ', currency = '.$mdb2->quote($fields['currency']); @@ -691,9 +717,11 @@ function updateGroup($fields) { if (isset($fields['record_type'])) $record_type_part = ', record_type = '.(int) $fields['record_type']; if (isset($fields['bcc_email'])) $bcc_email_part = ', bcc_email = '.$mdb2->quote($fields['bcc_email']); if (isset($fields['allow_ip'])) $allow_ip_part = ', allow_ip = '.$mdb2->quote($fields['allow_ip']); + if (isset($fields['password_complexity'])) $password_complexity_part = ', password_complexity = '.$mdb2->quote($fields['password_complexity']); if (isset($fields['plugins'])) $plugins_part = ', plugins = '.$mdb2->quote($fields['plugins']); if (isset($fields['config'])) $config_part = ', config = '.$mdb2->quote($fields['config']); if (isset($fields['custom_css'])) $custom_css_part = ', custom_css = '.$mdb2->quote($fields['custom_css']); + if (isset($fields['custom_translation'])) $custom_translation_part = ', custom_translation = '.$mdb2->quote($fields['custom_translation']); if (isset($fields['lock_spec'])) $lock_spec_part = ', lock_spec = '.$mdb2->quote($fields['lock_spec']); if (isset($fields['holidays'])) $holidays_part = ', holidays = '.$mdb2->quote($fields['holidays']); if (isset($fields['workday_minutes'])) $workday_minutes_part = ', workday_minutes = '.$mdb2->quote($fields['workday_minutes']); @@ -701,7 +729,8 @@ function updateGroup($fields) { $parts = trim($name_part.$description_part.$currency_part.$lang_part.$decimal_mark_part.$date_format_part. $time_format_part.$week_start_part.$tracking_mode_part.$project_required_part.$record_type_part. - $bcc_email_part.$allow_ip_part.$plugins_part.$config_part.$custom_css_part.$lock_spec_part.$holidays_part.$workday_minutes_part.$modified_part, ','); + $bcc_email_part.$allow_ip_part.$password_complexity_part.$plugins_part.$config_part.$custom_css_part. + $custom_translation_part.$lock_spec_part.$holidays_part.$workday_minutes_part.$modified_part, ','); $sql = "update tt_groups set $parts where id = $group_id and org_id = $this->org_id"; $affected = $mdb2->exec($sql); @@ -742,8 +771,8 @@ function markUserDeleted($user_id) { if (is_a($affected, 'PEAR_Error')) return false; - // Mark user custom fields as deleted, - require_once('plugins/CustomFields.class.php'); + // Mark user custom fields as deleted. + require_once(APP_DIR.'/plugins/CustomFields.class.php'); $entity_type = CustomFields::ENTITY_USER; $modified_part = ', modified = now(), modified_ip = '.$mdb2->quote($_SERVER['REMOTE_ADDR']).', modified_by = '.$mdb2->quote($this->id); $sql = "update tt_entity_custom_fields set status = null $modified_part". @@ -832,7 +861,7 @@ function getUserPartForHeader() { $user_part .= ', '.htmlspecialchars($this->behalf_group_name).''; } else { if ($this->group_name) // Note: we did not require group names in the past. - $user_part .= ', '.$this->group_name; + $user_part .= ', '.htmlspecialchars($this->group_name); } return $user_part; } diff --git a/WEB-INF/lib/ttUserHelper.class.php b/WEB-INF/lib/ttUserHelper.class.php index 122c7889c..2aeada43c 100644 --- a/WEB-INF/lib/ttUserHelper.class.php +++ b/WEB-INF/lib/ttUserHelper.class.php @@ -68,7 +68,8 @@ static function getUserIdByTmpRef($ref) { if (!is_a($res, 'PEAR_Error')) { $val = $res->fetchRow(); - return $val['user_id']; + if ($val) + return $val['user_id']; } return false; } diff --git a/WEB-INF/lib/ttWeekViewHelper.class.php b/WEB-INF/lib/ttWeekViewHelper.class.php index 9487f7805..cd1e4a952 100644 --- a/WEB-INF/lib/ttWeekViewHelper.class.php +++ b/WEB-INF/lib/ttWeekViewHelper.class.php @@ -167,22 +167,37 @@ static function getDataForWeekView($records, $dayHeaders) { global $i18n; $dataArray = array(); + $includeWeekends = $user->isOptionEnabled('weekends'); $includeNotes = $user->isOptionEnabled('week_notes'); // Construct the first row for a brand new entry. $dataArray[] = array('row_id' => null,'label' => $i18n->get('form.week.new_entry').':'); // Insert row. // Insert empty cells with proper control ids. - for ($i = 0; $i < 7; $i++) { - $control_id = '0_'. $dayHeaders[$i]; - $dataArray[0][$dayHeaders[$i]] = array('control_id' => $control_id, 'tt_log_id' => null,'duration' => null); + if ($includeWeekends) { + for ($i = 0; $i < 7; $i++) { + $control_id = '0_'. $dayHeaders[$i]; + $dataArray[0][$dayHeaders[$i]] = array('control_id' => $control_id, 'tt_log_id' => null,'duration' => null); + } + } else { + for ($i = 0; $i < 5; $i++) { + $control_id = '0_'. $dayHeaders[$i]; + $dataArray[0][$dayHeaders[$i]] = array('control_id' => $control_id, 'tt_log_id' => null,'duration' => null); + } } if ($includeNotes) { // Construct the second row for daily comments for a brand new entry. $dataArray[] = array('row_id' => null,'label' => $i18n->get('label.notes').':'); // Insert row. // Insert empty cells with proper control ids. - for ($i = 0; $i < 7; $i++) { - $control_id = '1_'. $dayHeaders[$i]; - $dataArray[1][$dayHeaders[$i]] = array('control_id' => $control_id, 'tt_log_id' => null,'note' => null); + if ($includeWeekends) { + for ($i = 0; $i < 7; $i++) { + $control_id = '1_'. $dayHeaders[$i]; + $dataArray[1][$dayHeaders[$i]] = array('control_id' => $control_id, 'tt_log_id' => null,'note' => null); + } + } else { + for ($i = 0; $i < 5; $i++) { + $control_id = '1_'. $dayHeaders[$i]; + $dataArray[1][$dayHeaders[$i]] = array('control_id' => $control_id, 'tt_log_id' => null,'note' => null); + } } } @@ -205,18 +220,32 @@ static function getDataForWeekView($records, $dayHeaders) { $dataArray[] = array('row_id' => $row_id,'label' => ttWeekViewHelper::makeRowLabel($record)); $pos = ttWeekViewHelper::findRow($row_id, $dataArray); // Insert empty cells with proper control ids. - for ($i = 0; $i < 7; $i++) { - $control_id = $pos.'_'. $dayHeaders[$i]; - $dataArray[$pos][$dayHeaders[$i]] = array('control_id' => $control_id, 'tt_log_id' => null,'duration' => null); + if ($includeWeekends) { + for ($i = 0; $i < 7; $i++) { + $control_id = $pos.'_'. $dayHeaders[$i]; + $dataArray[$pos][$dayHeaders[$i]] = array('control_id' => $control_id, 'tt_log_id' => null,'duration' => null); + } + } else { + for ($i = 0; $i < 5; $i++) { + $control_id = $pos.'_'. $dayHeaders[$i]; + $dataArray[$pos][$dayHeaders[$i]] = array('control_id' => $control_id, 'tt_log_id' => null,'duration' => null); + } } // Insert row for comments. if ($includeNotes) { $dataArray[] = array('row_id' => $row_id.'_notes','label' => $i18n->get('label.notes').':'); $pos++; // Insert empty cells with proper control ids. - for ($i = 0; $i < 7; $i++) { - $control_id = $pos.'_'. $dayHeaders[$i]; - $dataArray[$pos][$dayHeaders[$i]] = array('control_id' => $control_id, 'tt_log_id' => null,'note' => null); + if ($includeWeekends) { + for ($i = 0; $i < 7; $i++) { + $control_id = $pos.'_'. $dayHeaders[$i]; + $dataArray[$pos][$dayHeaders[$i]] = array('control_id' => $control_id, 'tt_log_id' => null,'note' => null); + } + } else { + for ($i = 0; $i < 5; $i++) { + $control_id = $pos.'_'. $dayHeaders[$i]; + $dataArray[$pos][$dayHeaders[$i]] = array('control_id' => $control_id, 'tt_log_id' => null,'note' => null); + } } $pos--; } @@ -242,23 +271,23 @@ static function prePopulateFromPastWeeks($startDate, $dayHeaders) { global $i18n; // First, determine past week start and end dates. - $objDate = new DateAndTime(DB_DATEFORMAT, $startDate); - $objDate->decDay(7); - $pastWeekStartDate = $objDate->toString(DB_DATEFORMAT); - $objDate->incDay(6); - $pastWeekEndDate = $objDate->toString(DB_DATEFORMAT); + $objDate = new ttDate($startDate); + $objDate->decrementDay(7); + $pastWeekStartDate = $objDate->toString(); + $objDate->incrementDay(6); + $pastWeekEndDate = $objDate->toString(); unset($objDate); // Obtain past week(s) records. $records = ttWeekViewHelper::getRecordsForInterval($pastWeekStartDate, $pastWeekEndDate); - // Handle potential situation of no records by re-trying for up to 4 more previous weeks (after a long vacation, etc.). + // Handle a potential situation of no records by re-trying for up to 4 more previous weeks (after a long vacation, etc.). if (!$records) { for ($i = 0; $i < 4; $i++) { - $objDate = new DateAndTime(DB_DATEFORMAT, $pastWeekStartDate); - $objDate->decDay(7); - $pastWeekStartDate = $objDate->toString(DB_DATEFORMAT); - $objDate->incDay(6); - $pastWeekEndDate = $objDate->toString(DB_DATEFORMAT); + $objDate = new ttDate($pastWeekStartDate); + $objDate->decrementDay(7); + $pastWeekStartDate = $objDate->toString(); + $objDate->incrementDay(6); + $pastWeekEndDate = $objDate->toString(); unset($objDate); $records = ttWeekViewHelper::getRecordsForInterval($pastWeekStartDate, $pastWeekEndDate); @@ -267,21 +296,38 @@ static function prePopulateFromPastWeeks($startDate, $dayHeaders) { } } + // We will need this for iterations below. + $includeWeekends = $user->isOptionEnabled('weekends'); + // Construct the first row for a brand new entry. $dataArray[] = array('row_id' => null,'label' => $i18n->get('form.week.new_entry').':'); // Insert row. // Insert empty cells with proper control ids. - for ($i = 0; $i < 7; $i++) { - $control_id = '0_'. $dayHeaders[$i]; - $dataArray[0][$dayHeaders[$i]] = array('control_id' => $control_id, 'tt_log_id' => null,'duration' => null); + if ($includeWeekends) { + for ($i = 0; $i < 7; $i++) { + $control_id = '0_'. $dayHeaders[$i]; + $dataArray[0][$dayHeaders[$i]] = array('control_id' => $control_id, 'tt_log_id' => null,'duration' => null); + } + } else { + for ($i = 0; $i < 5; $i++) { + $control_id = '0_'. $dayHeaders[$i]; + $dataArray[0][$dayHeaders[$i]] = array('control_id' => $control_id, 'tt_log_id' => null,'duration' => null); + } } $includeNotes = $user->isOptionEnabled('week_notes'); if ($includeNotes) { // Construct the second row for daily comments for a brand new entry. $dataArray[] = array('row_id' => null,'label' => $i18n->get('label.notes').':'); // Insert row. // Insert empty cells with proper control ids. - for ($i = 0; $i < 7; $i++) { - $control_id = '1_'. $dayHeaders[$i]; - $dataArray[1][$dayHeaders[$i]] = array('control_id' => $control_id, 'tt_log_id' => null,'note' => null); + if ($includeWeekends) { + for ($i = 0; $i < 7; $i++) { + $control_id = '1_'. $dayHeaders[$i]; + $dataArray[1][$dayHeaders[$i]] = array('control_id' => $control_id, 'tt_log_id' => null,'note' => null); + } + } else { + for ($i = 0; $i < 5; $i++) { + $control_id = '1_'. $dayHeaders[$i]; + $dataArray[1][$dayHeaders[$i]] = array('control_id' => $control_id, 'tt_log_id' => null,'note' => null); + } } } @@ -296,18 +342,32 @@ static function prePopulateFromPastWeeks($startDate, $dayHeaders) { $dataArray[] = array('row_id' => $row_id,'label' => ttWeekViewHelper::makeRowLabel($record)); $pos = ttWeekViewHelper::findRow($row_id, $dataArray); // Insert empty cells with proper control ids. - for ($i = 0; $i < 7; $i++) { - $control_id = $pos.'_'. $dayHeaders[$i]; - $dataArray[$pos][$dayHeaders[$i]] = array('control_id' => $control_id, 'tt_log_id' => null,'duration' => null); + if ($includeWeekends) { + for ($i = 0; $i < 7; $i++) { + $control_id = $pos.'_'. $dayHeaders[$i]; + $dataArray[$pos][$dayHeaders[$i]] = array('control_id' => $control_id, 'tt_log_id' => null,'duration' => null); + } + } else { + for ($i = 0; $i < 5; $i++) { + $control_id = $pos.'_'. $dayHeaders[$i]; + $dataArray[$pos][$dayHeaders[$i]] = array('control_id' => $control_id, 'tt_log_id' => null,'duration' => null); + } } // Insert row for comments. if ($includeNotes) { $dataArray[] = array('row_id' => $row_id.'_notes','label' => $i18n->get('label.notes').':'); $pos++; // Insert empty cells with proper control ids. - for ($i = 0; $i < 7; $i++) { - $control_id = $pos.'_'. $dayHeaders[$i]; - $dataArray[$pos][$dayHeaders[$i]] = array('control_id' => $control_id, 'tt_log_id' => null,'note' => null); + if ($includeWeekends) { + for ($i = 0; $i < 7; $i++) { + $control_id = $pos.'_'. $dayHeaders[$i]; + $dataArray[$pos][$dayHeaders[$i]] = array('control_id' => $control_id, 'tt_log_id' => null,'note' => null); + } + } else { + for ($i = 0; $i < 5; $i++) { + $control_id = $pos.'_'. $dayHeaders[$i]; + $dataArray[$pos][$dayHeaders[$i]] = array('control_id' => $control_id, 'tt_log_id' => null,'note' => null); + } } $pos--; } @@ -366,23 +426,11 @@ static function getDayTotals($dataArray, $dayHeaders) { // getDayHeadersForWeek - obtains day column headers for week view, which are simply day numbers in month. static function getDayHeadersForWeek($start_date) { $dayHeaders = array(); - $objDate = new DateAndTime(DB_DATEFORMAT, $start_date); - $dayHeaders[] = (string) $objDate->getDate(); // It returns an int on first call. - if (strlen($dayHeaders[0]) == 1) // Which is an implementation detail of DateAndTime class. - $dayHeaders[0] = '0'.$dayHeaders[0]; // Add a 0 for single digit day. - $objDate->incDay(); - $dayHeaders[] = $objDate->getDate(); // After incDay it returns a string with leading 0, when necessary. - $objDate->incDay(); - $dayHeaders[] = $objDate->getDate(); - $objDate->incDay(); - $dayHeaders[] = $objDate->getDate(); - $objDate->incDay(); - $dayHeaders[] = $objDate->getDate(); - $objDate->incDay(); - $dayHeaders[] = $objDate->getDate(); - $objDate->incDay(); - $dayHeaders[] = $objDate->getDate(); - unset($objDate); + $objDate = new ttDate($start_date); + for ($i = 0; $i < 7; $i++) { + $dayHeaders[] = $objDate->getDay(); + $objDate->incrementDay(); + } return $dayHeaders; } @@ -390,12 +438,11 @@ static function getDayHeadersForWeek($start_date) { static function getLockedDaysForWeek($start_date) { global $user; $lockedDays = array(); - $objDate = new DateAndTime(DB_DATEFORMAT, $start_date); + $objDate = new ttDate($start_date); for ($i = 0; $i < 7; $i++) { $lockedDays[] = $user->isDateLocked($objDate); - $objDate->incDay(); + $objDate->incrementDay(); } - unset($objDate); return $lockedDays; } @@ -493,18 +540,15 @@ static function parseFromWeekViewRow($row_id, $field_label, $base64decode = fals // dateFromDayHeader calculates date from start date and day header in week view. static function dateFromDayHeader($start_date, $day_header) { - $objDate = new DateAndTime(DB_DATEFORMAT, $start_date); - $currentDayHeader = (string) $objDate->getDate(); // It returns an int on first call. - if (strlen($currentDayHeader) == 1) // Which is an implementation detail of DateAndTime class. - $currentDayHeader = '0'.$currentDayHeader; // Add a 0 for single digit day. - $i = 1; - while ($currentDayHeader != $day_header && $i < 7) { - // Iterate through remaining days to find a match. - $objDate->incDay(); - $currentDayHeader = $objDate->getDate(); // After incDay it returns a string with leading 0, when necessary. - $i++; + $objDate = new ttDate($start_date); + $currentDayHeader = $objDate->getDay(); + for ($i = 0; $i < 7; $i++) { + if ($currentDayHeader == $day_header) + break; + $objDate->incrementDay(); + $currentDayHeader = $objDate->getDay(); } - return $objDate->toString(DB_DATEFORMAT); + return $objDate->toString(); } // insertDurationFromWeekView - inserts a new record in log tables from a week view post. @@ -514,12 +558,14 @@ static function insertDurationFromWeekView($fields, $custom_fields, $err) { // Determine date for a new entry. $entry_date = ttWeekViewHelper::dateFromDayHeader($fields['start_date'], $fields['day_header']); - $objEntryDate = new DateAndTime(DB_DATEFORMAT, $entry_date); + $objEntryDate = new ttDate($entry_date); // Prohibit creating entries in future. if (!$user->isOptionEnabled('future_entries') && $fields['browser_today']) { - $objBrowserToday = new DateAndTime(DB_DATEFORMAT, $fields['browser_today']); - if ($objEntryDate->after($objBrowserToday)) { + $objBrowserToday = new ttDate($fields['browser_today']); + $server_tomorrow = new ttDate(); + $server_tomorrow->incrementDay(); + if ($objEntryDate->after($objBrowserToday) || $objEntryDate->after($server_tomorrow)) { $err->add($i18n->get('error.future_date')); return false; } diff --git a/WEB-INF/lib/ttWorkHelper.class.php b/WEB-INF/lib/ttWorkHelper.class.php deleted file mode 100644 index 6ddf87513..000000000 --- a/WEB-INF/lib/ttWorkHelper.class.php +++ /dev/null @@ -1,1617 +0,0 @@ -errors = &$errors; - - $this->work_server_uri = defined('WORK_SERVER_URI') ? WORK_SERVER_URI : "https://www.anuko.com/work/"; - - // Note: at some point a need will arise for API versioning. - // When this happens, we will append an API version number to the end of URI, - // for example: register_0_1 instead of register. - // This should theoretically allow a remote work server to be able to work with - // a complete variety of deployed clients, including those without versions. - $this->register_uri = $this->work_server_uri.'register'; // register_0_0 - $this->put_own_work_item_uri = $this->work_server_uri.'putownworkitem'; - $this->get_own_work_item_uri = $this->work_server_uri.'getownworkitem'; - $this->get_own_work_item_offer_uri = $this->work_server_uri.'getownworkitemoffer'; - $this->get_own_work_item_offers_uri = $this->work_server_uri.'getownworkitemoffers'; - $this->get_own_work_items_uri = $this->work_server_uri.'getownworkitems'; - $this->get_available_work_items_uri = $this->work_server_uri.'getavailableworkitems'; - $this->get_available_work_item_uri = $this->work_server_uri.'getavailableworkitem'; - $this->delete_own_work_item_uri = $this->work_server_uri.'deleteownworkitem'; - $this->accept_own_work_item_offer_uri = $this->work_server_uri.'acceptownworkitemoffer'; - $this->decline_own_work_item_offer_uri = $this->work_server_uri.'declineownworkitemoffer'; - $this->update_own_work_item_uri = $this->work_server_uri.'updateownworkitem'; - $this->update_own_work_item_on_offer_uri = $this->work_server_uri.'updateownworkitemonoffer'; - $this->put_own_offer_uri = $this->work_server_uri.'putownoffer'; - $this->get_own_offer_uri = $this->work_server_uri.'getownoffer'; - $this->get_own_offers_uri = $this->work_server_uri.'getownoffers'; - - $this->get_available_offers_uri = $this->work_server_uri.'getavailableoffers'; - $this->get_available_offer_uri = $this->work_server_uri.'getavailableoffer'; - $this->get_group_items_uri = $this->work_server_uri.'getgroupitems'; - $this->delete_own_offer_uri = $this->work_server_uri.'deleteownoffer'; - $this->update_own_offer_uri = $this->work_server_uri.'updateownoffer'; - $this->send_message_to_work_owner_uri = $this->work_server_uri.'sendmessagetoworkowner'; - $this->send_message_to_offer_owner_uri = $this->work_server_uri.'sendmessagetoofferowner'; - $this->checkSiteRegistration(); - } - - // checkSiteRegistration - obtains site id and key from local database. - // If not found, it tries to register with remote work server. - function checkSiteRegistration() { - global $i18n; - global $user; - $mdb2 = getConnection(); - - // Obtain site id. - $sql = "select param_value as id from tt_site_config where param_name = 'worksite_id'"; - $res = $mdb2->query($sql); - $val = $res->fetchRow(); - if (!$val) { - // No site id found, need to register. - $group_id = $user->getGroup(); - $org_id = $user->org_id; - $fields = array('lang' => urlencode($user->lang), - 'name' => urlencode('time tracker'), - 'origin' => urlencode('time tracker source'), - 'org_id' => urlencode($org_id), - 'group_id' => urlencode($group_id), - 'group_name' => urlencode(base64_encode($user->getGroupName())), - 'user_id' => urlencode($user->getUser()), - 'user_name' => urlencode(base64_encode($user->getName())), - 'user_email' => urlencode(base64_encode($user->getEmail())), - 'user_ip' => urlencode($_SERVER['REMOTE_ADDR'])); - - // Urlify the data for the POST. - foreach($fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; } - $fields_string = rtrim($fields_string, '&'); - - // Open connection. - $ch = curl_init(); - - // Set the url, number of POST vars, POST data. - curl_setopt($ch, CURLOPT_URL, $this->register_uri); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - - // Execute a post request. - $result = curl_exec($ch); - - // Close connection. - curl_close($ch); - - if (!$result) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - - $result_array = json_decode($result, true); - - // Check for errors. - $call_status = $result_array['call_status']; - if (!$call_status) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - if ($call_status['code'] != TT_CURL_SUCCESS) { - $this->errors->add($call_status['error']); - return false; - } - - $reg_status = $result_array['reg_status']; // Registration status. - if ($reg_status['site_id'] && $reg_status['site_key']) { - $this->site_id = $reg_status['site_id']; - $this->site_key = $reg_status['site_key']; - - // Registration successful. Store id and key locally for future use. - $sql = "insert into tt_site_config values('worksite_id', $this->site_id, now(), null)"; - $mdb2->exec($sql); - $sql = "insert into tt_site_config values('worksite_key', ".$mdb2->quote($this->site_key).", now(), null)"; - $mdb2->exec($sql); - } else { - $this->errors->add($i18n->get('error.remote_work')); - } - } else { - // Site id found. - $this->site_id = $val['id']; - - // Obtain site key. - $sql = "select param_value as site_key from tt_site_config where param_name = 'worksite_key'"; - $res = $mdb2->query($sql); - $val = $res->fetchRow(); - $this->site_key = $val['site_key']; - } - } - - // putOwnWorkItem - publishes a work item in remote work server. - // - // Note about some fields using additional base64 encoding. - // There is a problem with data posted by curl calls on the server side for - // some languages. Data arrives corrupted. - // - // For example: consider a case for a single Russian letter ф in the subject field. - // UTF-8 encoding for ф: 0xD1 0x84 (2 bytes). - // urlencoded ф: %D1%84 - a string of 6 characters. - // If we use a curl call like here with only urlencoded ф, what arrives on the server in POST is "C3 91 C2 84" - // no idea what it is. - // - // A workaround for now is to use use an additional base64 encoding for all text fields, - // which are decoded back to utf-8 strings on the server side. - function putOwnWorkItem($fields) { - global $i18n; - global $user; - $mdb2 = getConnection(); - - $group_id = $user->getGroup(); - $org_id = $user->org_id; - $curl_fields = array('lang' => urlencode($user->lang), - 'site_id' => urlencode($this->site_id), - 'site_key' => urlencode($this->site_key), - 'org_id' => urlencode($org_id), - 'org_key' => urlencode($user->getOrgKey()), - 'group_id' => urlencode($group_id), - 'group_name' => urlencode(base64_encode($user->getGroupName())), - 'group_key' => urlencode($user->getGroupKey()), - 'offer_id' => urlencode($fields['offer_id']), - 'type' => urlencode($fields['type']), - 'subject' => urlencode(base64_encode($fields['subject'])), - 'descr_short' => urlencode(base64_encode($fields['descr_short'])), - 'descr_long' => urlencode(base64_encode($fields['descr_long'])), - 'currency' => urlencode($fields['currency']), - 'amount' => urlencode($fields['amount']), - 'created_ip' => urlencode($_SERVER['REMOTE_ADDR']), - 'created_by' => urlencode($user->getUser()), - 'created_by_name' => urlencode(base64_encode($user->getName())), - 'created_by_email' => urlencode(base64_encode($user->getEmail())) - ); - - // url-ify the data for the POST. - foreach($curl_fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; } - $fields_string = rtrim($fields_string, '&'); - - // Open connection. - $ch = curl_init(); - - // Set the url, number of POST vars, POST data. - curl_setopt($ch, CURLOPT_URL, $this->put_own_work_item_uri); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - - // Execute a post request. - $result = curl_exec($ch); - - // Close connection. - curl_close($ch); - - if (!$result) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - - $result_array = json_decode($result, true); - - // Check for errors. - $call_status = $result_array['call_status']; - if (!$call_status) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - if ($call_status['code'] != TT_CURL_SUCCESS) { - $this->errors->add($call_status['error']); - return false; - } - - return true; - } - - // updateOwnWorkItem - updates a work item in remote work server. - function updateOwnWorkItem($fields) { - global $i18n; - global $user; - $mdb2 = getConnection(); - - $group_id = $user->getGroup(); - $org_id = $user->org_id; - - $curl_fields = array('lang' => urlencode($user->lang), - 'site_id' => urlencode($this->site_id), - 'site_key' => urlencode($this->site_key), - 'org_id' => urlencode($org_id), - 'org_key' => urlencode($user->getOrgKey()), - 'group_id' => urlencode($group_id), - 'group_name' => urlencode(base64_encode($user->getGroupName())), - 'group_key' => urlencode($user->getGroupKey()), - 'work_id' => urlencode($fields['work_id']), - 'type' => urlencode($fields['type']), - 'subject' => urlencode(base64_encode($fields['subject'])), - 'descr_short' => urlencode(base64_encode($fields['descr_short'])), - 'descr_long' => urlencode(base64_encode($fields['descr_long'])), - 'currency' => urlencode($fields['currency']), - 'amount' => urlencode($fields['amount']), - 'modified_ip' => urlencode($_SERVER['REMOTE_ADDR']), - 'modified_by' => urlencode($user->getUser()), - 'modified_by_name' => urlencode(base64_encode($user->getName())), - 'modified_by_email' => urlencode(base64_encode($user->getEmail())) - ); - - // url-ify the data for the POST. - foreach($curl_fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; } - $fields_string = rtrim($fields_string, '&'); - - // Open connection. - $ch = curl_init(); - - // Set the url, number of POST vars, POST data. - curl_setopt($ch, CURLOPT_URL, $this->update_own_work_item_uri); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - - // Execute a post request. - $result = curl_exec($ch); - - // Close connection. - curl_close($ch); - - if (!$result) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - - $result_array = json_decode($result, true); - - // Check for errors. - $call_status = $result_array['call_status']; - if (!$call_status) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - if ($call_status['code'] != TT_CURL_SUCCESS) { - $this->errors->add($call_status['error']); - return false; - } - - return true; - } - - // updateOwnWorkItemOnOffer - updates own work item posted on offer in remote work server. - function updateOwnWorkItemOnOffer($fields) { - global $i18n; - global $user; - $mdb2 = getConnection(); - - $group_id = $user->getGroup(); - $org_id = $user->org_id; - - $curl_fields = array('lang' => urlencode($user->lang), - 'site_id' => urlencode($this->site_id), - 'site_key' => urlencode($this->site_key), - 'org_id' => urlencode($org_id), - 'org_key' => urlencode($user->getOrgKey()), - 'group_id' => urlencode($group_id), - 'group_name' => urlencode(base64_encode($user->getGroupName())), - 'group_key' => urlencode($user->getGroupKey()), - 'work_id' => urlencode($fields['work_id']), - 'descr_short' => urlencode(base64_encode($fields['descr_short'])), - 'descr_long' => urlencode(base64_encode($fields['descr_long'])), - 'modified_ip' => urlencode($_SERVER['REMOTE_ADDR']), - 'modified_by' => urlencode($user->getUser()), - 'modified_by_name' => urlencode(base64_encode($user->getName())), - 'modified_by_email' => urlencode(base64_encode($user->getEmail())) - ); - - // url-ify the data for the POST. - foreach($curl_fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; } - $fields_string = rtrim($fields_string, '&'); - - // Open connection. - $ch = curl_init(); - - // Set the url, number of POST vars, POST data. - curl_setopt($ch, CURLOPT_URL, $this->update_own_work_item_on_offer_uri); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - - // Execute a post request. - $result = curl_exec($ch); - - // Close connection. - curl_close($ch); - - if (!$result) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - - $result_array = json_decode($result, true); - - // Check for errors. - $call_status = $result_array['call_status']; - if (!$call_status) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - if ($call_status['code'] != TT_CURL_SUCCESS) { - $this->errors->add($call_status['error']); - return false; - } - - return true; - } - - // getOwnWorkItems - obtains a list of work items this group is currently outsourcing. - function getOwnWorkItems() { - global $i18n; - global $user; - $mdb2 = getConnection(); - - $group_id = $user->getGroup(); - $org_id = $user->org_id; - - $curl_fields = array('lang' => urlencode($user->lang), - 'site_id' => urlencode($this->site_id), - 'site_key' => urlencode($this->site_key), - 'org_id' => urlencode($org_id), - 'org_key' => urlencode($user->getOrgKey()), - 'group_id' => urlencode($group_id), - 'group_key' => urlencode($user->getGroupKey()) - ); - - // url-ify the data for the POST. - foreach($curl_fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; } - $fields_string = rtrim($fields_string, '&'); - - // Open connection. - $ch = curl_init(); - - // Set the url, number of POST vars, POST data. - curl_setopt($ch, CURLOPT_URL, $this->get_own_work_items_iri); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - - // Execute a post request. - $result = curl_exec($ch); - - // Close connection. - curl_close($ch); - - if (!$result) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - - $result_array = json_decode($result, true); - - // Check for errors. - $call_status = $result_array['call_status']; - if (!$call_status) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - if ($call_status['code'] != TT_CURL_SUCCESS) { - $this->errors->add($call_status['error']); - return false; - } - - $active_work = $result_array['active_work']; - return $active_work; - } - - // getAvailableWorkItems - obtains a list of available work items this group can bid on. - function getAvailableWorkItems() { - global $i18n; - global $user; - $mdb2 = getConnection(); - - $group_id = $user->getGroup(); - $org_id = $user->org_id; - - $curl_fields = array('lang' => urlencode($user->lang), - 'site_id' => urlencode($this->site_id), - 'site_key' => urlencode($this->site_key), - 'org_id' => urlencode($org_id) - ); - - // url-ify the data for the POST. - foreach($curl_fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; } - $fields_string = rtrim($fields_string, '&'); - - // Open connection. - $ch = curl_init(); - - // Set the url, number of POST vars, POST data. - curl_setopt($ch, CURLOPT_URL, $this->get_available_work_items_uri); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - - // Execute a post request. - $result = curl_exec($ch); - - // Close connection. - curl_close($ch); - - if (!$result) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - - $result_array = json_decode($result, true); - - // Check for errors. - $call_status = $result_array['call_status']; - if (!$call_status) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - if ($call_status['code'] != TT_CURL_SUCCESS) { - $this->errors->add($call_status['error']); - return false; - } - - $available_work = $result_array['available_work']; - return $available_work; - } - - // getOwnWorkItem - gets work item details from remote work server. - function getOwnWorkItem($work_id) { - global $i18n; - global $user; - $mdb2 = getConnection(); - - $group_id = $user->getGroup(); - $org_id = $user->org_id; - - $curl_fields = array('lang' => urlencode($user->lang), - 'site_id' => urlencode($this->site_id), - 'site_key' => urlencode($this->site_key), - 'org_id' => urlencode($org_id), - 'org_key' => urlencode($user->getOrgKey()), - 'group_id' => urlencode($group_id), - 'group_key' => urlencode($user->getGroupKey()), - 'work_id' => urlencode($work_id)); - - // url-ify the data for the POST. - foreach($curl_fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; } - $fields_string = rtrim($fields_string, '&'); - - // Open connection. - $ch = curl_init(); - - // Set the url, number of POST vars, POST data. - curl_setopt($ch, CURLOPT_URL, $this->get_own_work_item_uri); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - - // Execute a post request. - $result = curl_exec($ch); - - // Close connection. - curl_close($ch); - - if (!$result) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - - $result_array = json_decode($result, true); - - // Check for errors. - $call_status = $result_array['call_status']; - if (!$call_status) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - if ($call_status['code'] != TT_CURL_SUCCESS) { - $this->errors->add($call_status['error']); - return false; - } - - $work_item = $result_array['work_item']; - return $work_item; - } - - // getOwnWorkItemOffers - gets offers on own work item from remote work server. - function getOwnWorkItemOffers($work_id) { - global $i18n; - global $user; - $mdb2 = getConnection(); - - $group_id = $user->getGroup(); - $org_id = $user->org_id; - - $curl_fields = array('lang' => urlencode($user->lang), - 'site_id' => urlencode($this->site_id), - 'site_key' => urlencode($this->site_key), - 'org_id' => urlencode($org_id), - 'org_key' => urlencode($user->getOrgKey()), - 'group_id' => urlencode($group_id), - 'group_key' => urlencode($user->getGroupKey()), - 'work_id' => urlencode($work_id)); - - // url-ify the data for the POST. - foreach($curl_fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; } - $fields_string = rtrim($fields_string, '&'); - - // Open connection. - $ch = curl_init(); - - // Set the url, number of POST vars, POST data. - curl_setopt($ch, CURLOPT_URL, $this->get_own_work_item_offers_uri); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - - // Execute a post request. - $result = curl_exec($ch); - - // Close connection. - curl_close($ch); - - if (!$result) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - - $result_array = json_decode($result, true); - - // Check for errors. - $call_status = $result_array['call_status']; - if (!$call_status) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - if ($call_status['code'] != TT_CURL_SUCCESS) { - $this->errors->add($call_status['error']); - return false; - } - - $work_item_offers = $result_array['work_item_offers']; - return $work_item_offers; - } - - // deleteOwnWorkItem - deletes work item from remote work server. - function deleteOwnWorkItem($work_id) { - global $i18n; - global $user; - $mdb2 = getConnection(); - - $group_id = $user->getGroup(); - $org_id = $user->org_id; - - $curl_fields = array('lang' => urlencode($user->lang), - 'site_id' => urlencode($this->site_id), - 'site_key' => urlencode($this->site_key), - 'org_id' => urlencode($org_id), - 'org_key' => urlencode($user->getOrgKey()), - 'group_id' => urlencode($group_id), - 'group_key' => urlencode($user->getGroupKey()), - 'work_id' => urlencode($work_id), - 'modified_ip' => urlencode($_SERVER['REMOTE_ADDR']), - 'modified_by' => urlencode($user->getUser()), - 'modified_by_name' => urlencode(base64_encode($user->getName())), - 'modified_by_email' => urlencode(base64_encode($user->getEmail()))); - - // url-ify the data for the POST. - foreach($curl_fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; } - $fields_string = rtrim($fields_string, '&'); - - // Open connection. - $ch = curl_init(); - - // Set the url, number of POST vars, POST data. - curl_setopt($ch, CURLOPT_URL, $this->delete_own_work_item_uri); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - - // Execute a post request. - $result = curl_exec($ch); - - // Close connection. - curl_close($ch); - - if (!$result) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - - $result_array = json_decode($result, true); - - // Check for errors. - $call_status = $result_array['call_status']; - if (!$call_status) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - if ($call_status['code'] != TT_CURL_SUCCESS) { - $this->errors->add($call_status['error']); - return false; - } - - return true; - } - - // putOwnOffer - publishes an offer in remote work server. - function putOwnOffer($fields) { - global $i18n; - global $user; - $mdb2 = getConnection(); - - $group_id = $user->getGroup(); - $org_id = $user->org_id; - $curl_fields = array('lang' => urlencode($user->lang), - 'site_id' => urlencode($this->site_id), - 'site_key' => urlencode($this->site_key), - 'org_id' => urlencode($org_id), - 'org_key' => urlencode($user->getOrgKey()), - 'group_id' => urlencode($group_id), - 'group_name' => urlencode(base64_encode($user->getGroupName())), - 'group_key' => urlencode($user->getGroupKey()), - 'work_id' => urlencode($fields['work_id']), - 'subject' => urlencode(base64_encode($fields['subject'])), - 'descr_short' => urlencode(base64_encode($fields['descr_short'])), - 'descr_long' => urlencode(base64_encode($fields['descr_long'])), - 'currency' => urlencode($fields['currency']), - 'amount' => urlencode($fields['amount']), - 'payment_info' => urlencode(base64_encode($fields['payment_info'])), - 'created_ip' => urlencode($_SERVER['REMOTE_ADDR']), - 'created_by' => urlencode($user->getUser()), - 'created_by_name' => urlencode(base64_encode($user->getName())), - 'created_by_email' => urlencode(base64_encode($user->getEmail())) - ); - - // url-ify the data for the POST. - foreach($curl_fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; } - $fields_string = rtrim($fields_string, '&'); - - // Open connection. - $ch = curl_init(); - - // Set the url, number of POST vars, POST data. - curl_setopt($ch, CURLOPT_URL, $this->put_own_offer_uri); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - - // Execute a post request. - $result = curl_exec($ch); - - // Close connection. - curl_close($ch); - - if (!$result) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - - $result_array = json_decode($result, true); - - // Check for errors. - $call_status = $result_array['call_status']; - if (!$call_status) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - if ($call_status['code'] != TT_CURL_SUCCESS) { - $this->errors->add($call_status['error']); - return false; - } - - return true; - } - - // getOwnOffers - obtains a list of offers this group made available to other groups. - function getOwnOffers() { - global $i18n; - global $user; - $mdb2 = getConnection(); - - $group_id = $user->getGroup(); - $org_id = $user->org_id; - - $curl_fields = array('lang' => urlencode($user->lang), - 'site_id' => urlencode($this->site_id), - 'site_key' => urlencode($this->site_key), - 'org_id' => urlencode($org_id), - 'org_key' => urlencode($user->getOrgKey()), - 'group_id' => urlencode($group_id), - 'group_key' => urlencode($user->getGroupKey()) - ); - - // url-ify the data for the POST. - foreach($curl_fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; } - $fields_string = rtrim($fields_string, '&'); - - // Open connection. - $ch = curl_init(); - - // Set the url, number of POST vars, POST data. - curl_setopt($ch, CURLOPT_URL, $this->get_own_offers_uri); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - - // Execute a post request. - $result = curl_exec($ch); - - // Close connection. - curl_close($ch); - - if (!$result) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - - $result_array = json_decode($result, true); - - // Check for errors. - $call_status = $result_array['call_status']; - if (!$call_status) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - if ($call_status['code'] != TT_CURL_SUCCESS) { - $this->errors->add($call_status['error']); - return false; - } - - $active_offers = $result_array['active_offers']; - return $active_offers; - } - - // getAvailableOffers - obtains a list of available offers from other organizations. - function getAvailableOffers() { - global $i18n; - global $user; - $mdb2 = getConnection(); - - $group_id = $user->getGroup(); - $org_id = $user->org_id; - - $curl_fields = array('lang' => urlencode($user->lang), - 'site_id' => urlencode($this->site_id), - 'site_key' => urlencode($this->site_key), - 'org_id' => urlencode($org_id) - ); - - // url-ify the data for the POST. - foreach($curl_fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; } - $fields_string = rtrim($fields_string, '&'); - - // Open connection. - $ch = curl_init(); - - // Set the url, number of POST vars, POST data. - curl_setopt($ch, CURLOPT_URL, $this->get_available_offers_uri); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - - // Execute a post request. - $result = curl_exec($ch); - - // Close connection. - curl_close($ch); - - if (!$result) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - - $result_array = json_decode($result, true); - - // Check for errors. - $call_status = $result_array['call_status']; - if (!$call_status) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - if ($call_status['code'] != TT_CURL_SUCCESS) { - $this->errors->add($call_status['error']); - return false; - } - - $available_offers = $result_array['available_offers']; - return $available_offers; - } - - // getOwnOffer - gets offer details from remote work server. - function getOwnOffer($offer_id) { - global $i18n; - global $user; - $mdb2 = getConnection(); - - $group_id = $user->getGroup(); - $org_id = $user->org_id; - - $curl_fields = array('lang' => urlencode($user->lang), - 'site_id' => urlencode($this->site_id), - 'site_key' => urlencode($this->site_key), - 'org_id' => urlencode($org_id), - 'org_key' => urlencode($user->getOrgKey()), - 'group_id' => urlencode($group_id), - 'group_key' => urlencode($user->getGroupKey()), - 'offer_id' => urlencode($offer_id)); - - // url-ify the data for the POST. - foreach($curl_fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; } - $fields_string = rtrim($fields_string, '&'); - - // Open connection. - $ch = curl_init(); - - // Set the url, number of POST vars, POST data. - curl_setopt($ch, CURLOPT_URL, $this->get_own_offer_uri); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - - // Execute a post request. - $result = curl_exec($ch); - - // Close connection. - curl_close($ch); - - if (!$result) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - - $result_array = json_decode($result, true); - - // Check for errors. - $call_status = $result_array['call_status']; - if (!$call_status) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - if ($call_status['code'] != TT_CURL_SUCCESS) { - $this->errors->add($call_status['error']); - return false; - } - - $offer = $result_array['offer']; - return $offer; - } - - // updateOwnOffer - updates an offer in remote work server. - function updateOwnOffer($fields) { - global $i18n; - global $user; - $mdb2 = getConnection(); - - $group_id = $user->getGroup(); - $org_id = $user->org_id; - - $curl_fields = array('lang' => urlencode($user->lang), - 'site_id' => urlencode($this->site_id), - 'site_key' => urlencode($this->site_key), - 'org_id' => urlencode($org_id), - 'org_key' => urlencode($user->getOrgKey()), - 'group_id' => urlencode($group_id), - 'group_name' => urlencode(base64_encode($user->getGroupName())), - 'group_key' => urlencode($user->getGroupKey()), - 'offer_id' => urlencode($fields['offer_id']), - 'subject' => urlencode(base64_encode($fields['subject'])), - 'descr_short' => urlencode(base64_encode($fields['descr_short'])), - 'descr_long' => urlencode(base64_encode($fields['descr_long'])), - 'currency' => urlencode($fields['currency']), - 'amount' => urlencode($fields['amount']), - 'payment_info' => urlencode(base64_encode($fields['payment_info'])), - 'modified_ip' => urlencode($_SERVER['REMOTE_ADDR']), - 'modified_by' => urlencode($user->getUser()), - 'modified_by_name' => urlencode(base64_encode($user->getName())), - 'modified_by_email' => urlencode(base64_encode($user->getEmail())) - ); - - // url-ify the data for the POST. - foreach($curl_fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; } - $fields_string = rtrim($fields_string, '&'); - - // Open connection. - $ch = curl_init(); - - // Set the url, number of POST vars, POST data. - curl_setopt($ch, CURLOPT_URL, $this->update_own_offer_uri); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - - // Execute a post request. - $result = curl_exec($ch); - - // Close connection. - curl_close($ch); - - if (!$result) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - - $result_array = json_decode($result, true); - - // Check for errors. - $call_status = $result_array['call_status']; - if (!$call_status) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - if ($call_status['code'] != TT_CURL_SUCCESS) { - $this->errors->add($call_status['error']); - return false; - } - - return true; - } - - // deleteOwnOffer - deletes an offer from remote work server. - function deleteOwnOffer($offer_id) { - global $i18n; - global $user; - $mdb2 = getConnection(); - - $group_id = $user->getGroup(); - $org_id = $user->org_id; - - $curl_fields = array('lang' => urlencode($user->lang), - 'site_id' => urlencode($this->site_id), - 'site_key' => urlencode($this->site_key), - 'org_id' => urlencode($org_id), - 'org_key' => urlencode($user->getOrgKey()), - 'group_id' => urlencode($group_id), - 'group_key' => urlencode($user->getGroupKey()), - 'offer_id' => urlencode($offer_id), - 'modified_ip' => urlencode($_SERVER['REMOTE_ADDR']), - 'modified_by' => urlencode($user->getUser()), - 'modified_by_name' => urlencode(base64_encode($user->getName())), - 'modified_by_email' => urlencode(base64_encode($user->getEmail()))); - - // url-ify the data for the POST. - foreach($curl_fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; } - $fields_string = rtrim($fields_string, '&'); - - // Open connection. - $ch = curl_init(); - - // Set the url, number of POST vars, POST data. - curl_setopt($ch, CURLOPT_URL, $this->delete_own_offer_uri); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - - // Execute a post request. - $result = curl_exec($ch); - - // Close connection. - curl_close($ch); - - if (!$result) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - - $result_array = json_decode($result, true); - - // Check for errors. - $call_status = $result_array['call_status']; - if (!$call_status) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - if ($call_status['code'] != TT_CURL_SUCCESS) { - $this->errors->add($call_status['error']); - return false; - } - - return true; - } - - // getOwnWorkItemOffer - gets offer details from remote work server - // for an offer posted on own work item. - function getOwnWorkItemOffer($offer_id) { - global $i18n; - global $user; - $mdb2 = getConnection(); - - $group_id = $user->getGroup(); - $org_id = $user->org_id; - - $curl_fields = array('lang' => urlencode($user->lang), - 'site_id' => urlencode($this->site_id), - 'site_key' => urlencode($this->site_key), - 'org_id' => urlencode($org_id), - 'org_key' => urlencode($user->getOrgKey()), - 'group_id' => urlencode($group_id), - 'group_key' => urlencode($user->getGroupKey()), - 'offer_id' => urlencode($offer_id)); - - // url-ify the data for the POST. - foreach($curl_fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; } - $fields_string = rtrim($fields_string, '&'); - - // Open connection. - $ch = curl_init(); - - // Set the url, number of POST vars, POST data. - curl_setopt($ch, CURLOPT_URL, $this->get_own_work_item_offer_uri); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - - // Execute a post request. - $result = curl_exec($ch); - - // Close connection. - curl_close($ch); - - if (!$result) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - - $result_array = json_decode($result, true); - - // Check for errors. - $call_status = $result_array['call_status']; - if (!$call_status) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - if ($call_status['code'] != TT_CURL_SUCCESS) { - $this->errors->add($call_status['error']); - return false; - } - - $offer = $result_array['offer']; - return $offer; - } - - // declineOwnWorkItemOffer - declines an offer posted on own work item in remote work server. - function declineOwnWorkItemOffer($fields) { - global $i18n; - global $user; - $mdb2 = getConnection(); - - $group_id = $user->getGroup(); - $org_id = $user->org_id; - - $curl_fields = array('lang' => urlencode($user->lang), - 'site_id' => urlencode($this->site_id), - 'site_key' => urlencode($this->site_key), - 'org_id' => urlencode($org_id), - 'org_key' => urlencode($user->getOrgKey()), - 'group_id' => urlencode($group_id), - 'group_key' => urlencode($user->getGroupKey()), - 'offer_id' => urlencode($fields['offer_id']), - 'client_comment' => urlencode(base64_encode($fields['client_comment']))); - //'modified_ip' => urlencode($_SERVER['REMOTE_ADDR']), - //'modified_by' => urlencode($user->getUser()), - //'modified_by_name' => urlencode(base64_encode($user->getName())), - //'modified_by_email' => urlencode(base64_encode($user->getEmail()))); - - // url-ify the data for the POST. - foreach($curl_fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; } - $fields_string = rtrim($fields_string, '&'); - - // Open connection. - $ch = curl_init(); - - // Set the url, number of POST vars, POST data. - curl_setopt($ch, CURLOPT_URL, $this->decline_own_work_item_offer_uri); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - - // Execute a post request. - $result = curl_exec($ch); - - // Close connection. - curl_close($ch); - - if (!$result) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - - $result_array = json_decode($result, true); - - // Check for errors. - $call_status = $result_array['call_status']; - if (!$call_status) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - if ($call_status['code'] != TT_CURL_SUCCESS) { - $this->errors->add($call_status['error']); - return false; - } - - return true; - } - - // acceptOwnWorkItemOffer - accepts an offer posted on own work item in remote work server. - function acceptOwnWorkItemOffer($fields) { - global $i18n; - global $user; - $mdb2 = getConnection(); - - $group_id = $user->getGroup(); - $org_id = $user->org_id; - - $curl_fields = array('lang' => urlencode($user->lang), - 'site_id' => urlencode($this->site_id), - 'site_key' => urlencode($this->site_key), - 'org_id' => urlencode($org_id), - 'org_key' => urlencode($user->getOrgKey()), - 'group_id' => urlencode($group_id), - 'group_key' => urlencode($user->getGroupKey()), - 'offer_id' => urlencode($fields['offer_id']), - 'client_comment' => urlencode(base64_encode($fields['client_comment'])), - 'accepted_ip' => urlencode($_SERVER['REMOTE_ADDR']), - 'accepted_by' => urlencode($user->getUser()), - 'accepted_by_name' => urlencode(base64_encode($user->getName())), - 'accepted_by_email' => urlencode(base64_encode($user->getEmail()))); - - // url-ify the data for the POST. - foreach($curl_fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; } - $fields_string = rtrim($fields_string, '&'); - - // Open connection. - $ch = curl_init(); - - // Set the url, number of POST vars, POST data. - curl_setopt($ch, CURLOPT_URL, $this->accept_own_work_item_offer_uri); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - - // Execute a post request. - $result = curl_exec($ch); - - // Close connection. - curl_close($ch); - - if (!$result) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - - $result_array = json_decode($result, true); - - // Check for errors. - $call_status = $result_array['call_status']; - if (!$call_status) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - if ($call_status['code'] != TT_CURL_SUCCESS) { - $this->errors->add($call_status['error']); - return false; - } - - return true; - } - - // getCurrencies - obtains a list of supported currencies. - static function getCurrencies() { - $mdb2 = getConnection(); - - $sql = "select id, name from tt_work_currencies order by id"; - $res = $mdb2->query($sql); - if (is_a($res, 'PEAR_Error')) return false; - - while ($val = $res->fetchRow()) { - $result[] = array('id'=>$val['id'], 'name'=>$val['name']); - } - return $result; - } - - // getCurrencyID - obtains an ID for currency. - static function getCurrencyID($currency) { - $mdb2 = getConnection(); - - $currency_id = null; - $sql = "select id from tt_work_currencies where name = ".$mdb2->quote($currency); - $res = $mdb2->query($sql); - if (is_a($res, 'PEAR_Error')) return null; - - if($val = $res->fetchRow()) { - $currency_id = $val['id']; - } - return $currency_id; - } - - // getCurrencyName - obtains currency name using its id. - static function getCurrencyName($currency_id) { - $mdb2 = getConnection(); - - $currency_name = null; - $sql = "select name from tt_work_currencies where id = $currency_id"; - $res = $mdb2->query($sql); - if (is_a($res, 'PEAR_Error')) return null; - - if($val = $res->fetchRow()) { - $currency_name = $val['name']; - } - return $currency_name; - } - - // getGroupItems - obtains a list of all items relevant to group in one API call to Remote Work Server. - function getGroupItems() { - global $i18n; - global $user; - $mdb2 = getConnection(); - - $group_id = $user->getGroup(); - $org_id = $user->org_id; - - $curl_fields = array('lang' => urlencode($user->lang), - 'site_id' => urlencode($this->site_id), - 'site_key' => urlencode($this->site_key), - 'org_id' => urlencode($org_id), - 'org_key' => urlencode($user->getOrgKey()), - 'group_id' => urlencode($group_id), - 'group_key' => urlencode($user->getGroupKey()) - ); - - // url-ify the data for the POST. - foreach($curl_fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; } - $fields_string = rtrim($fields_string, '&'); - - // Open connection. - $ch = curl_init(); - - // Set the url, number of POST vars, POST data. - curl_setopt($ch, CURLOPT_URL, $this->get_group_items_uri); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - - // Execute a post request. - $result = curl_exec($ch); - - // Close connection. - curl_close($ch); - - if (!$result) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - - $result_array = json_decode($result, true); - - // Check for errors. - $call_status = $result_array['call_status']; - if (!$call_status) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - if ($call_status['code'] != TT_CURL_SUCCESS) { - $this->errors->add($call_status['error']); - return false; - } - - unset($result_array['call_status']); // Remove call_status element, - return $result_array; - } - - // getAvailableWorkItem - gets available work item details from remote work server. - function getAvailableWorkItem($work_id) { - global $i18n; - global $user; - $mdb2 = getConnection(); - - $group_id = $user->getGroup(); - $org_id = $user->org_id; - - $curl_fields = array('lang' => urlencode($user->lang), - 'site_id' => urlencode($this->site_id), - 'site_key' => urlencode($this->site_key), - 'org_id' => urlencode($org_id), - 'group_id' => urlencode($group_id), - 'group_name' => urlencode(base64_encode($user->getGroupName())), - 'user_id' => urlencode($user->getUser()), - 'user_name' => urlencode(base64_encode($user->getName())), - 'user_email' => urlencode(base64_encode($user->getEmail())), - 'user_ip' => urlencode($_SERVER['REMOTE_ADDR']), - 'work_id' => urlencode($work_id)); - - // url-ify the data for the POST. - foreach($curl_fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; } - $fields_string = rtrim($fields_string, '&'); - - // Open connection. - $ch = curl_init(); - - // Set the url, number of POST vars, POST data. - curl_setopt($ch, CURLOPT_URL, $this->get_available_work_item_uri); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - - // Execute a post request. - $result = curl_exec($ch); - - // Close connection. - curl_close($ch); - - if (!$result) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - - $result_array = json_decode($result, true); - - // Check for errors. - $call_status = $result_array['call_status']; - if (!$call_status) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - if ($call_status['code'] != TT_CURL_SUCCESS) { - $this->errors->add($call_status['error']); - return false; - } - - $work_item = $result_array['work_item']; - return $work_item; - } - - // getAvailableOffer - gets available offer details from remote work server. - function getAvailableOffer($offer_id) { - global $i18n; - global $user; - $mdb2 = getConnection(); - - $group_id = $user->getGroup(); - $org_id = $user->org_id; - - $curl_fields = array('lang' => urlencode($user->lang), - 'site_id' => urlencode($this->site_id), - 'site_key' => urlencode($this->site_key), - 'org_id' => urlencode($org_id), - 'group_id' => urlencode($group_id), - 'group_name' => urlencode(base64_encode($user->getGroupName())), - 'user_id' => urlencode($user->getUser()), - 'user_name' => urlencode(base64_encode($user->getName())), - 'user_email' => urlencode(base64_encode($user->getEmail())), - 'user_ip' => urlencode($_SERVER['REMOTE_ADDR']), - 'offer_id' => urlencode($offer_id)); - - // url-ify the data for the POST. - foreach($curl_fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; } - $fields_string = rtrim($fields_string, '&'); - - // Open connection. - $ch = curl_init(); - - // Set the url, number of POST vars, POST data. - curl_setopt($ch, CURLOPT_URL, $this->get_available_offer_uri); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - - // Execute a post request. - $result = curl_exec($ch); - - // Close connection. - curl_close($ch); - - if (!$result) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - - $result_array = json_decode($result, true); - - // Check for errors. - $call_status = $result_array['call_status']; - if (!$call_status) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - if ($call_status['code'] != TT_CURL_SUCCESS) { - $this->errors->add($call_status['error']); - return false; - } - - $offer = $result_array['offer']; - return $offer; - } - - // sendMessageToWorkOwner - sends a message to work owner via remote work server. - function sendMessageToWorkOwner($fields) { - global $i18n; - global $user; - $mdb2 = getConnection(); - - $group_id = $user->getGroup(); - $org_id = $user->org_id; - - $curl_fields = array('lang' => urlencode($user->lang), - 'site_id' => urlencode($this->site_id), - 'site_key' => urlencode($this->site_key), - 'org_id' => urlencode($org_id), - 'org_key' => urlencode($user->getOrgKey()), - 'group_id' => urlencode($group_id), - 'group_name' => urlencode(base64_encode($user->getGroupName())), - 'group_key' => urlencode($user->getGroupKey()), - 'work_id' => urlencode($fields['work_id']), - 'sender_ip' => urlencode($_SERVER['REMOTE_ADDR']), - 'sender_user_id' => urlencode($user->getUser()), - 'sender_name' => urlencode(base64_encode($user->getName())), - 'sender_email' => urlencode(base64_encode($user->getEmail())), - 'message_body' => urlencode(base64_encode($fields['message_body']))); - - // url-ify the data for the POST. - foreach($curl_fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; } - $fields_string = rtrim($fields_string, '&'); - - // Open connection. - $ch = curl_init(); - - // Set the url, number of POST vars, POST data. - curl_setopt($ch, CURLOPT_URL, $this->send_message_to_work_owner_uri); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - - // Execute a post request. - $result = curl_exec($ch); - - // Close connection. - curl_close($ch); - - if (!$result) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - - $result_array = json_decode($result, true); - - // Check for errors. - $call_status = $result_array['call_status']; - if (!$call_status) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - if ($call_status['code'] != TT_CURL_SUCCESS) { - $this->errors->add($call_status['error']); - return false; - } - - return true; - } - - // sendMessageToOfferOwner - sends a message to offer owner via remote work server. - function sendMessageToOfferOwner($fields) { - global $i18n; - global $user; - $mdb2 = getConnection(); - - $group_id = $user->getGroup(); - $org_id = $user->org_id; - - $curl_fields = array('lang' => urlencode($user->lang), - 'site_id' => urlencode($this->site_id), - 'site_key' => urlencode($this->site_key), - 'org_id' => urlencode($org_id), - 'org_key' => urlencode($user->getOrgKey()), - 'group_id' => urlencode($group_id), - 'group_name' => urlencode(base64_encode($user->getGroupName())), - 'group_key' => urlencode($user->getGroupKey()), - 'offer_id' => urlencode($fields['offer_id']), - 'sender_ip' => urlencode($_SERVER['REMOTE_ADDR']), - 'sender_user_id' => urlencode($user->getUser()), - 'sender_name' => urlencode(base64_encode($user->getName())), - 'sender_email' => urlencode(base64_encode($user->getEmail())), - 'message_body' => urlencode(base64_encode($fields['message_body']))); - - // url-ify the data for the POST. - foreach($curl_fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; } - $fields_string = rtrim($fields_string, '&'); - - // Open connection. - $ch = curl_init(); - - // Set the url, number of POST vars, POST data. - curl_setopt($ch, CURLOPT_URL, $this->send_message_to_offer_owner_uri); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - - // Execute a post request. - $result = curl_exec($ch); - - // Close connection. - curl_close($ch); - - if (!$result) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - - $result_array = json_decode($result, true); - - // Check for errors. - $call_status = $result_array['call_status']; - if (!$call_status) { - $this->errors->add($i18n->get('error.remote_work')); - return false; - } - if ($call_status['code'] != TT_CURL_SUCCESS) { - $this->errors->add($call_status['error']); - return false; - } - - return true; - } -} diff --git a/WEB-INF/resources/ca.lang.php b/WEB-INF/resources/ca.lang.php index f9d9952db..327132ed8 100644 --- a/WEB-INF/resources/ca.lang.php +++ b/WEB-INF/resources/ca.lang.php @@ -2,7 +2,8 @@ /* Copyright (c) Anuko International Ltd. https://www.anuko.com License: See license.txt */ -// Note: escape apostrophes with THREE backslashes, like here: choisir l\\\'option. +// Note: escape apostrophes with THREE backslashes, like here: 'choisir l\\\'option'. +// Alternatively: use one backslash and surround by double-quotes: "choisir l\'option". // Other characters (such as double-quotes in http links, etc.) do not have to be escaped. $i18n_language = 'Catalan (Català)'; @@ -74,6 +75,8 @@ // 'error.record' => 'Select record.', 'error.auth' => 'Usuari o parula de pas incorrecta.', // TODO: translate the following. +// 'error.2fa_code' => 'Invalid 2FA code.', +// 'error.weak_password' => 'Weak password.', // 'error.user_exists' => 'User with this login already exists.', // 'error.object_exists' => 'Object with this name already exists.', // 'error.invoice_exists' => 'Invoice with this number already exists.', @@ -97,7 +100,6 @@ // 'error.user_count' => 'Limit on user count.', // 'error.expired' => 'Expiration date reached.', // 'error.file_storage' => 'File storage server error.', // See comment in English file. -// 'error.remote_work' => 'Remote work server error.', // See comment in English file. // Warning messages. // TODO: translate the following. @@ -215,9 +217,6 @@ // 'label.ldap_hint' => 'Type your Windows login and password in the fields below.', 'label.required_fields' => '* camps requerits', 'label.on_behalf' => 'a nom de', -'label.role_manager' => '(manejador)', -'label.role_comanager' => '(auxiliar del manejador)', -'label.role_admin' => '(administrador)', // TODO: translate the following. // 'label.page' => 'Page', // 'label.condition' => 'Condition', @@ -245,6 +244,7 @@ // 'label.mark_paid' => 'Mark paid', // 'label.week_note' => 'Week note', // 'label.week_list' => 'Week list', +// 'label.weekends' => 'Weekends', // 'label.work_units' => 'Work units', // 'label.work_units_short' => 'Units', 'label.totals_only' => 'Només totals', @@ -263,16 +263,6 @@ // 'label.file' => 'File', // 'label.active_users' => 'Active Users', // 'label.inactive_users' => 'Inactive Users', -// 'label.details' => 'Details', -// 'label.budget' => 'Budget', -// 'label.work' => 'Work', // Table column header for work items, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// 'label.offer' => 'Offer', // Table column header for offers, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// 'label.contractor' => 'Contractor', // Table column header for offers (contractor is someone who offers to do work). - // Technically, it is either an org name or a combination of org and group names - // because both work items and offers are owned by Time Tracker groups of users. -// 'label.how_to_pay' => 'How to pay', // Label for the "How to pay" field on offers, which allows contractors to specify - // how to pay them, for example: paypal email, check by mail to a specific address, etc. -// 'label.moderator_comment' => 'Moderator comment', // Label for "Moderator comment" field that explains something. // Entity names. We use lower case (in English) because they are used in dropdowns, too. // They are used to associate a custom field with an entity type. @@ -290,6 +280,8 @@ // 'title.error' => 'Error', // 'title.success' => 'Success', 'title.login' => 'Sessió iniciada', +// TODO: translate the follolwing. +// 'title.2fa' => 'Two Factor Authentication', 'title.groups' => 'Grups', // TODO: translate the following. // 'title.add_group' => 'Adding Group', @@ -389,22 +381,6 @@ // 'title.edit_file' => 'Editing File', // 'title.delete_file' => 'Deleting File', // 'title.download_file' => 'Downloading File', -// 'title.work' => 'Work', -// 'title.add_work' => 'Adding Work', -// 'title.edit_work' => 'Editing Work', -// 'title.delete_work' => 'Deleting Work', -// 'title.active_work' => 'Active Work', // Active work items this group outsources to other groups. -// 'title.available_work' => 'Available Work', // Available work items from other organizations. -// 'title.inactive_work' => 'Inactive Work', // Inactive work items this group was outsourcing to other groups. -// 'title.pending_work' => 'Pending Work', // Work items pending moderator approval. -// 'title.offer' => 'Offer', -// 'title.add_offer' => 'Adding Offer', -// 'title.edit_offer' => 'Editing Offer', -// 'title.delete_offer' => 'Deleting Offer', -// 'title.active_offers' => 'Active Offers', // Active offers this group makes available to other groups. -// 'title.available_offers' => 'Available Offers', // Available offers from other organizations. -// 'title.inactive_offers' => 'Inactive Offers', // Inactive offers for group. -// 'title.pending_offers' => 'Pending Offers', // Offers pending moderator approval. // Section for common strings inside combo boxes on forms. Strings shared between forms shall be placed here. // Strings that are used in a single form must go to the specific form section. @@ -437,7 +413,6 @@ // 'dropdown.status_inactive' => 'inactive', // 'dropdown.delete' => 'delete', // 'dropdown.do_not_delete' => 'do not delete', -// 'dropdown.pending_approval' => 'pending approval', // 'dropdown.approved' => 'approved', // 'dropdown.not_approved' => 'not approved', // 'dropdown.paid' => 'paid', @@ -450,6 +425,16 @@ // TODO: translate the following. // 'form.login.about' => 'Anuko Time Tracker is an open source time tracking system.', +// Email subject and body for two-factor authentication. +// TODO: translate the following. +// 'email.2fa_code.subject' => 'Anuko Time Tracker two-factor authentication code', +// 'email.2fa_code.body' => "Dear User,\n\nYour two-factor authentication code is:\n\n%s\n\n", + +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// 'form.2fa.hint' => 'Check your email for 2FA code and enter it here.', +// 'form.2fa.2fa_code' => '2FA code', + // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. 'form.reset_password.message' => 'S\\\'ha enviat la petició de restablir paraula de pas.', // TODO: add "by email" to match the English string. 'form.reset_password.email_subject' => 'Sol·licitud de restabliment de la paraula de pas de Anuko Time Tracker', @@ -490,10 +475,13 @@ // 'form.reports.include_pending' => 'pending', 'form.reports.select_period' => 'Seleccionar període de temps', 'form.reports.set_period' => 'o establir dates', +// TODO: translate the following. +// 'form.reports.note_containing' => 'Note containing', 'form.reports.show_fields' => 'Mostrar camps', // TODO: translate the following. // 'form.reports.time_fields' => 'Time fields', // 'form.reports.user_fields' => 'User fields', +// 'form.reports.project_fields' => 'Project fields', 'form.reports.group_by' => 'Agrupar per', 'form.reports.group_by_no' => '--- no agrupar ---', 'form.reports.group_by_date' => 'data', @@ -508,6 +496,7 @@ // (after generating a report at https://timetracker.anuko.com/reports.php). 'form.report.export' => 'Exportar', // TODO: translate the following. +// 'form.report.per_hour' => 'Per hour', // 'form.report.assign_to_invoice' => 'Assign to invoice', // 'form.report.assign_to_timesheet' => 'Assign to timesheet', @@ -549,6 +538,7 @@ // Users form. See example at https://timetracker.anuko.com/users.php // TODO: translate the following. +// 'form.users.uncompleted_entry_today' => 'User has an uncompleted time entry today', // 'form.users.uncompleted_entry' => 'User has an uncompleted time entry', 'form.users.role' => 'Rol', 'form.users.manager' => 'Manejador', @@ -610,13 +600,19 @@ // 'form.group_edit.type_start_finish' => 'start and finish', // 'form.group_edit.type_duration' => 'duration', // 'form.group_edit.punch_mode' => 'Punch mode', +// 'form.group_edit.one_uncompleted' => 'One uncompleted', // 'form.group_edit.allow_overlap' => 'Allow overlap', // 'form.group_edit.future_entries' => 'Future entries', // 'form.group_edit.uncompleted_indicators' => 'Uncompleted indicators', // 'form.group_edit.confirm_save' => 'Confirm saving', -// 'form.group_edit.allow_ip' => 'Allow IP', // 'form.group_edit.advanced_settings' => 'Advanced settings', +// Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. +// TODO: Translate the following. +// 'form.group_advanced_edit.allow_ip' => 'Allow IP', +// 'form.group_advanced_edit.password_complexity' => 'Password complexity', +// 'form.group_advanced_edit.2fa' => 'Two factor authentication', + // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php // TODO: translate the following. // 'form.group_delete.hint' => 'Are you sure you want to delete the entire group?', @@ -684,21 +680,7 @@ // 'form.display_options.note_on_separate_row' => 'Note on separate row', // 'form.display_options.not_complete_days' => 'Not complete days', // 'form.display_options.inactive_projects' => 'Inactive projects', +// 'form.display_options.cost_per_hour' => 'Cost per hour', // 'form.display_options.custom_css' => 'Custom CSS', - -// Work plugin strings. See example at https://timetracker.anuko.com/work.php -// TODO: translate the following. -// 'work.error.work_not_available' => 'Work item is not available.', -// 'work.error.offer_not_available' => 'Offer is not available.', -// 'work.type.one_time' => 'one time', // Work type is "one time job" for well defined work ("do exactly this"). -// 'work.type.ongoing' => 'ongoing', // Work type is "ongoing" for complex jobs (billed by the hour, multiple contractors, etc.) -// 'work.label.own_work' => 'Own work', -// 'work.label.own_offers' => 'Own offers', -// 'work.label.offers' => 'Offers', -// 'work.button.send_message' => 'Send message', -// 'work.button.make_offer' => 'Make offer', -// 'work.button.accept' => 'Accept', -// 'work.button.decline' => 'Decline', -// 'work.title.send_message' => 'Sending Message', -// 'work.msg.message_sent' => 'Message sent.', +// 'form.display_options.custom_translation' => 'Custom translation', ); diff --git a/WEB-INF/resources/cs.lang.php b/WEB-INF/resources/cs.lang.php index 453a51000..c89457e6d 100644 --- a/WEB-INF/resources/cs.lang.php +++ b/WEB-INF/resources/cs.lang.php @@ -2,7 +2,8 @@ /* Copyright (c) Anuko International Ltd. https://www.anuko.com License: See license.txt */ -// Note: escape apostrophes with THREE backslashes, like here: choisir l\\\'option. +// Note: escape apostrophes with THREE backslashes, like here: 'choisir l\\\'option'. +// Alternatively: use one backslash and surround by double-quotes: "choisir l\'option". // Other characters (such as double-quotes in http links, etc.) do not have to be escaped. // Note to translators: Use proper capitalization rules for your language. @@ -76,6 +77,8 @@ // 'error.record' => 'Select record.', 'error.auth' => 'Nesprávné jméno nebo heslo.', // TODO: translate the following. +// 'error.2fa_code' => 'Invalid 2FA code.', +// 'error.weak_password' => 'Weak password.', // 'error.user_exists' => 'User with this login already exists.', // 'error.object_exists' => 'Object with this name already exists.', // 'error.invoice_exists' => 'Invoice with this number already exists.', @@ -99,7 +102,6 @@ // 'error.user_count' => 'Limit on user count.', // 'error.expired' => 'Expiration date reached.', // 'error.file_storage' => 'File storage server error.', // See comment in English file. -// 'error.remote_work' => 'Remote work server error.', // See comment in English file. // Warning messages. // TODO: translate the following. @@ -225,10 +227,6 @@ 'label.required_fields' => '* nutno vyplnit', // TODO: translate the following. // 'label.on_behalf' => 'on behalf of', -'label.role_manager' => '(manažer)', -'label.role_comanager' => '(spolumanažer)', -'label.role_admin' => '(administrator)', -// TODO: translate the following. // 'label.page' => 'Page', // 'label.condition' => 'Condition', // 'label.yes' => 'yes', @@ -255,6 +253,7 @@ // 'label.mark_paid' => 'Mark paid', // 'label.week_note' => 'Week note', // 'label.week_list' => 'Week list', +// 'label.weekends' => 'Weekends', // 'label.work_units' => 'Work units', // 'label.work_units_short' => 'Units', 'label.totals_only' => 'Pouze součty', @@ -273,16 +272,6 @@ // 'label.file' => 'File', // 'label.active_users' => 'Active Users', // 'label.inactive_users' => 'Inactive Users', -// 'label.details' => 'Details', -// 'label.budget' => 'Budget', -// 'label.work' => 'Work', // Table column header for work items, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// 'label.offer' => 'Offer', // Table column header for offers, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// 'label.contractor' => 'Contractor', // Table column header for offers (contractor is someone who offers to do work). - // Technically, it is either an org name or a combination of org and group names - // because both work items and offers are owned by Time Tracker groups of users. -// 'label.how_to_pay' => 'How to pay', // Label for the "How to pay" field on offers, which allows contractors to specify - // how to pay them, for example: paypal email, check by mail to a specific address, etc. -// 'label.moderator_comment' => 'Moderator comment', // Label for "Moderator comment" field that explains something. // Entity names. We use lower case (in English) because they are used in dropdowns, too. // They are used to associate a custom field with an entity type. @@ -300,6 +289,8 @@ // 'title.error' => 'Error', // 'title.success' => 'Success', 'title.login' => 'Přihlásit', +// TODO: translate the follolwing. +// 'title.2fa' => 'Two Factor Authentication', 'title.groups' => 'Týmy', // TODO: change "teams" to "groups". // TODO: translate the following. // 'title.add_group' => 'Adding Group', @@ -399,22 +390,6 @@ // 'title.edit_file' => 'Editing File', // 'title.delete_file' => 'Deleting File', // 'title.download_file' => 'Downloading File', -// 'title.work' => 'Work', -// 'title.add_work' => 'Adding Work', -// 'title.edit_work' => 'Editing Work', -// 'title.delete_work' => 'Deleting Work', -// 'title.active_work' => 'Active Work', // Active work items this group outsources to other groups. -// 'title.available_work' => 'Available Work', // Available work items from other organizations. -// 'title.inactive_work' => 'Inactive Work', // Inactive work items this group was outsourcing to other groups. -// 'title.pending_work' => 'Pending Work', // Work items pending moderator approval. -// 'title.offer' => 'Offer', -// 'title.add_offer' => 'Adding Offer', -// 'title.edit_offer' => 'Editing Offer', -// 'title.delete_offer' => 'Deleting Offer', -// 'title.active_offers' => 'Active Offers', // Active offers this group makes available to other groups. -// 'title.available_offers' => 'Available Offers', // Available offers from other organizations. -// 'title.inactive_offers' => 'Inactive Offers', // Inactive offers for group. -// 'title.pending_offers' => 'Pending Offers', // Offers pending moderator approval. // Section for common strings inside combo boxes on forms. Strings shared between forms shall be placed here. // Strings that are used in a single form must go to the specific form section. @@ -445,7 +420,6 @@ // 'dropdown.status_inactive' => 'inactive', // 'dropdown.delete' => 'delete', // 'dropdown.do_not_delete' => 'do not delete', -// 'dropdown.pending_approval' => 'pending approval', // 'dropdown.approved' => 'approved', // 'dropdown.not_approved' => 'not approved', // 'dropdown.paid' => 'paid', @@ -458,6 +432,16 @@ // TODO: translate the following. // 'form.login.about' => 'Anuko Time Tracker is an open source time tracking system.', +// Email subject and body for two-factor authentication. +// TODO: translate the following. +// 'email.2fa_code.subject' => 'Anuko Time Tracker two-factor authentication code', +// 'email.2fa_code.body' => "Dear User,\n\nYour two-factor authentication code is:\n\n%s\n\n", + +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// 'form.2fa.hint' => 'Check your email for 2FA code and enter it here.', +// 'form.2fa.2fa_code' => '2FA code', + // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. 'form.reset_password.message' => 'Zaslán požadavek k vymazání hesla.', // TODO: add "by email" to match the English string. 'form.reset_password.email_subject' => 'Anuko Time Tracker požadavek na vymazání hesla', @@ -499,10 +483,13 @@ // 'form.reports.include_pending' => 'pending', 'form.reports.select_period' => 'Výberte období', 'form.reports.set_period' => 'nebo určete dny', +// TODO: translate the following. +// 'form.reports.note_containing' => 'Note containing', 'form.reports.show_fields' => 'Zobrazit pole', // TODO: translate the following. // 'form.reports.time_fields' => 'Time fields', // 'form.reports.user_fields' => 'User fields', +// 'form.reports.project_fields' => 'Project fields', 'form.reports.group_by' => 'Seskupit podle', // TODO: translate the following. // 'form.reports.group_by_no' => '--- no grouping ---', @@ -517,6 +504,7 @@ // (after generating a report at https://timetracker.anuko.com/reports.php). 'form.report.export' => 'Exportovat', // TODO: translate the following. +// 'form.report.per_hour' => 'Per hour', // 'form.report.assign_to_invoice' => 'Assign to invoice', // 'form.report.assign_to_timesheet' => 'Assign to timesheet', @@ -558,6 +546,7 @@ // Users form. See example at https://timetracker.anuko.com/users.php // TODO: translate the following. +// 'form.users.uncompleted_entry_today' => 'User has an uncompleted time entry today', // 'form.users.uncompleted_entry' => 'User has an uncompleted time entry', 'form.users.role' => 'Role', 'form.users.manager' => 'Manažer', @@ -623,13 +612,19 @@ // 'form.group_edit.type_start_finish' => 'start and finish', // 'form.group_edit.type_duration' => 'duration', // 'form.group_edit.punch_mode' => 'Punch mode', +// 'form.group_edit.one_uncompleted' => 'One uncompleted', // 'form.group_edit.allow_overlap' => 'Allow overlap', // 'form.group_edit.future_entries' => 'Future entries', // 'form.group_edit.uncompleted_indicators' => 'Uncompleted indicators', // 'form.group_edit.confirm_save' => 'Confirm saving', -// 'form.group_edit.allow_ip' => 'Allow IP', // 'form.group_edit.advanced_settings' => 'Advanced settings', +// Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. +// TODO: Translate the following. +// 'form.group_advanced_edit.allow_ip' => 'Allow IP', +// 'form.group_advanced_edit.password_complexity' => 'Password complexity', +// 'form.group_advanced_edit.2fa' => 'Two factor authentication', + // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php // TODO: translate the following. // 'form.group_delete.hint' => 'Are you sure you want to delete the entire group?', @@ -697,21 +692,7 @@ // 'form.display_options.note_on_separate_row' => 'Note on separate row', // 'form.display_options.not_complete_days' => 'Not complete days', // 'form.display_options.inactive_projects' => 'Inactive projects', +// 'form.display_options.cost_per_hour' => 'Cost per hour', // 'form.display_options.custom_css' => 'Custom CSS', - -// Work plugin strings. See example at https://timetracker.anuko.com/work.php -// TODO: translate the following. -// 'work.error.work_not_available' => 'Work item is not available.', -// 'work.error.offer_not_available' => 'Offer is not available.', -// 'work.type.one_time' => 'one time', // Work type is "one time job" for well defined work ("do exactly this"). -// 'work.type.ongoing' => 'ongoing', // Work type is "ongoing" for complex jobs (billed by the hour, multiple contractors, etc.) -// 'work.label.own_work' => 'Own work', -// 'work.label.own_offers' => 'Own offers', -// 'work.label.offers' => 'Offers', -// 'work.button.send_message' => 'Send message', -// 'work.button.make_offer' => 'Make offer', -// 'work.button.accept' => 'Accept', -// 'work.button.decline' => 'Decline', -// 'work.title.send_message' => 'Sending Message', -// 'work.msg.message_sent' => 'Message sent.', +// 'form.display_options.custom_translation' => 'Custom translation', ); diff --git a/WEB-INF/resources/da.lang.php b/WEB-INF/resources/da.lang.php index a33224168..6fe93c261 100644 --- a/WEB-INF/resources/da.lang.php +++ b/WEB-INF/resources/da.lang.php @@ -2,7 +2,8 @@ /* Copyright (c) Anuko International Ltd. https://www.anuko.com License: See license.txt */ -// Note: escape apostrophes with THREE backslashes, like here: choisir l\\\'option. +// Note: escape apostrophes with THREE backslashes, like here: 'choisir l\\\'option'. +// Alternatively: use one backslash and surround by double-quotes: "choisir l\'option". // Other characters (such as double-quotes in http links, etc.) do not have to be escaped. $i18n_language = 'Danish (Dansk)'; @@ -66,6 +67,9 @@ // TODO: translate the following. // 'error.record' => 'Select record.', 'error.auth' => 'Forkert brugernavn eller adgangskode.', +// TODO: translate the following. +// 'error.2fa_code' => 'Invalid 2FA code.', +// 'error.weak_password' => 'Weak password.', 'error.user_exists' => 'Brugernavn eksistere allerede.', // TODO: translate the following. // 'error.object_exists' => 'Object with this name already exists.', @@ -94,7 +98,6 @@ // 'error.user_count' => 'Limit on user count.', // 'error.expired' => 'Expiration date reached.', // 'error.file_storage' => 'File storage server error.', // See comment in English file. -// 'error.remote_work' => 'Remote work server error.', // See comment in English file. // Warning messages. // TODO: translate the following. @@ -208,9 +211,6 @@ 'label.ldap_hint' => 'Skriv dit Windows brugernavn eller adgangskode i felterne her under.', 'label.required_fields' => '* - obligatorisk felt', 'label.on_behalf' => 'på vegne af', -'label.role_manager' => '(Manager)', -'label.role_comanager' => '(Co-Manager)', -'label.role_admin' => '(Administrator)', 'label.page' => 'Side', 'label.condition' => 'Betingelse', // TODO: translate the following. @@ -237,6 +237,7 @@ // 'label.mark_paid' => 'Mark paid', // 'label.week_note' => 'Week note', // 'label.week_list' => 'Week list', +// 'label.weekends' => 'Weekends', // 'label.work_units' => 'Work units', // 'label.work_units_short' => 'Units', 'label.totals_only' => 'Kun Total', @@ -255,17 +256,6 @@ // 'label.file' => 'File', 'label.active_users' => 'Aktive Brugere', 'label.inactive_users' => 'Inaktive Brugere', -// TODO: translate the following. -// 'label.details' => 'Details', -// 'label.budget' => 'Budget', -// 'label.work' => 'Work', // Table column header for work items, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// 'label.offer' => 'Offer', // Table column header for offers, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// 'label.contractor' => 'Contractor', // Table column header for offers (contractor is someone who offers to do work). - // Technically, it is either an org name or a combination of org and group names - // because both work items and offers are owned by Time Tracker groups of users. -// 'label.how_to_pay' => 'How to pay', // Label for the "How to pay" field on offers, which allows contractors to specify - // how to pay them, for example: paypal email, check by mail to a specific address, etc. -// 'label.moderator_comment' => 'Moderator comment', // Label for "Moderator comment" field that explains something. // Entity names. We use lower case (in English) because they are used in dropdowns, too. // They are used to associate a custom field with an entity type. @@ -279,6 +269,8 @@ // TODO: Translate the following. // 'title.success' => 'Success', 'title.login' => 'Login', +// TODO: translate the follolwing. +// 'title.2fa' => 'Two Factor Authentication', 'title.groups' => 'Teams', // TODO: change "teams" to "groups". // TODO: translate the following. // 'title.add_group' => 'Adding Group', @@ -374,22 +366,6 @@ // 'title.edit_file' => 'Editing File', // 'title.delete_file' => 'Deleting File', // 'title.download_file' => 'Downloading File', -// 'title.work' => 'Work', -// 'title.add_work' => 'Adding Work', -// 'title.edit_work' => 'Editing Work', -// 'title.delete_work' => 'Deleting Work', -// 'title.active_work' => 'Active Work', // Active work items this group outsources to other groups. -// 'title.available_work' => 'Available Work', // Available work items from other organizations. -// 'title.inactive_work' => 'Inactive Work', // Inactive work items this group was outsourcing to other groups. -// 'title.pending_work' => 'Pending Work', // Work items pending moderator approval. -// 'title.offer' => 'Offer', -// 'title.add_offer' => 'Adding Offer', -// 'title.edit_offer' => 'Editing Offer', -// 'title.delete_offer' => 'Deleting Offer', -// 'title.active_offers' => 'Active Offers', // Active offers this group makes available to other groups. -// 'title.available_offers' => 'Available Offers', // Available offers from other organizations. -// 'title.inactive_offers' => 'Inactive Offers', // Inactive offers for group. -// 'title.pending_offers' => 'Pending Offers', // Offers pending moderator approval. // Section for common strings inside combo boxes on forms. Strings shared between forms shall be placed here. // Strings that are used in a single form must go to the specific form section. @@ -422,7 +398,6 @@ 'dropdown.delete' => 'Slet', 'dropdown.do_not_delete' => 'Slet ikke', // TODO: translate the following. -// 'dropdown.pending_approval' => 'pending approval', // 'dropdown.approved' => 'approved', // 'dropdown.not_approved' => 'not approved', // 'dropdown.paid' => 'paid', @@ -434,6 +409,16 @@ 'form.login.forgot_password' => 'Har du glemt din adgangskode?', 'form.login.about' => 'Anuko Time Tracker er et open source tidsregistrerings system.', +// Email subject and body for two-factor authentication. +// TODO: translate the following. +// 'email.2fa_code.subject' => 'Anuko Time Tracker two-factor authentication code', +// 'email.2fa_code.body' => "Dear User,\n\nYour two-factor authentication code is:\n\n%s\n\n", + +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// 'form.2fa.hint' => 'Check your email for 2FA code and enter it here.', +// 'form.2fa.2fa_code' => '2FA code', + // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. 'form.reset_password.message' => 'Nulstilling af adgangskode er sendt på email.', 'form.reset_password.email_subject' => 'Anuko Time Tracker - Anmodning om nulstilling af adgangskode', @@ -475,10 +460,13 @@ // 'form.reports.include_pending' => 'pending', 'form.reports.select_period' => 'Vælg en periode', 'form.reports.set_period' => 'eller sæt datoer', +// TODO: translate the following. +// 'form.reports.note_containing' => 'Note containing', 'form.reports.show_fields' => 'Vis felter', // TODO: translate the following. // 'form.reports.time_fields' => 'Time fields', // 'form.reports.user_fields' => 'User fields', +// 'form.reports.project_fields' => 'Project fields', 'form.reports.group_by' => 'Gruppér ved', 'form.reports.group_by_no' => '--- Ingen gruppereing ---', 'form.reports.group_by_date' => 'Dato', @@ -491,6 +479,7 @@ // (after generating a report at https://timetracker.anuko.com/reports.php). 'form.report.export' => 'Eksport', // TODO: translate the following. +// 'form.report.per_hour' => 'Per hour', // 'form.report.assign_to_invoice' => 'Assign to invoice', // 'form.report.assign_to_timesheet' => 'Assign to timesheet', @@ -528,6 +517,8 @@ 'form.tasks.inactive_tasks' => 'Inaktive Opgaver', // Users form. See example at https://timetracker.anuko.com/users.php +// TODO: translate the following. +// 'form.users.uncompleted_entry_today' => 'User has an uncompleted time entry today', 'form.users.uncompleted_entry' => 'Bruger har en uafsluttet tidsregistrering', 'form.users.role' => 'Rolle', 'form.users.manager' => 'Manager', @@ -588,14 +579,20 @@ 'form.group_edit.type_duration' => 'Varighed', // TODO: translate the following. // 'form.group_edit.punch_mode' => 'Punch mode', +// 'form.group_edit.one_uncompleted' => 'One uncompleted', // 'form.group_edit.allow_overlap' => 'Allow overlap', // 'form.group_edit.future_entries' => 'Future entries', 'form.group_edit.uncompleted_indicators' => 'Uafsluttede indikatore', // TODO: translate the following. // 'form.group_edit.confirm_save' => 'Confirm saving', -// 'form.group_edit.allow_ip' => 'Allow IP', // 'form.group_edit.advanced_settings' => 'Advanced settings', +// Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. +// TODO: Translate the following. +// 'form.group_advanced_edit.allow_ip' => 'Allow IP', +// 'form.group_advanced_edit.password_complexity' => 'Password complexity', +// 'form.group_advanced_edit.2fa' => 'Two factor authentication', + // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php // TODO: translate the following. // 'form.group_delete.hint' => 'Are you sure you want to delete the entire group?', @@ -661,21 +658,7 @@ // 'form.display_options.note_on_separate_row' => 'Note on separate row', // 'form.display_options.not_complete_days' => 'Not complete days', // 'form.display_options.inactive_projects' => 'Inactive projects', +// 'form.display_options.cost_per_hour' => 'Cost per hour', // 'form.display_options.custom_css' => 'Custom CSS', - -// Work plugin strings. See example at https://timetracker.anuko.com/work.php -// TODO: translate the following. -// 'work.error.work_not_available' => 'Work item is not available.', -// 'work.error.offer_not_available' => 'Offer is not available.', -// 'work.type.one_time' => 'one time', // Work type is "one time job" for well defined work ("do exactly this"). -// 'work.type.ongoing' => 'ongoing', // Work type is "ongoing" for complex jobs (billed by the hour, multiple contractors, etc.) -// 'work.label.own_work' => 'Own work', -// 'work.label.own_offers' => 'Own offers', -// 'work.label.offers' => 'Offers', -// 'work.button.send_message' => 'Send message', -// 'work.button.make_offer' => 'Make offer', -// 'work.button.accept' => 'Accept', -// 'work.button.decline' => 'Decline', -// 'work.title.send_message' => 'Sending Message', -// 'work.msg.message_sent' => 'Message sent.', +// 'form.display_options.custom_translation' => 'Custom translation', ); diff --git a/WEB-INF/resources/de.lang.php b/WEB-INF/resources/de.lang.php index 47eb84a16..9e9ad0a3d 100644 --- a/WEB-INF/resources/de.lang.php +++ b/WEB-INF/resources/de.lang.php @@ -2,7 +2,8 @@ /* Copyright (c) Anuko International Ltd. https://www.anuko.com License: See license.txt */ -// Note: escape apostrophes with THREE backslashes, like here: choisir l\\\'option. +// Note: escape apostrophes with THREE backslashes, like here: 'choisir l\\\'option'. +// Alternatively: use one backslash and surround by double-quotes: "choisir l\'option". // Other characters (such as double-quotes in http links, etc.) do not have to be escaped. $i18n_language = 'German (Deutsch)'; @@ -19,19 +20,16 @@ 'menu.logout' => 'Abmelden', 'menu.forum' => 'Forum', 'menu.help' => 'Hilfe', -// TODO: translate the following. -// 'menu.register' => 'Register', +'menu.register' => 'Registrieren', 'menu.profile' => 'Profil', 'menu.group' => 'Gruppe', 'menu.plugins' => 'Erweiterungen', 'menu.time' => 'Zeiten', -// TODO: translate the following. -// 'menu.puncher' => 'Punch', +'menu.puncher' => 'Stempeluhr', 'menu.week' => 'Woche', 'menu.expenses' => 'Kosten', 'menu.reports' => 'Berichte', -// TODO: translate the following. -// 'menu.timesheets' => 'Timesheets', +'menu.timesheets' => 'Arbeitszeittabellen', 'menu.charts' => 'Diagramme', 'menu.projects' => 'Projekte', 'menu.tasks' => 'Aufgaben', @@ -51,8 +49,7 @@ 'error.access_denied' => 'Zugriff verweigert.', 'error.sys' => 'Systemfehler.', 'error.db' => 'Datenbankfehler.', -// TODO: translate the following. -// 'error.registered_recently' => 'Registered recently.', +'error.registered_recently' => 'Kürzlich registriert.', 'error.feature_disabled' => 'Funktion ist deaktiviert.', 'error.field' => 'Ungültige "{0}" Daten.', 'error.empty' => 'Feld "{0}" ist leer.', @@ -64,38 +61,34 @@ 'error.report' => 'Bericht auswählen.', 'error.record' => 'Eintrag auswählen.', 'error.auth' => 'Benutzername oder Passwort ungültig.', +'error.2fa_code' => 'Ungültiger 2FA-Code.', +'error.weak_password' => 'Schwaches Passwort.', 'error.user_exists' => 'Benutzer mit diesem Konto ist bereits vorhanden.', 'error.object_exists' => 'Objekt mit diesem Namen ist bereits vorhanden.', 'error.invoice_exists' => 'Rechnung mit dieser Nummer existiert bereits.', 'error.role_exists' => 'Rolle mit diesem Rang existiert bereits.', 'error.no_invoiceable_items' => 'Keine Einträge zur Rechnungsstellung gefunden.', -// TODO: translate the following. -// 'error.no_records' => 'There are no records.', +'error.no_records' => 'Es gibt keine Einträge.', 'error.no_login' => 'Benutzer mit diesen Anmeldedaten nicht vorhanden.', 'error.no_groups' => 'Die Datenbank ist leer. Als Administrator anmelden und ein neues Gruppe erzeugen.', 'error.upload' => 'Fehler beim hochladen einer Datei.', -'error.range_locked' => 'Zeitinterval ist gesperrt.', -'error.mail_send' => 'Fehler beim versenden einer E-Mail.', -// TODO: improve the translation above by adding MAIL_SMTP_DEBUG part. -// 'error.mail_send' => 'Error sending mail. Use MAIL_SMTP_DEBUG for diagnostics.', +'error.range_locked' => 'Zeitintervall ist gesperrt.', +'error.mail_send' => 'Fehler beim versenden einer E-Mail. Verwenden Sie MAIL_SMTP_DEBUG für die Diagnose.', 'error.no_email' => 'Dieser Benutzer besitzt keine e-Mail Adresse.', 'error.uncompleted_exists' => 'Unvollständiger Eintrag bereits vorhanden. Schließen oder Löschen.', 'error.goto_uncompleted' => 'Zum unvollständigen Eintrag gehen.', -'error.overlap' => 'Der Zeitinterval überschneidet sich mit vorhandenen Einträgen.', +'error.overlap' => 'Der Zeitintervall überschneidet sich mit vorhandenen Einträgen.', 'error.future_date' => 'Datum ist in der Zukunft.', -// TODO: translate the following. -// 'error.xml' => 'Error in XML file at line %d: %s.', -// 'error.cannot_import' => 'Cannot import: %s.', -// 'error.format' => 'Invalid file format.', -// 'error.user_count' => 'Limit on user count.', -// 'error.expired' => 'Expiration date reached.', -// 'error.file_storage' => 'File storage server error.', // See comment in English file. -// 'error.remote_work' => 'Remote work server error.', // See comment in English file. +'error.xml' => 'Fehler in der XML-Datei in der Zeile %d: %s.', +'error.cannot_import' => 'Kann nicht importiert werden: %s.', +'error.format' => 'Ungültiges Dateiformat.', +'error.user_count' => 'Begrenzung der Benutzeranzahl erreicht.', +'error.expired' => 'Ablaufdatum erreicht.', +'error.file_storage' => 'Dateispeicherserver-Fehler.', // Warning messages. 'warn.sure' => 'Sind Sie sicher?', -// TODO: translate the following. -// 'warn.confirm_save' => 'Date has changed. Confirm saving, not copying this item.', +'warn.confirm_save' => 'Das Datum hat sich geändert. Bestätigen Sie das Speichern, nicht das Kopieren dieses Elements.', // Success messages. 'msg.success' => 'Operation vollständig abgeschlossen.', @@ -117,19 +110,14 @@ 'button.export' => 'Gruppe exportieren', 'button.import' => 'Gruppe importieren', 'button.close' => 'Schließen', -// TODO: translate the following. -// 'button.start' => 'Start', +'button.start' => 'Start', 'button.stop' => 'Stop', -// TODO: translate the following. -// (PR#81 suggested 'Freigeben / Genehmigen' for 'Approve' and 'Freigabe zurücknehmen' for 'Disapprove'. -// The problem is they do not appear precise, deviate from the meaning of approval / disaproval of report items.) -// 'button.approve' => 'Approve', (suggested 'Freigeben / Genehmigen' does not appear precise) -// 'button.disapprove' => 'Disapprove', -// 'button.sync' => 'Sync', // Used in Android app. The meaning is to synchronize offline time records with server. +'button.approve' => 'Annehmen', +'button.disapprove' => 'Ablehnen', +'button.sync' => 'Sync', // Labels for controls on forms. Labels in this section are used on multiple forms. -// TODO: translate the following. -// 'label.menu' => 'Menu', +'label.menu' => 'Menü', 'label.group_name' => 'Gruppenname', 'label.address' => 'Adresse', 'label.currency' => 'Währung', @@ -149,9 +137,8 @@ 'label.end_date' => 'Enddatum', 'label.user' => 'Benutzer', 'label.users' => 'Personen', -// TODO: translate the following. -// 'label.group' => 'Group', -// 'label.subgroups' => 'Subgroups', +'label.group' => 'Gruppe', +'label.subgroups' => 'Untergruppen', 'label.roles' => 'Rollen', 'label.client' => 'Kunde', 'label.clients' => 'Kunden', @@ -182,8 +169,7 @@ 'label.select_none' => 'Alle abwählen', 'label.day_view' => 'Tagesansicht', 'label.week_view' => 'Wochenansicht', -// TODO: translate the following: -// 'label.puncher' => 'Puncher', +'label.puncher' => 'Stempeluhr', 'label.id' => 'ID', 'label.language' => 'Sprache', 'label.decimal_mark' => 'Dezimaltrennzeichen', @@ -202,9 +188,6 @@ 'label.ldap_hint' => 'Geben Sie unten Ihren Windows Benutzernamen und Ihr Passwort ein.', 'label.required_fields' => '* - Pflichtfelder', 'label.on_behalf' => 'für', -'label.role_manager' => '(Manager)', -'label.role_comanager' => '(Co-Manager)', -'label.role_admin' => '(Administrator)', 'label.page' => 'Seite', 'label.condition' => 'Bedingung', 'label.yes' => 'Ja', @@ -213,8 +196,7 @@ // Labels for plugins (extensions to Time Tracker that provide additional features). 'label.custom_fields' => 'Benutzerfelder', 'label.monthly_quotas' => 'Monatliche Quoten', -// TODO: translate the following. -// 'label.entity' => 'Entity', +'label.entity' => 'Einheit', 'label.type' => 'Typ', 'label.type_dropdown' => 'Ausklappen', 'label.type_text' => 'Text', @@ -229,51 +211,36 @@ 'label.mark_paid' => 'Als bezahlt setzen', 'label.week_note' => 'Wochennotiz', 'label.week_list' => 'Wochenliste', +'label.weekends' => 'Wochenenden', 'label.work_units' => 'Arbeitseinheiten', 'label.work_units_short' => 'Einheiten', 'label.totals_only' => 'Nur Gesamtstunden', 'label.quota' => 'Quote', -// TODO: translate the following. -// 'label.timesheet' => 'Timesheet', -// 'label.submitted' => 'Submitted', -// 'label.approved' => 'Approved', -// 'label.approval' => 'Report approval', -// 'label.mark_approved' => 'Mark approved', -// 'label.template' => 'Template', -// 'label.bind_templates_with_projects' => 'Bind templates with projects', -// 'label.prepopulate_note' => 'Prepopulate Note field', -// 'label.attachments' => 'Attachments', -// 'label.files' => 'Files', -// 'label.file' => 'File', +'label.timesheet' => 'Arbeitszeittabelle', +'label.submitted' => 'Eingereicht', +'label.approved' => 'Genehmigt', +'label.approval' => 'Genehmigung des Berichts', +'label.mark_approved' => 'Als genehmigt markieren', +'label.template' => 'Vorlage', +'label.bind_templates_with_projects' => 'Vorlagen mit Projekten verbinden', +'label.prepopulate_note' => 'Notizfeld vorab ausfüllen', +'label.attachments' => 'Anhänge', +'label.files' => 'Dateien', +'label.file' => 'Datei', 'label.active_users' => 'Aktive Nutzer', 'label.inactive_users' => 'Inaktive Nutzer', -// TODO: translate the following or confirm that "Details" is also correct for German (exactly as the English string). -// label.details is used to identify a field for LONG DESCRIPTION of a work item used in Remote Work plugin. -// For example, a work item could be "Design a logo", and the Details hold EXACT anfd PRECISE specs of what a customer needs. -// Another use is with offers with Remote Work plugin, where details hold a long, precise, and complete description of the offer. -// 'label.details' => 'Details', -'label.budget' => 'Budget', -// TODO: translate the following. -// 'label.work' => 'Work', // Table column header for work items, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// 'label.offer' => 'Offer', // Table column header for offers, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// 'label.contractor' => 'Contractor', // Table column header for offers (contractor is someone who offers to do work). - // Technically, it is either an org name or a combination of org and group names - // because both work items and offers are owned by Time Tracker groups of users. -// 'label.how_to_pay' => 'How to pay', // Label for the "How to pay" field on offers, which allows contractors to specify - // how to pay them, for example: paypal email, check by mail to a specific address, etc. -// 'label.moderator_comment' => 'Moderator comment', // Label for "Moderator comment" field that explains something. // Entity names. We use lower case (in English) because they are used in dropdowns, too. // They are used to associate a custom field with an entity type. -// TODO: translate the following. -// 'entity.time' => 'time', -// 'entity.user' => 'user', -// 'entity.project' => 'project', +'entity.time' => 'Zeit', +'entity.user' => 'Benutzer', +'entity.project' => 'Projekt', // Form titles. 'title.error' => 'Fehler', -'title.success' => 'Erfol', +'title.success' => 'Erfolg', 'title.login' => 'Anmelden', +'title.2fa' => 'Zwei-Faktor-Authentifizierung', 'title.groups' => 'Gruppen', 'title.add_group' => 'Gruppe anlegen', 'title.edit_group' => 'Gruppe bearbeiten', @@ -283,21 +250,18 @@ 'title.time' => 'Zeiten', 'title.edit_time_record' => 'Bearbeiten des Stundeneintrags', 'title.delete_time_record' => 'Eintrag löschen', -// TODO: Translate the following. -// 'title.time_files' => 'Time Record Files', -// 'title.puncher' => 'Puncher', +'title.time_files' => 'Zeiterfassungsdateien', +'title.puncher' => 'Stempeluhr', 'title.expenses' => 'Kosten', 'title.edit_expense' => 'Kostenposition ändern', 'title.delete_expense' => 'Kostenposition löschen', -// TODO: translate the following. -// 'title.expense_files' => 'Expense Item Files', +'title.expense_files' => 'Ausgabenartikel-Dateien', 'title.reports' => 'Berichte', 'title.report' => 'Bericht', 'title.send_report' => 'Bericht senden', -// TODO: Translate the following. -// 'title.timesheets' => 'Timesheets', -// 'title.timesheet' => 'Timesheet', -// 'title.timesheet_files' => 'Timesheet Files', +'title.timesheets' => 'Arbeitszeittabellen', +'title.timesheet' => 'Arbeitszeittabelle', +'title.timesheet_files' => 'Arbeitszeittabellendateien', 'title.invoice' => 'Rechnung', 'title.send_invoice' => 'Rechnung senden', 'title.charts' => 'Diagramme', @@ -330,10 +294,9 @@ 'title.add_notification' => 'Benachrichtigung hinzufügen', 'title.edit_notification' => 'Benachrichtigung bearbeiten', 'title.delete_notification' => 'Benachrichtigung löschen', -// TODO: translate the following. -// 'title.add_timesheet' => 'Adding Timesheet', -// 'title.edit_timesheet' => 'Editing Timesheet', -// 'title.delete_timesheet' => 'Deleting Timesheet', +'title.add_timesheet' => 'Arbeitszeittabelle hinzufügen', +'title.edit_timesheet' => 'Arbeitszeittabelle bearbeiten', +'title.delete_timesheet' => 'Arbeitszeittabelle löschen', 'title.monthly_quotas' => 'Monatliche Quoten', 'title.export' => 'Daten exportieren', 'title.import' => 'Daten importieren', @@ -353,30 +316,13 @@ 'title.week_view' => 'Wochenansicht', 'title.swap_roles' => 'Tausche Rollen', 'title.work_units' => 'Arbeitseinheiten', -// TODO: translate the following. -// 'title.templates' => 'Templates', -// 'title.add_template' => 'Adding Template', -// 'title.edit_template' => 'Editing Template', -// 'title.delete_template' => 'Deleting Template', -// 'title.edit_file' => 'Editing File', -// 'title.delete_file' => 'Deleting File', -// 'title.download_file' => 'Downloading File', -// 'title.work' => 'Work', -// 'title.add_work' => 'Adding Work', -// 'title.edit_work' => 'Editing Work', -// 'title.delete_work' => 'Deleting Work', -// 'title.active_work' => 'Active Work', // Active work items this group outsources to other groups. -// 'title.available_work' => 'Available Work', // Available work items from other organizations. -// 'title.inactive_work' => 'Inactive Work', // Inactive work items this group was outsourcing to other groups. -// 'title.pending_work' => 'Pending Work', // Work items pending moderator approval. -// 'title.offer' => 'Offer', -// 'title.add_offer' => 'Adding Offer', -// 'title.edit_offer' => 'Editing Offer', -// 'title.delete_offer' => 'Deleting Offer', -// 'title.active_offers' => 'Active Offers', // Active offers this group makes available to other groups. -// 'title.available_offers' => 'Available Offers', // Available offers from other organizations. -// 'title.inactive_offers' => 'Inactive Offers', // Inactive offers for group. -// 'title.pending_offers' => 'Pending Offers', // Offers pending moderator approval. +'title.templates' => 'Vorlagen', +'title.add_template' => 'Vorlage hinzufügen', +'title.edit_template' => 'Vorlage bearbeiten', +'title.delete_template' => 'Vorlage löschen', +'title.edit_file' => 'Datei bearbeiten', +'title.delete_file' => 'Datei löschen', +'title.download_file' => 'Datei herunterladen', // Section for common strings inside combo boxes on forms. Strings shared between forms shall be placed here. // Strings that are used in a single form must go to the specific form section. @@ -400,21 +346,17 @@ 'dropdown.clients' => 'Kunden', 'dropdown.select' => '--- auswählen ---', 'dropdown.select_invoice' => '--- Rechnung auswählen ---', -// TODO: translate the following. -// 'dropdown.select_timesheet' => '--- select timesheet ---', +'dropdown.select_timesheet' => '--- Arbeitszeittabelle auswählen ---', 'dropdown.status_active' => 'aktiv', 'dropdown.status_inactive' => 'inaktiv', 'dropdown.delete' => 'löschen', 'dropdown.do_not_delete' => 'nicht löschen', -// TODO: translate the following. -// 'dropdown.pending_approval' => 'pending approval', -// 'dropdown.approved' => 'approved', -// 'dropdown.not_approved' => 'not approved', +'dropdown.approved' => 'genehmigt', +'dropdown.not_approved' => 'abgelehnt', 'dropdown.paid' => 'bezahlt', 'dropdown.not_paid' => 'nicht bezahlt', -// TODO: translate the following. -// 'dropdown.ascending' => 'ascending', -// 'dropdown.descending' => 'descending', +'dropdown.ascending' => 'aufsteigend', +'dropdown.descending' => 'absteigend', // Below is a section for strings that are used on individual forms. When a string is used only on one form it should be placed here. // One exception is for closely related forms such as "Time" and "Editing Time Record" with similar controls. In such cases @@ -425,6 +367,14 @@ 'form.login.forgot_password' => 'Passwort vergessen?', 'form.login.about' => 'Anuko Time Tracker ist ein Open-Source Zeiterfassungssystem.', +// Email subject and body for two-factor authentication. +'email.2fa_code.subject' => 'Anuko Time Tracker Zwei-Faktor-Authentifizierungscode', +'email.2fa_code.body' => "Sehr geehrter Benutzer,\n\nIhr Zwei-Faktor-Authentifizierungscode lautet:\n\n%s\n\n", + +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +'form.2fa.hint' => 'Überprüfen Sie Ihre E-Mails auf den 2FA-Code und geben Sie ihn hier ein.', +'form.2fa.2fa_code' => '2FA-Code', + // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. 'form.reset_password.message' => 'Anfrage zur Zurücksetzung des Passwortes wurde per E-mail gesendet.', 'form.reset_password.email_subject' => 'Anuko Time Tracker Anfrage zur Zurücksetzung des Passwortes', @@ -455,16 +405,14 @@ 'form.reports.include_not_billable' => 'nicht in Rechnung stellen', 'form.reports.include_invoiced' => 'berechnet', 'form.reports.include_not_invoiced' => 'nicht berechnet', -// TODO: translate the following. -// 'form.reports.include_assigned' => 'assigned', -// 'form.reports.include_not_assigned' => 'not assigned', -// 'form.reports.include_pending' => 'pending', +'form.reports.include_pending' => 'Ausstehend', 'form.reports.select_period' => 'Zeitraum auswählen', 'form.reports.set_period' => 'oder Datum eingeben', +'form.reports.note_containing' => 'Notiz enthält', 'form.reports.show_fields' => 'Felder anzeigen', -// TODO: translate the following. -// 'form.reports.time_fields' => 'Time fields', -// 'form.reports.user_fields' => 'User fields', +'form.reports.time_fields' => 'Zeitfelder', +'form.reports.user_fields' => 'Benutzerfelder', +'form.reports.project_fields' => 'Projektfelder', 'form.reports.group_by' => 'Gruppieren nach', 'form.reports.group_by_no' => '--- keine Gruppierung ---', 'form.reports.group_by_date' => 'Datum', @@ -476,19 +424,17 @@ // Report form. See example at https://timetracker.anuko.com/report.php // (after generating a report at https://timetracker.anuko.com/reports.php). 'form.report.export' => 'Exportiere', +'form.report.per_hour' => 'Stundensatz', 'form.report.assign_to_invoice' => 'Zu Rechnung hinzufügen', -// TODO: translate the following. -// 'form.report.assign_to_timesheet' => 'Assign to timesheet', +'form.report.assign_to_timesheet' => 'Zu Arbeitszeittabelle zuweisen', // Timesheets form. See example at https://timetracker.anuko.com/timesheets.php -// TODO: translate the following. -// 'form.timesheets.active_timesheets' => 'Active Timesheets', -// 'form.timesheets.inactive_timesheets' => 'Inactive Timesheets', +'form.timesheets.active_timesheets' => 'Aktive Arbeitszeittabellen', +'form.timesheets.inactive_timesheets' => 'Inaktive Arbeitszeittabellen', // Templates form. See example at https://timetracker.anuko.com/templates.php -// TODO: translate the following. -// 'form.templates.active_templates' => 'Active Templates', -// 'form.templates.inactive_templates' => 'Inactive Templates', +'form.templates.active_templates' => 'Aktive Vorlagen', +'form.templates.inactive_templates' => 'Inaktive Vorlagen', // Invoice form. See example at https://timetracker.anuko.com/invoice_view.php // (you can get to this form after generating an invoice). @@ -513,6 +459,7 @@ 'form.tasks.inactive_tasks' => 'Inaktive Tasks', // Users form. See example at https://timetracker.anuko.com/users.php +'form.users.uncompleted_entry_today' => 'Der Benutzer hat heute einen nicht abgeschlossenen Zeiteintrag', 'form.users.uncompleted_entry' => 'Nutzer hat einen unvollständigen Zeiteintrag', 'form.users.role' => 'Rolle', 'form.users.manager' => 'Manager', @@ -566,14 +513,18 @@ 'form.group_edit.type_all' => 'alle', 'form.group_edit.type_start_finish' => 'Start und Ende', 'form.group_edit.type_duration' => 'Dauer', -'form.group_edit.punch_mode' => 'Stechuhr-Modus', +'form.group_edit.punch_mode' => 'Stempeluhr-Modus', +'form.group_edit.one_uncompleted' => 'Eine unvollendete', 'form.group_edit.allow_overlap' => 'Erlaube Überschneidung', 'form.group_edit.future_entries' => 'Einträge in der Zukunft', 'form.group_edit.uncompleted_indicators' => 'Zeige unfertige Einträge', 'form.group_edit.confirm_save' => 'Speichern bestätigen', -'form.group_edit.allow_ip' => 'Erlaube IP', -// TODO: translate the following. -// 'form.group_edit.advanced_settings' => 'Advanced settings', +'form.group_edit.advanced_settings' => 'Erweiterte Einstellungen', + +// Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. +'form.group_advanced_edit.allow_ip' => 'Erlaube IP', +'form.group_advanced_edit.password_complexity' => 'Komplexität des Passworts', +'form.group_advanced_edit.2fa' => 'Zwei-Faktor-Authentifizierung', // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php 'form.group_delete.hint' => 'Sind Sie sicher, dass Sie die gesamte Gruppe löschen möchten?', @@ -589,10 +540,10 @@ 'form.quota.year' => 'Jahr', 'form.quota.month' => 'Monat', 'form.quota.workday_hours' => 'Arbeitsstunden pro Tag', -'form.quota.hint' => 'Wenn leergelassen wird die Quote automatisch berechnet (Basierend auf Arbeitsstunden pro Tag und Feiertagen)', +'form.quota.hint' => 'Wenn leer gelassen wird die Quote automatisch berechnet (Basierend auf Arbeitsstunden pro Tag und Feiertagen)', // Swap roles form. See example at https://timetracker.anuko.com/swap_roles.php. -'form.swap.hint' => 'Stufen Sie ihre Rolle auf eine niedrigere indem Sie mit jemadem die Rollen tauschen. Dies kann nicht rückgängig gemacht werden.', +'form.swap.hint' => 'Stufen Sie ihre Rolle auf eine niedrigere indem Sie mit jemandem die Rollen tauschen. Dies kann nicht rückgängig gemacht werden.', 'form.swap.swap_with' => 'Tausche Rolle mit', // Work Units configuration form. See example at https://timetracker.anuko.com/work_units.php after enabling Work units plugin. @@ -605,9 +556,7 @@ 'role.user.description' => 'Ein normaler Benutzer ohne Administrationsrechte.', 'role.client.label' => 'Kunde', 'role.client.low_case_label' => 'Kunde', -// TODO: translate the following. -// 'role.client.description' => 'A client can view its own data.', -'role.client.description' => 'Ein Kunde kann zu ihm gehörende Berichte und Rechnungen ansehen.', +'role.client.description' => 'Ein Kunde kann seine eigenen Daten einsehen.', 'role.supervisor.label' => 'Dienstvorgesetzter', 'role.supervisor.low_case_label' => 'Dienstvorgesetzter', 'role.supervisor.description' => 'Eine Person mit ein paar Administrationsrechten.', @@ -622,37 +571,21 @@ 'role.top_manager.description' => 'Top Gruppen-Manager. Kann alles innerhalb eines Gruppenbaums administrieren', 'role.admin.label' => 'Administrator', 'role.admin.low_case_label' => 'Administrator', -'role.admin.description' => 'Aadminsitrator der Seite.', +'role.admin.description' => 'Administrator der Seite.', // Timesheet View form. See example at https://timetracker.anuko.com/timesheet_view.php. -// TODO: translate the following. -// 'form.timesheet_view.submit_subject' => 'Timesheet approval request', -// 'form.timesheet_view.submit_body' => "A new timesheet requires approval.

User: %s.", -// 'form.timesheet_view.approve_subject' => 'Timesheet approved', -// 'form.timesheet_view.approve_body' => "Your timesheet %s was approved.

%s", -// 'form.timesheet_view.disapprove_subject' => 'Timesheet not approved', -// 'form.timesheet_view.disapprove_body' => "Your timesheet %s was not approved.

%s", +'form.timesheet_view.submit_subject' => 'Antrag auf Genehmigung der Arbeitszeittabelle', +'form.timesheet_view.submit_body' => "Eine neue Arbeitszeittabelle muss genehmigt werden.

Benutzer: %s.", +'form.timesheet_view.approve_subject' => 'Arbeitszeittabelle genehmigt', +'form.timesheet_view.approve_body' => "Ihre Arbeitszeittabelle %s wurde genehmigt.

%s", +'form.timesheet_view.disapprove_subject' => 'Arbeitszeittabelle nicht genehmigt', +'form.timesheet_view.disapprove_body' => "Ihre Arbeitszeittabelle %s wurde nicht genehmigt.

%s", // Display Options form. See example at https://timetracker.anuko.com/display_options.php. 'form.display_options.note_on_separate_row' => 'Beschreibung in separater Zeile', -// TODO: translate the following. -// 'form.display_options.not_complete_days' => 'Not complete days', -// 'form.display_options.inactive_projects' => 'Inactive projects', -// 'form.display_options.custom_css' => 'Custom CSS', - -// Work plugin strings. See example at https://timetracker.anuko.com/work.php -// TODO: translate the following. -// 'work.error.work_not_available' => 'Work item is not available.', -// 'work.error.offer_not_available' => 'Offer is not available.', -// 'work.type.one_time' => 'one time', // Work type is "one time job" for well defined work ("do exactly this"). -// 'work.type.ongoing' => 'ongoing', // Work type is "ongoing" for complex jobs (billed by the hour, multiple contractors, etc.) -// 'work.label.own_work' => 'Own work', -// 'work.label.own_offers' => 'Own offers', -// 'work.label.offers' => 'Offers', -// 'work.button.send_message' => 'Send message', -// 'work.button.make_offer' => 'Make offer', -// 'work.button.accept' => 'Accept', -// 'work.button.decline' => 'Decline', -// 'work.title.send_message' => 'Sending Message', -// 'work.msg.message_sent' => 'Message sent.', +'form.display_options.not_complete_days' => 'Nicht vollständige Tage', +'form.display_options.inactive_projects' => 'Inaktive Projekte', +'form.display_options.cost_per_hour' => 'Kosten pro Stunde', +'form.display_options.custom_css' => 'Benutzerdefiniertes CSS', +'form.display_options.custom_translation' => 'Benutzerdefinierte Übersetzung', ); diff --git a/WEB-INF/resources/en.lang.php b/WEB-INF/resources/en.lang.php index c1c00af9d..5858b63d4 100644 --- a/WEB-INF/resources/en.lang.php +++ b/WEB-INF/resources/en.lang.php @@ -2,7 +2,8 @@ /* Copyright (c) Anuko International Ltd. https://www.anuko.com License: See license.txt */ -// Note: escape apostrophes with THREE backslashes, like here: choisir l\\\'option. +// Note: escape apostrophes with THREE backslashes, like here: 'choisir l\\\'option'. +// Alternatively: use one backslash and surround by double-quotes: "choisir l\'option". // Other characters (such as double-quotes in http links, etc.) do not have to be escaped. $i18n_language = 'English'; @@ -60,6 +61,8 @@ 'error.report' => 'Select report.', 'error.record' => 'Select record.', 'error.auth' => 'Incorrect login or password.', +'error.2fa_code' => 'Invalid 2FA code.', +'error.weak_password' => 'Weak password.', 'error.user_exists' => 'User with this login already exists.', 'error.object_exists' => 'Object with this name already exists.', 'error.invoice_exists' => 'Invoice with this number already exists.', @@ -86,11 +89,6 @@ // "something went wrong" when trying to do some operation with attachments. // For example, File Storage server could be offline, or Time Tracker config option is wrong, etc. 'error.file_storage' => 'File storage server error.', -// Meaning of error.remote_work: an (unspecified) error occurred when trying to communicate with -// "Remote Work" server, the one that supports the "Work" plugin, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// It is a generic message telling us that "something went wrong" when trying to do some operation with Work plugin. -// For example, Remote Work server could be offline, among other things. -'error.remote_work' => 'Remote work server error.', // Warning messages. 'warn.sure' => 'Are you sure?', @@ -194,9 +192,6 @@ 'label.ldap_hint' => 'Type your Windows login and password in the fields below.', 'label.required_fields' => '* - required fields', 'label.on_behalf' => 'on behalf of', -'label.role_manager' => '(manager)', -'label.role_comanager' => '(co-manager)', -'label.role_admin' => '(administrator)', 'label.page' => 'Page', 'label.condition' => 'Condition', 'label.yes' => 'yes', @@ -220,6 +215,7 @@ 'label.mark_paid' => 'Mark paid', 'label.week_note' => 'Week note', 'label.week_list' => 'Week list', +'label.weekends' => 'Weekends', 'label.work_units' => 'Work units', 'label.work_units_short' => 'Units', 'label.totals_only' => 'Totals only', @@ -237,16 +233,6 @@ 'label.file' => 'File', 'label.active_users' => 'Active Users', 'label.inactive_users' => 'Inactive Users', -'label.details' => 'Details', -'label.budget' => 'Budget', -'label.work' => 'Work', // Table column header for work items, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -'label.offer' => 'Offer', // Table column header for offers, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -'label.contractor' => 'Contractor', // Table column header for offers (contractor is someone who offers to do work). - // Technically, it is either an org name or a combination of org and group names - // because both work items and offers are owned by Time Tracker groups of users. -'label.how_to_pay' => 'How to pay', // Label for the "How to pay" field on offers, which allows contractors to specify - // how to pay them, for example: paypal email, check by mail to a specific address, etc. -'label.moderator_comment' => 'Moderator comment', // Label for "Moderator comment" field that explains something. // Entity names. We use lower case (in English) because they are used in dropdowns, too. // They are used to associate a custom field with an entity type. @@ -258,6 +244,7 @@ 'title.error' => 'Error', 'title.success' => 'Success', 'title.login' => 'Login', +'title.2fa' => 'Two Factor Authentication', 'title.groups' => 'Groups', 'title.add_group' => 'Adding Group', 'title.edit_group' => 'Editing Group', @@ -346,22 +333,6 @@ 'title.edit_file' => 'Editing File', 'title.delete_file' => 'Deleting File', 'title.download_file' => 'Downloading File', -'title.work' => 'Work', -'title.add_work' => 'Adding Work', -'title.edit_work' => 'Editing Work', -'title.delete_work' => 'Deleting Work', -'title.active_work' => 'Active Work', // Active work items this group outsources to other groups. -'title.available_work' => 'Available Work', // Available work items from other organizations. -'title.inactive_work' => 'Inactive Work', // Inactive work items for group. -'title.pending_work' => 'Pending Work', // Work items pending moderator approval. -'title.offer' => 'Offer', -'title.add_offer' => 'Adding Offer', -'title.edit_offer' => 'Editing Offer', -'title.delete_offer' => 'Deleting Offer', -'title.active_offers' => 'Active Offers', // Active offers this group makes available to other groups. -'title.available_offers' => 'Available Offers', // Available offers from other organizations. -'title.inactive_offers' => 'Inactive Offers', // Inactive offers for group. -'title.pending_offers' => 'Pending Offers', // Offers pending moderator approval. // Section for common strings inside combo boxes on forms. Strings shared between forms shall be placed here. // Strings that are used in a single form must go to the specific form section. @@ -390,7 +361,6 @@ 'dropdown.status_inactive' => 'inactive', 'dropdown.delete' => 'delete', 'dropdown.do_not_delete' => 'do not delete', -'dropdown.pending_approval' => 'pending approval', 'dropdown.approved' => 'approved', 'dropdown.not_approved' => 'not approved', 'dropdown.paid' => 'paid', @@ -407,6 +377,14 @@ 'form.login.forgot_password' => 'Forgot password?', 'form.login.about' => 'Anuko Time Tracker is an open source time tracking system.', +// Email subject and body for two-factor authentication. +'email.2fa_code.subject' => 'Anuko Time Tracker two-factor authentication code', +'email.2fa_code.body' => "Dear User,\n\nYour two-factor authentication code is:\n\n%s\n\n", + +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +'form.2fa.hint' => 'Check your email for 2FA code and enter it here.', +'form.2fa.2fa_code' => '2FA code', + // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. 'form.reset_password.message' => 'Password reset request sent by email.', 'form.reset_password.email_subject' => 'Anuko Time Tracker password reset request', @@ -448,9 +426,11 @@ 'form.reports.include_pending' => 'pending', 'form.reports.select_period' => 'Select time period', 'form.reports.set_period' => 'or set dates', +'form.reports.note_containing' => 'Note containing', 'form.reports.show_fields' => 'Show fields', 'form.reports.time_fields' => 'Time fields', 'form.reports.user_fields' => 'User fields', +'form.reports.project_fields' => 'Project fields', 'form.reports.group_by' => 'Group by', 'form.reports.group_by_no' => '--- no grouping ---', 'form.reports.group_by_date' => 'date', @@ -462,6 +442,7 @@ // Report form. See example at https://timetracker.anuko.com/report.php // (after generating a report at https://timetracker.anuko.com/reports.php). 'form.report.export' => 'Export', +'form.report.per_hour' => 'Per hour', 'form.report.assign_to_invoice' => 'Assign to invoice', 'form.report.assign_to_timesheet' => 'Assign to timesheet', @@ -496,6 +477,7 @@ 'form.tasks.inactive_tasks' => 'Inactive Tasks', // Users form. See example at https://timetracker.anuko.com/users.php +'form.users.uncompleted_entry_today' => 'User has an uncompleted time entry today', 'form.users.uncompleted_entry' => 'User has an uncompleted time entry', 'form.users.role' => 'Role', 'form.users.manager' => 'Manager', @@ -550,13 +532,19 @@ 'form.group_edit.type_start_finish' => 'start and finish', 'form.group_edit.type_duration' => 'duration', 'form.group_edit.punch_mode' => 'Punch mode', +'form.group_edit.one_uncompleted' => 'One uncompleted', 'form.group_edit.allow_overlap' => 'Allow overlap', 'form.group_edit.future_entries' => 'Future entries', 'form.group_edit.uncompleted_indicators' => 'Uncompleted indicators', 'form.group_edit.confirm_save' => 'Confirm saving', -'form.group_edit.allow_ip' => 'Allow IP', +'form.group_edit.password_complexity' => 'Password complexity', 'form.group_edit.advanced_settings' => 'Advanced settings', +// Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. +'form.group_advanced_edit.allow_ip' => 'Allow IP', +'form.group_advanced_edit.password_complexity' => 'Password complexity', +'form.group_advanced_edit.2fa' => 'Two factor authentication', + // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php 'form.group_delete.hint' => 'Are you sure you want to delete the entire group?', @@ -616,21 +604,7 @@ 'form.display_options.note_on_separate_row' => 'Note on separate row', 'form.display_options.not_complete_days' => 'Not complete days', 'form.display_options.inactive_projects' => 'Inactive projects', +'form.display_options.cost_per_hour' => 'Cost per hour', 'form.display_options.custom_css' => 'Custom CSS', - -// Work plugin strings. See example at https://timetracker.anuko.com/work.php -'work.error.work_not_available' => 'Work item is not available.', -'work.error.offer_not_available' => 'Offer is not available.', -'work.type.one_time' => 'one time', // Work type is "one time job" for well defined work ("do exactly this"). -'work.type.ongoing' => 'ongoing', // Work type is "ongoing" for complex jobs (billed by the hour, multiple contractors, etc.) -'work.label.own_work' => 'Own work', -'work.label.own_offers' => 'Own offers', -'work.label.offers' => 'Offers', -'work.label.message' => 'Message', -'work.button.send_message' => 'Send message', -'work.button.make_offer' => 'Make offer', -'work.button.accept' => 'Accept', -'work.button.decline' => 'Decline', -'work.title.send_message' => 'Sending Message', -'work.msg.message_sent' => 'Message sent.', +'form.display_options.custom_translation' => 'Custom translation', ); diff --git a/WEB-INF/resources/es.lang.php b/WEB-INF/resources/es.lang.php index 8a8902f7d..b302de1c4 100644 --- a/WEB-INF/resources/es.lang.php +++ b/WEB-INF/resources/es.lang.php @@ -2,7 +2,8 @@ /* Copyright (c) Anuko International Ltd. https://www.anuko.com License: See license.txt */ -// Note: escape apostrophes with THREE backslashes, like here: choisir l\\\'option. +// Note: escape apostrophes with THREE backslashes, like here: 'choisir l\\\'option'. +// Alternatively: use one backslash and surround by double-quotes: "choisir l\'option". // Other characters (such as double-quotes in http links, etc.) do not have to be escaped. $i18n_language = 'Spanish (Español)'; @@ -72,6 +73,8 @@ // 'error.record' => 'Select record.', 'error.auth' => 'Usuario o contraseña incorrecta.', // TODO: translate the following. +// 'error.2fa_code' => 'Invalid 2FA code.', +// 'error.weak_password' => 'Weak password.', // 'error.user_exists' => 'User with this login already exists.', // 'error.object_exists' => 'Object with this name already exists.', // 'error.invoice_exists' => 'Invoice with this number already exists.', @@ -95,7 +98,6 @@ // 'error.user_count' => 'Limit on user count.', // 'error.expired' => 'Expiration date reached.', // 'error.file_storage' => 'File storage server error.', // See comment in English file. -// 'error.remote_work' => 'Remote work server error.', // See comment in English file. // Warning messages. // TODO: translate the following. @@ -219,9 +221,6 @@ // 'label.ldap_hint' => 'Type your Windows login and password in the fields below.', 'label.required_fields' => '* - campos requeridos', 'label.on_behalf' => 'a nombre de', -'label.role_manager' => '(manejador)', -'label.role_comanager' => '(auxiliar del manejador)', -'label.role_admin' => '(administrador)', // TODO: translate the following. // 'label.page' => 'Page', // 'label.condition' => 'Condition', @@ -248,6 +247,7 @@ // 'label.mark_paid' => 'Mark paid', // 'label.week_note' => 'Week note', // 'label.week_list' => 'Week list', +// 'label.weekends' => 'Weekends', // 'label.work_units' => 'Work units', // 'label.work_units_short' => 'Units', 'label.totals_only' => 'Solo totales', @@ -266,16 +266,6 @@ // 'label.file' => 'File', // 'label.active_users' => 'Active Users', // 'label.inactive_users' => 'Inactive Users', -// 'label.details' => 'Details', -// 'label.budget' => 'Budget', -// 'label.work' => 'Work', // Table column header for work items, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// 'label.offer' => 'Offer', // Table column header for offers, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// 'label.contractor' => 'Contractor', // Table column header for offers (contractor is someone who offers to do work). - // Technically, it is either an org name or a combination of org and group names - // because both work items and offers are owned by Time Tracker groups of users. -// 'label.how_to_pay' => 'How to pay', // Label for the "How to pay" field on offers, which allows contractors to specify - // how to pay them, for example: paypal email, check by mail to a specific address, etc. -// 'label.moderator_comment' => 'Moderator comment', // Label for "Moderator comment" field that explains something. // Entity names. We use lower case (in English) because they are used in dropdowns, too. // They are used to associate a custom field with an entity type. @@ -289,6 +279,8 @@ // 'title.error' => 'Error', // 'title.success' => 'Success', 'title.login' => 'Sesión iniciada', +// TODO: translate the follolwing. +// 'title.2fa' => 'Two Factor Authentication', 'title.groups' => 'Grupos', // TODO: translate the following. // 'title.add_group' => 'Adding Group', @@ -388,22 +380,6 @@ // 'title.edit_file' => 'Editing File', // 'title.delete_file' => 'Deleting File', // 'title.download_file' => 'Downloading File', -// 'title.work' => 'Work', -// 'title.add_work' => 'Adding Work', -// 'title.edit_work' => 'Editing Work', -// 'title.delete_work' => 'Deleting Work', -// 'title.active_work' => 'Active Work', // Active work items this group outsources to other groups. -// 'title.available_work' => 'Available Work', // Available work items from other organizations. -// 'title.inactive_work' => 'Inactive Work', // Inactive work items this group was outsourcing to other groups. -// 'title.pending_work' => 'Pending Work', // Work items pending moderator approval. -// 'title.offer' => 'Offer', -// 'title.add_offer' => 'Adding Offer', -// 'title.edit_offer' => 'Editing Offer', -// 'title.delete_offer' => 'Deleting Offer', -// 'title.active_offers' => 'Active Offers', // Active offers this group makes available to other groups. -// 'title.available_offers' => 'Available Offers', // Available offers from other organizations. -// 'title.inactive_offers' => 'Inactive Offers', // Inactive offers for group. -// 'title.pending_offers' => 'Pending Offers', // Offers pending moderator approval. // Section for common strings inside combo boxes on forms. Strings shared between forms shall be placed here. // Strings that are used in a single form must go to the specific form section. @@ -436,7 +412,6 @@ // 'dropdown.status_inactive' => 'inactive', // 'dropdown.delete' => 'delete', // 'dropdown.do_not_delete' => 'do not delete', -// 'dropdown.pending_approval' => 'pending approval', // 'dropdown.approved' => 'approved', // 'dropdown.not_approved' => 'not approved', // 'dropdown.paid' => 'paid', @@ -454,6 +429,16 @@ // TODO: translate the following. // 'form.login.about' => 'Anuko Time Tracker is an open source time tracking system.', +// Email subject and body for two-factor authentication. +// TODO: translate the following. +// 'email.2fa_code.subject' => 'Anuko Time Tracker two-factor authentication code', +// 'email.2fa_code.body' => "Dear User,\n\nYour two-factor authentication code is:\n\n%s\n\n", + +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// 'form.2fa.hint' => 'Check your email for 2FA code and enter it here.', +// 'form.2fa.2fa_code' => '2FA code', + // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. // TODO: check / improve translation of form.reset_password.message. // English form is: 'form.reset_password.message' => 'Password reset request sent by email.', @@ -503,10 +488,13 @@ // 'form.reports.include_pending' => 'pending', 'form.reports.select_period' => 'Seleccionar período de tiempo', 'form.reports.set_period' => 'o establecer fechas', +// TODO: translate the following. +// 'form.reports.note_containing' => 'Note containing', 'form.reports.show_fields' => 'Mostrar campos', // TODO: translate the following. // 'form.reports.time_fields' => 'Time fields', // 'form.reports.user_fields' => 'User fields', +// 'form.reports.project_fields' => 'Project fields', 'form.reports.group_by' => 'Agrupar por', 'form.reports.group_by_no' => '--- no agrupar ---', 'form.reports.group_by_date' => 'fecha', @@ -521,6 +509,7 @@ // (after generating a report at https://timetracker.anuko.com/reports.php). 'form.report.export' => 'Exportar', // TODO: translate the following. +// 'form.report.per_hour' => 'Per hour', // 'form.report.assign_to_invoice' => 'Assign to invoice', // 'form.report.assign_to_timesheet' => 'Assign to timesheet', @@ -561,6 +550,7 @@ // Users form. See example at https://timetracker.anuko.com/users.php // TODO: translate the following. +// 'form.users.uncompleted_entry_today' => 'User has an uncompleted time entry today', // 'form.users.uncompleted_entry' => 'User has an uncompleted time entry', 'form.users.role' => 'Rol', 'form.users.manager' => 'Manejador', @@ -624,13 +614,19 @@ // 'form.group_edit.type_start_finish' => 'start and finish', // 'form.group_edit.type_duration' => 'duration', // 'form.group_edit.punch_mode' => 'Punch mode', +// 'form.group_edit.one_uncompleted' => 'One uncompleted', // 'form.group_edit.allow_overlap' => 'Allow overlap', // 'form.group_edit.future_entries' => 'Future entries', // 'form.group_edit.uncompleted_indicators' => 'Uncompleted indicators', // 'form.group_edit.confirm_save' => 'Confirm saving', -// 'form.group_edit.allow_ip' => 'Allow IP', // 'form.group_edit.advanced_settings' => 'Advanced settings', +// Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. +// TODO: Translate the following. +// 'form.group_advanced_edit.allow_ip' => 'Allow IP', +// 'form.group_advanced_edit.password_complexity' => 'Password complexity', +// 'form.group_advanced_edit.2fa' => 'Two factor authentication', + // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php // TODO: translate the following. // 'form.group_delete.hint' => 'Are you sure you want to delete the entire group?', @@ -699,21 +695,7 @@ // 'form.display_options.note_on_separate_row' => 'Note on separate row', // 'form.display_options.not_complete_days' => 'Not complete days', // 'form.display_options.inactive_projects' => 'Inactive projects', +// 'form.display_options.cost_per_hour' => 'Cost per hour', // 'form.display_options.custom_css' => 'Custom CSS', - -// Work plugin strings. See example at https://timetracker.anuko.com/work.php -// TODO: translate the following. -// 'work.error.work_not_available' => 'Work item is not available.', -// 'work.error.offer_not_available' => 'Offer is not available.', -// 'work.type.one_time' => 'one time', // Work type is "one time job" for well defined work ("do exactly this"). -// 'work.type.ongoing' => 'ongoing', // Work type is "ongoing" for complex jobs (billed by the hour, multiple contractors, etc.) -// 'work.label.own_work' => 'Own work', -// 'work.label.own_offers' => 'Own offers', -// 'work.label.offers' => 'Offers', -// 'work.button.send_message' => 'Send message', -// 'work.button.make_offer' => 'Make offer', -// 'work.button.accept' => 'Accept', -// 'work.button.decline' => 'Decline', -// 'work.title.send_message' => 'Sending Message', -// 'work.msg.message_sent' => 'Message sent.', +// 'form.display_options.custom_translation' => 'Custom translation', ); diff --git a/WEB-INF/resources/et.lang.php b/WEB-INF/resources/et.lang.php index 3299e8ada..19a74b4cd 100644 --- a/WEB-INF/resources/et.lang.php +++ b/WEB-INF/resources/et.lang.php @@ -2,7 +2,8 @@ /* Copyright (c) Anuko International Ltd. https://www.anuko.com License: See license.txt */ -// Note: escape apostrophes with THREE backslashes, like here: choisir l\\\'option. +// Note: escape apostrophes with THREE backslashes, like here: 'choisir l\\\'option'. +// Alternatively: use one backslash and surround by double-quotes: "choisir l\'option". // Other characters (such as double-quotes in http links, etc.) do not have to be escaped. // Note to translators: Use proper capitalization rules for your language. @@ -65,6 +66,9 @@ 'error.report' => 'Vali raport.', 'error.record' => 'Vali kirje.', 'error.auth' => 'Autentimine ebaõnnestus.', +// TODO: translate the following. +// 'error.2fa_code' => 'Invalid 2FA code.', +// 'error.weak_password' => 'Weak password.', 'error.user_exists' => 'Selle nimega kasutaja on juba kasutusel.', 'error.object_exists' => 'Sellise nimega objekt on juba olemas.', 'error.invoice_exists' => 'Arve number on juba kasutusel.', @@ -107,7 +111,6 @@ 'error.expired' => 'Kehtivusaeg on lõppenud.', // TODO: translate the following. // 'error.file_storage' => 'File storage server error.', // See comment in English file. -// 'error.remote_work' => 'Remote work server error.', // See comment in English file. // Warning messages. 'warn.sure' => 'Oled kindel?', @@ -217,9 +220,6 @@ 'label.required_fields' => '* nõutud väljad', // TODO: Translate label.on_behalf, perhaps trying as "instead of". // 'label.on_behalf' => 'on behalf of', -'label.role_manager' => '(haldur)', -'label.role_comanager' => '(kaashaldur)', -'label.role_admin' => '(administraator)', 'label.page' => 'Lehekülg', 'label.condition' => 'Tingimus', 'label.yes' => 'jah', @@ -245,6 +245,8 @@ 'label.mark_paid' => 'Märgi makstuks', 'label.week_note' => 'Nädala märge', 'label.week_list' => 'Nädala nimekiri', +// TODO: translate the following: +// 'label.weekends' => 'Weekends', 'label.work_units' => 'Töö ühikud', 'label.work_units_short' => 'Ühikud', 'label.totals_only' => 'Ainult summad', @@ -263,17 +265,6 @@ // 'label.file' => 'File', 'label.active_users' => 'Aktiivsed kasutajad', 'label.inactive_users' => 'Mitteaktiivsed kasutajad', -// TODO: translate the following. -// 'label.details' => 'Details', -// 'label.budget' => 'Budget', -// 'label.work' => 'Work', // Table column header for work items, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// 'label.offer' => 'Offer', // Table column header for offers, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// 'label.contractor' => 'Contractor', // Table column header for offers (contractor is someone who offers to do work). - // Technically, it is either an org name or a combination of org and group names - // because both work items and offers are owned by Time Tracker groups of users. -// 'label.how_to_pay' => 'How to pay', // Label for the "How to pay" field on offers, which allows contractors to specify - // how to pay them, for example: paypal email, check by mail to a specific address, etc. -// 'label.moderator_comment' => 'Moderator comment', // Label for "Moderator comment" field that explains something. // Entity names. We use lower case (in English) because they are used in dropdowns, too. // They are used to associate a custom field with an entity type. @@ -290,6 +281,8 @@ 'title.error' => 'Viga', 'title.success' => 'Õnnestumine', 'title.login' => 'Sisene', +// TODO: translate the follolwing. +// 'title.2fa' => 'Two Factor Authentication', 'title.groups' => 'Grupid', 'title.add_group' => 'Lisa grupp', 'title.edit_group' => 'Muuda gruppi', @@ -383,22 +376,6 @@ // 'title.edit_file' => 'Editing File', // 'title.delete_file' => 'Deleting File', // 'title.download_file' => 'Downloading File', -// 'title.work' => 'Work', -// 'title.add_work' => 'Adding Work', -// 'title.edit_work' => 'Editing Work', -// 'title.delete_work' => 'Deleting Work', -// 'title.active_work' => 'Active Work', // Active work items this group outsources to other groups. -// 'title.available_work' => 'Available Work', // Available work items from other organizations. -// 'title.inactive_work' => 'Inactive Work', // Inactive work items this group was outsourcing to other groups. -// 'title.pending_work' => 'Pending Work', // Work items pending moderator approval. -// 'title.offer' => 'Offer', -// 'title.add_offer' => 'Adding Offer', -// 'title.edit_offer' => 'Editing Offer', -// 'title.delete_offer' => 'Deleting Offer', -// 'title.active_offers' => 'Active Offers', // Active offers this group makes available to other groups. -// 'title.available_offers' => 'Available Offers', // Available offers from other organizations. -// 'title.inactive_offers' => 'Inactive Offers', // Inactive offers for group. -// 'title.pending_offers' => 'Pending Offers', // Offers pending moderator approval. // Section for common strings inside combo boxes on forms. Strings shared between forms shall be placed here. // Strings that are used in a single form must go to the specific form section. @@ -429,7 +406,6 @@ 'dropdown.delete' => 'kustuta', 'dropdown.do_not_delete' => 'ära kustuta', // TODO: translate the following. -// 'dropdown.pending_approval' => 'pending approval', // 'dropdown.approved' => 'approved', // 'dropdown.not_approved' => 'not approved', 'dropdown.paid' => 'makstud', @@ -442,6 +418,16 @@ 'form.login.forgot_password' => 'Unustasid salasõna?', 'form.login.about' => 'Anuko Time Tracker on avatud lähtekoodiga ajaarvestussüsteem.', +// Email subject and body for two-factor authentication. +// TODO: translate the following. +// 'email.2fa_code.subject' => 'Anuko Time Tracker two-factor authentication code', +// 'email.2fa_code.body' => "Dear User,\n\nYour two-factor authentication code is:\n\n%s\n\n", + +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// 'form.2fa.hint' => 'Check your email for 2FA code and enter it here.', +// 'form.2fa.2fa_code' => '2FA code', + // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. 'form.reset_password.message' => 'Salasõna tühistamise teade on saadetud e-postile.', 'form.reset_password.email_subject' => 'Anuko Time Tracker, parooli tühistamise nõue', @@ -487,10 +473,13 @@ // 'form.reports.include_pending' => 'pending', 'form.reports.select_period' => 'Vali ajaperiood', 'form.reports.set_period' => 'või märgi kuupäevad', +// TODO: translate the following. +// 'form.reports.note_containing' => 'Note containing', 'form.reports.show_fields' => 'Näita välju', // TODO: translate the following. // 'form.reports.time_fields' => 'Time fields', // 'form.reports.user_fields' => 'User fields', +// 'form.reports.project_fields' => 'Project fields', 'form.reports.group_by' => 'Grupeeri', 'form.reports.group_by_no' => '--- grupeerimata ---', 'form.reports.group_by_date' => 'kuupäev', @@ -502,6 +491,8 @@ // Report form. See example at https://timetracker.anuko.com/report.php // (after generating a report at https://timetracker.anuko.com/reports.php). 'form.report.export' => 'Eksport', +// TODO: translate the following. +// 'form.report.per_hour' => 'Per hour', 'form.report.assign_to_invoice' => 'Lisa arvele', // TODO: translate the following. // 'form.report.assign_to_timesheet' => 'Assign to timesheet', @@ -539,6 +530,8 @@ 'form.tasks.inactive_tasks' => 'Mitteaktiivsed tööülesanded', // Users form. See example at https://timetracker.anuko.com/users.php +// TODO: translate the following. +// 'form.users.uncompleted_entry_today' => 'User has an uncompleted time entry today', 'form.users.uncompleted_entry' => 'Kasutajal on lõpetamata aja kanne', 'form.users.role' => 'Roll', 'form.users.manager' => 'Haldur', @@ -597,14 +590,21 @@ 'form.group_edit.type_start_finish' => 'algus ja lõpp', 'form.group_edit.type_duration' => 'vahemik', 'form.group_edit.punch_mode' => 'Kellast-kellani režiim', +// TODO: translate the following. +// 'form.group_edit.one_uncompleted' => 'One uncompleted', 'form.group_edit.allow_overlap' => 'Luba ajaline ülekate', 'form.group_edit.future_entries' => 'Tuleviku kirjed', 'form.group_edit.uncompleted_indicators' => 'Lõpetamata kirjete indikaator', // TODO: Fix this. Indicators (plural), not indicator. 'form.group_edit.confirm_save' => 'Kinnita salvestamine', -'form.group_edit.allow_ip' => 'Luba IP', // TODO: translate the following. // 'form.group_edit.advanced_settings' => 'Advanced settings', +// Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. +'form.group_advanced_edit.allow_ip' => 'Luba IP', +// TODO: Translate the following. +// 'form.group_advanced_edit.password_complexity' => 'Password complexity', +// 'form.group_advanced_edit.2fa' => 'Two factor authentication', + // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php 'form.group_delete.hint' => 'Oled kindel, et soovid kogu gruppi kustutada?', @@ -669,21 +669,7 @@ // 'form.display_options.note_on_separate_row' => 'Note on separate row', // 'form.display_options.not_complete_days' => 'Not complete days', // 'form.display_options.inactive_projects' => 'Inactive projects', +// 'form.display_options.cost_per_hour' => 'Cost per hour', // 'form.display_options.custom_css' => 'Custom CSS', - -// Work plugin strings. See example at https://timetracker.anuko.com/work.php -// TODO: translate the following. -// 'work.error.work_not_available' => 'Work item is not available.', -// 'work.error.offer_not_available' => 'Offer is not available.', -// 'work.type.one_time' => 'one time', // Work type is "one time job" for well defined work ("do exactly this"). -// 'work.type.ongoing' => 'ongoing', // Work type is "ongoing" for complex jobs (billed by the hour, multiple contractors, etc.) -// 'work.label.own_work' => 'Own work', -// 'work.label.own_offers' => 'Own offers', -// 'work.label.offers' => 'Offers', -// 'work.button.send_message' => 'Send message', -// 'work.button.make_offer' => 'Make offer', -// 'work.button.accept' => 'Accept', -// 'work.button.decline' => 'Decline', -// 'work.title.send_message' => 'Sending Message', -// 'work.msg.message_sent' => 'Message sent.', +// 'form.display_options.custom_translation' => 'Custom translation', ); diff --git a/WEB-INF/resources/fa.lang.php b/WEB-INF/resources/fa.lang.php index 019c4ca31..32dacdf00 100644 --- a/WEB-INF/resources/fa.lang.php +++ b/WEB-INF/resources/fa.lang.php @@ -2,8 +2,10 @@ /* Copyright (c) Anuko International Ltd. https://www.anuko.com License: See license.txt */ -// Note: escape apostrophes with THREE backslashes, like here: choisir l\\\'option. +// Note: escape apostrophes with THREE backslashes, like here: 'choisir l\\\'option'. +// Alternatively: use one backslash and surround by double-quotes: "choisir l\'option". // Other characters (such as double-quotes in http links, etc.) do not have to be escaped. + $i18n_language = 'Persian (فارسی)'; // TODO: translate the following. $i18n_months = array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'); @@ -71,6 +73,9 @@ // 'error.report' => 'Select report.', // 'error.record' => 'Select record.', 'error.auth' => 'نام کاربری یا رمز عبور اشتباه است.', +// TODO: translate the following. +// 'error.2fa_code' => 'Invalid 2FA code.', +// 'error.weak_password' => 'Weak password.', 'error.user_exists' => 'کاربری با این نام کاربری موجود است.', // TODO: translate the following. // 'error.object_exists' => 'Object with this name already exists.', @@ -102,7 +107,6 @@ // 'error.expired' => 'Expiration date reached.', // TODO: translate the following. // 'error.file_storage' => 'File storage server error.', // See comment in English file. -// 'error.remote_work' => 'Remote work server error.', // See comment in English file. // Warning messages. // TODO: translate the following. @@ -219,9 +223,6 @@ 'label.ldap_hint' => 'نام کاربری ویندوزو رمزعبورخود را در فیلدهای زیر وارد کنید', 'label.required_fields' => '* - فیلد های اجباری', 'label.on_behalf' => 'از دیدگاه', -'label.role_manager' => '(مدیر)', -'label.role_comanager' => '(دستیار مدیر)', -'label.role_admin' => '(مدیر ارشد)', // Translate the following. // 'label.page' => 'Page', // 'label.condition' => 'Condition', @@ -249,6 +250,7 @@ // 'label.mark_paid' => 'Mark paid', // 'label.week_note' => 'Week note', // 'label.week_list' => 'Week list', +// 'label.weekends' => 'Weekends', // 'label.work_units' => 'Work units', // 'label.work_units_short' => 'Units', // 'label.totals_only' => 'Totals only', @@ -266,17 +268,6 @@ // 'label.file' => 'File', 'label.active_users' => 'کاربران فعال', 'label.inactive_users' => 'کاربران غیرفعال', -// TODO: translate the following. -// 'label.details' => 'Details', -// 'label.budget' => 'Budget', -// 'label.work' => 'Work', // Table column header for work items, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// 'label.offer' => 'Offer', // Table column header for offers, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// 'label.contractor' => 'Contractor', // Table column header for offers (contractor is someone who offers to do work). - // Technically, it is either an org name or a combination of org and group names - // because both work items and offers are owned by Time Tracker groups of users. -// 'label.how_to_pay' => 'How to pay', // Label for the "How to pay" field on offers, which allows contractors to specify - // how to pay them, for example: paypal email, check by mail to a specific address, etc. -// 'label.moderator_comment' => 'Moderator comment', // Label for "Moderator comment" field that explains something. // Entity names. We use lower case (in English) because they are used in dropdowns, too. // They are used to associate a custom field with an entity type. @@ -290,6 +281,8 @@ // TODO: Translate the following. // 'title.success' => 'Success', 'title.login' => 'ورود', +// TODO: translate the follolwing. +// 'title.2fa' => 'Two Factor Authentication', 'title.groups' => 'تیم ها', // TODO: change "teams" to "groups". // TODO: translate the following. // 'title.add_group' => 'Adding Group', @@ -383,22 +376,6 @@ // 'title.edit_file' => 'Editing File', // 'title.delete_file' => 'Deleting File', // 'title.download_file' => 'Downloading File', -// 'title.work' => 'Work', -// 'title.add_work' => 'Adding Work', -// 'title.edit_work' => 'Editing Work', -// 'title.delete_work' => 'Deleting Work', -// 'title.active_work' => 'Active Work', // Active work items this group outsources to other groups. -// 'title.available_work' => 'Available Work', // Available work items from other organizations. -// 'title.inactive_work' => 'Inactive Work', // Inactive work items this group was outsourcing to other groups. -// 'title.pending_work' => 'Pending Work', // Work items pending moderator approval. -// 'title.offer' => 'Offer', -// 'title.add_offer' => 'Adding Offer', -// 'title.edit_offer' => 'Editing Offer', -// 'title.delete_offer' => 'Deleting Offer', -// 'title.active_offers' => 'Active Offers', // Active offers this group makes available to other groups. -// 'title.available_offers' => 'Available Offers', // Available offers from other organizations. -// 'title.inactive_offers' => 'Inactive Offers', // Inactive offers for group. -// 'title.pending_offers' => 'Pending Offers', // Offers pending moderator approval. // Section for common strings inside combo boxes on forms. Strings shared between forms shall be placed here. // Strings that are used in a single form must go to the specific form section. @@ -434,7 +411,6 @@ // TODO: translate the following. // 'dropdown.delete' => 'delete', // 'dropdown.do_not_delete' => 'do not delete', -// 'dropdown.pending_approval' => 'pending approval', // 'dropdown.approved' => 'approved', // 'dropdown.not_approved' => 'not approved', // 'dropdown.paid' => 'paid', @@ -449,8 +425,18 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => 'بازیابی رمز عبور؟', -// TODO: translate form.login.about. -'form.login.about' => 'Anuko Time Tracker is an open source time tracking system.', +// TODO: translate the following. +// 'form.login.about' => 'Anuko Time Tracker is an open source time tracking system.', + +// Email subject and body for two-factor authentication. +// TODO: translate the following. +// 'email.2fa_code.subject' => 'Anuko Time Tracker two-factor authentication code', +// 'email.2fa_code.body' => "Dear User,\n\nYour two-factor authentication code is:\n\n%s\n\n", + +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// 'form.2fa.hint' => 'Check your email for 2FA code and enter it here.', +// 'form.2fa.2fa_code' => '2FA code', // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. 'form.reset_password.message' => 'درخواست بازیابی رمزعبور به ایمیل فرستاده شد.', @@ -496,10 +482,13 @@ // 'form.reports.include_pending' => 'pending', 'form.reports.select_period' => 'انتخاب بازه زمانی', 'form.reports.set_period' => 'یا تعیین تاریخ', +// TODO: translate the following. +// 'form.reports.note_containing' => 'Note containing', 'form.reports.show_fields' => 'نمایش فیلدها', // TODO: translate the following. // 'form.reports.time_fields' => 'Time fields', // 'form.reports.user_fields' => 'User fields', +// 'form.reports.project_fields' => 'Project fields', 'form.reports.group_by' => 'گروه بندی شده با', 'form.reports.group_by_no' => '--- بدون گروه ---', 'form.reports.group_by_date' => 'تاریخ', @@ -512,6 +501,7 @@ // (after generating a report at https://timetracker.anuko.com/reports.php). 'form.report.export' => 'پشتیبانی', // TODO: translate the following. +// 'form.report.per_hour' => 'Per hour', // 'form.report.assign_to_invoice' => 'Assign to invoice', // 'form.report.assign_to_timesheet' => 'Assign to timesheet', @@ -550,6 +540,7 @@ // Users form. See example at https://timetracker.anuko.com/users.php // TODO: translate the following. + // 'form.users.uncompleted_entry_today' => 'User has an uncompleted time entry today', // 'form.users.uncompleted_entry' => 'User has an uncompleted time entry', 'form.users.role' => 'سمت', 'form.users.manager' => 'مدیر', @@ -611,13 +602,19 @@ 'form.group_edit.type_duration' => 'مدت زمان', // TODO: translate the following. // 'form.group_edit.punch_mode' => 'Punch mode', +// 'form.group_edit.one_uncompleted' => 'One uncompleted', // 'form.group_edit.allow_overlap' => 'Allow overlap', // 'form.group_edit.future_entries' => 'Future entries', // 'form.group_edit.uncompleted_indicators' => 'Uncompleted indicators', // 'form.group_edit.confirm_save' => 'Confirm saving', -// 'form.group_edit.allow_ip' => 'Allow IP', // 'form.group_edit.advanced_settings' => 'Advanced settings', +// Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. +// TODO: Translate the following. +// 'form.group_advanced_edit.allow_ip' => 'Allow IP', +// 'form.group_advanced_edit.password_complexity' => 'Password complexity', +// 'form.group_advanced_edit.2fa' => 'Two factor authentication', + // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php // TODO: translate the following. // 'form.group_delete.hint' => 'Are you sure you want to delete the entire group?', @@ -685,21 +682,7 @@ // 'form.display_options.note_on_separate_row' => 'Note on separate row', // 'form.display_options.not_complete_days' => 'Not complete days', // 'form.display_options.inactive_projects' => 'Inactive projects', +// 'form.display_options.cost_per_hour' => 'Cost per hour', // 'form.display_options.custom_css' => 'Custom CSS', - -// Work plugin strings. See example at https://timetracker.anuko.com/work.php -// TODO: translate the following. -// 'work.error.work_not_available' => 'Work item is not available.', -// 'work.error.offer_not_available' => 'Offer is not available.', -// 'work.type.one_time' => 'one time', // Work type is "one time job" for well defined work ("do exactly this"). -// 'work.type.ongoing' => 'ongoing', // Work type is "ongoing" for complex jobs (billed by the hour, multiple contractors, etc.) -// 'work.label.own_work' => 'Own work', -// 'work.label.own_offers' => 'Own offers', -// 'work.label.offers' => 'Offers', -// 'work.button.send_message' => 'Send message', -// 'work.button.make_offer' => 'Make offer', -// 'work.button.accept' => 'Accept', -// 'work.button.decline' => 'Decline', -// 'work.title.send_message' => 'Sending Message', -// 'work.msg.message_sent' => 'Message sent.', +// 'form.display_options.custom_translation' => 'Custom translation', ); diff --git a/WEB-INF/resources/fi.lang.php b/WEB-INF/resources/fi.lang.php index 0940dc597..cdfc4c5fe 100644 --- a/WEB-INF/resources/fi.lang.php +++ b/WEB-INF/resources/fi.lang.php @@ -2,7 +2,8 @@ /* Copyright (c) Anuko International Ltd. https://www.anuko.com License: See license.txt */ -// Note: escape apostrophes with THREE backslashes, like here: choisir l\\\'option. +// Note: escape apostrophes with THREE backslashes, like here: 'choisir l\\\'option'. +// Alternatively: use one backslash and surround by double-quotes: "choisir l\'option". // Other characters (such as double-quotes in http links, etc.) do not have to be escaped. $i18n_language = 'Finnish (Suomi)'; @@ -68,6 +69,9 @@ // TODO: translate the following. // 'error.record' => 'Select record.', 'error.auth' => 'Virheellinen käyttäjänimi tai salasana.', +// TODO: translate the following. +// 'error.2fa_code' => 'Invalid 2FA code.', +// 'error.weak_password' => 'Weak password.', 'error.user_exists' => 'Tämä käyttäjänimi on jo olemassa.', // TODO: translate the following. // 'error.object_exists' => 'Object with this name already exists.', @@ -96,7 +100,6 @@ // 'error.user_count' => 'Limit on user count.', // 'error.expired' => 'Expiration date reached.', // 'error.file_storage' => 'File storage server error.', // See comment in English file. -// 'error.remote_work' => 'Remote work server error.', // See comment in English file. // Warning messages. // TODO: translate the following. @@ -211,9 +214,6 @@ 'label.ldap_hint' => 'Syötä Windows-käyttäjätunnuksesi ja salasanasi ao. kenttiin.', 'label.required_fields' => '* - pakolliset kentät', 'label.on_behalf' => 'puolesta', -'label.role_manager' => '(esimies)', -'label.role_comanager' => '(apu-esimies)', -'label.role_admin' => '(ylläpitäjä)', 'label.page' => 'Sivu', // TODO: translate the following. // 'label.condition' => 'Condition', @@ -241,6 +241,7 @@ // 'label.mark_paid' => 'Mark paid', // 'label.week_note' => 'Week note', // 'label.week_list' => 'Week list', +// 'label.weekends' => 'Weekends', // 'label.work_units' => 'Work units', // 'label.work_units_short' => 'Units', 'label.totals_only' => 'Vain yhteissummat', @@ -259,17 +260,6 @@ // 'label.file' => 'File', 'label.active_users' => 'Aktiiviset käyttäjät', 'label.inactive_users' => 'Ei-aktiiviset käyttäjät', -// TODO: translate the following. -// 'label.details' => 'Details', -// 'label.budget' => 'Budget', -// 'label.work' => 'Work', // Table column header for work items, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// 'label.offer' => 'Offer', // Table column header for offers, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// 'label.contractor' => 'Contractor', // Table column header for offers (contractor is someone who offers to do work). - // Technically, it is either an org name or a combination of org and group names - // because both work items and offers are owned by Time Tracker groups of users. -// 'label.how_to_pay' => 'How to pay', // Label for the "How to pay" field on offers, which allows contractors to specify - // how to pay them, for example: paypal email, check by mail to a specific address, etc. -// 'label.moderator_comment' => 'Moderator comment', // Label for "Moderator comment" field that explains something. // Entity names. We use lower case (in English) because they are used in dropdowns, too. // They are used to associate a custom field with an entity type. @@ -283,6 +273,8 @@ // TODO: Translate the following. // 'title.success' => 'Success', 'title.login' => 'Kirjautuminen', +// TODO: translate the follolwing. +// 'title.2fa' => 'Two Factor Authentication', 'title.groups' => 'Tiimit', // TODO: change "teams" to "groups". // TODO: translate the following. // 'title.add_group' => 'Adding Group', @@ -374,22 +366,6 @@ // 'title.edit_file' => 'Editing File', // 'title.delete_file' => 'Deleting File', // 'title.download_file' => 'Downloading File', -// 'title.work' => 'Work', -// 'title.add_work' => 'Adding Work', -// 'title.edit_work' => 'Editing Work', -// 'title.delete_work' => 'Deleting Work', -// 'title.active_work' => 'Active Work', // Active work items this group outsources to other groups. -// 'title.available_work' => 'Available Work', // Available work items from other organizations. -// 'title.inactive_work' => 'Inactive Work', // Inactive work items this group was outsourcing to other groups. -// 'title.pending_work' => 'Pending Work', // Work items pending moderator approval. -// 'title.offer' => 'Offer', -// 'title.add_offer' => 'Adding Offer', -// 'title.edit_offer' => 'Editing Offer', -// 'title.delete_offer' => 'Deleting Offer', -// 'title.active_offers' => 'Active Offers', // Active offers this group makes available to other groups. -// 'title.available_offers' => 'Available Offers', // Available offers from other organizations. -// 'title.inactive_offers' => 'Inactive Offers', // Inactive offers for group. -// 'title.pending_offers' => 'Pending Offers', // Offers pending moderator approval. // Section for common strings inside combo boxes on forms. Strings shared between forms shall be placed here. // Strings that are used in a single form must go to the specific form section. @@ -422,7 +398,6 @@ 'dropdown.delete' => 'poista', 'dropdown.do_not_delete' => 'älä poista', // TODO: translate the following. -// 'dropdown.pending_approval' => 'pending approval', // 'dropdown.approved' => 'approved', // 'dropdown.not_approved' => 'not approved', // 'dropdown.paid' => 'paid', @@ -437,9 +412,19 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => 'Salasana unohtunut?', - // TODO: check translation of form.login.about - is open source "vapaan koodin"? +// TODO: check translation of form.login.about - is open source "vapaan koodin"? 'form.login.about' => 'Anuko Time Tracker on vapaan koodin tuntiseurantaohjelmisto.', +// Email subject and body for two-factor authentication. +// TODO: translate the following. +// 'email.2fa_code.subject' => 'Anuko Time Tracker two-factor authentication code', +// 'email.2fa_code.body' => "Dear User,\n\nYour two-factor authentication code is:\n\n%s\n\n", + +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// 'form.2fa.hint' => 'Check your email for 2FA code and enter it here.', +// 'form.2fa.2fa_code' => '2FA code', + // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. 'form.reset_password.message' => 'Salasanan nollauspyyntöviesti lähetetty.', 'form.reset_password.email_subject' => 'Anuko Time Tracker -salasanan nollauspyyntö', @@ -481,10 +466,13 @@ // 'form.reports.include_pending' => 'pending', 'form.reports.select_period' => 'Valitse ajanjakso', 'form.reports.set_period' => 'tai aseta päivät', +// TODO: translate the following. +// 'form.reports.note_containing' => 'Note containing', 'form.reports.show_fields' => 'Näytä kentät', // TODO: translate the following. // 'form.reports.time_fields' => 'Time fields', // 'form.reports.user_fields' => 'User fields', +// 'form.reports.project_fields' => 'Project fields', 'form.reports.group_by' => 'Ryhmittelyperuste', 'form.reports.group_by_no' => '--- ei ryhmitystä ---', 'form.reports.group_by_date' => 'päivä', @@ -497,6 +485,7 @@ // (after generating a report at https://timetracker.anuko.com/reports.php). 'form.report.export' => 'Vie', // TODO: translate the following. +// 'form.report.per_hour' => 'Per hour', // 'form.report.assign_to_invoice' => 'Assign to invoice', // 'form.report.assign_to_timesheet' => 'Assign to timesheet', @@ -535,6 +524,7 @@ // Users form. See example at https://timetracker.anuko.com/users.php // TODO: translate the following. +// 'form.users.uncompleted_entry_today' => 'User has an uncompleted time entry today', // 'form.users.uncompleted_entry' => 'User has an uncompleted time entry', 'form.users.role' => 'Rooli', 'form.users.manager' => 'Esimies', @@ -595,13 +585,19 @@ 'form.group_edit.type_duration' => 'kesto', // TODO: translate the following. // 'form.group_edit.punch_mode' => 'Punch mode', +// 'form.group_edit.one_uncompleted' => 'One uncompleted', // 'form.group_edit.allow_overlap' => 'Allow overlap', // 'form.group_edit.future_entries' => 'Future entries', // 'form.group_edit.uncompleted_indicators' => 'Uncompleted indicators', // 'form.group_edit.confirm_save' => 'Confirm saving', -// 'form.group_edit.allow_ip' => 'Allow IP', // 'form.group_edit.advanced_settings' => 'Advanced settings', +// Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. +// TODO: Translate the following. +// 'form.group_advanced_edit.allow_ip' => 'Allow IP', +// 'form.group_advanced_edit.password_complexity' => 'Password complexity', +// 'form.group_advanced_edit.2fa' => 'Two factor authentication', + // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php // TODO: translate the following. // 'form.group_delete.hint' => 'Are you sure you want to delete the entire group?', @@ -669,21 +665,7 @@ // 'form.display_options.note_on_separate_row' => 'Note on separate row', // 'form.display_options.not_complete_days' => 'Not complete days', // 'form.display_options.inactive_projects' => 'Inactive projects', +// 'form.display_options.cost_per_hour' => 'Cost per hour', // 'form.display_options.custom_css' => 'Custom CSS', - -// Work plugin strings. See example at https://timetracker.anuko.com/work.php -// TODO: translate the following. -// 'work.error.work_not_available' => 'Work item is not available.', -// 'work.error.offer_not_available' => 'Offer is not available.', -// 'work.type.one_time' => 'one time', // Work type is "one time job" for well defined work ("do exactly this"). -// 'work.type.ongoing' => 'ongoing', // Work type is "ongoing" for complex jobs (billed by the hour, multiple contractors, etc.) -// 'work.label.own_work' => 'Own work', -// 'work.label.own_offers' => 'Own offers', -// 'work.label.offers' => 'Offers', -// 'work.button.send_message' => 'Send message', -// 'work.button.make_offer' => 'Make offer', -// 'work.button.accept' => 'Accept', -// 'work.button.decline' => 'Decline', -// 'work.title.send_message' => 'Sending Message', -// 'work.msg.message_sent' => 'Message sent.', +// 'form.display_options.custom_translation' => 'Custom translation', ); diff --git a/WEB-INF/resources/fr.lang.php b/WEB-INF/resources/fr.lang.php index 88a96690e..b488be0bc 100644 --- a/WEB-INF/resources/fr.lang.php +++ b/WEB-INF/resources/fr.lang.php @@ -2,7 +2,8 @@ /* Copyright (c) Anuko International Ltd. https://www.anuko.com License: See license.txt */ -// Note: escape apostrophes with THREE backslashes, like here: choisir l\\\'option. +// Note: escape apostrophes with THREE backslashes, like here: 'choisir l\\\'option'. +// Alternatively: use one backslash and surround by double-quotes: "choisir l\'option". // Other characters (such as double-quotes in http links, etc.) do not have to be escaped. $i18n_language = 'French (Français)'; @@ -66,6 +67,9 @@ // TODO: translate the following. // 'error.record' => 'Select record.', 'error.auth' => 'Nom d\\\'utilisateur ou mot de passe incorrect.', +// TODO: translate the following. +// 'error.2fa_code' => 'Invalid 2FA code.', +// 'error.weak_password' => 'Weak password.', 'error.user_exists' => 'Un utilisateur avec cet identifiant existe déjà.', // TODO: translate the following. // 'error.object_exists' => 'Object with this name already exists.', @@ -94,7 +98,6 @@ // 'error.user_count' => 'Limit on user count.', // 'error.expired' => 'Expiration date reached.', // 'error.file_storage' => 'File storage server error.', // See comment in English file. -// 'error.remote_work' => 'Remote work server error.', // See comment in English file. // Warning messages. // TODO: translate the following. @@ -207,9 +210,6 @@ 'label.ldap_hint' => 'Entrer votre Identifiant Windows et votre mot de passe dans les champs suivants.', 'label.required_fields' => '* - champs obligatoires', 'label.on_behalf' => 'de la part de', -'label.role_manager' => '(responsable)', -'label.role_comanager' => '(coresponsable)', -'label.role_admin' => '(administrateur)', 'label.page' => 'Page', 'label.condition' => 'Condition', // TODO: translate the following. @@ -235,6 +235,7 @@ // 'label.mark_paid' => 'Mark paid', // 'label.week_note' => 'Week note', // 'label.week_list' => 'Week list', +// 'label.weekends' => 'Weekends', // 'label.work_units' => 'Work units', // 'label.work_units_short' => 'Units', 'label.totals_only' => 'Totaux uniquement', @@ -253,17 +254,6 @@ // 'label.file' => 'File', 'label.active_users' => 'Utilisateurs actifs', 'label.inactive_users' => 'Utilisateurs inactifs', -// TODO: translate the following. -// 'label.details' => 'Details', -// 'label.budget' => 'Budget', -// 'label.work' => 'Work', // Table column header for work items, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// 'label.offer' => 'Offer', // Table column header for offers, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// 'label.contractor' => 'Contractor', // Table column header for offers (contractor is someone who offers to do work). - // Technically, it is either an org name or a combination of org and group names - // because both work items and offers are owned by Time Tracker groups of users. -// 'label.how_to_pay' => 'How to pay', // Label for the "How to pay" field on offers, which allows contractors to specify - // how to pay them, for example: paypal email, check by mail to a specific address, etc. -// 'label.moderator_comment' => 'Moderator comment', // Label for "Moderator comment" field that explains something. // Entity names. We use lower case (in English) because they are used in dropdowns, too. // They are used to associate a custom field with an entity type. @@ -277,6 +267,8 @@ // TODO: Translate the following. // 'title.success' => 'Success', 'title.login' => 'Connexion', +// TODO: translate the follolwing. +// 'title.2fa' => 'Two Factor Authentication', 'title.groups' => 'Équipes', // TODO: change "teams" to "groups". // TODO: translate the following. // 'title.add_group' => 'Adding Group', @@ -368,22 +360,6 @@ // 'title.edit_file' => 'Editing File', // 'title.delete_file' => 'Deleting File', // 'title.download_file' => 'Downloading File', -// 'title.work' => 'Work', -// 'title.add_work' => 'Adding Work', -// 'title.edit_work' => 'Editing Work', -// 'title.delete_work' => 'Deleting Work', -// 'title.active_work' => 'Active Work', // Active work items this group outsources to other groups. -// 'title.available_work' => 'Available Work', // Available work items from other organizations. -// 'title.inactive_work' => 'Inactive Work', // Inactive work items this group was outsourcing to other groups. -// 'title.pending_work' => 'Pending Work', // Work items pending moderator approval. -// 'title.offer' => 'Offer', -// 'title.add_offer' => 'Adding Offer', -// 'title.edit_offer' => 'Editing Offer', -// 'title.delete_offer' => 'Deleting Offer', -// 'title.active_offers' => 'Active Offers', // Active offers this group makes available to other groups. -// 'title.available_offers' => 'Available Offers', // Available offers from other organizations. -// 'title.inactive_offers' => 'Inactive Offers', // Inactive offers for group. -// 'title.pending_offers' => 'Pending Offers', // Offers pending moderator approval. // Section for common strings inside combo boxes on forms. Strings shared between forms shall be placed here. // Strings that are used in a single form must go to the specific form section. @@ -414,7 +390,6 @@ 'dropdown.delete' => 'supprimer', 'dropdown.do_not_delete' => 'ne pas supprimer', // TODO: translate the following. -// 'dropdown.pending_approval' => 'pending approval', // 'dropdown.approved' => 'approved', // 'dropdown.not_approved' => 'not approved', // 'dropdown.paid' => 'paid', @@ -431,6 +406,16 @@ 'form.login.forgot_password' => 'Mot de passe oublié?', 'form.login.about' => 'Anuko Time Tracker est un système de gestion du temps, open source.', +// Email subject and body for two-factor authentication. +// TODO: translate the following. +// 'email.2fa_code.subject' => 'Anuko Time Tracker two-factor authentication code', +// 'email.2fa_code.body' => "Dear User,\n\nYour two-factor authentication code is:\n\n%s\n\n", + +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// 'form.2fa.hint' => 'Check your email for 2FA code and enter it here.', +// 'form.2fa.2fa_code' => '2FA code', + // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. 'form.reset_password.message' => 'Une demande de réinitialisation du mot de passe a été envoyé par courriel.', 'form.reset_password.email_subject' => 'Demande de réinitialisation de mot de passe Anuko Time Tracker', @@ -472,10 +457,13 @@ // 'form.reports.include_pending' => 'pending', 'form.reports.select_period' => 'Sélectionner la période de temps', 'form.reports.set_period' => 'ou dates indiquées', +// TODO: translate the following. +// 'form.reports.note_containing' => 'Note containing', 'form.reports.show_fields' => 'Afficher les champs', // TODO: translate the following. // 'form.reports.time_fields' => 'Time fields', // 'form.reports.user_fields' => 'User fields', +// 'form.reports.project_fields' => 'Project fields', 'form.reports.group_by' => 'Regroupés par', 'form.reports.group_by_no' => '--- Aucun regroupement ---', 'form.reports.group_by_date' => 'Date', @@ -488,6 +476,7 @@ // (after generating a report at https://timetracker.anuko.com/reports.php). 'form.report.export' => 'Exporter', // TODO: translate the following. +// 'form.report.per_hour' => 'Per hour', // 'form.report.assign_to_invoice' => 'Assign to invoice', // 'form.report.assign_to_timesheet' => 'Assign to timesheet', @@ -525,6 +514,8 @@ 'form.tasks.inactive_tasks' => 'Tâches inactives', // Users form. See example at https://timetracker.anuko.com/users.php +// TODO: translate the following. +// 'form.users.uncompleted_entry_today' => 'User has an uncompleted time entry today', 'form.users.uncompleted_entry' => 'L\\\'utilisateur a une entrée incomplète', 'form.users.role' => 'Rôle', 'form.users.manager' => 'Responsable', @@ -585,13 +576,19 @@ 'form.group_edit.type_duration' => 'Durée', // TODO: translate the following. // 'form.group_edit.punch_mode' => 'Punch mode', +// 'form.group_edit.one_uncompleted' => 'One uncompleted', // 'form.group_edit.allow_overlap' => 'Allow overlap', // 'form.group_edit.future_entries' => 'Future entries', // 'form.group_edit.uncompleted_indicators' => 'Uncompleted indicators', // 'form.group_edit.confirm_save' => 'Confirm saving', -// 'form.group_edit.allow_ip' => 'Allow IP', // 'form.group_edit.advanced_settings' => 'Advanced settings', +// Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. +// TODO: Translate the following. +// 'form.group_advanced_edit.allow_ip' => 'Allow IP', +// 'form.group_advanced_edit.password_complexity' => 'Password complexity', +// 'form.group_advanced_edit.2fa' => 'Two factor authentication', + // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php // TODO: translate the following. // 'form.group_delete.hint' => 'Are you sure you want to delete the entire group?', @@ -657,21 +654,7 @@ // 'form.display_options.note_on_separate_row' => 'Note on separate row', // 'form.display_options.not_complete_days' => 'Not complete days', // 'form.display_options.inactive_projects' => 'Inactive projects', +// 'form.display_options.cost_per_hour' => 'Cost per hour', // 'form.display_options.custom_css' => 'Custom CSS', - -// Work plugin strings. See example at https://timetracker.anuko.com/work.php -// TODO: translate the following. -// 'work.error.work_not_available' => 'Work item is not available.', -// 'work.error.offer_not_available' => 'Offer is not available.', -// 'work.type.one_time' => 'one time', // Work type is "one time job" for well defined work ("do exactly this"). -// 'work.type.ongoing' => 'ongoing', // Work type is "ongoing" for complex jobs (billed by the hour, multiple contractors, etc.) -// 'work.label.own_work' => 'Own work', -// 'work.label.own_offers' => 'Own offers', -// 'work.label.offers' => 'Offers', -// 'work.button.send_message' => 'Send message', -// 'work.button.make_offer' => 'Make offer', -// 'work.button.accept' => 'Accept', -// 'work.button.decline' => 'Decline', -// 'work.title.send_message' => 'Sending Message', -// 'work.msg.message_sent' => 'Message sent.', +// 'form.display_options.custom_translation' => 'Custom translation', ); diff --git a/WEB-INF/resources/gr.lang.php b/WEB-INF/resources/gr.lang.php index 489185a8c..34f1890eb 100644 --- a/WEB-INF/resources/gr.lang.php +++ b/WEB-INF/resources/gr.lang.php @@ -2,7 +2,8 @@ /* Copyright (c) Anuko International Ltd. https://www.anuko.com License: See license.txt */ -// Note: escape apostrophes with THREE backslashes, like here: choisir l\\\'option. +// Note: escape apostrophes with THREE backslashes, like here: 'choisir l\\\'option'. +// Alternatively: use one backslash and surround by double-quotes: "choisir l\'option". // Other characters (such as double-quotes in http links, etc.) do not have to be escaped. $i18n_language = 'Greek (Ελληνικά)'; @@ -63,6 +64,9 @@ 'error.report' => 'Επιλογή αναφοράς.', 'error.record' => 'Επιλογή εγγραφής.', 'error.auth' => 'Λανθασμένο όνομα εισόδου ή κωδικός.', +// TODO: translate the following. +// 'error.2fa_code' => 'Invalid 2FA code.', +// 'error.weak_password' => 'Weak password.', 'error.user_exists' => 'Ο χρήστης με αυτήν τη σύνδεση υπάρχει ήδη.', 'error.object_exists' => 'Το αντικείμενο με αυτό το όνομα υπάρχει ήδη.', 'error.invoice_exists' => 'Το τιμολόγιο με αυτόν τον αριθμό υπάρχει ήδη.', @@ -89,7 +93,6 @@ // 'error.user_count' => 'Limit on user count.', // 'error.expired' => 'Expiration date reached.', // 'error.file_storage' => 'File storage server error.', // See comment in English file. -// 'error.remote_work' => 'Remote work server error.', // See comment in English file. // Warning messages. // TODO: translate the following. @@ -201,9 +204,6 @@ 'label.ldap_hint' => 'Εισάγετε το όνομα σύνδεσης των Windows και κωδικό πρόσβασης στα παρακάτω πεδία.', 'label.required_fields' => '* - υποχρεωτικά πεδία', 'label.on_behalf' => 'εκ μέρους του', -'label.role_manager' => '(Διευθυντής)', -'label.role_comanager' => '(Υποδιευθυντής)', -'label.role_admin' => '(Διαχειριστής)', 'label.page' => 'Σελίδα', 'label.condition' => 'Κατάσταση', 'label.yes' => 'ναι', @@ -230,6 +230,7 @@ 'label.week_note' => 'Σημείωση εβδομάδας', 'label.week_list' => 'Λίστα εβδομάδων', // TODO: translate the following. +// 'label.weekends' => 'Weekends', // 'label.work_units' => 'Work units', // 'label.work_units_short' => 'Units', 'label.totals_only' => 'Σύνολα μόνο', @@ -248,17 +249,6 @@ // 'label.file' => 'File', 'label.active_users' => 'Ενεργοί χρήστες', 'label.inactive_users' => 'Ανενεργοί χρήστες', -// TODO: translate the following. -// 'label.details' => 'Details', -// 'label.budget' => 'Budget', -// 'label.work' => 'Work', // Table column header for work items, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// 'label.offer' => 'Offer', // Table column header for offers, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// 'label.contractor' => 'Contractor', // Table column header for offers (contractor is someone who offers to do work). - // Technically, it is either an org name or a combination of org and group names - // because both work items and offers are owned by Time Tracker groups of users. -// 'label.how_to_pay' => 'How to pay', // Label for the "How to pay" field on offers, which allows contractors to specify - // how to pay them, for example: paypal email, check by mail to a specific address, etc. -// 'label.moderator_comment' => 'Moderator comment', // Label for "Moderator comment" field that explains something. // Entity names. We use lower case (in English) because they are used in dropdowns, too. // They are used to associate a custom field with an entity type. @@ -272,6 +262,8 @@ // TODO: Translate the following. // 'title.success' => 'Success', 'title.login' => 'Σύνδεση', +// TODO: translate the follolwing. +// 'title.2fa' => 'Two Factor Authentication', 'title.groups' => 'Ομάδες', // TODO: translate the following. // 'title.add_group' => 'Adding Group', @@ -366,22 +358,6 @@ // 'title.edit_file' => 'Editing File', // 'title.delete_file' => 'Deleting File', // 'title.download_file' => 'Downloading File', -// 'title.work' => 'Work', -// 'title.add_work' => 'Adding Work', -// 'title.edit_work' => 'Editing Work', -// 'title.delete_work' => 'Deleting Work', -// 'title.active_work' => 'Active Work', // Active work items this group outsources to other groups. -// 'title.available_work' => 'Available Work', // Available work items from other organizations. -// 'title.inactive_work' => 'Inactive Work', // Inactive work items this group was outsourcing to other groups. -// 'title.pending_work' => 'Pending Work', // Work items pending moderator approval. -// 'title.offer' => 'Offer', -// 'title.add_offer' => 'Adding Offer', -// 'title.edit_offer' => 'Editing Offer', -// 'title.delete_offer' => 'Deleting Offer', -// 'title.active_offers' => 'Active Offers', // Active offers this group makes available to other groups. -// 'title.available_offers' => 'Available Offers', // Available offers from other organizations. -// 'title.inactive_offers' => 'Inactive Offers', // Inactive offers for group. -// 'title.pending_offers' => 'Pending Offers', // Offers pending moderator approval. // Section for common strings inside combo boxes on forms. Strings shared between forms shall be placed here. // Strings that are used in a single form must go to the specific form section. @@ -412,7 +388,6 @@ 'dropdown.delete' => 'διαγραφή', 'dropdown.do_not_delete' => 'μη το διαγράψετε', // TODO: translate the following. -// 'dropdown.pending_approval' => 'pending approval', // 'dropdown.approved' => 'approved', // 'dropdown.not_approved' => 'not approved', 'dropdown.paid' => 'εξοφλημένο', @@ -430,6 +405,16 @@ 'form.login.forgot_password' => 'Ξεχάσατε τον κωδικό πρόσβασης;', 'form.login.about' => 'Anuko Time Tracker είναι ένα ανοικτού κώδικα σύστημα παρακολούθησης χρόνου.', +// Email subject and body for two-factor authentication. +// TODO: translate the following. +// 'email.2fa_code.subject' => 'Anuko Time Tracker two-factor authentication code', +// 'email.2fa_code.body' => "Dear User,\n\nYour two-factor authentication code is:\n\n%s\n\n", + +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// 'form.2fa.hint' => 'Check your email for 2FA code and enter it here.', +// 'form.2fa.2fa_code' => '2FA code', + // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. 'form.reset_password.message' => 'Το αίτημα επαναφοράς κωδικού πρόσβασης αποστέλλεται μέσω ηλεκτρονικού ταχυδρομείου.', 'form.reset_password.email_subject' => 'Αίτημα επαναφοράς κωδικού Anuko Time Tracker', @@ -467,10 +452,13 @@ // 'form.reports.include_pending' => 'pending', 'form.reports.select_period' => 'Επιλογή χρονικής περιόδου', 'form.reports.set_period' => 'ή εύρος ημερομηνιών', +// TODO: translate the following. +// 'form.reports.note_containing' => 'Note containing', 'form.reports.show_fields' => 'Εμφάνιση πεδίων', // TODO: translate the following. // 'form.reports.time_fields' => 'Time fields', // 'form.reports.user_fields' => 'User fields', +// 'form.reports.project_fields' => 'Project fields', 'form.reports.group_by' => 'Ομαδοποίηση με βάση', 'form.reports.group_by_no' => '--- χωρίς ομαδοποίηση ---', 'form.reports.group_by_date' => 'ημερομηνία', @@ -482,6 +470,8 @@ // Report form. See example at https://timetracker.anuko.com/report.php // (after generating a report at https://timetracker.anuko.com/reports.php). 'form.report.export' => 'Εξαγωγή', +// TODO: translate the following. +// 'form.report.per_hour' => 'Per hour', 'form.report.assign_to_invoice' => 'Ανάθεση στο τιμολόγιο', // TODO: translate the following. // 'form.report.assign_to_timesheet' => 'Assign to timesheet', @@ -519,6 +509,8 @@ 'form.tasks.inactive_tasks' => 'Ανενεργές εργασίες', // Users form. See example at https://timetracker.anuko.com/users.php +// TODO: translate the following. +// 'form.users.uncompleted_entry_today' => 'User has an uncompleted time entry today', 'form.users.uncompleted_entry' => 'Ο χρήστης έχει μια μη ολοκληρωμένη εισαγωγή χρόνου', 'form.users.role' => 'Ρόλος', 'form.users.manager' => 'Διευθυντής', @@ -575,14 +567,21 @@ 'form.group_edit.type_start_finish' => 'αρχή και τέλος', 'form.group_edit.type_duration' => 'διάρκεια', 'form.group_edit.punch_mode' => 'Λειτουργία διάτρησης', +// TODO: translate the following. +// 'form.group_edit.one_uncompleted' => 'One uncompleted', 'form.group_edit.allow_overlap' => 'Επικάλυψη επιτρεπτή', 'form.group_edit.future_entries' => 'Μελλοντικές καταχωρήσεις', 'form.group_edit.uncompleted_indicators' => 'Μη ολοκληρωμένες ενδείξεις', // TODO: translate the following. // 'form.group_edit.confirm_save' => 'Confirm saving', -// 'form.group_edit.allow_ip' => 'Allow IP', // 'form.group_edit.advanced_settings' => 'Advanced settings', +// Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. +// TODO: Translate the following. +// 'form.group_advanced_edit.allow_ip' => 'Allow IP', +// 'form.group_advanced_edit.password_complexity' => 'Password complexity', +// 'form.group_advanced_edit.2fa' => 'Two factor authentication', + // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php // TODO: translate the following. // 'form.group_delete.hint' => 'Are you sure you want to delete the entire group?', @@ -649,21 +648,7 @@ // 'form.display_options.note_on_separate_row' => 'Note on separate row', // 'form.display_options.not_complete_days' => 'Not complete days', // 'form.display_options.inactive_projects' => 'Inactive projects', +// 'form.display_options.cost_per_hour' => 'Cost per hour', // 'form.display_options.custom_css' => 'Custom CSS', - -// Work plugin strings. See example at https://timetracker.anuko.com/work.php -// TODO: translate the following. -// 'work.error.work_not_available' => 'Work item is not available.', -// 'work.error.offer_not_available' => 'Offer is not available.', -// 'work.type.one_time' => 'one time', // Work type is "one time job" for well defined work ("do exactly this"). -// 'work.type.ongoing' => 'ongoing', // Work type is "ongoing" for complex jobs (billed by the hour, multiple contractors, etc.) -// 'work.label.own_work' => 'Own work', -// 'work.label.own_offers' => 'Own offers', -// 'work.label.offers' => 'Offers', -// 'work.button.send_message' => 'Send message', -// 'work.button.make_offer' => 'Make offer', -// 'work.button.accept' => 'Accept', -// 'work.button.decline' => 'Decline', -// 'work.title.send_message' => 'Sending Message', -// 'work.msg.message_sent' => 'Message sent.', +// 'form.display_options.custom_translation' => 'Custom translation', ); diff --git a/WEB-INF/resources/he.lang.php b/WEB-INF/resources/he.lang.php index 918b5f7dc..585a7ec2f 100644 --- a/WEB-INF/resources/he.lang.php +++ b/WEB-INF/resources/he.lang.php @@ -2,7 +2,8 @@ /* Copyright (c) Anuko International Ltd. https://www.anuko.com License: See license.txt */ -// Note: escape apostrophes with THREE backslashes, like here: choisir l\\\'option. +// Note: escape apostrophes with THREE backslashes, like here: 'choisir l\\\'option'. +// Alternatively: use one backslash and surround by double-quotes: "choisir l\'option". // Other characters (such as double-quotes in http links, etc.) do not have to be escaped. $i18n_language = 'Hebrew (עברית)'; @@ -81,6 +82,9 @@ // 'error.report' => 'Select report.' // 'error.record' => 'Select record.', 'error.auth' => 'שם משתמש או סיסמה שגויים', +// TODO: translate the following. +// 'error.2fa_code' => 'Invalid 2FA code.', +// 'error.weak_password' => 'Weak password.', 'error.user_exists' => 'שם משתמש כבר קיים', // TODO: translate the following. // 'error.object_exists' => 'Object with this name already exists.', @@ -110,7 +114,6 @@ // 'error.user_count' => 'Limit on user count.', // 'error.expired' => 'Expiration date reached.', // 'error.file_storage' => 'File storage server error.', // See comment in English file. -// 'error.remote_work' => 'Remote work server error.', // See comment in English file. // Warning messages. // TODO: translate the following. @@ -227,9 +230,6 @@ 'label.ldap_hint' => 'הכנס את שם המשתמש ואת הסיסמה של ווינדוז בשדות.', 'label.required_fields' => '* - שדות חובה', 'label.on_behalf' => 'מטעם', -'label.role_manager' => '(מנהל)', -'label.role_comanager' => '(מנהל משנה)', -'label.role_admin' => '(מנהל המערכת)', // Translate the following. // 'label.page' => 'Page', // 'label.condition' => 'Condition', @@ -256,6 +256,7 @@ // 'label.mark_paid' => 'Mark paid', // 'label.week_note' => 'Week note', // 'label.week_list' => 'Week list', +// 'label.weekends' => 'Weekends', // 'label.work_units' => 'Work units', // 'label.work_units_short' => 'Units', 'labl.totals_only' => 'סיכומים בלבד', @@ -274,17 +275,6 @@ // 'label.file' => 'File', 'label.active_users' => 'משתמשים פעילים', 'label.inactive_users' => 'משתמשים לא פעילים', -// TODO: translate the following. -// 'label.details' => 'Details', -// 'label.budget' => 'Budget', -// 'label.work' => 'Work', // Table column header for work items, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// 'label.offer' => 'Offer', // Table column header for offers, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// 'label.contractor' => 'Contractor', // Table column header for offers (contractor is someone who offers to do work). - // Technically, it is either an org name or a combination of org and group names - // because both work items and offers are owned by Time Tracker groups of users. -// 'label.how_to_pay' => 'How to pay', // Label for the "How to pay" field on offers, which allows contractors to specify - // how to pay them, for example: paypal email, check by mail to a specific address, etc. -// 'label.moderator_comment' => 'Moderator comment', // Label for "Moderator comment" field that explains something. // Entity names. We use lower case (in English) because they are used in dropdowns, too. // They are used to associate a custom field with an entity type. @@ -298,6 +288,8 @@ // TODO: Translate the following. // 'title.success' => 'Success', 'title.login' => 'כניסה', +// TODO: translate the follolwing. +// 'title.2fa' => 'Two Factor Authentication', 'title.groups' => 'צוותים', // TODO: change "teams" to "groups". // TODO: translate the following. // 'title.add_group' => 'Adding Group', @@ -390,22 +382,6 @@ // 'title.edit_file' => 'Editing File', // 'title.delete_file' => 'Deleting File', // 'title.download_file' => 'Downloading File', -// 'title.work' => 'Work', -// 'title.add_work' => 'Adding Work', -// 'title.edit_work' => 'Editing Work', -// 'title.delete_work' => 'Deleting Work', -// 'title.active_work' => 'Active Work', // Active work items this group outsources to other groups. -// 'title.available_work' => 'Available Work', // Available work items from other organizations. -// 'title.inactive_work' => 'Inactive Work', // Inactive work items this group was outsourcing to other groups. -// 'title.pending_work' => 'Pending Work', // Work items pending moderator approval. -// 'title.offer' => 'Offer', -// 'title.add_offer' => 'Adding Offer', -// 'title.edit_offer' => 'Editing Offer', -// 'title.delete_offer' => 'Deleting Offer', -// 'title.active_offers' => 'Active Offers', // Active offers this group makes available to other groups. -// 'title.available_offers' => 'Available Offers', // Available offers from other organizations. -// 'title.inactive_offers' => 'Inactive Offers', // Inactive offers for group. -// 'title.pending_offers' => 'Pending Offers', // Offers pending moderator approval. // Section for common strings inside combo boxes on forms. Strings shared between forms shall be placed here. // Strings that are used in a single form must go to the specific form section. @@ -439,7 +415,6 @@ // TODO: translate the following. // 'dropdown.delete' => 'delete', // 'dropdown.do_not_delete' => 'do not delete', -// 'dropdown.pending_approval' => 'pending approval', // 'dropdown.approved' => 'approved', // 'dropdown.not_approved' => 'not approved', // 'dropdown.paid' => 'paid', @@ -458,6 +433,16 @@ // 'form.login.about' => 'Anuko Time Tracker is an open source time tracking system.', 'form.login.about' => 'Anuko Time Tracker הינה מערכת פשוטה, קלה לשימוש וחינמית לניהול זמן.', +// Email subject and body for two-factor authentication. +// TODO: translate the following. +// 'email.2fa_code.subject' => 'Anuko Time Tracker two-factor authentication code', +// 'email.2fa_code.body' => "Dear User,\n\nYour two-factor authentication code is:\n\n%s\n\n", + +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// 'form.2fa.hint' => 'Check your email for 2FA code and enter it here.', +// 'form.2fa.2fa_code' => '2FA code', + // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. 'form.reset_password.message' => 'הבקשה לאיפוס בסיסמה נשלחה בדואר אלקטרוני.', 'form.reset_password.email_subject' => 'בקשה לאיפוס סיסמה למערכת Anuko Time Tracker', @@ -499,10 +484,13 @@ // 'form.reports.include_pending' => 'pending', 'form.reports.select_period' => 'בחר תקופת זמן', 'form.reports.set_period' => 'או הגדר תאריכים', +// TODO: translate the following. +// 'form.reports.note_containing' => 'Note containing', 'form.reports.show_fields' => 'הראה שדות', // TODO: translate the following. // 'form.reports.time_fields' => 'Time fields', // 'form.reports.user_fields' => 'User fields', +// 'form.reports.project_fields' => 'Project fields', 'form.reports.group_by' => 'סדר לפי', 'form.reports.group_by_no' => '--- ללא סדר ---', 'form.reports.group_by_date' => 'תאריך', @@ -516,6 +504,7 @@ // TODO: form.report.export is just "Export" now in the English file. Shorten this translation. 'form.report.export' => 'ייצא נתונים בתבנית', // TODO: translate the following. +// 'form.report.per_hour' => 'Per hour', // 'form.report.assign_to_invoice' => 'Assign to invoice', // 'form.report.assign_to_timesheet' => 'Assign to timesheet', @@ -554,6 +543,7 @@ // Users form. See example at https://timetracker.anuko.com/users.php // TODO: translate the following. +// 'form.users.uncompleted_entry_today' => 'User has an uncompleted time entry today', // 'form.users.uncompleted_entry' => 'User has an uncompleted time entry', 'form.users.role' => 'תפקיד', 'form.users.manager' => 'מנהל', @@ -615,13 +605,19 @@ 'form.group_edit.type_duration' => 'משך זמן', // TODO: translate the following. // 'form.group_edit.punch_mode' => 'Punch mode', +// 'form.group_edit.one_uncompleted' => 'One uncompleted', // 'form.group_edit.allow_overlap' => 'Allow overlap', // 'form.group_edit.future_entries' => 'Future entries', // 'form.group_edit.uncompleted_indicators' => 'Uncompleted indicators', // 'form.group_edit.confirm_save' => 'Confirm saving', -// 'form.group_edit.allow_ip' => 'Allow IP', // 'form.group_edit.advanced_settings' => 'Advanced settings', +// Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. +// TODO: Translate the following. +// 'form.group_advanced_edit.allow_ip' => 'Allow IP', +// 'form.group_advanced_edit.password_complexity' => 'Password complexity', +// 'form.group_advanced_edit.2fa' => 'Two factor authentication', + // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php // TODO: translate the following. // 'form.group_delete.hint' => 'Are you sure you want to delete the entire group?', @@ -690,21 +686,7 @@ // 'form.display_options.note_on_separate_row' => 'Note on separate row', // 'form.display_options.not_complete_days' => 'Not complete days', // 'form.display_options.inactive_projects' => 'Inactive projects', +// 'form.display_options.cost_per_hour' => 'Cost per hour', // 'form.display_options.custom_css' => 'Custom CSS', - -// Work plugin strings. See example at https://timetracker.anuko.com/work.php -// TODO: translate the following. -// 'work.error.work_not_available' => 'Work item is not available.', -// 'work.error.offer_not_available' => 'Offer is not available.', -// 'work.type.one_time' => 'one time', // Work type is "one time job" for well defined work ("do exactly this"). -// 'work.type.ongoing' => 'ongoing', // Work type is "ongoing" for complex jobs (billed by the hour, multiple contractors, etc.) -// 'work.label.own_work' => 'Own work', -// 'work.label.own_offers' => 'Own offers', -// 'work.label.offers' => 'Offers', -// 'work.button.send_message' => 'Send message', -// 'work.button.make_offer' => 'Make offer', -// 'work.button.accept' => 'Accept', -// 'work.button.decline' => 'Decline', -// 'work.title.send_message' => 'Sending Message', -// 'work.msg.message_sent' => 'Message sent.', +// 'form.display_options.custom_translation' => 'Custom translation', ); diff --git a/WEB-INF/resources/hu.lang.php b/WEB-INF/resources/hu.lang.php index 407c22cd4..e2d7500d3 100644 --- a/WEB-INF/resources/hu.lang.php +++ b/WEB-INF/resources/hu.lang.php @@ -2,7 +2,8 @@ /* Copyright (c) Anuko International Ltd. https://www.anuko.com License: See license.txt */ -// Note: escape apostrophes with THREE backslashes, like here: choisir l\\\'option. +// Note: escape apostrophes with THREE backslashes, like here: 'choisir l\\\'option'. +// Alternatively: use one backslash and surround by double-quotes: "choisir l\'option". // Other characters (such as double-quotes in http links, etc.) do not have to be escaped. // Note to translators: Use proper capitalization rules for your language. @@ -74,6 +75,8 @@ // 'error.report' => 'Select report.', // 'error.record' => 'Select record.', // 'error.auth' => 'Incorrect login or password.', +// 'error.2fa_code' => 'Invalid 2FA code.', +// 'error.weak_password' => 'Weak password.', // 'error.user_exists' => 'User with this login already exists.', // 'error.object_exists' => 'Object with this name already exists.', // 'error.invoice_exists' => 'Invoice with this number already exists.', @@ -98,7 +101,6 @@ // 'error.user_count' => 'Limit on user count.', // 'error.expired' => 'Expiration date reached.', // 'error.file_storage' => 'File storage server error.', // See comment in English file. -// 'error.remote_work' => 'Remote work server error.', // See comment in English file. // Warning messages. // TODO: translate the following. @@ -222,9 +224,6 @@ // 'label.ldap_hint' => 'Type your Windows login and password in the fields below.', 'label.required_fields' => '* kötelezően kitöltendő mezők', 'label.on_behalf' => 'helyett', -'label.role_manager' => '(vezető)', -'label.role_comanager' => '(helyettes)', -'label.role_admin' => '(adminisztrátor)', // TODO: translate the following. // 'label.page' => 'Page', // 'label.condition' => 'Condition', @@ -251,6 +250,7 @@ // 'label.mark_paid' => 'Mark paid', // 'label.week_note' => 'Week note', // 'label.week_list' => 'Week list', +// 'label.weekends' => 'Weekends', // 'label.work_units' => 'Work units', // 'label.work_units_short' => 'Units', // 'label.totals_only' => 'Totals only', @@ -268,16 +268,6 @@ // 'label.file' => 'File', // 'label.active_users' => 'Active Users', // 'label.inactive_users' => 'Inactive Users', -// 'label.details' => 'Details', -// 'label.budget' => 'Budget', -// 'label.work' => 'Work', // Table column header for work items, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// 'label.offer' => 'Offer', // Table column header for offers, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// 'label.contractor' => 'Contractor', // Table column header for offers (contractor is someone who offers to do work). - // Technically, it is either an org name or a combination of org and group names - // because both work items and offers are owned by Time Tracker groups of users. -// 'label.how_to_pay' => 'How to pay', // Label for the "How to pay" field on offers, which allows contractors to specify - // how to pay them, for example: paypal email, check by mail to a specific address, etc. -// 'label.moderator_comment' => 'Moderator comment', // Label for "Moderator comment" field that explains something. // Entity names. We use lower case (in English) because they are used in dropdowns, too. // They are used to associate a custom field with an entity type. @@ -291,6 +281,8 @@ // 'title.error' => 'Error', // 'title.success' => 'Success', 'title.login' => 'Bejelentkezés', +// TODO: translate the follolwing. +// 'title.2fa' => 'Two Factor Authentication', 'title.groups' => 'Csoportok', // TODO: translate the following. // 'title.add_group' => 'Adding Group', @@ -388,22 +380,6 @@ // 'title.edit_file' => 'Editing File', // 'title.delete_file' => 'Deleting File', // 'title.download_file' => 'Downloading File', -// 'title.work' => 'Work', -// 'title.add_work' => 'Adding Work', -// 'title.edit_work' => 'Editing Work', -// 'title.delete_work' => 'Deleting Work', -// 'title.active_work' => 'Active Work', // Active work items this group outsources to other groups. -// 'title.available_work' => 'Available Work', // Available work items from other organizations. -// 'title.inactive_work' => 'Inactive Work', // Inactive work items this group was outsourcing to other groups. -// 'title.pending_work' => 'Pending Work', // Work items pending moderator approval. -// 'title.offer' => 'Offer', -// 'title.add_offer' => 'Adding Offer', -// 'title.edit_offer' => 'Editing Offer', -// 'title.delete_offer' => 'Deleting Offer', -// 'title.active_offers' => 'Active Offers', // Active offers this group makes available to other groups. -// 'title.available_offers' => 'Available Offers', // Available offers from other organizations. -// 'title.inactive_offers' => 'Inactive Offers', // Inactive offers for group. -// 'title.pending_offers' => 'Pending Offers', // Offers pending moderator approval. // Section for common strings inside combo boxes on forms. Strings shared between forms shall be placed here. // Strings that are used in a single form must go to the specific form section. @@ -438,7 +414,6 @@ // 'dropdown.status_inactive' => 'inactive', // 'dropdown.delete' => 'delete', // 'dropdown.do_not_delete' => 'do not delete', -// 'dropdown.pending_approval' => 'pending approval', // 'dropdown.approved' => 'approved', // 'dropdown.not_approved' => 'not approved', // 'dropdown.paid' => 'paid', @@ -451,6 +426,16 @@ // TODO: translate the following. // 'form.login.about' => 'Anuko Time Tracker is an open source time tracking system.', +// Email subject and body for two-factor authentication. +// TODO: translate the following. +// 'email.2fa_code.subject' => 'Anuko Time Tracker two-factor authentication code', +// 'email.2fa_code.body' => "Dear User,\n\nYour two-factor authentication code is:\n\n%s\n\n", + +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// 'form.2fa.hint' => 'Check your email for 2FA code and enter it here.', +// 'form.2fa.2fa_code' => '2FA code', + // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. // TODO: translate the following. // 'form.reset_password.message' => 'Password reset request sent by email.', @@ -494,9 +479,11 @@ 'form.reports.select_period' => 'Jelölj meg egy időszakot', 'form.reports.set_period' => 'vagy állíts be konkrét dátumot', // TODO: translate the following. +// 'form.reports.note_containing' => 'Note containing', // 'form.reports.show_fields' => 'Show fields', // 'form.reports.time_fields' => 'Time fields', // 'form.reports.user_fields' => 'User fields', +// 'form.reports.project_fields' => 'Project fields', 'form.reports.group_by' => 'Csoportosítva', 'form.reports.group_by_no' => '--- csoportosítás nélkül ---', 'form.reports.group_by_date' => 'dátum', @@ -511,6 +498,7 @@ // (after generating a report at https://timetracker.anuko.com/reports.php). 'form.report.export' => 'Exportálása', // TODO: is this correct? // TODO: translate the following. +// 'form.report.per_hour' => 'Per hour', // 'form.report.assign_to_invoice' => 'Assign to invoice', // 'form.report.assign_to_timesheet' => 'Assign to timesheet', @@ -551,6 +539,7 @@ // Users form. See example at https://timetracker.anuko.com/users.php // TODO: translate the following. +// 'form.users.uncompleted_entry_today' => 'User has an uncompleted time entry today', // 'form.users.uncompleted_entry' => 'User has an uncompleted time entry', 'form.users.role' => 'Szerepkör', 'form.users.manager' => 'Vezető', @@ -614,13 +603,19 @@ // 'form.group_edit.type_start_finish' => 'start and finish', // 'form.group_edit.type_duration' => 'duration', // 'form.group_edit.punch_mode' => 'Punch mode', +// 'form.group_edit.one_uncompleted' => 'One uncompleted', // 'form.group_edit.allow_overlap' => 'Allow overlap', // 'form.group_edit.future_entries' => 'Future entries', // 'form.group_edit.uncompleted_indicators' => 'Uncompleted indicators', // 'form.group_edit.confirm_save' => 'Confirm saving', -// 'form.group_edit.allow_ip' => 'Allow IP', // 'form.group_edit.advanced_settings' => 'Advanced settings', +// Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. +// TODO: Translate the following. +// 'form.group_advanced_edit.allow_ip' => 'Allow IP', +// 'form.group_advanced_edit.password_complexity' => 'Password complexity', +// 'form.group_advanced_edit.2fa' => 'Two factor authentication', + // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php // TODO: translate the following. // 'form.group_delete.hint' => 'Are you sure you want to delete the entire group?', @@ -688,21 +683,7 @@ // 'form.display_options.note_on_separate_row' => 'Note on separate row', // 'form.display_options.not_complete_days' => 'Not complete days', // 'form.display_options.inactive_projects' => 'Inactive projects', +// 'form.display_options.cost_per_hour' => 'Cost per hour', // 'form.display_options.custom_css' => 'Custom CSS', - -// Work plugin strings. See example at https://timetracker.anuko.com/work.php -// TODO: translate the following. -// 'work.error.work_not_available' => 'Work item is not available.', -// 'work.error.offer_not_available' => 'Offer is not available.', -// 'work.type.one_time' => 'one time', // Work type is "one time job" for well defined work ("do exactly this"). -// 'work.type.ongoing' => 'ongoing', // Work type is "ongoing" for complex jobs (billed by the hour, multiple contractors, etc.) -// 'work.label.own_work' => 'Own work', -// 'work.label.own_offers' => 'Own offers', -// 'work.label.offers' => 'Offers', -// 'work.button.send_message' => 'Send message', -// 'work.button.make_offer' => 'Make offer', -// 'work.button.accept' => 'Accept', -// 'work.button.decline' => 'Decline', -// 'work.title.send_message' => 'Sending Message', -// 'work.msg.message_sent' => 'Message sent.', +// 'form.display_options.custom_translation' => 'Custom translation', ); diff --git a/WEB-INF/resources/it.lang.php b/WEB-INF/resources/it.lang.php index 165afe880..73ca8a022 100644 --- a/WEB-INF/resources/it.lang.php +++ b/WEB-INF/resources/it.lang.php @@ -2,7 +2,8 @@ /* Copyright (c) Anuko International Ltd. https://www.anuko.com License: See license.txt */ -// Note: escape apostrophes with THREE backslashes, like here: choisir l\\\'option. +// Note: escape apostrophes with THREE backslashes, like here: 'choisir l\\\'option'. +// Alternatively: use one backslash and surround by double-quotes: "choisir l\'option". // Other characters (such as double-quotes in http links, etc.) do not have to be escaped. // Note to translators: Use proper capitalization rules for your language. @@ -65,6 +66,9 @@ 'error.report' => 'Seleziona rapporto.', 'error.record' => 'Seleziona record.', 'error.auth' => 'Login o password errati.', +// TODO: translate the following. +// 'error.2fa_code' => 'Invalid 2FA code.', +// 'error.weak_password' => 'Weak password.', 'error.user_exists' => 'Esiste già un utente con questo username.', // TODO: translate the following. // 'error.object_exists' => 'Object with this name already exists.', @@ -93,7 +97,6 @@ // 'error.user_count' => 'Limit on user count.', // 'error.expired' => 'Expiration date reached.', // 'error.file_storage' => 'File storage server error.', // See comment in English file. -// 'error.remote_work' => 'Remote work server error.', // See comment in English file. // Warning messages. // TODO: translate the following. @@ -205,9 +208,6 @@ 'label.ldap_hint' => 'Digita il tuo Login Windows e la tua password nei campi qui sotto.', 'label.required_fields' => '* campi obbligatori', 'label.on_behalf' => 'a favore di', -'label.role_manager' => '(manager)', -'label.role_comanager' => '(co-manager)', -'label.role_admin' => '(amministratore)', 'label.page' => 'Pagina', 'label.condition' => 'Condizione', 'label.yes' => 'si', @@ -234,6 +234,7 @@ 'label.week_note' => 'Nota settimanale', 'label.week_list' => 'Lista settimanale', // TODO: translate the following. +// 'label.weekends' => 'Weekends', // 'label.work_units' => 'Work units', // 'label.work_units_short' => 'Units', 'label.totals_only' => 'Solo i totali', @@ -252,17 +253,6 @@ // 'label.file' => 'File', 'label.active_users' => 'Utenti attivi', 'label.inactive_users' => 'Utenti inattivi', -// TODO: translate the following. -// 'label.details' => 'Details', -// 'label.budget' => 'Budget', -// 'label.work' => 'Work', // Table column header for work items, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// 'label.offer' => 'Offer', // Table column header for offers, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// 'label.contractor' => 'Contractor', // Table column header for offers (contractor is someone who offers to do work). - // Technically, it is either an org name or a combination of org and group names - // because both work items and offers are owned by Time Tracker groups of users. -// 'label.how_to_pay' => 'How to pay', // Label for the "How to pay" field on offers, which allows contractors to specify - // how to pay them, for example: paypal email, check by mail to a specific address, etc. -// 'label.moderator_comment' => 'Moderator comment', // Label for "Moderator comment" field that explains something. // Entity names. We use lower case (in English) because they are used in dropdowns, too. // They are used to associate a custom field with an entity type. @@ -280,6 +270,8 @@ // TODO: Translate the following. // 'title.success' => 'Success', 'title.login' => 'Login', +// TODO: translate the follolwing. +// 'title.2fa' => 'Two Factor Authentication', 'title.groups' => 'Gruppi', // TODO: translate the following. // 'title.add_group' => 'Adding Group', @@ -376,22 +368,6 @@ // 'title.edit_file' => 'Editing File', // 'title.delete_file' => 'Deleting File', // 'title.download_file' => 'Downloading File', -// 'title.work' => 'Work', -// 'title.add_work' => 'Adding Work', -// 'title.edit_work' => 'Editing Work', -// 'title.delete_work' => 'Deleting Work', -// 'title.active_work' => 'Active Work', // Active work items this group outsources to other groups. -// 'title.available_work' => 'Available Work', // Available work items from other organizations. -// 'title.inactive_work' => 'Inactive Work', // Inactive work items this group was outsourcing to other groups. -// 'title.pending_work' => 'Pending Work', // Work items pending moderator approval. -// 'title.offer' => 'Offer', -// 'title.add_offer' => 'Adding Offer', -// 'title.edit_offer' => 'Editing Offer', -// 'title.delete_offer' => 'Deleting Offer', -// 'title.active_offers' => 'Active Offers', // Active offers this group makes available to other groups. -// 'title.available_offers' => 'Available Offers', // Available offers from other organizations. -// 'title.inactive_offers' => 'Inactive Offers', // Inactive offers for group. -// 'title.pending_offers' => 'Pending Offers', // Offers pending moderator approval. // Section for common strings inside combo boxes on forms. Strings shared between forms shall be placed here. // Strings that are used in a single form must go to the specific form section. @@ -422,7 +398,6 @@ 'dropdown.delete' => 'elimina', 'dropdown.do_not_delete' => 'non eliminare', // TODO: translate the following. -// 'dropdown.pending_approval' => 'pending approval', // 'dropdown.approved' => 'approved', // 'dropdown.not_approved' => 'not approved', 'dropdown.paid' => 'pagato', @@ -440,6 +415,16 @@ 'form.login.forgot_password' => 'Password dimenticata?', 'form.login.about' => 'Anuko Time Tracker è un sistema open source per registrare i tempi di lavoro.', +// Email subject and body for two-factor authentication. +// TODO: translate the following. +// 'email.2fa_code.subject' => 'Anuko Time Tracker two-factor authentication code', +// 'email.2fa_code.body' => "Dear User,\n\nYour two-factor authentication code is:\n\n%s\n\n", + +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// 'form.2fa.hint' => 'Check your email for 2FA code and enter it here.', +// 'form.2fa.2fa_code' => '2FA code', + // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. 'form.reset_password.message' => 'Richiesta di reset password inviata via mail.', 'form.reset_password.email_subject' => 'Richiesta reset password per Anuko Time Tracker', @@ -480,10 +465,13 @@ // 'form.reports.include_pending' => 'pending', 'form.reports.select_period' => 'Seleziona il periodo di tempo', 'form.reports.set_period' => 'oppure setta le date', +// TODO: translate the following. +// 'form.reports.note_containing' => 'Note containing', 'form.reports.show_fields' => 'Mostra i campi', // TODO: translate the following. // 'form.reports.time_fields' => 'Time fields', // 'form.reports.user_fields' => 'User fields', +// 'form.reports.project_fields' => 'Project fields', 'form.reports.group_by' => 'Raggruppa per', 'form.reports.group_by_no' => '--- non raggruppare ---', 'form.reports.group_by_date' => 'data', @@ -495,6 +483,8 @@ // Report form. See example at https://timetracker.anuko.com/report.php // (after generating a report at https://timetracker.anuko.com/reports.php). 'form.report.export' => 'Esporta', +// TODO: translate the following. +// 'form.report.per_hour' => 'Per hour', 'form.report.assign_to_invoice' => 'Assegna alla fattura', // TODO: translate the following. // 'form.report.assign_to_timesheet' => 'Assign to timesheet', @@ -532,6 +522,8 @@ 'form.tasks.inactive_tasks' => 'Compiti inattivi', // Users form. See example at https://timetracker.anuko.com/users.php +// TODO: translate the following. +// 'form.users.uncompleted_entry_today' => 'User has an uncompleted time entry today', 'form.users.uncompleted_entry' => 'Questo utente ha un record temporale incompleto', 'form.users.role' => 'Ruolo', 'form.users.manager' => 'Manager', @@ -590,14 +582,20 @@ 'form.group_edit.type_duration' => 'durata', // TODO: translate the following. // 'form.group_edit.punch_mode' => 'Punch mode', +// 'form.group_edit.one_uncompleted' => 'One uncompleted', // 'form.group_edit.allow_overlap' => 'Allow overlap', // 'form.group_edit.future_entries' => 'Future entries', 'form.group_edit.uncompleted_indicators' => 'Indicatori incompleti', // TODO: translate the following. // 'form.group_edit.confirm_save' => 'Confirm saving', -// 'form.group_edit.allow_ip' => 'Allow IP', // 'form.group_edit.advanced_settings' => 'Advanced settings', +// Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. +// TODO: Translate the following. +// 'form.group_advanced_edit.allow_ip' => 'Allow IP', +// 'form.group_advanced_edit.password_complexity' => 'Password complexity', +// 'form.group_advanced_edit.2fa' => 'Two factor authentication', + // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php // TODO: translate the following. // 'form.group_delete.hint' => 'Are you sure you want to delete the entire group?', @@ -663,21 +661,7 @@ // 'form.display_options.note_on_separate_row' => 'Note on separate row', // 'form.display_options.not_complete_days' => 'Not complete days', // 'form.display_options.inactive_projects' => 'Inactive projects', +// 'form.display_options.cost_per_hour' => 'Cost per hour', // 'form.display_options.custom_css' => 'Custom CSS', - -// Work plugin strings. See example at https://timetracker.anuko.com/work.php -// TODO: translate the following. -// 'work.error.work_not_available' => 'Work item is not available.', -// 'work.error.offer_not_available' => 'Offer is not available.', -// 'work.type.one_time' => 'one time', // Work type is "one time job" for well defined work ("do exactly this"). -// 'work.type.ongoing' => 'ongoing', // Work type is "ongoing" for complex jobs (billed by the hour, multiple contractors, etc.) -// 'work.label.own_work' => 'Own work', -// 'work.label.own_offers' => 'Own offers', -// 'work.label.offers' => 'Offers', -// 'work.button.send_message' => 'Send message', -// 'work.button.make_offer' => 'Make offer', -// 'work.button.accept' => 'Accept', -// 'work.button.decline' => 'Decline', -// 'work.title.send_message' => 'Sending Message', -// 'work.msg.message_sent' => 'Message sent.', +// 'form.display_options.custom_translation' => 'Custom translation', ); diff --git a/WEB-INF/resources/ja.lang.php b/WEB-INF/resources/ja.lang.php index dbac8f190..0de6e88ea 100644 --- a/WEB-INF/resources/ja.lang.php +++ b/WEB-INF/resources/ja.lang.php @@ -2,7 +2,8 @@ /* Copyright (c) Anuko International Ltd. https://www.anuko.com License: See license.txt */ -// Note: escape apostrophes with THREE backslashes, like here: choisir l\\\'option. +// Note: escape apostrophes with THREE backslashes, like here: 'choisir l\\\'option'. +// Alternatively: use one backslash and surround by double-quotes: "choisir l\'option". // Other characters (such as double-quotes in http links, etc.) do not have to be escaped. $i18n_language = 'Japanese (日本語)'; @@ -73,6 +74,9 @@ // 'error.report' => 'Select report.', // 'error.record' => 'Select record.', 'error.auth' => '不正確なログインあるいはパスワードが不正確です。', +// TODO: translate the following. +// 'error.2fa_code' => 'Invalid 2FA code.', +// 'error.weak_password' => 'Weak password.', 'error.user_exists' => 'このログインと関連されたユーザーは既に存在します。', // TODO: translate the following. // 'error.object_exists' => 'Object with this name already exists.', @@ -101,7 +105,6 @@ // 'error.user_count' => 'Limit on user count.', // 'error.expired' => 'Expiration date reached.', // 'error.file_storage' => 'File storage server error.', // See comment in English file. -// 'error.remote_work' => 'Remote work server error.', // See comment in English file. // Warning messages. // TODO: translate the following. @@ -224,11 +227,6 @@ 'label.ldap_hint' => '下記のフィールドにあなたのWindowsのログインIDパスワードを入力してください。', 'label.required_fields' => '* 必須のフィールド', 'label.on_behalf' => 'を代表して', -// TODO: translate all 3 roles properly, see https://www.anuko.com/time_tracker/user_guide/user_accounts.htm -// This may require different terms for role_manager and role_comanager. -'label.role_manager' => '(管理者)', -'label.role_comanager' => '(共同管理者)', -// 'label.role_admin' => '(administrator)', // TODO: translate the following. // 'label.page' => 'Page', // 'label.condition' => 'Condition', @@ -255,6 +253,7 @@ // 'label.mark_paid' => 'Mark paid', // 'label.week_note' => 'Week note', // 'label.week_list' => 'Week list', +// 'label.weekends' => 'Weekends', // 'label.work_units' => 'Work units', // 'label.work_units_short' => 'Units', 'label.totals_only' => '全体だけ', @@ -273,16 +272,6 @@ // 'label.file' => 'File', // 'label.active_users' => 'Active Users', // 'label.inactive_users' => 'Inactive Users', -// 'label.details' => 'Details', -// 'label.budget' => 'Budget', -// 'label.work' => 'Work', // Table column header for work items, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// 'label.offer' => 'Offer', // Table column header for offers, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// 'label.contractor' => 'Contractor', // Table column header for offers (contractor is someone who offers to do work). - // Technically, it is either an org name or a combination of org and group names - // because both work items and offers are owned by Time Tracker groups of users. -// 'label.how_to_pay' => 'How to pay', // Label for the "How to pay" field on offers, which allows contractors to specify - // how to pay them, for example: paypal email, check by mail to a specific address, etc. -// 'label.moderator_comment' => 'Moderator comment', // Label for "Moderator comment" field that explains something. // Entity names. We use lower case (in English) because they are used in dropdowns, too. // They are used to associate a custom field with an entity type. @@ -296,6 +285,8 @@ // 'title.error' => 'Error', // 'title.success' => 'Success', 'title.login' => 'ログイン', +// TODO: translate the follolwing. +// 'title.2fa' => 'Two Factor Authentication', 'title.groups' => 'チーム', // TODO: change "teams" to "groups". // TODO: translate the following. // 'title.add_group' => 'Adding Group', @@ -395,22 +386,6 @@ // 'title.edit_file' => 'Editing File', // 'title.delete_file' => 'Deleting File', // 'title.download_file' => 'Downloading File', -// 'title.work' => 'Work', -// 'title.add_work' => 'Adding Work', -// 'title.edit_work' => 'Editing Work', -// 'title.delete_work' => 'Deleting Work', -// 'title.active_work' => 'Active Work', // Active work items this group outsources to other groups. -// 'title.available_work' => 'Available Work', // Available work items from other organizations. -// 'title.inactive_work' => 'Inactive Work', // Inactive work items this group was outsourcing to other groups. -// 'title.pending_work' => 'Pending Work', // Work items pending moderator approval. -// 'title.offer' => 'Offer', -// 'title.add_offer' => 'Adding Offer', -// 'title.edit_offer' => 'Editing Offer', -// 'title.delete_offer' => 'Deleting Offer', -// 'title.active_offers' => 'Active Offers', // Active offers this group makes available to other groups. -// 'title.available_offers' => 'Available Offers', // Available offers from other organizations. -// 'title.inactive_offers' => 'Inactive Offers', // Inactive offers for group. -// 'title.pending_offers' => 'Pending Offers', // Offers pending moderator approval. // Section for common strings inside combo boxes on forms. Strings shared between forms shall be placed here. // Strings that are used in a single form must go to the specific form section. @@ -440,7 +415,6 @@ // 'dropdown.status_inactive' => 'inactive', // 'dropdown.delete' => 'delete', // 'dropdown.do_not_delete' => 'do not delete', -// 'dropdown.pending_approval' => 'pending approval', // 'dropdown.approved' => 'approved', // 'dropdown.not_approved' => 'not approved', // 'dropdown.paid' => 'paid', @@ -453,6 +427,16 @@ // TODO: translate the following. // 'form.login.about' => 'Anuko Time Tracker is an open source time tracking system.', +// Email subject and body for two-factor authentication. +// TODO: translate the following. +// 'email.2fa_code.subject' => 'Anuko Time Tracker two-factor authentication code', +// 'email.2fa_code.body' => "Dear User,\n\nYour two-factor authentication code is:\n\n%s\n\n", + +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// 'form.2fa.hint' => 'Check your email for 2FA code and enter it here.', +// 'form.2fa.2fa_code' => '2FA code', + // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. 'form.reset_password.message' => '送信したパスワードの初期化の要求。', // TODO: add "by email" to match the English string. 'form.reset_password.email_subject' => 'Anuko Time Trackerのパスワードの初期化の要求', @@ -499,10 +483,13 @@ // 'form.reports.include_pending' => 'pending', 'form.reports.select_period' => '時間期間の選択', 'form.reports.set_period' => 'あるいは日付を設定', +// TODO: translate the following. +// 'form.reports.note_containing' => 'Note containing', 'form.reports.show_fields' => 'フィールドの表示', // TODO: translate the following. // 'form.reports.time_fields' => 'Time fields', // 'form.reports.user_fields' => 'User fields', +// 'form.reports.project_fields' => 'Project fields', 'form.reports.group_by' => '次のようにグループ化', 'form.reports.group_by_no' => '--- グループの機能がありません ---', 'form.reports.group_by_date' => '日付', @@ -517,6 +504,7 @@ // (after generating a report at https://timetracker.anuko.com/reports.php). 'form.report.export' => '輸出する', // TODO: is this correct? // TODO: translate the following. +// 'form.report.per_hour' => 'Per hour', // 'form.report.assign_to_invoice' => 'Assign to invoice', // 'form.report.assign_to_timesheet' => 'Assign to timesheet', @@ -557,6 +545,7 @@ // Users form. See example at https://timetracker.anuko.com/users.php // TODO: translate the following. +// 'form.users.uncompleted_entry_today' => 'User has an uncompleted time entry today', // 'form.users.uncompleted_entry' => 'User has an uncompleted time entry', 'form.users.role' => '役割', // TODO: is this correct? 'form.users.manager' => '管理者', @@ -618,13 +607,19 @@ // 'form.group_edit.type_start_finish' => 'start and finish', // 'form.group_edit.type_duration' => 'duration', // 'form.group_edit.punch_mode' => 'Punch mode', +// 'form.group_edit.one_uncompleted' => 'One uncompleted', // 'form.group_edit.allow_overlap' => 'Allow overlap', // 'form.group_edit.future_entries' => 'Future entries', // 'form.group_edit.uncompleted_indicators' => 'Uncompleted indicators', // 'form.group_edit.confirm_save' => 'Confirm saving', -// 'form.group_edit.allow_ip' => 'Allow IP', // 'form.group_edit.advanced_settings' => 'Advanced settings', +// Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. +// TODO: Translate the following. +// 'form.group_advanced_edit.allow_ip' => 'Allow IP', +// 'form.group_advanced_edit.password_complexity' => 'Password complexity', +// 'form.group_advanced_edit.2fa' => 'Two factor authentication', + // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php // TODO: translate the following. // 'form.group_delete.hint' => 'Are you sure you want to delete the entire group?', @@ -692,21 +687,7 @@ // 'form.display_options.note_on_separate_row' => 'Note on separate row', // 'form.display_options.not_complete_days' => 'Not complete days', // 'form.display_options.inactive_projects' => 'Inactive projects', +// 'form.display_options.cost_per_hour' => 'Cost per hour', // 'form.display_options.custom_css' => 'Custom CSS', - -// Work plugin strings. See example at https://timetracker.anuko.com/work.php -// TODO: translate the following. -// 'work.error.work_not_available' => 'Work item is not available.', -// 'work.error.offer_not_available' => 'Offer is not available.', -// 'work.type.one_time' => 'one time', // Work type is "one time job" for well defined work ("do exactly this"). -// 'work.type.ongoing' => 'ongoing', // Work type is "ongoing" for complex jobs (billed by the hour, multiple contractors, etc.) -// 'work.label.own_work' => 'Own work', -// 'work.label.own_offers' => 'Own offers', -// 'work.label.offers' => 'Offers', -// 'work.button.send_message' => 'Send message', -// 'work.button.make_offer' => 'Make offer', -// 'work.button.accept' => 'Accept', -// 'work.button.decline' => 'Decline', -// 'work.title.send_message' => 'Sending Message', -// 'work.msg.message_sent' => 'Message sent.', +// 'form.display_options.custom_translation' => 'Custom translation', ); diff --git a/WEB-INF/resources/ko.lang.php b/WEB-INF/resources/ko.lang.php index 896f1939f..fcbe14d24 100644 --- a/WEB-INF/resources/ko.lang.php +++ b/WEB-INF/resources/ko.lang.php @@ -2,7 +2,8 @@ /* Copyright (c) Anuko International Ltd. https://www.anuko.com License: See license.txt */ -// Note: escape apostrophes with THREE backslashes, like here: choisir l\\\'option. +// Note: escape apostrophes with THREE backslashes, like here: 'choisir l\\\'option'. +// Alternatively: use one backslash and surround by double-quotes: "choisir l\'option". // Other characters (such as double-quotes in http links, etc.) do not have to be escaped. $i18n_language = 'Korean (한국어)'; @@ -73,6 +74,9 @@ // 'error.report' => 'Select report.', // 'error.record' => 'Select record.', 'error.auth' => '부정확한 로그인 혹은 암호가 틀립니다.', +// TODO: translate the following. +// 'error.2fa_code' => 'Invalid 2FA code.', +// 'error.weak_password' => 'Weak password.', 'error.user_exists' => '본 로그인과 연계된 사용자가 이미 있습니다.', // TODO: translate the following. // 'error.object_exists' => 'Object with this name already exists.', @@ -100,7 +104,6 @@ // 'error.user_count' => 'Limit on user count.', // 'error.expired' => 'Expiration date reached.', // 'error.file_storage' => 'File storage server error.', // See comment in English file. -// 'error.remote_work' => 'Remote work server error.', // See comment in English file. // Warning messages. // TODO: translate the following. @@ -223,11 +226,6 @@ 'label.ldap_hint' => '아래의 필드들에서 Windows 로그인암호 를 입력하십시오.', 'label.required_fields' => '* 필수 필드', 'label.on_behalf' => '을 대표하여', -// TODO: translate all 3 roles properly, see https://www.anuko.com/time_tracker/user_guide/user_accounts.htm -// This may require different terms for role_manager and role_comanager. -'label.role_manager' => '(관리자)', -'label.role_comanager' => '(공동관리자)', -// 'label.role_admin' => '(administrator)', // TODO: translate the following. // 'label.page' => 'Page', // 'label.condition' => 'Condition', @@ -255,6 +253,7 @@ // 'label.mark_paid' => 'Mark paid', // 'label.week_note' => 'Week note', // 'label.week_list' => 'Week list', +// 'label.weekends' => 'Weekends', // 'label.work_units' => 'Work units', // 'label.work_units_short' => 'Units', 'label.totals_only' => '오직 전체만', @@ -273,16 +272,6 @@ // 'label.file' => 'File', // 'label.active_users' => 'Active Users', // 'label.inactive_users' => 'Inactive Users', -// 'label.details' => 'Details', -// 'label.budget' => 'Budget', -// 'label.work' => 'Work', // Table column header for work items, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// 'label.offer' => 'Offer', // Table column header for offers, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// 'label.contractor' => 'Contractor', // Table column header for offers (contractor is someone who offers to do work). - // Technically, it is either an org name or a combination of org and group names - // because both work items and offers are owned by Time Tracker groups of users. -// 'label.how_to_pay' => 'How to pay', // Label for the "How to pay" field on offers, which allows contractors to specify - // how to pay them, for example: paypal email, check by mail to a specific address, etc. -// 'label.moderator_comment' => 'Moderator comment', // Label for "Moderator comment" field that explains something. // Entity names. We use lower case (in English) because they are used in dropdowns, too. // They are used to associate a custom field with an entity type. @@ -296,6 +285,8 @@ // 'title.error' => 'Error', // 'title.success' => 'Success', 'title.login' => '로그인', +// TODO: translate the follolwing. +// 'title.2fa' => 'Two Factor Authentication', 'title.groups' => '팀', // TODO: change "teams" to "groups". // TODO: translate the following. // 'title.add_group' => 'Adding Group', @@ -396,22 +387,6 @@ // 'title.edit_file' => 'Editing File', // 'title.delete_file' => 'Deleting File', // 'title.download_file' => 'Downloading File', -// 'title.work' => 'Work', -// 'title.add_work' => 'Adding Work', -// 'title.edit_work' => 'Editing Work', -// 'title.delete_work' => 'Deleting Work', -// 'title.active_work' => 'Active Work', // Active work items this group outsources to other groups. -// 'title.available_work' => 'Available Work', // Available work items from other organizations. -// 'title.inactive_work' => 'Inactive Work', // Inactive work items this group was outsourcing to other groups. -// 'title.pending_work' => 'Pending Work', // Work items pending moderator approval. -// 'title.offer' => 'Offer', -// 'title.add_offer' => 'Adding Offer', -// 'title.edit_offer' => 'Editing Offer', -// 'title.delete_offer' => 'Deleting Offer', -// 'title.active_offers' => 'Active Offers', // Active offers this group makes available to other groups. -// 'title.available_offers' => 'Available Offers', // Available offers from other organizations. -// 'title.inactive_offers' => 'Inactive Offers', // Inactive offers for group. -// 'title.pending_offers' => 'Pending Offers', // Offers pending moderator approval. // Section for common strings inside combo boxes on forms. Strings shared between forms shall be placed here. // Strings that are used in a single form must go to the specific form section. @@ -444,7 +419,6 @@ // 'dropdown.status_inactive' => 'inactive', // 'dropdown.delete' => 'delete', // 'dropdown.do_not_delete' => 'do not delete', -// 'dropdown.pending_approval' => 'pending approval', // 'dropdown.approved' => 'approved', // 'dropdown.not_approved' => 'not approved', // 'dropdown.paid' => 'paid', @@ -457,6 +431,16 @@ // TODO: translate the following. // 'form.login.about' => 'Anuko Time Tracker is an open source time tracking system.', +// Email subject and body for two-factor authentication. +// TODO: translate the following. +// 'email.2fa_code.subject' => 'Anuko Time Tracker two-factor authentication code', +// 'email.2fa_code.body' => "Dear User,\n\nYour two-factor authentication code is:\n\n%s\n\n", + +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// 'form.2fa.hint' => 'Check your email for 2FA code and enter it here.', +// 'form.2fa.2fa_code' => '2FA code', + // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. 'form.reset_password.message' => '송신한 암호 재설정 요청.', // TODO: add "by email" to match the English string. 'form.reset_password.email_subject' => 'Anuko Time Tracker 암호 재설정 요청', @@ -497,10 +481,13 @@ // 'form.reports.include_pending' => 'pending', 'form.reports.select_period' => '시간 기간을 선택', 'form.reports.set_period' => '혹은 날짜를 설정', +// TODO: translate the following. +// 'form.reports.note_containing' => 'Note containing', 'form.reports.show_fields' => '필드들을 보기', // TODO: translate the following. // 'form.reports.time_fields' => 'Time fields', // 'form.reports.user_fields' => 'User fields', +// 'form.reports.project_fields' => 'Project fields', 'form.reports.group_by' => '다음것에 의한 그룹화', 'form.reports.group_by_no' => '--- 그룹화되지 않음 ---', 'form.reports.group_by_date' => '날짜', @@ -515,6 +502,7 @@ // (after generating a report at https://timetracker.anuko.com/reports.php). 'form.report.export' => '익스포트', // TODO: is this correct? // TODO: translate the following. +// 'form.report.per_hour' => 'Per hour', // 'form.report.assign_to_invoice' => 'Assign to invoice', // 'form.report.assign_to_timesheet' => 'Assign to timesheet', @@ -555,6 +543,7 @@ // Users form. See example at https://timetracker.anuko.com/users.php // TODO: translate the following. +// 'form.users.uncompleted_entry_today' => 'User has an uncompleted time entry today', // 'form.users.uncompleted_entry' => 'User has an uncompleted time entry', 'form.users.role' => '직위', // TODO: is this correct? The term "role" describes user function, as in "team manager role". 'form.users.manager' => '관리자', @@ -616,13 +605,19 @@ // 'form.group_edit.type_start_finish' => 'start and finish', // 'form.group_edit.type_duration' => 'duration', // 'form.group_edit.punch_mode' => 'Punch mode', +// 'form.group_edit.one_uncompleted' => 'One uncompleted', // 'form.group_edit.allow_overlap' => 'Allow overlap', // 'form.group_edit.future_entries' => 'Future entries', // 'form.group_edit.uncompleted_indicators' => 'Uncompleted indicators', // 'form.group_edit.confirm_save' => 'Confirm saving', -// 'form.group_edit.allow_ip' => 'Allow IP', // 'form.group_edit.advanced_settings' => 'Advanced settings', +// Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. +// TODO: Translate the following. +// 'form.group_advanced_edit.allow_ip' => 'Allow IP', +// 'form.group_advanced_edit.password_complexity' => 'Password complexity', +// 'form.group_advanced_edit.2fa' => 'Two factor authentication', + // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php // TODO: translate the following. // 'form.group_delete.hint' => 'Are you sure you want to delete the entire group?', @@ -690,21 +685,7 @@ // 'form.display_options.note_on_separate_row' => 'Note on separate row', // 'form.display_options.not_complete_days' => 'Not complete days', // 'form.display_options.inactive_projects' => 'Inactive projects', +// 'form.display_options.cost_per_hour' => 'Cost per hour', // 'form.display_options.custom_css' => 'Custom CSS', - -// Work plugin strings. See example at https://timetracker.anuko.com/work.php -// TODO: translate the following. -// 'work.error.work_not_available' => 'Work item is not available.', -// 'work.error.offer_not_available' => 'Offer is not available.', -// 'work.type.one_time' => 'one time', // Work type is "one time job" for well defined work ("do exactly this"). -// 'work.type.ongoing' => 'ongoing', // Work type is "ongoing" for complex jobs (billed by the hour, multiple contractors, etc.) -// 'work.label.own_work' => 'Own work', -// 'work.label.own_offers' => 'Own offers', -// 'work.label.offers' => 'Offers', -// 'work.button.send_message' => 'Send message', -// 'work.button.make_offer' => 'Make offer', -// 'work.button.accept' => 'Accept', -// 'work.button.decline' => 'Decline', -// 'work.title.send_message' => 'Sending Message', -// 'work.msg.message_sent' => 'Message sent.', +// 'form.display_options.custom_translation' => 'Custom translation', ); diff --git a/WEB-INF/resources/nl.lang.php b/WEB-INF/resources/nl.lang.php index 921d50af8..2ca444c0a 100644 --- a/WEB-INF/resources/nl.lang.php +++ b/WEB-INF/resources/nl.lang.php @@ -2,7 +2,8 @@ /* Copyright (c) Anuko International Ltd. https://www.anuko.com License: See license.txt */ -// Note: escape apostrophes with THREE backslashes, like here: choisir l\\\'option. +// Note: escape apostrophes with THREE backslashes, like here: 'choisir l\\\'option'. +// Alternatively: use one backslash and surround by double-quotes: "choisir l\'option". // Other characters (such as double-quotes in http links, etc.) do not have to be escaped. $i18n_language = 'Dutch (Nederlands)'; @@ -59,6 +60,8 @@ 'error.report' => 'Kies rapport.', 'error.record' => 'Kies record.', 'error.auth' => 'Onjuiste inlognaam of wachtwoord.', +'error.2fa_code' => 'Niet geldige 2FA code.', +'error.weak_password' => 'Zwak wachtwoord.', 'error.user_exists' => 'Een gebruiker met deze inlognaam bestaat al.', 'error.object_exists' => 'Een object met deze naam bestaat al.', 'error.invoice_exists' => 'Dit nummer is al eens toegekend aan een factuur.', @@ -81,7 +84,6 @@ 'error.user_count' => 'Limiet op aantal gebruikers.', 'error.expired' => 'Verloop datum is bereikt.', 'error.file_storage' => 'Bestand server probleem.', -'error.remote_work' => 'Werk op afstand server probleem.', // Warning messages. 'warn.sure' => 'Ben je er zeker van?', @@ -111,7 +113,7 @@ 'button.stop' => 'Stop', 'button.approve' => 'Goedkeuren', 'button.disapprove' => 'Afkeuren', -// 'button.sync' => 'Sync', // Used in Android app. The meaning is to synchronize offline time records with server. +'button.sync' => 'Sychroniseer', // Labels for controls on forms. Labels in this section are used on multiple forms. 'label.menu' => 'Menu', @@ -185,9 +187,6 @@ 'label.ldap_hint' => 'Type uw Windows login en wachtwoord in de onderstaande velden.', 'label.required_fields' => '* - verplichte velden', 'label.on_behalf' => 'namens', -'label.role_manager' => '(manager)', -'label.role_comanager' => '(co-manager)', -'label.role_admin' => '(beheerder)', 'label.page' => 'Pagina', 'label.condition' => 'Voorwaarde', 'label.yes' => 'ja', @@ -211,6 +210,7 @@ 'label.mark_paid' => 'Markeer als betaald', 'label.week_note' => 'Week aantekening', 'label.week_list' => 'Week overzicht', +'label.weekends' => 'Weekeinden', 'label.work_units' => 'Werk eenheid', 'label.work_units_short' => 'Eenheid', 'label.totals_only' => 'Alleen totalen', @@ -228,13 +228,6 @@ 'label.file' => 'Bestand', 'label.active_users' => 'Actieve medewerkers', 'label.inactive_users' => 'Inactieve medewerkers', -'label.details' => 'Details', -'label.budget' => 'Budget', -'label.work' => 'Werk', -'label.offer' => 'Aanbod', -'label.contractor' => 'Aanbieder', -'label.how_to_pay' => 'Betaalwijze', -'label.moderator_comment' => 'Commentaar', // Entity names. 'entity.time' => 'tijd', @@ -245,6 +238,7 @@ 'title.error' => 'Fout', 'title.success' => 'Succes', 'title.login' => 'Aanmelden', +'title.2fa' => 'Two Factor Authentication', 'title.groups' => 'Groepen', 'title.add_group' => 'Groep toevoegen', 'title.edit_group' => 'Groep bewerken', @@ -331,22 +325,6 @@ 'title.edit_file' => 'Bestand bewerken', 'title.delete_file' => 'Bestand verwijderen', 'title.download_file' => ' Bestand downloaden', -'title.work' => 'Werk', -'title.add_work' => 'Werk toevoegen', -'title.edit_work' => 'Werk bewerken', -'title.delete_work' => 'Werk verwijderen', -'title.active_work' => 'Actief werk', -'title.available_work' => 'Beschikbaar werk', -'title.inactive_work' => 'Inactief werk', -'title.pending_work' => 'Werk in afwachting van goedkeuring', -'title.offer' => 'Aanbod', -'title.add_offer' => 'Aanbieding toevoegen', -'title.edit_offer' => 'Aanbieding bewerken', -'title.delete_offer' => 'Aanbieding verwijderen', -'title.active_offers' => 'Actieve aanbiedingen', -'title.available_offers' => 'Beschikbare aanbiedingen', -'title.inactive_offers' => 'Inactieve aanbiedingen', -'title.pending_offers' => 'Aangeboden werk in afwachting van goedkeuring', // Section for common strings inside combo boxes on forms. Strings shared between forms shall be placed here. // Strings that are used in a single form must go to the specific form section. @@ -375,7 +353,6 @@ 'dropdown.status_inactive' => 'inactief', 'dropdown.delete' => 'verwijderen', 'dropdown.do_not_delete' => 'niet verwijderen', -'dropdown.pending_approval' => 'in afwachting van goedkeuring', 'dropdown.approved' => 'goedgekeurd', 'dropdown.not_approved' => 'afgekeurd', 'dropdown.paid' => 'betaald', @@ -392,6 +369,14 @@ 'form.login.forgot_password' => 'Wachtwoord vergeten?', 'form.login.about' => 'Anuko Time Tracker is een open source tijdregistratiesysteem.', +// Email subject and body for two-factor authentication. +'email.2fa_code.subject' => 'Anuko Time Tracker two-factor authentication code', +'email.2fa_code.body' => "Beste gebruiker,\n\nJouw two-factor authentication code is:\n\n%s\n\n", + +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +'form.2fa.hint' => 'Je kunt je 2FA code vinden in een email die aan je is verstuurd en hier invullen.', +'form.2fa.2fa_code' => '2FA code', + // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. 'form.reset_password.message' => 'Het verzoek om het wachtwoord te herstellen is verzonden per email.', 'form.reset_password.email_subject' => 'Anuko Time Tracker wachtwoord herstel verzoek', @@ -427,9 +412,11 @@ 'form.reports.include_pending' => 'in afwachting', 'form.reports.select_period' => 'Kies periode', 'form.reports.set_period' => 'of stel datums in', +'form.reports.note_containing' => 'Notitie bevat', 'form.reports.show_fields' => 'Toon velden', 'form.reports.time_fields' => 'Tijd velden', 'form.reports.user_fields' => 'Medewerker velden', +'form.reports.project_fields' => 'Project velden', 'form.reports.group_by' => 'Groeperen op', 'form.reports.group_by_no' => '--- niet groeperen ---', 'form.reports.group_by_date' => 'datum', @@ -441,6 +428,7 @@ // Report form. See example at https://timetracker.anuko.com/report.php // (after generating a report at https://timetracker.anuko.com/reports.php). 'form.report.export' => 'Exporteer', +'form.report.per_hour' => 'Per uur', 'form.report.assign_to_invoice' => 'Voeg toe aan factuur', 'form.report.assign_to_timesheet' => 'Wijs toe aan tijdenoverzicht', @@ -475,6 +463,7 @@ 'form.tasks.inactive_tasks' => 'Inactieve taken', // Users form. See example at https://timetracker.anuko.com/users.php +'form.users.uncompleted_entry_today' => 'Gebruiker heeft vandaag een tijd ingevoerd die niet compleet is', 'form.users.uncompleted_entry' => 'Gebruiker heeft tijd ingevoerd die niet compleet is', 'form.users.role' => 'Rol', 'form.users.manager' => 'Manager', @@ -529,13 +518,18 @@ 'form.group_edit.type_start_finish' => 'begin en einde', 'form.group_edit.type_duration' => 'duur', 'form.group_edit.punch_mode' => 'Start/stop modus', +'form.group_edit.one_uncompleted' => 'Één niet-volledige registratie toestaan', 'form.group_edit.allow_overlap' => 'Sta overlapping van tijden toe', 'form.group_edit.future_entries' => 'Toevoegingen toestaan in de toekomst', 'form.group_edit.uncompleted_indicators' => 'Onvolledige indicatoren', 'form.group_edit.confirm_save' => 'Bevestigen dat je wilt opslaan', -'form.group_edit.allow_ip' => 'Toegestane IP adressen', 'form.group_edit.advanced_settings' => 'Geavanceerde instellingen', +// Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. +'form.group_advanced_edit.allow_ip' => 'Toegestane IP adressen', +'form.group_advanced_edit.password_complexity' => 'Wachtwoord complexiteit', +'form.group_advanced_edit.2fa' => 'Two Factor Authentication (2FA)', + // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php 'form.group_delete.hint' => 'Bent u er zeker van dat u de hele groep wilt verwijderen?', @@ -594,22 +588,8 @@ // Display Options form. See example at https://timetracker.anuko.com/display_options.php. 'form.display_options.note_on_separate_row' => 'Notitie in aparte kolom', // Translator (Henk) comment: "kolom is the right word in Dutch." 'form.display_options.not_complete_days' => 'Niet complete dagen', -// TODO: translate the following. -// 'form.display_options.inactive_projects' => 'Inactive projects', +'form.display_options.inactive_projects' => 'Niet actieve projecten', +'form.display_options.cost_per_hour' => 'Kosten per uur', 'form.display_options.custom_css' => 'Aangepaste CSS', - -// Work plugin strings. See example at https://timetracker.anuko.com/work.php -'work.error.work_not_available' => 'Werk onderdeel is niet beschikbaar.', -'work.error.offer_not_available' => 'Aanbod is niet beschikbaar.', -'work.type.one_time' => 'eenmalig', -'work.type.ongoing' => 'doorlopend', -'work.label.own_work' => 'Eigen werk', -'work.label.own_offers' => 'Eigen aanbod', -'work.label.offers' => 'Aanbiedingen', -'work.button.send_message' => 'Verstuur bericht', -'work.button.make_offer' => 'maak aanbieding', -'work.button.accept' => 'Accepteer', -'work.button.decline' => 'Weiger', -'work.title.send_message' => 'Bericht wordt verstuurd', -'work.msg.message_sent' => 'Bericht is verstuurd.', +'form.display_options.custom_translation' => 'Maatwerk vertaling', ); diff --git a/WEB-INF/resources/no.lang.php b/WEB-INF/resources/no.lang.php index 5cb39137d..720f71f34 100644 --- a/WEB-INF/resources/no.lang.php +++ b/WEB-INF/resources/no.lang.php @@ -2,7 +2,8 @@ /* Copyright (c) Anuko International Ltd. https://www.anuko.com License: See license.txt */ -// Note: escape apostrophes with THREE backslashes, like here: choisir l\\\'option. +// Note: escape apostrophes with THREE backslashes, like here: 'choisir l\\\'option'. +// Alternatively: use one backslash and surround by double-quotes: "choisir l\'option". // Other characters (such as double-quotes in http links, etc.) do not have to be escaped. $i18n_language = 'Norwegian (Norsk)'; @@ -74,6 +75,9 @@ // 'error.report' => 'Select report.', // 'error.record' => 'Select record.', 'error.auth' => 'Feil brukernavn eller passord.', +// TODO: translate the following. +// 'error.2fa_code' => 'Invalid 2FA code.', +// 'error.weak_password' => 'Weak password.', 'error.user_exists' => 'Bruker med et slikt brukernavn eksisterer allerede.', // TODO: translate the following. // 'error.object_exists' => 'Object with this name already exists.', @@ -101,7 +105,6 @@ // 'error.user_count' => 'Limit on user count.', // 'error.expired' => 'Expiration date reached.', // 'error.file_storage' => 'File storage server error.', // See comment in English file. -// 'error.remote_work' => 'Remote work server error.', // See comment in English file. // Warning messages. // TODO: translate the following. @@ -225,9 +228,6 @@ 'label.required_fields' => '* obligatoriske felt', 'label.on_behalf' => 'på vegne av', // TODO: translate the following. -// 'label.role_manager' => '(manager)', -// 'label.role_comanager' => '(co-manager)', -// 'label.role_admin' => '(administrator)', // 'label.page' => 'Page', // 'label.condition' => 'Condition', // 'label.yes' => 'yes', @@ -253,6 +253,7 @@ // 'label.mark_paid' => 'Mark paid', // 'label.week_note' => 'Week note', // 'label.week_list' => 'Week list', +// 'label.weekends' => 'Weekends', // 'label.work_units' => 'Work units', // 'label.work_units_short' => 'Units', // 'label.totals_only' => 'Totals only', @@ -270,16 +271,6 @@ // 'label.file' => 'File', // 'label.active_users' => 'Active Users', // 'label.inactive_users' => 'Inactive Users', -// 'label.details' => 'Details', -// 'label.budget' => 'Budget', -// 'label.work' => 'Work', // Table column header for work items, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// 'label.offer' => 'Offer', // Table column header for offers, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// 'label.contractor' => 'Contractor', // Table column header for offers (contractor is someone who offers to do work). - // Technically, it is either an org name or a combination of org and group names - // because both work items and offers are owned by Time Tracker groups of users. -// 'label.how_to_pay' => 'How to pay', // Label for the "How to pay" field on offers, which allows contractors to specify - // how to pay them, for example: paypal email, check by mail to a specific address, etc. -// 'label.moderator_comment' => 'Moderator comment', // Label for "Moderator comment" field that explains something. // Entity names. We use lower case (in English) because they are used in dropdowns, too. // They are used to associate a custom field with an entity type. @@ -293,7 +284,8 @@ // 'title.error' => 'Error', // 'title.success' => 'Success', 'title.login' => 'Innlogging', -// TODO: translate the following. +// TODO: translate the follolwing. +// 'title.2fa' => 'Two Factor Authentication', // 'title.groups' => 'Groups', // 'title.add_group' => 'Adding Group', // 'title.edit_group' => 'Editing Group', @@ -392,22 +384,6 @@ // 'title.edit_file' => 'Editing File', // 'title.delete_file' => 'Deleting File', // 'title.download_file' => 'Downloading File', -// 'title.work' => 'Work', -// 'title.add_work' => 'Adding Work', -// 'title.edit_work' => 'Editing Work', -// 'title.delete_work' => 'Deleting Work', -// 'title.active_work' => 'Active Work', // Active work items this group outsources to other groups. -// 'title.available_work' => 'Available Work', // Available work items from other organizations. -// 'title.inactive_work' => 'Inactive Work', // Inactive work items this group was outsourcing to other groups. -// 'title.pending_work' => 'Pending Work', // Work items pending moderator approval. -// 'title.offer' => 'Offer', -// 'title.add_offer' => 'Adding Offer', -// 'title.edit_offer' => 'Editing Offer', -// 'title.delete_offer' => 'Deleting Offer', -// 'title.active_offers' => 'Active Offers', // Active offers this group makes available to other groups. -// 'title.available_offers' => 'Available Offers', // Available offers from other organizations. -// 'title.inactive_offers' => 'Inactive Offers', // Inactive offers for group. -// 'title.pending_offers' => 'Pending Offers', // Offers pending moderator approval. // Section for common strings inside combo boxes on forms. Strings shared between forms shall be placed here. // Strings that are used in a single form must go to the specific form section. @@ -440,7 +416,6 @@ // TODO: translate the following. // 'dropdown.delete' => 'delete', // 'dropdown.do_not_delete' => 'do not delete', -// 'dropdown.pending_approval' => 'pending approval', // 'dropdown.approved' => 'approved', // 'dropdown.not_approved' => 'not approved', // 'dropdown.paid' => 'paid', @@ -454,6 +429,16 @@ // 'form.login.about' => 'Anuko Time Tracker is an open source time tracking system.', 'form.login.about' => 'Anuko Time Tracker er et enkelt, brukervennlig tidsregistreringssystem basert på åpen kildekode.', +// Email subject and body for two-factor authentication. +// TODO: translate the following. +// 'email.2fa_code.subject' => 'Anuko Time Tracker two-factor authentication code', +// 'email.2fa_code.body' => "Dear User,\n\nYour two-factor authentication code is:\n\n%s\n\n", + +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// 'form.2fa.hint' => 'Check your email for 2FA code and enter it here.', +// 'form.2fa.2fa_code' => '2FA code', + // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. // TODO: translate the following. // 'form.reset_password.message' => 'Password reset request sent by email.', @@ -498,10 +483,13 @@ // 'form.reports.include_pending' => 'pending', 'form.reports.select_period' => 'Velg tidsperiode', 'form.reports.set_period' => 'eller sett dato', +// TODO: translate the following. +// 'form.reports.note_containing' => 'Note containing', 'form.reports.show_fields' => 'Vis feltene', // TODO: translate the following. // 'form.reports.time_fields' => 'Time fields', // 'form.reports.user_fields' => 'User fields', +// 'form.reports.project_fields' => 'Project fields', // 'form.reports.group_by' => 'Group by', // 'form.reports.group_by_no' => '--- no grouping ---', 'form.reports.group_by_date' => 'dato', @@ -515,6 +503,7 @@ // (after generating a report at https://timetracker.anuko.com/reports.php). 'form.report.export' => 'Eksporter', // TODO: translate the following. +// 'form.report.per_hour' => 'Per hour', // 'form.report.assign_to_invoice' => 'Assign to invoice', // 'form.report.assign_to_timesheet' => 'Assign to timesheet', @@ -555,6 +544,7 @@ // Users form. See example at https://timetracker.anuko.com/users.php // TODO: translate the following. +// 'form.users.uncompleted_entry_today' => 'User has an uncompleted time entry today', // 'form.users.uncompleted_entry' => 'User has an uncompleted time entry', 'form.users.role' => 'Rolle', // TODO: translate the following. @@ -617,13 +607,19 @@ // 'form.group_edit.type_start_finish' => 'start and finish', // 'form.group_edit.type_duration' => 'duration', // 'form.group_edit.punch_mode' => 'Punch mode', +// 'form.group_edit.one_uncompleted' => 'One uncompleted', // 'form.group_edit.allow_overlap' => 'Allow overlap', // 'form.group_edit.future_entries' => 'Future entries', // 'form.group_edit.uncompleted_indicators' => 'Uncompleted indicators', // 'form.group_edit.confirm_save' => 'Confirm saving', -// 'form.group_edit.allow_ip' => 'Allow IP', // 'form.group_edit.advanced_settings' => 'Advanced settings', +// Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. +// TODO: Translate the following. +// 'form.group_advanced_edit.allow_ip' => 'Allow IP', +// 'form.group_advanced_edit.password_complexity' => 'Password complexity', +// 'form.group_advanced_edit.2fa' => 'Two factor authentication', + // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php // TODO: translate the following. // 'form.group_delete.hint' => 'Are you sure you want to delete the entire group?', @@ -691,21 +687,7 @@ // 'form.display_options.note_on_separate_row' => 'Note on separate row', // 'form.display_options.not_complete_days' => 'Not complete days', // 'form.display_options.inactive_projects' => 'Inactive projects', +// 'form.display_options.cost_per_hour' => 'Cost per hour', // 'form.display_options.custom_css' => 'Custom CSS', - -// Work plugin strings. See example at https://timetracker.anuko.com/work.php -// TODO: translate the following. -// 'work.error.work_not_available' => 'Work item is not available.', -// 'work.error.offer_not_available' => 'Offer is not available.', -// 'work.type.one_time' => 'one time', // Work type is "one time job" for well defined work ("do exactly this"). -// 'work.type.ongoing' => 'ongoing', // Work type is "ongoing" for complex jobs (billed by the hour, multiple contractors, etc.) -// 'work.label.own_work' => 'Own work', -// 'work.label.own_offers' => 'Own offers', -// 'work.label.offers' => 'Offers', -// 'work.button.send_message' => 'Send message', -// 'work.button.make_offer' => 'Make offer', -// 'work.button.accept' => 'Accept', -// 'work.button.decline' => 'Decline', -// 'work.title.send_message' => 'Sending Message', -// 'work.msg.message_sent' => 'Message sent.', +// 'form.display_options.custom_translation' => 'Custom translation', ); diff --git a/WEB-INF/resources/pl.lang.php b/WEB-INF/resources/pl.lang.php index d77f11c3d..69dddf311 100644 --- a/WEB-INF/resources/pl.lang.php +++ b/WEB-INF/resources/pl.lang.php @@ -2,7 +2,8 @@ /* Copyright (c) Anuko International Ltd. https://www.anuko.com License: See license.txt */ -// Note: escape apostrophes with THREE backslashes, like here: choisir l\\\'option. +// Note: escape apostrophes with THREE backslashes, like here: 'choisir l\\\'option'. +// Alternatively: use one backslash and surround by double-quotes: "choisir l\'option". // Other characters (such as double-quotes in http links, etc.) do not have to be escaped. $i18n_language = 'Polish (Polski)'; @@ -69,6 +70,9 @@ // TODO: translate the following. // 'error.record' => 'Select record.', 'error.auth' => 'Błędny login lub hasło.', +// TODO: translate the following. +// 'error.2fa_code' => 'Invalid 2FA code.', +// 'error.weak_password' => 'Weak password.', 'error.user_exists' => 'Użytkownik o takiej nazwie już istnieje.', // TODO: translate the following. // 'error.object_exists' => 'Object with this name already exists.', @@ -98,7 +102,6 @@ // 'error.user_count' => 'Limit on user count.', // 'error.expired' => 'Expiration date reached.', // 'error.file_storage' => 'File storage server error.', // See comment in English file. -// 'error.remote_work' => 'Remote work server error.', // See comment in English file. // Warning messages. // TODO: translate the following. @@ -213,9 +216,6 @@ 'label.ldap_hint' => 'Wpisz swoją nazwę użytkownika Windows i hasło w polach poniżej.', 'label.required_fields' => '* - pola wymagane', 'label.on_behalf' => 'w imieniu', -'label.role_manager' => '(Manager)', -'label.role_comanager' => '(Co-manager)', -'label.role_admin' => '(Administrator)', // TODO: translate the following. // 'label.page' => 'Page', // 'label.condition' => 'Condition', @@ -242,6 +242,7 @@ // 'label.mark_paid' => 'Mark paid', // 'label.week_note' => 'Week note', // 'label.week_list' => 'Week list', +// 'label.weekends' => 'Weekends', // 'label.work_units' => 'Work units', // 'label.work_units_short' => 'Units', 'label.totals_only' => 'Tylko sumy', @@ -260,17 +261,6 @@ // 'label.file' => 'File', 'label.active_users' => 'Aktywni użytkownicy', 'label.inactive_users' => 'Nieaktywni użytkownicy', -// TODO: translate the following. -// 'label.details' => 'Details', -// 'label.budget' => 'Budget', -// 'label.work' => 'Work', // Table column header for work items, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// 'label.offer' => 'Offer', // Table column header for offers, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// 'label.contractor' => 'Contractor', // Table column header for offers (contractor is someone who offers to do work). - // Technically, it is either an org name or a combination of org and group names - // because both work items and offers are owned by Time Tracker groups of users. -// 'label.how_to_pay' => 'How to pay', // Label for the "How to pay" field on offers, which allows contractors to specify - // how to pay them, for example: paypal email, check by mail to a specific address, etc. -// 'label.moderator_comment' => 'Moderator comment', // Label for "Moderator comment" field that explains something. // Entity names. We use lower case (in English) because they are used in dropdowns, too. // They are used to associate a custom field with an entity type. @@ -284,6 +274,8 @@ // TODO: Translate the following. // 'title.success' => 'Success', 'title.login' => 'Logowanie', +// TODO: translate the follolwing. +// 'title.2fa' => 'Two Factor Authentication', 'title.groups' => 'Zespoły', // TODO: change "teams" to "groups". // TODO: translate the following. // 'title.add_group' => 'Adding Group', @@ -377,22 +369,6 @@ // 'title.edit_file' => 'Editing File', // 'title.delete_file' => 'Deleting File', // 'title.download_file' => 'Downloading File', -// 'title.work' => 'Work', -// 'title.add_work' => 'Adding Work', -// 'title.edit_work' => 'Editing Work', -// 'title.delete_work' => 'Deleting Work', -// 'title.active_work' => 'Active Work', // Active work items this group outsources to other groups. -// 'title.available_work' => 'Available Work', // Available work items from other organizations. -// 'title.inactive_work' => 'Inactive Work', // Inactive work items this group was outsourcing to other groups. -// 'title.pending_work' => 'Pending Work', // Work items pending moderator approval. -// 'title.offer' => 'Offer', -// 'title.add_offer' => 'Adding Offer', -// 'title.edit_offer' => 'Editing Offer', -// 'title.delete_offer' => 'Deleting Offer', -// 'title.active_offers' => 'Active Offers', // Active offers this group makes available to other groups. -// 'title.available_offers' => 'Available Offers', // Available offers from other organizations. -// 'title.inactive_offers' => 'Inactive Offers', // Inactive offers for group. -// 'title.pending_offers' => 'Pending Offers', // Offers pending moderator approval. // Section for common strings inside combo boxes on forms. Strings shared between forms shall be placed here. // Strings that are used in a single form must go to the specific form section. @@ -425,7 +401,6 @@ 'dropdown.delete' => 'usuń', 'dropdown.do_not_delete' => 'nie usuwaj', // TODO: translate the following. -// 'dropdown.pending_approval' => 'pending approval', // 'dropdown.approved' => 'approved', // 'dropdown.not_approved' => 'not approved', // 'dropdown.paid' => 'paid', @@ -442,6 +417,16 @@ 'form.login.forgot_password' => 'Nie pamiętasz hasła?', 'form.login.about' => 'Anuko Time Tracker jest otwartoźródłowym systemem śledzenia czasu.', +// Email subject and body for two-factor authentication. +// TODO: translate the following. +// 'email.2fa_code.subject' => 'Anuko Time Tracker two-factor authentication code', +// 'email.2fa_code.body' => "Dear User,\n\nYour two-factor authentication code is:\n\n%s\n\n", + +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// 'form.2fa.hint' => 'Check your email for 2FA code and enter it here.', +// 'form.2fa.2fa_code' => '2FA code', + // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. 'form.reset_password.message' => 'Instrukcje zmiany hasła zostały wysłane na adres e-mail połączony z kontem.', 'form.reset_password.email_subject' => 'Anuko Time Tracker - żądanie zmiany hasła', @@ -483,10 +468,13 @@ // 'form.reports.include_pending' => 'pending', 'form.reports.select_period' => 'Wybierz okres', 'form.reports.set_period' => 'lub ustaw daty', +// TODO: translate the following. +// 'form.reports.note_containing' => 'Note containing', 'form.reports.show_fields' => 'Pokaż pola', // TODO: translate the following. // 'form.reports.time_fields' => 'Time fields', // 'form.reports.user_fields' => 'User fields', +// 'form.reports.project_fields' => 'Project fields', 'form.reports.group_by' => 'Grupowanie wg', 'form.reports.group_by_no' => '--- bez grupowania ---', 'form.reports.group_by_date' => 'daty', @@ -499,6 +487,7 @@ // (after generating a report at https://timetracker.anuko.com/reports.php). 'form.report.export' => 'Eksport', // TODO: translate the following. +// 'form.report.per_hour' => 'Per hour', // 'form.report.assign_to_invoice' => 'Assign to invoice', // 'form.report.assign_to_timesheet' => 'Assign to timesheet', @@ -537,6 +526,7 @@ // Users form. See example at https://timetracker.anuko.com/users.php // TODO: translate the following. + // 'form.users.uncompleted_entry_today' => 'User has an uncompleted time entry today', // 'form.users.uncompleted_entry' => 'User has an uncompleted time entry', 'form.users.role' => 'Rola', 'form.users.manager' => 'Manager', @@ -597,13 +587,19 @@ 'form.group_edit.type_duration' => 'czas trwania', // TODO: translate the following. // 'form.group_edit.punch_mode' => 'Punch mode', +// 'form.group_edit.one_uncompleted' => 'One uncompleted', // 'form.group_edit.allow_overlap' => 'Allow overlap', // 'form.group_edit.future_entries' => 'Future entries', // 'form.group_edit.uncompleted_indicators' => 'Uncompleted indicators', // 'form.group_edit.confirm_save' => 'Confirm saving', -// 'form.group_edit.allow_ip' => 'Allow IP', // 'form.group_edit.advanced_settings' => 'Advanced settings', +// Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. +// TODO: Translate the following. +// 'form.group_advanced_edit.allow_ip' => 'Allow IP', +// 'form.group_advanced_edit.password_complexity' => 'Password complexity', +// 'form.group_advanced_edit.2fa' => 'Two factor authentication', + // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php // TODO: translate the following. // 'form.group_delete.hint' => 'Are you sure you want to delete the entire group?', @@ -670,21 +666,7 @@ // 'form.display_options.note_on_separate_row' => 'Note on separate row', // 'form.display_options.not_complete_days' => 'Not complete days', // 'form.display_options.inactive_projects' => 'Inactive projects', +// 'form.display_options.cost_per_hour' => 'Cost per hour', // 'form.display_options.custom_css' => 'Custom CSS', - -// Work plugin strings. See example at https://timetracker.anuko.com/work.php -// TODO: translate the following. -// 'work.error.work_not_available' => 'Work item is not available.', -// 'work.error.offer_not_available' => 'Offer is not available.', -// 'work.type.one_time' => 'one time', // Work type is "one time job" for well defined work ("do exactly this"). -// 'work.type.ongoing' => 'ongoing', // Work type is "ongoing" for complex jobs (billed by the hour, multiple contractors, etc.) -// 'work.label.own_work' => 'Own work', -// 'work.label.own_offers' => 'Own offers', -// 'work.label.offers' => 'Offers', -// 'work.button.send_message' => 'Send message', -// 'work.button.make_offer' => 'Make offer', -// 'work.button.accept' => 'Accept', -// 'work.button.decline' => 'Decline', -// 'work.title.send_message' => 'Sending Message', -// 'work.msg.message_sent' => 'Message sent.', +// 'form.display_options.custom_translation' => 'Custom translation', ); diff --git a/WEB-INF/resources/pt-br.lang.php b/WEB-INF/resources/pt-br.lang.php index 15e3ef91f..c9a4ad998 100644 --- a/WEB-INF/resources/pt-br.lang.php +++ b/WEB-INF/resources/pt-br.lang.php @@ -2,7 +2,8 @@ /* Copyright (c) Anuko International Ltd. https://www.anuko.com License: See license.txt */ -// Note: escape apostrophes with THREE backslashes, like here: choisir l\\\'option. +// Note: escape apostrophes with THREE backslashes, like here: 'choisir l\\\'option'. +// Alternatively: use one backslash and surround by double-quotes: "choisir l\'option". // Other characters (such as double-quotes in http links, etc.) do not have to be escaped. $i18n_language = 'Portuguese (Português Brasileiro)'; @@ -59,6 +60,9 @@ 'error.report' => 'Selecione relatório.', 'error.record' => 'Selecione o registro.', 'error.auth' => 'Usuário ou senha incorretos.', +// TODO: translate the following. +// 'error.2fa_code' => 'Invalid 2FA code.', +// 'error.weak_password' => 'Weak password.', 'error.user_exists' => 'Já existe usuário com este login.', 'error.object_exists' => 'Já existe um objeto com este nome.', 'error.invoice_exists' => 'Já existe fatura com este número.', @@ -81,7 +85,6 @@ 'error.user_count' => 'Limite na contagem de usuários.', 'error.expired' => 'Data de expiração atingida.', 'error.file_storage' => 'Erro relacionado ao servidor de armazenamento de arquivos.', -'error.remote_work' => 'Erro relacionado ao servidor responsável pelo plugin work.', // Warning messages. 'warn.sure' => 'Tem certeza?', @@ -186,9 +189,6 @@ 'label.ldap_hint' => 'Entre com o seu login do Windows e senha nos campos abaixo.', 'label.required_fields' => '* - campos obrigatórios', 'label.on_behalf' => 'em nome de', -'label.role_manager' => '(gerente)', -'label.role_comanager' => '(coordenador)', -'label.role_admin' => '(administrador)', 'label.page' => 'Página', 'label.condition' => 'Condição', 'label.yes' => 'sim', @@ -212,6 +212,8 @@ 'label.mark_paid' => 'Marcar como pago', 'label.week_note' => 'Anotação da semana', 'label.week_list' => 'Lista da semana', +// TODO: translate the following. +// 'label.weekends' => 'Weekends', 'label.work_units' => 'Unidades de trabalho', 'label.work_units_short' => 'Unidades', 'label.totals_only' => 'Somente totais', @@ -229,13 +231,6 @@ 'label.file' => 'Arquivo', 'label.active_users' => 'Usuários ativos', 'label.inactive_users' => 'Usuários inativos', -'label.details' => 'Detalhes', -'label.budget' => 'Orçamento', -'label.work' => 'Trabalho', -'label.offer' => 'Oferta', -'label.contractor' => 'Contratante', -'label.how_to_pay' => 'Como pagar', -'label.moderator_comment' => 'Comentário do moderador', // Entity names. We use lower case (in English) because they are used in dropdowns, too. // They are used to associate a custom field with an entity type. @@ -247,6 +242,8 @@ 'title.error' => 'Erro', 'title.success' => 'Sucesso', 'title.login' => 'Login', +// TODO: translate the follolwing. +// 'title.2fa' => 'Two Factor Authentication', 'title.groups' => 'Grupos', 'title.add_group' => 'Adicionando grupo', 'title.edit_group' => 'Editando grupo', @@ -329,22 +326,6 @@ 'title.edit_file' => 'Editando arquivo', 'title.delete_file' => 'Apagando arquivo', 'title.download_file' => 'Baixando arquivo', -'title.work' => 'Trabalho', -'title.add_work' => 'Adicionando trabalho', -'title.edit_work' => 'Editando trabalho', -'title.delete_work' => 'Apagando trabalho', -'title.active_work' => 'Trabalho ativo', -'title.available_work' => 'Trabalho disponível', -'title.inactive_work' => 'Trabalho inativo', -'title.pending_work' => 'Trabalho pendente', -'title.offer' => 'Oferta', -'title.add_offer' => 'Adicionando oferta', -'title.edit_offer' => 'Editando oferta', -'title.delete_offer' => 'Apagando oferta', -'title.active_offers' => 'Ofertas ativas', -'title.available_offers' => 'Ofertas disponíveis', -'title.inactive_offers' => 'Ofertas inativas', -'title.pending_offers' => 'Ofertas pendentes', // Section for common strings inside combo boxes on forms. Strings shared between forms shall be placed here. // Strings that are used in a single form must go to the specific form section. @@ -373,7 +354,6 @@ 'dropdown.status_inactive' => 'inativo', 'dropdown.delete' => 'apagar', 'dropdown.do_not_delete' => 'não apagar', -'dropdown.pending_approval' => 'aprovação pendente', 'dropdown.approved' => 'aprovado', 'dropdown.not_approved' => 'não aprovado', 'dropdown.paid' => 'pago', @@ -390,6 +370,16 @@ 'form.login.forgot_password' => 'Esqueceu a senha?', 'form.login.about' => 'Anuko Time Tracker é um sistema de código aberto de rastreamento do tempo.', +// Email subject and body for two-factor authentication. +// TODO: translate the following. +// 'email.2fa_code.subject' => 'Anuko Time Tracker two-factor authentication code', +// 'email.2fa_code.body' => "Dear User,\n\nYour two-factor authentication code is:\n\n%s\n\n", + +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// 'form.2fa.hint' => 'Check your email for 2FA code and enter it here.', +// 'form.2fa.2fa_code' => '2FA code', + // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. 'form.reset_password.message' => 'Pedido para resetar a senha enviado por e-mail.', 'form.reset_password.email_subject' => 'Pedido de alteração de senha no Anuko Time Tracker', @@ -425,9 +415,13 @@ 'form.reports.include_pending' => 'pendente', 'form.reports.select_period' => 'Selecione o período de tempo', 'form.reports.set_period' => 'ou selecionar datas', +// TODO: translate the following. +// 'form.reports.note_containing' => 'Note containing', 'form.reports.show_fields' => 'Exibir campos', 'form.reports.time_fields' => 'Campos de tempo', 'form.reports.user_fields' => 'Campos de usuário', +// TODO: translate the following. +// 'form.reports.project_fields' => 'Project fields', 'form.reports.group_by' => 'Agrupar por', 'form.reports.group_by_no' => '--- sem agrupar ---', 'form.reports.group_by_date' => 'data', @@ -439,6 +433,8 @@ // Report form. See example at https://timetracker.anuko.com/report.php // (after generating a report at https://timetracker.anuko.com/reports.php). 'form.report.export' => 'Exportar', +// TODO: translate the following. +// 'form.report.per_hour' => 'Per hour', 'form.report.assign_to_invoice' => 'Atribuir a fatura', 'form.report.assign_to_timesheet' => 'Atribuir a planilha de horas', @@ -473,6 +469,8 @@ 'form.tasks.inactive_tasks' => 'Tarefas inativas', // Users form. See example at https://timetracker.anuko.com/users.php. +// TODO: translate the following. +// 'form.users.uncompleted_entry_today' => 'User has an uncompleted time entry today', 'form.users.uncompleted_entry' => 'O usuário tem uma entrada incompleta', 'form.users.role' => 'Função', 'form.users.manager' => 'Gerente', @@ -527,13 +525,20 @@ 'form.group_edit.type_start_finish' => 'início e fim', 'form.group_edit.type_duration' => 'duração', 'form.group_edit.punch_mode' => 'Modo punch', +// TODO: translate the following. +// 'form.group_edit.one_uncompleted' => 'One uncompleted', 'form.group_edit.allow_overlap' => 'Permitir sobreposição', 'form.group_edit.future_entries' => 'Entradas futuros', 'form.group_edit.uncompleted_indicators' => 'Indicadores incompletos', 'form.group_edit.confirm_save' => 'Confirme o salvamento', -'form.group_edit.allow_ip' => 'Permitir IP', 'form.group_edit.advanced_settings' => 'Configurações avançadas', +// Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. +'form.group_advanced_edit.allow_ip' => 'Permitir IP', +// TODO: Translate the following. +// 'form.group_advanced_edit.password_complexity' => 'Password complexity', +// 'form.group_advanced_edit.2fa' => 'Two factor authentication', + // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php. 'form.group_delete.hint' => 'Tem certeza de que deseja excluir todo o grupo?', @@ -594,21 +599,9 @@ 'form.display_options.not_complete_days' => 'Dias não completos', // TODO: translate the following. // 'form.display_options.inactive_projects' => 'Inactive projects', +// 'form.display_options.cost_per_hour' => 'Cost per hour', 'form.display_options.custom_css' => 'CSS customizado', - -// Work plugin strings. See example at https://timetracker.anuko.com/work.php. -'work.error.work_not_available' => 'Item de trabalho não está disponível.', -'work.error.offer_not_available' => 'Oferta não disponível', -'work.type.one_time' => 'Uma interação', -'work.type.ongoing' => 'Em progresso', -'work.label.own_work' => 'Trabalho próprio', -'work.label.own_offers' => 'Ofertas próprias', -'work.label.offers' => 'Ofertas', -'work.button.send_message' => 'Enviar mensagem', -'work.button.make_offer' => 'Fazer oferta', -'work.button.accept' => 'Aceitar', -'work.button.decline' => 'Recusar', -'work.title.send_message' => 'Enviando mensagem', -'work.msg.message_sent' => 'Mensagem enviada.', +// TODO: translate the following. +// 'form.display_options.custom_translation' => 'Custom translation', ); diff --git a/WEB-INF/resources/pt.lang.php b/WEB-INF/resources/pt.lang.php index 6a1598cc7..abcd5793f 100644 --- a/WEB-INF/resources/pt.lang.php +++ b/WEB-INF/resources/pt.lang.php @@ -2,7 +2,8 @@ /* Copyright (c) Anuko International Ltd. https://www.anuko.com License: See license.txt */ -// Note: escape apostrophes with THREE backslashes, like here: choisir l\\\'option. +// Note: escape apostrophes with THREE backslashes, like here: 'choisir l\\\'option'. +// Alternatively: use one backslash and surround by double-quotes: "choisir l\'option". // Other characters (such as double-quotes in http links, etc.) do not have to be escaped. // Note to translators: Please use proper capitalization rules for your language. @@ -72,6 +73,8 @@ // 'error.report' => 'Select report.', // 'error.record' => 'Select record.', // 'error.auth' => 'Incorrect login or password.', +// 'error.2fa_code' => 'Invalid 2FA code.', +// 'error.weak_password' => 'Weak password.', // 'error.user_exists' => 'User with this login already exists.', // 'error.object_exists' => 'Object with this name already exists.', // 'error.invoice_exists' => 'Invoice with this number already exists.', @@ -94,7 +97,6 @@ // 'error.user_count' => 'Limit on user count.', // 'error.expired' => 'Expiration date reached.', // 'error.file_storage' => 'File storage server error.', // See comment in English file. -// 'error.remote_work' => 'Remote work server error.', // See comment in English file. // Warning messages. // TODO: translate the following. @@ -213,10 +215,6 @@ 'label.required_fields' => '* campos obrigatórios', // TODO: translate the following. // 'label.on_behalf' => 'on behalf of', -'label.role_manager' => '(gerente)', -// TODO: translate the following. -// 'label.role_comanager' => '(co-manager)', -// 'label.role_admin' => '(administrator)', // 'label.page' => 'Page', // 'label.condition' => 'Condition', // 'label.yes' => 'yes', @@ -241,6 +239,7 @@ // 'label.mark_paid' => 'Mark paid', // 'label.week_note' => 'Week note', // 'label.week_list' => 'Week list', +// 'label.weekends' => 'Weekends', // 'label.work_units' => 'Work units', // 'label.work_units_short' => 'Units', // 'label.totals_only' => 'Totals only', @@ -258,16 +257,6 @@ // 'label.file' => 'File', // 'label.active_users' => 'Active Users', // 'label.inactive_users' => 'Inactive Users', -// 'label.details' => 'Details', -// 'label.budget' => 'Budget', -// 'label.work' => 'Work', // Table column header for work items, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// 'label.offer' => 'Offer', // Table column header for offers, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// 'label.contractor' => 'Contractor', // Table column header for offers (contractor is someone who offers to do work). - // Technically, it is either an org name or a combination of org and group names - // because both work items and offers are owned by Time Tracker groups of users. -// 'label.how_to_pay' => 'How to pay', // Label for the "How to pay" field on offers, which allows contractors to specify - // how to pay them, for example: paypal email, check by mail to a specific address, etc. -// 'label.moderator_comment' => 'Moderator comment', // Label for "Moderator comment" field that explains something. // Entity names. We use lower case (in English) because they are used in dropdowns, too. // They are used to associate a custom field with an entity type. @@ -281,7 +270,8 @@ // 'title.error' => 'Error', // 'title.success' => 'Success', 'title.login' => 'Login', -// TODO: translate the following. +// TODO: translate the follolwing. +// 'title.2fa' => 'Two Factor Authentication', // 'title.groups' => 'Groups', // 'title.add_group' => 'Adding Group', // 'title.edit_group' => 'Editing Group', @@ -376,22 +366,6 @@ // 'title.edit_file' => 'Editing File', // 'title.delete_file' => 'Deleting File', // 'title.download_file' => 'Downloading File', -// 'title.work' => 'Work', -// 'title.add_work' => 'Adding Work', -// 'title.edit_work' => 'Editing Work', -// 'title.delete_work' => 'Deleting Work', -// 'title.active_work' => 'Active Work', // Active work items this group outsources to other groups. -// 'title.available_work' => 'Available Work', // Available work items from other organizations. -// 'title.inactive_work' => 'Inactive Work', // Inactive work items this group was outsourcing to other groups. -// 'title.pending_work' => 'Pending Work', // Work items pending moderator approval. -// 'title.offer' => 'Offer', -// 'title.add_offer' => 'Adding Offer', -// 'title.edit_offer' => 'Editing Offer', -// 'title.delete_offer' => 'Deleting Offer', -// 'title.active_offers' => 'Active Offers', // Active offers this group makes available to other groups. -// 'title.available_offers' => 'Available Offers', // Available offers from other organizations. -// 'title.inactive_offers' => 'Inactive Offers', // Inactive offers for group. -// 'title.pending_offers' => 'Pending Offers', // Offers pending moderator approval. // Section for common strings inside combo boxes on forms. Strings shared between forms shall be placed here. // Strings that are used in a single form must go to the specific form section. @@ -423,7 +397,6 @@ // 'dropdown.status_inactive' => 'inactive', // 'dropdown.delete' => 'delete', // 'dropdown.do_not_delete' => 'do not delete', -// 'dropdown.pending_approval' => 'pending approval', // 'dropdown.approved' => 'approved', // 'dropdown.not_approved' => 'not approved', // 'dropdown.paid' => 'paid', @@ -436,6 +409,16 @@ // TODO: translate the following. // 'form.login.about' => 'Anuko Time Tracker is an open source time tracking system.', +// Email subject and body for two-factor authentication. +// TODO: translate the following. +// 'email.2fa_code.subject' => 'Anuko Time Tracker two-factor authentication code', +// 'email.2fa_code.body' => "Dear User,\n\nYour two-factor authentication code is:\n\n%s\n\n", + +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// 'form.2fa.hint' => 'Check your email for 2FA code and enter it here.', +// 'form.2fa.2fa_code' => '2FA code', + // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. // TODO: translate the following. // 'form.reset_password.message' => 'Password reset request sent by email.', @@ -477,10 +460,13 @@ // 'form.reports.include_pending' => 'pending', 'form.reports.select_period' => 'Selecione o período de tempo', 'form.reports.set_period' => 'ou selecionar datas', +// TODO: translate the following. +// 'form.reports.note_containing' => 'Note containing', 'form.reports.show_fields' => 'Exibir campos', // TODO: translate the following. // 'form.reports.time_fields' => 'Time fields', // 'form.reports.user_fields' => 'User fields', +// 'form.reports.project_fields' => 'Project fields', // 'form.reports.group_by' => 'Group by', // 'form.reports.group_by_no' => '--- no grouping ---', // 'form.reports.group_by_date' => 'date', @@ -493,6 +479,7 @@ // (after generating a report at https://timetracker.anuko.com/reports.php). // TODO: translate the following. // 'form.report.export' => 'Export', +// 'form.report.per_hour' => 'Per hour', // 'form.report.assign_to_invoice' => 'Assign to invoice', // 'form.report.assign_to_timesheet' => 'Assign to timesheet', @@ -534,6 +521,7 @@ // Users form. See example at https://timetracker.anuko.com/users.php // TODO: translate the following. +// 'form.users.uncompleted_entry_today' => 'User has an uncompleted time entry today', // 'form.users.uncompleted_entry' => 'User has an uncompleted time entry', // 'form.users.role' => 'Role', 'form.users.manager' => 'Gerente', @@ -597,13 +585,19 @@ // 'form.group_edit.type_start_finish' => 'start and finish', // 'form.group_edit.type_duration' => 'duration', // 'form.group_edit.punch_mode' => 'Punch mode', +// 'form.group_edit.one_uncompleted' => 'One uncompleted', // 'form.group_edit.allow_overlap' => 'Allow overlap', // 'form.group_edit.future_entries' => 'Future entries', // 'form.group_edit.uncompleted_indicators' => 'Uncompleted indicators', // 'form.group_edit.confirm_save' => 'Confirm saving', -// 'form.group_edit.allow_ip' => 'Allow IP', // 'form.group_edit.advanced_settings' => 'Advanced settings', +// Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. +// TODO: Translate the following. +// 'form.group_advanced_edit.allow_ip' => 'Allow IP', +// 'form.group_advanced_edit.password_complexity' => 'Password complexity', +// 'form.group_advanced_edit.2fa' => 'Two factor authentication', + // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php // TODO: translate the following. // 'form.group_delete.hint' => 'Are you sure you want to delete the entire group?', @@ -671,21 +665,7 @@ // 'form.display_options.note_on_separate_row' => 'Note on separate row', // 'form.display_options.not_complete_days' => 'Not complete days', // 'form.display_options.inactive_projects' => 'Inactive projects', +// 'form.display_options.cost_per_hour' => 'Cost per hour', // 'form.display_options.custom_css' => 'Custom CSS', - -// Work plugin strings. See example at https://timetracker.anuko.com/work.php -// TODO: translate the following. -// 'work.error.work_not_available' => 'Work item is not available.', -// 'work.error.offer_not_available' => 'Offer is not available.', -// 'work.type.one_time' => 'one time', // Work type is "one time job" for well defined work ("do exactly this"). -// 'work.type.ongoing' => 'ongoing', // Work type is "ongoing" for complex jobs (billed by the hour, multiple contractors, etc.) -// 'work.label.own_work' => 'Own work', -// 'work.label.own_offers' => 'Own offers', -// 'work.label.offers' => 'Offers', -// 'work.button.send_message' => 'Send message', -// 'work.button.make_offer' => 'Make offer', -// 'work.button.accept' => 'Accept', -// 'work.button.decline' => 'Decline', -// 'work.title.send_message' => 'Sending Message', -// 'work.msg.message_sent' => 'Message sent.', +// 'form.display_options.custom_translation' => 'Custom translation', ); diff --git a/WEB-INF/resources/ro.lang.php b/WEB-INF/resources/ro.lang.php index f93f12085..03db7da44 100644 --- a/WEB-INF/resources/ro.lang.php +++ b/WEB-INF/resources/ro.lang.php @@ -2,7 +2,8 @@ /* Copyright (c) Anuko International Ltd. https://www.anuko.com License: See license.txt */ -// Note: escape apostrophes with THREE backslashes, like here: choisir l\\\'option. +// Note: escape apostrophes with THREE backslashes, like here: 'choisir l\\\'option'. +// Alternatively: use one backslash and surround by double-quotes: "choisir l\'option". // Other characters (such as double-quotes in http links, etc.) do not have to be escaped. // Note to translators: Please use proper capitalization rules for your language. @@ -78,6 +79,8 @@ // 'error.record' => 'Select record.', 'error.auth' => 'Nume de utilizator sau parola incorecta.', // TODO: translate the following. +// 'error.2fa_code' => 'Invalid 2FA code.', +// 'error.weak_password' => 'Weak password.', // 'error.user_exists' => 'User with this login already exists.', // 'error.object_exists' => 'Object with this name already exists.', // 'error.invoice_exists' => 'Invoice with this number already exists.', @@ -101,7 +104,6 @@ // 'error.user_count' => 'Limit on user count.', // 'error.expired' => 'Expiration date reached.', // 'error.file_storage' => 'File storage server error.', // See comment in English file. -// 'error.remote_work' => 'Remote work server error.', // See comment in English file. // Warning messages. // TODO: translate the following. @@ -223,9 +225,6 @@ // 'label.ldap_hint' => 'Type your Windows login and password in the fields below.', 'label.required_fields' => '* date obligatorii', 'label.on_behalf' => 'in numele', -'label.role_manager' => '(manager)', -'label.role_comanager' => '(co-manager)', -'label.role_admin' => '(administrator)', // TODO: translate the following. // 'label.page' => 'Page', // 'label.condition' => 'Condition', @@ -252,6 +251,7 @@ // 'label.mark_paid' => 'Mark paid', // 'label.week_note' => 'Week note', // 'label.week_list' => 'Week list', +// 'label.weekends' => 'Weekends', // 'label.work_units' => 'Work units', // 'label.work_units_short' => 'Units', 'label.totals_only' => 'Numai totaluri', @@ -270,16 +270,6 @@ // 'label.file' => 'File', // 'label.active_users' => 'Active Users', // 'label.inactive_users' => 'Inactive Users', -// 'label.details' => 'Details', -// 'label.budget' => 'Budget', -// 'label.work' => 'Work', // Table column header for work items, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// 'label.offer' => 'Offer', // Table column header for offers, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// 'label.contractor' => 'Contractor', // Table column header for offers (contractor is someone who offers to do work). - // Technically, it is either an org name or a combination of org and group names - // because both work items and offers are owned by Time Tracker groups of users. -// 'label.how_to_pay' => 'How to pay', // Label for the "How to pay" field on offers, which allows contractors to specify - // how to pay them, for example: paypal email, check by mail to a specific address, etc. -// 'label.moderator_comment' => 'Moderator comment', // Label for "Moderator comment" field that explains something. // Entity names. We use lower case (in English) because they are used in dropdowns, too. // They are used to associate a custom field with an entity type. @@ -296,6 +286,8 @@ // 'title.error' => 'Error', // 'title.success' => 'Success', 'title.login' => 'Autentificare', +// TODO: translate the follolwing. +// 'title.2fa' => 'Two Factor Authentication', 'title.groups' => 'Echipe', // TODO: change "teams" to "groups". // TODO: translate the following. // 'title.add_group' => 'Adding Group', @@ -394,22 +386,6 @@ // 'title.edit_file' => 'Editing File', // 'title.delete_file' => 'Deleting File', // 'title.download_file' => 'Downloading File', -// 'title.work' => 'Work', -// 'title.add_work' => 'Adding Work', -// 'title.edit_work' => 'Editing Work', -// 'title.delete_work' => 'Deleting Work', -// 'title.active_work' => 'Active Work', // Active work items this group outsources to other groups. -// 'title.available_work' => 'Available Work', // Available work items from other organizations. -// 'title.inactive_work' => 'Inactive Work', // Inactive work items this group was outsourcing to other groups. -// 'title.pending_work' => 'Pending Work', // Work items pending moderator approval. -// 'title.add_offer' => 'Adding Offer', -// 'title.offer' => 'Offer', -// 'title.edit_offer' => 'Editing Offer', -// 'title.delete_offer' => 'Deleting Offer', -// 'title.active_offers' => 'Active Offers', // Active offers this group makes available to other groups. -// 'title.available_offers' => 'Available Offers', // Available offers from other organizations. -// 'title.inactive_offers' => 'Inactive Offers', // Inactive offers for group. -// 'title.pending_offers' => 'Pending Offers', // Offers pending moderator approval. // Section for common strings inside combo boxes on forms. Strings shared between forms shall be placed here. // Strings that are used in a single form must go to the specific form section. @@ -443,7 +419,6 @@ // TODO: translate the following. // 'dropdown.delete' => 'delete', // 'dropdown.do_not_delete' => 'do not delete', -// 'dropdown.pending_approval' => 'pending approval', // 'dropdown.approved' => 'approved', // 'dropdown.not_approved' => 'not approved', // 'dropdown.paid' => 'paid', @@ -456,6 +431,16 @@ // TODO: translate the following. // 'form.login.about' => 'Anuko Time Tracker is an open source time tracking system.', +// Email subject and body for two-factor authentication. +// TODO: translate the following. +// 'email.2fa_code.subject' => 'Anuko Time Tracker two-factor authentication code', +// 'email.2fa_code.body' => "Dear User,\n\nYour two-factor authentication code is:\n\n%s\n\n", + +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// 'form.2fa.hint' => 'Check your email for 2FA code and enter it here.', +// 'form.2fa.2fa_code' => '2FA code', + // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. 'form.reset_password.message' => 'Cererea de resetare a parolei a fost trimisa.', // TODO: add "by email" to match the English string. 'form.reset_password.email_subject' => 'Anuko Time Tracker - cerere de resetare a parolei', @@ -498,10 +483,13 @@ // 'form.reports.include_pending' => 'pending', 'form.reports.select_period' => 'Alege perioada', 'form.reports.set_period' => 'sau introdu intervalul de date', +// TODO: translate the following. +// 'form.reports.note_containing' => 'Note containing', 'form.reports.show_fields' => 'Arata campuri', // TODO: translate the following. // 'form.reports.time_fields' => 'Time fields', // 'form.reports.user_fields' => 'User fields', +// 'form.reports.project_fields' => 'Project fields', 'form.reports.group_by' => 'Grupat dupa', 'form.reports.group_by_no' => '--- fara grupare ---', 'form.reports.group_by_date' => 'data', @@ -515,6 +503,7 @@ // (after generating a report at https://timetracker.anuko.com/reports.php). 'form.report.export' => 'Exporta', // TODO: translate the following. +// 'form.report.per_hour' => 'Per hour', // 'form.report.assign_to_invoice' => 'Assign to invoice', // 'form.report.assign_to_timesheet' => 'Assign to timesheet', @@ -555,6 +544,7 @@ // Users form. See example at https://timetracker.anuko.com/users.php // TODO: translate the following. +// 'form.users.uncompleted_entry_today' => 'User has an uncompleted time entry today', // 'form.users.uncompleted_entry' => 'User has an uncompleted time entry', 'form.users.role' => 'Functie', // TODO: is "Rol" a better term here? 'form.users.manager' => 'Manager', @@ -618,13 +608,19 @@ // 'form.group_edit.type_start_finish' => 'start and finish', // 'form.group_edit.type_duration' => 'duration', // 'form.group_edit.punch_mode' => 'Punch mode', +// 'form.group_edit.one_uncompleted' => 'One uncompleted', // 'form.group_edit.allow_overlap' => 'Allow overlap', // 'form.group_edit.future_entries' => 'Future entries', // 'form.group_edit.uncompleted_indicators' => 'Uncompleted indicators', // 'form.group_edit.confirm_save' => 'Confirm saving', -// 'form.group_edit.allow_ip' => 'Allow IP', // 'form.group_edit.advanced_settings' => 'Advanced settings', +// Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. +// TODO: Translate the following. +// 'form.group_advanced_edit.allow_ip' => 'Allow IP', +// 'form.group_advanced_edit.password_complexity' => 'Password complexity', +// 'form.group_advanced_edit.2fa' => 'Two factor authentication', + // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php // TODO: translate the following. // 'form.group_delete.hint' => 'Are you sure you want to delete the entire group?', @@ -692,21 +688,7 @@ // 'form.display_options.note_on_separate_row' => 'Note on separate row', // 'form.display_options.not_complete_days' => 'Not complete days', // 'form.display_options.inactive_projects' => 'Inactive projects', +// 'form.display_options.cost_per_hour' => 'Cost per hour', // 'form.display_options.custom_css' => 'Custom CSS', - -// Work plugin strings. See example at https://timetracker.anuko.com/work.php -// TODO: translate the following. -// 'work.error.work_not_available' => 'Work item is not available.', -// 'work.error.offer_not_available' => 'Offer is not available.', -// 'work.type.one_time' => 'one time', // Work type is "one time job" for well defined work ("do exactly this"). -// 'work.type.ongoing' => 'ongoing', // Work type is "ongoing" for complex jobs (billed by the hour, multiple contractors, etc.) -// 'work.label.own_work' => 'Own work', -// 'work.label.own_offers' => 'Own offers', -// 'work.label.offers' => 'Offers', -// 'work.button.send_message' => 'Send message', -// 'work.button.make_offer' => 'Make offer', -// 'work.button.accept' => 'Accept', -// 'work.button.decline' => 'Decline', -// 'work.title.send_message' => 'Sending Message', -// 'work.msg.message_sent' => 'Message sent.', +// 'form.display_options.custom_translation' => 'Custom translation', ); diff --git a/WEB-INF/resources/ru.lang.php b/WEB-INF/resources/ru.lang.php index 71bc709bf..6c924df2d 100644 --- a/WEB-INF/resources/ru.lang.php +++ b/WEB-INF/resources/ru.lang.php @@ -2,7 +2,8 @@ /* Copyright (c) Anuko International Ltd. https://www.anuko.com License: See license.txt */ -// Note: escape apostrophes with THREE backslashes, like here: choisir l\\\'option. +// Note: escape apostrophes with THREE backslashes, like here: 'choisir l\\\'option'. +// Alternatively: use one backslash and surround by double-quotes: "choisir l\'option". // Other characters (such as double-quotes in http links, etc.) do not have to be escaped. $i18n_language = 'Russian (Русский)'; @@ -59,6 +60,8 @@ 'error.report' => 'Выберите отчёт.', 'error.record' => 'Выберите запись.', 'error.auth' => 'Неправильно введен логин или пароль.', +'error.2fa_code' => 'Неверный код 2FA.', +'error.weak_password' => 'Слабый пароль.', 'error.user_exists' => 'Пользователь с таким логином уже существует.', 'error.object_exists' => 'Объект с таким именем уже есть.', 'error.invoice_exists' => 'Счёт с таким номером уже есть.', @@ -81,7 +84,6 @@ 'error.user_count' => 'Ограничение на количество пользователей.', 'error.expired' => 'Достигнута дата экспирации.', 'error.file_storage' => 'Ошибка сервера для хранения файлов.', -'error.remote_work' => 'Ошибка сервера удаленной работы.', // Warning messages. 'warn.sure' => 'Вы уверены?', @@ -185,9 +187,6 @@ 'label.ldap_hint' => 'Введите свои Windows логин и пароль в поля ниже.', 'label.required_fields' => '* - обязательные для заполнения поля', 'label.on_behalf' => 'от имени', -'label.role_manager' => '(менеджер)', -'label.role_comanager' => '(ассистент менеджера)', -'label.role_admin' => '(администратор)', 'label.page' => 'Стр', 'label.condition' => 'Условие', 'label.yes' => 'да', @@ -211,6 +210,7 @@ 'label.mark_paid' => 'Отметить оплату', 'label.week_note' => 'Комментарий недели', 'label.week_list' => 'Список недели', +'label.weekends' => 'Выходные', 'label.work_units' => 'Единицы работы', 'label.work_units_short' => 'Единицы', 'label.totals_only' => 'Только итоги', @@ -228,13 +228,6 @@ 'label.file' => 'Файл', 'label.active_users' => 'Активные пользователи', 'label.inactive_users' => 'Неактивные пользователи', -'label.details' => 'Детали', -'label.budget' => 'Бюджет', -'label.work' => 'Работа', -'label.offer' => 'Предложение', -'label.contractor' => 'Подрядчик', -'label.how_to_pay' => 'Как оплатить', -'label.moderator_comment' => 'Комментарий модератора', // Entity names. We use lower case (in English) because they are used in dropdowns, too. // They are used to associate a custom field with an entity type. @@ -246,6 +239,7 @@ 'title.error' => 'Ошибка', 'title.success' => 'Успех', 'title.login' => 'Вход в систему', +'title.2fa' => 'Двухфакторная аутентификация', 'title.groups' => 'Группы', 'title.add_group' => 'Добавление группы', 'title.edit_group' => 'Редактирование группы', @@ -332,22 +326,6 @@ 'title.edit_file' => 'Редактирование файла', 'title.delete_file' => 'Удаление файла', 'title.download_file' => 'Скачивание файла', -'title.work' => 'Работа', -'title.add_work' => 'Добавление работы', -'title.edit_work' => 'Редактирование работы', -'title.delete_work' => 'Удаление работы', -'title.active_work' => 'Активная работа', -'title.available_work' => 'Имеющаяся работа', -'title.inactive_work' => 'Неактивная работа', -'title.pending_work' => 'Работа в ожидании', -'title.offer' => 'Предложение', -'title.add_offer' => 'Добавление предложения', -'title.edit_offer' => 'Редактирование предложения', -'title.delete_offer' => 'Удаление предложения', -'title.active_offers' => 'Активные предложения', -'title.available_offers' => 'Имеющиеся предложения', -'title.inactive_offers' => 'Неактивные предложения', -'title.pending_offers' => 'Предложения в ожидании', // Section for common strings inside combo boxes on forms. Strings shared between forms shall be placed here. // Strings that are used in a single form must go to the specific form section. @@ -376,7 +354,6 @@ 'dropdown.status_inactive' => 'неактивный', 'dropdown.delete' => 'удалить', 'dropdown.do_not_delete' => 'не удалять', -'dropdown.pending_approval' => 'в ожидании одобрения', 'dropdown.approved' => 'одобрено', 'dropdown.not_approved' => 'не одобрено', 'dropdown.paid' => 'оплачено', @@ -393,6 +370,14 @@ 'form.login.forgot_password' => 'Забыли пароль?', 'form.login.about' => 'Anuko Time Tracker - это открытая (open source) система трекинга рабочего времени.', +// Email subject and body for two-factor authentication. +'email.2fa_code.subject' => 'Anuko Time Tracker код для двухфакторной аутентификации', +'email.2fa_code.body' => "Уважаемый пользователь,\n\nВаш код для двухфакторной аутентификации:\n\n%s\n\n", + +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +'form.2fa.hint' => 'Проверьте электронную почту и введите высланный вам 2FA код здесь.', +'form.2fa.2fa_code' => '2FA код', + // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. 'form.reset_password.message' => 'Запрос на сброс пароля отослан по e-mail.', 'form.reset_password.email_subject' => 'Сброс пароля к Anuko Time Tracker', @@ -428,9 +413,11 @@ 'form.reports.include_pending' => 'в ожидании', 'form.reports.select_period' => 'Выберите интервал времени', 'form.reports.set_period' => 'или укажите даты', +'form.reports.note_containing' => 'Комментарий содержит', 'form.reports.show_fields' => 'Показывать поля', 'form.reports.time_fields' => 'Поля времени', 'form.reports.user_fields' => 'Поля пользователя', +'form.reports.project_fields' => 'Поля проекта', 'form.reports.group_by' => 'Группировать по', 'form.reports.group_by_no' => '--- без группировки ---', 'form.reports.group_by_date' => 'дате', @@ -442,6 +429,7 @@ // Report form. See example at https://timetracker.anuko.com/report.php // (after generating a report at https://timetracker.anuko.com/reports.php). 'form.report.export' => 'Экспортировать', +'form.report.per_hour' => 'За час', 'form.report.assign_to_invoice' => 'Включить в счёт', 'form.report.assign_to_timesheet' => 'Включить в табель', @@ -476,6 +464,7 @@ 'form.tasks.inactive_tasks' => 'Неактивные задачи', // Users form. See example at https://timetracker.anuko.com/users.php +'form.users.uncompleted_entry_today' => 'Пользователь имеет неоконченную запись сегодня', 'form.users.uncompleted_entry' => 'Пользователь имеет неоконченную запись', 'form.users.role' => 'Роль', 'form.users.manager' => 'Менеджер', @@ -530,13 +519,18 @@ 'form.group_edit.type_start_finish' => 'начало и конец', 'form.group_edit.type_duration' => 'длительность', 'form.group_edit.punch_mode' => 'Пробивать время', +'form.group_edit.one_uncompleted' => 'Одна незавершённая', 'form.group_edit.allow_overlap' => 'Возможное перекрывание', 'form.group_edit.future_entries' => 'Будущие записи', 'form.group_edit.uncompleted_indicators' => 'Индикаторы незавершения', 'form.group_edit.confirm_save' => 'Предупреждать при сохранении', -'form.group_edit.allow_ip' => 'Разрешить доступ с IP', 'form.group_edit.advanced_settings' => 'Продвинутые настройки', +// Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. +'form.group_advanced_edit.allow_ip' => 'Разрешить доступ с IP', +'form.group_advanced_edit.password_complexity' => 'Сложность пароля', +'form.group_advanced_edit.2fa' => 'Двухфакторная аутентификация', + // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php 'form.group_delete.hint' => 'Вы уверены, что хотите удалить всю группу?', @@ -596,20 +590,7 @@ 'form.display_options.note_on_separate_row' => 'Комментарий в отдельном ряду', 'form.display_options.not_complete_days' => 'Незавершенные дни', 'form.display_options.inactive_projects' => 'Неактивные проекты', +'form.display_options.cost_per_hour' => 'Стоимость за час', 'form.display_options.custom_css' => 'Пользовательская CSS', - -// Work plugin strings. See example at https://timetracker.anuko.com/work.php -'work.error.work_not_available' => 'Работа отсутствует.', -'work.error.offer_not_available' => 'Предложение отсутствует.', -'work.type.one_time' => 'одноразовая', -'work.type.ongoing' => 'постоянная', -'work.label.own_work' => 'Своя работа', -'work.label.own_offers' => 'Свои предложения', -'work.label.offers' => 'Предложения', -'work.button.send_message' => 'Отправить сообщение', -'work.button.make_offer' => 'Сделать предложение', -'work.button.accept' => 'Принять', -'work.button.decline' => 'Отклонить', -'work.title.send_message' => 'Отсылка сообщения', -'work.msg.message_sent' => 'Сообщение отослано.', +'form.display_options.custom_translation' => 'Пользовательский перевод', ); diff --git a/WEB-INF/resources/sk.lang.php b/WEB-INF/resources/sk.lang.php index da24d8caf..0404fb8d0 100644 --- a/WEB-INF/resources/sk.lang.php +++ b/WEB-INF/resources/sk.lang.php @@ -2,7 +2,8 @@ /* Copyright (c) Anuko International Ltd. https://www.anuko.com License: See license.txt */ -// Note: escape apostrophes with THREE backslashes, like here: choisir l\\\'option. +// Note: escape apostrophes with THREE backslashes, like here: 'choisir l\\\'option'. +// Alternatively: use one backslash and surround by double-quotes: "choisir l\'option". // Other characters (such as double-quotes in http links, etc.) do not have to be escaped. $i18n_language = 'Slovak (Slovenčina)'; @@ -70,6 +71,9 @@ // 'error.report' => 'Select report.', // 'error.record' => 'Select record.', 'error.auth' => 'Nesprávne prihlasovacie meno alebo heslo.', +// TODO: translate the following. +// 'error.2fa_code' => 'Invalid 2FA code.', +// 'error.weak_password' => 'Weak password.', 'error.user_exists' => 'Používateľ s týmto prihlasovacím menom už existuje.', // TODO: translate the following. // 'error.object_exists' => 'Object with this name already exists.', @@ -99,7 +103,6 @@ // 'error.user_count' => 'Limit on user count.', // 'error.expired' => 'Expiration date reached.', // 'error.file_storage' => 'File storage server error.', // See comment in English file. -// 'error.remote_work' => 'Remote work server error.', // See comment in English file. // Warning messages. // TODO: translate the following. @@ -215,9 +218,6 @@ 'label.ldap_hint' => 'Zadajte prihlasovacie meno do Windowsu a heslo do polí nižšie.', 'label.required_fields' => '* - povinné polia', 'label.on_behalf' => 'v zastúpení', -'label.role_manager' => '(manažér)', -'label.role_comanager' => '(spolu-manažér)', -'label.role_admin' => '(administrátor)', // TODO: translate the following. // 'label.page' => 'Page', // 'label.condition' => 'Condition', @@ -245,6 +245,7 @@ // 'label.mark_paid' => 'Mark paid', // 'label.week_note' => 'Week note', // 'label.week_list' => 'Week list', +// 'label.weekends' => 'Weekends', // 'label.work_units' => 'Work units', // 'label.work_units_short' => 'Units', 'label.totals_only' => 'Iba celkové', @@ -263,17 +264,6 @@ // 'label.file' => 'File', 'label.active_users' => 'Aktívny používatelia', 'label.inactive_users' => 'Neaktívny používatelia', -// TODO: translate the following. -// 'label.details' => 'Details', -// 'label.budget' => 'Budget', -// 'label.work' => 'Work', // Table column header for work items, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// 'label.offer' => 'Offer', // Table column header for offers, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// 'label.contractor' => 'Contractor', // Table column header for offers (contractor is someone who offers to do work). - // Technically, it is either an org name or a combination of org and group names - // because both work items and offers are owned by Time Tracker groups of users. -// 'label.how_to_pay' => 'How to pay', // Label for the "How to pay" field on offers, which allows contractors to specify - // how to pay them, for example: paypal email, check by mail to a specific address, etc. -// 'label.moderator_comment' => 'Moderator comment', // Label for "Moderator comment" field that explains something. // Entity names. We use lower case (in English) because they are used in dropdowns, too. // They are used to associate a custom field with an entity type. @@ -287,6 +277,8 @@ // TODO: Translate the following. // 'title.success' => 'Success', 'title.login' => 'Prihlásenie', +// TODO: translate the follolwing. +// 'title.2fa' => 'Two Factor Authentication', 'title.groups' => 'Tímy', // TODO: change "teams" to "groups". // TODO: translate the following. // 'title.add_group' => 'Adding Group', @@ -379,22 +371,6 @@ // 'title.edit_file' => 'Editing File', // 'title.delete_file' => 'Deleting File', // 'title.download_file' => 'Downloading File', -// 'title.work' => 'Work', -// 'title.add_work' => 'Adding Work', -// 'title.edit_work' => 'Editing Work', -// 'title.delete_work' => 'Deleting Work', -// 'title.active_work' => 'Active Work', // Active work items this group outsources to other groups. -// 'title.available_work' => 'Available Work', // Available work items from other organizations. -// 'title.inactive_work' => 'Inactive Work', // Inactive work items this group was outsourcing to other groups. -// 'title.pending_work' => 'Pending Work', // Work items pending moderator approval. -// 'title.offer' => 'Offer', -// 'title.add_offer' => 'Adding Offer', -// 'title.edit_offer' => 'Editing Offer', -// 'title.delete_offer' => 'Deleting Offer', -// 'title.active_offers' => 'Active Offers', // Active offers this group makes available to other groups. -// 'title.available_offers' => 'Available Offers', // Available offers from other organizations. -// 'title.inactive_offers' => 'Inactive Offers', // Inactive offers for group. -// 'title.pending_offers' => 'Pending Offers', // Offers pending moderator approval. // Section for common strings inside combo boxes on forms. Strings shared between forms shall be placed here. // Strings that are used in a single form must go to the specific form section. @@ -428,7 +404,6 @@ // TODO: translate the following. // 'dropdown.delete' => 'delete', // 'dropdown.do_not_delete' => 'do not delete', -// 'dropdown.pending_approval' => 'pending approval', // 'dropdown.approved' => 'approved', // 'dropdown.not_approved' => 'not approved', // 'dropdown.paid' => 'paid', @@ -445,6 +420,16 @@ 'form.login.forgot_password' => 'Zabudnuté heslo?', 'form.login.about' => 'Anuko Time Tracker je systém na sledovanie času s otvoreným zdrojovým kódom.', +// Email subject and body for two-factor authentication. +// TODO: translate the following. +// 'email.2fa_code.subject' => 'Anuko Time Tracker two-factor authentication code', +// 'email.2fa_code.body' => "Dear User,\n\nYour two-factor authentication code is:\n\n%s\n\n", + +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// 'form.2fa.hint' => 'Check your email for 2FA code and enter it here.', +// 'form.2fa.2fa_code' => '2FA code', + // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. 'form.reset_password.message' => 'Žiadosť o obnovenie hesla bola odoslaná e-mailom.', 'form.reset_password.email_subject' => 'Žiadosť o obnovenie hesla do Anuko Time Tracker', @@ -486,10 +471,13 @@ // 'form.reports.include_pending' => 'pending', 'form.reports.select_period' => 'Vyberte časový rozsah', 'form.reports.set_period' => 'alebo nastavte dátumy', +// TODO: translate the following. +// 'form.reports.note_containing' => 'Note containing', 'form.reports.show_fields' => 'Zobraziť polia', // TODO: translate the following. // 'form.reports.time_fields' => 'Time fields', // 'form.reports.user_fields' => 'User fields', +// 'form.reports.project_fields' => 'Project fields', // 'form.reports.group_by' => 'Group by', // 'form.reports.group_by_no' => '--- no grouping ---', 'form.reports.group_by_date' => 'dátum', @@ -502,6 +490,7 @@ // (after generating a report at https://timetracker.anuko.com/reports.php). 'form.report.export' => 'Exportovať', // TODO: translate the following. +// 'form.report.per_hour' => 'Per hour', // 'form.report.assign_to_invoice' => 'Assign to invoice', // 'form.report.assign_to_timesheet' => 'Assign to timesheet', @@ -540,6 +529,7 @@ // Users form. See example at https://timetracker.anuko.com/users.php // TODO: translate the following. +// 'form.users.uncompleted_entry_today' => 'User has an uncompleted time entry today', // 'form.users.uncompleted_entry' => 'User has an uncompleted time entry', 'form.users.role' => 'Rola', 'form.users.manager' => 'Manažér', @@ -601,13 +591,19 @@ 'form.group_edit.type_duration' => 'trvanie', // TODO: translate the following. // 'form.group_edit.punch_mode' => 'Punch mode', +// 'form.group_edit.one_uncompleted' => 'One uncompleted', // 'form.group_edit.allow_overlap' => 'Allow overlap', // 'form.group_edit.future_entries' => 'Future entries', // 'form.group_edit.uncompleted_indicators' => 'Uncompleted indicators', // 'form.group_edit.confirm_save' => 'Confirm saving', -// 'form.group_edit.allow_ip' => 'Allow IP', // 'form.group_edit.advanced_settings' => 'Advanced settings', +// Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. +// TODO: Translate the following. +// 'form.group_advanced_edit.allow_ip' => 'Allow IP', +// 'form.group_advanced_edit.password_complexity' => 'Password complexity', +// 'form.group_advanced_edit.2fa' => 'Two factor authentication', + // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php // TODO: translate the following. // 'form.group_delete.hint' => 'Are you sure you want to delete the entire group?', @@ -674,21 +670,7 @@ // 'form.display_options.note_on_separate_row' => 'Note on separate row', // 'form.display_options.not_complete_days' => 'Not complete days', // 'form.display_options.inactive_projects' => 'Inactive projects', +// 'form.display_options.cost_per_hour' => 'Cost per hour', // 'form.display_options.custom_css' => 'Custom CSS', - -// Work plugin strings. See example at https://timetracker.anuko.com/work.php -// TODO: translate the following. -// 'work.error.work_not_available' => 'Work item is not available.', -// 'work.error.offer_not_available' => 'Offer is not available.', -// 'work.type.one_time' => 'one time', // Work type is "one time job" for well defined work ("do exactly this"). -// 'work.type.ongoing' => 'ongoing', // Work type is "ongoing" for complex jobs (billed by the hour, multiple contractors, etc.) -// 'work.label.own_work' => 'Own work', -// 'work.label.own_offers' => 'Own offers', -// 'work.label.offers' => 'Offers', -// 'work.button.send_message' => 'Send message', -// 'work.button.make_offer' => 'Make offer', -// 'work.button.accept' => 'Accept', -// 'work.button.decline' => 'Decline', -// 'work.title.send_message' => 'Sending Message', -// 'work.msg.message_sent' => 'Message sent.', +// 'form.display_options.custom_translation' => 'Custom translation', ); diff --git a/WEB-INF/resources/sl.lang.php b/WEB-INF/resources/sl.lang.php index 40abaabea..b3153ad79 100644 --- a/WEB-INF/resources/sl.lang.php +++ b/WEB-INF/resources/sl.lang.php @@ -2,7 +2,8 @@ /* Copyright (c) Anuko International Ltd. https://www.anuko.com License: See license.txt */ -// Note: escape apostrophes with THREE backslashes, like here: choisir l\\\'option. +// Note: escape apostrophes with THREE backslashes, like here: 'choisir l\\\'option'. +// Alternatively: use one backslash and surround by double-quotes: "choisir l\'option". // Other characters (such as double-quotes in http links, etc.) do not have to be escaped. // Note to translators: Use proper capitalization rules for your language. @@ -69,6 +70,8 @@ // 'error.report' => 'Select report.', // 'error.record' => 'Select record.', // 'error.auth' => 'Incorrect login or password.', +// 'error.2fa_code' => 'Invalid 2FA code.', +// 'error.weak_password' => 'Weak password.', // 'error.user_exists' => 'User with this login already exists.', // 'error.object_exists' => 'Object with this name already exists.', // 'error.invoice_exists' => 'Invoice with this number already exists.', @@ -91,7 +94,6 @@ // 'error.user_count' => 'Limit on user count.', // 'error.expired' => 'Expiration date reached.', // 'error.file_storage' => 'File storage server error.', // See comment in English file. -// 'error.remote_work' => 'Remote work server error.', // See comment in English file. // Warning messages. // TODO: translate the following. @@ -209,9 +211,6 @@ // 'label.ldap_hint' => 'Type your Windows login and password in the fields below.', // 'label.required_fields' => '* - required fields', // 'label.on_behalf' => 'on behalf of', -// 'label.role_manager' => '(manager)', -// 'label.role_comanager' => '(co-manager)', -// 'label.role_admin' => '(administrator)', // 'label.page' => 'Page', // 'label.condition' => 'Condition', // 'label.yes' => 'yes', @@ -236,6 +235,7 @@ // 'label.mark_paid' => 'Mark paid', // 'label.week_note' => 'Week note', // 'label.week_list' => 'Week list', +// 'label.weekends' => 'Weekends', // 'label.work_units' => 'Work units', // 'label.work_units_short' => 'Units', // 'label.totals_only' => 'Totals only', @@ -253,16 +253,6 @@ // 'label.file' => 'File', // 'label.active_users' => 'Active Users', // 'label.inactive_users' => 'Inactive Users', -// 'label.details' => 'Details', -// 'label.budget' => 'Budget', -// 'label.work' => 'Work', // Table column header for work items, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// 'label.offer' => 'Offer', // Table column header for offers, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// 'label.contractor' => 'Contractor', // Table column header for offers (contractor is someone who offers to do work). - // Technically, it is either an org name or a combination of org and group names - // because both work items and offers are owned by Time Tracker groups of users. -// 'label.how_to_pay' => 'How to pay', // Label for the "How to pay" field on offers, which allows contractors to specify - // how to pay them, for example: paypal email, check by mail to a specific address, etc. -// 'label.moderator_comment' => 'Moderator comment', // Label for "Moderator comment" field that explains something. // Entity names. We use lower case (in English) because they are used in dropdowns, too. // They are used to associate a custom field with an entity type. @@ -276,6 +266,8 @@ // 'title.error' => 'Error', // 'title.success' => 'Success', 'title.login' => 'Prijava', +// TODO: translate the follolwing. +// 'title.2fa' => 'Two Factor Authentication', 'title.groups' => 'Timi', // TODO: change "teams" to "groups". // TODO: translate the following. // 'title.add_group' => 'Adding Group', @@ -369,22 +361,6 @@ // 'title.edit_file' => 'Editing File', // 'title.delete_file' => 'Deleting File', // 'title.download_file' => 'Downloading File', -// 'title.work' => 'Work', -// 'title.add_work' => 'Adding Work', -// 'title.edit_work' => 'Editing Work', -// 'title.delete_work' => 'Deleting Work', -// 'title.active_work' => 'Active Work', // Active work items this group outsources to other groups. -// 'title.available_work' => 'Available Work', // Available work items from other organizations. -// 'title.inactive_work' => 'Inactive Work', // Inactive work items this group was outsourcing to other groups. -// 'title.pending_work' => 'Pending Work', // Work items pending moderator approval. -// 'title.offer' => 'Offer', -// 'title.add_offer' => 'Adding Offer', -// 'title.edit_offer' => 'Editing Offer', -// 'title.delete_offer' => 'Deleting Offer', -// 'title.active_offers' => 'Active Offers', // Active offers this group makes available to other groups. -// 'title.available_offers' => 'Available Offers', // Available offers from other organizations. -// 'title.inactive_offers' => 'Inactive Offers', // Inactive offers for group. -// 'title.pending_offers' => 'Pending Offers', // Offers pending moderator approval. // Section for common strings inside combo boxes on forms. Strings shared between forms shall be placed here. // Strings that are used in a single form must go to the specific form section. @@ -415,7 +391,6 @@ // 'dropdown.status_inactive' => 'inactive', // 'dropdown.delete' => 'delete', // 'dropdown.do_not_delete' => 'do not delete', -// 'dropdown.pending_approval' => 'pending approval', // 'dropdown.approved' => 'approved', // 'dropdown.not_approved' => 'not approved', // 'dropdown.paid' => 'paid', @@ -428,6 +403,16 @@ // 'form.login.forgot_password' => 'Forgot password?', // 'form.login.about' => 'Anuko Time Tracker is an open source time tracking system.', +// Email subject and body for two-factor authentication. +// TODO: translate the following. +// 'email.2fa_code.subject' => 'Anuko Time Tracker two-factor authentication code', +// 'email.2fa_code.body' => "Dear User,\n\nYour two-factor authentication code is:\n\n%s\n\n", + +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// 'form.2fa.hint' => 'Check your email for 2FA code and enter it here.', +// 'form.2fa.2fa_code' => '2FA code', + // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. 'form.reset_password.message' => 'Zahteva za razveljavitev gesla je bila poslana.', // TODO: add "by email" to match the English string. 'form.reset_password.email_subject' => 'Anuko Time Tracker zahteva za razveljavitev gesla', @@ -472,9 +457,11 @@ // 'form.reports.include_pending' => 'pending', // 'form.reports.select_period' => 'Select time period', // 'form.reports.set_period' => 'or set dates', +// 'form.reports.note_containing' => 'Note containing', // 'form.reports.show_fields' => 'Show fields', // 'form.reports.time_fields' => 'Time fields', // 'form.reports.user_fields' => 'User fields', +// 'form.reports.project_fields' => 'Project fields', // 'form.reports.group_by' => 'Group by', // 'form.reports.group_by_no' => '--- no grouping ---', // 'form.reports.group_by_date' => 'date', @@ -488,6 +475,7 @@ // (after generating a report at https://timetracker.anuko.com/reports.php). // TODO: translate the following. // 'form.report.export' => 'Export', +// 'form.report.per_hour' => 'Per hour', // 'form.report.assign_to_invoice' => 'Assign to invoice', // 'form.report.assign_to_timesheet' => 'Assign to timesheet', @@ -529,6 +517,7 @@ // Users form. See example at https://timetracker.anuko.com/users.php // TODO: translate the following. +// 'form.users.uncompleted_entry_today' => 'User has an uncompleted time entry today', // 'form.users.uncompleted_entry' => 'User has an uncompleted time entry', // 'form.users.role' => 'Role', // 'form.users.manager' => 'Manager', @@ -591,13 +580,19 @@ // 'form.group_edit.type_start_finish' => 'start and finish', // 'form.group_edit.type_duration' => 'duration', // 'form.group_edit.punch_mode' => 'Punch mode', +// 'form.group_edit.one_uncompleted' => 'One uncompleted', // 'form.group_edit.allow_overlap' => 'Allow overlap', // 'form.group_edit.future_entries' => 'Future entries', // 'form.group_edit.uncompleted_indicators' => 'Uncompleted indicators', // 'form.group_edit.confirm_save' => 'Confirm saving', -// 'form.group_edit.allow_ip' => 'Allow IP', // 'form.group_edit.advanced_settings' => 'Advanced settings', +// Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. +// TODO: Translate the following. +// 'form.group_advanced_edit.allow_ip' => 'Allow IP', +// 'form.group_advanced_edit.password_complexity' => 'Password complexity', +// 'form.group_advanced_edit.2fa' => 'Two factor authentication', + // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php // TODO: translate the following. // 'form.group_delete.hint' => 'Are you sure you want to delete the entire group?', @@ -665,21 +660,7 @@ // 'form.display_options.note_on_separate_row' => 'Note on separate row', // 'form.display_options.not_complete_days' => 'Not complete days', // 'form.display_options.inactive_projects' => 'Inactive projects', +// 'form.display_options.cost_per_hour' => 'Cost per hour', // 'form.display_options.custom_css' => 'Custom CSS', - -// Work plugin strings. See example at https://timetracker.anuko.com/work.php -// TODO: translate the following. -// 'work.error.work_not_available' => 'Work item is not available.', -// 'work.error.offer_not_available' => 'Offer is not available.', -// 'work.type.one_time' => 'one time', // Work type is "one time job" for well defined work ("do exactly this"). -// 'work.type.ongoing' => 'ongoing', // Work type is "ongoing" for complex jobs (billed by the hour, multiple contractors, etc.) -// 'work.label.own_work' => 'Own work', -// 'work.label.own_offers' => 'Own offers', -// 'work.label.offers' => 'Offers', -// 'work.button.send_message' => 'Send message', -// 'work.button.make_offer' => 'Make offer', -// 'work.button.accept' => 'Accept', -// 'work.button.decline' => 'Decline', -// 'work.title.send_message' => 'Sending Message', -// 'work.msg.message_sent' => 'Message sent.', +// 'form.display_options.custom_translation' => 'Custom translation', ); diff --git a/WEB-INF/resources/sr.lang.php b/WEB-INF/resources/sr.lang.php index 8e9fe4e8f..6902d9223 100644 --- a/WEB-INF/resources/sr.lang.php +++ b/WEB-INF/resources/sr.lang.php @@ -2,7 +2,8 @@ /* Copyright (c) Anuko International Ltd. https://www.anuko.com License: See license.txt */ -// Note: escape apostrophes with THREE backslashes, like here: choisir l\\\'option. +// Note: escape apostrophes with THREE backslashes, like here: 'choisir l\\\'option'. +// Alternatively: use one backslash and surround by double-quotes: "choisir l\'option". // Other characters (such as double-quotes in http links, etc.) do not have to be escaped. $i18n_language = 'Serbian (Srpski)'; @@ -66,6 +67,9 @@ // TODO: translate the following. // 'error.record' => 'Select record.', 'error.auth' => 'Pogrešno korisničko ime ili lozinka.', +// TODO: translate the following. +// 'error.2fa_code' => 'Invalid 2FA code.', +// 'error.weak_password' => 'Weak password.', 'error.user_exists' => 'Korisnik pod ovim imenom već postoji.', // TODO: translate the following. // 'error.object_exists' => 'Object with this name already exists.', @@ -95,7 +99,6 @@ // 'error.user_count' => 'Limit on user count.', // 'error.expired' => 'Expiration date reached.', // 'error.file_storage' => 'File storage server error.', // See comment in English file. -// 'error.remote_work' => 'Remote work server error.', // See comment in English file. // Warning messages. // TODO: translate the following. @@ -210,9 +213,6 @@ 'label.ldap_hint' => 'Unesi tvoju Windows prijavu i lozinku u polje ispod.', 'label.required_fields' => '* - obavezna polja', 'label.on_behalf' => 'ispred', -'label.role_manager' => '(menadžer)', -'label.role_comanager' => '(saradnik)', -'label.role_admin' => '(administrator)', 'label.page' => 'Strana', // TODO: translate the following. // 'label.condition' => 'Condition', @@ -241,6 +241,7 @@ // 'label.mark_paid' => 'Mark paid', // 'label.week_note' => 'Week note', // 'label.week_list' => 'Week list', +// 'label.weekends' => 'Weekends', // 'label.work_units' => 'Work units', 'label.totals_only' => 'Samo zbirno', // TODO: translate the following. @@ -258,17 +259,6 @@ // 'label.file' => 'File', 'label.active_users' => 'Aktivni korisnik', 'label.inactive_users' => 'Neaktivni korisnik', -// TODO: translate the following. -// 'label.details' => 'Details', -// 'label.budget' => 'Budget', -// 'label.work' => 'Work', // Table column header for work items, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// 'label.offer' => 'Offer', // Table column header for offers, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// 'label.contractor' => 'Contractor', // Table column header for offers (contractor is someone who offers to do work). - // Technically, it is either an org name or a combination of org and group names - // because both work items and offers are owned by Time Tracker groups of users. -// 'label.how_to_pay' => 'How to pay', // Label for the "How to pay" field on offers, which allows contractors to specify - // how to pay them, for example: paypal email, check by mail to a specific address, etc. -// 'label.moderator_comment' => 'Moderator comment', // Label for "Moderator comment" field that explains something. // Entity names. We use lower case (in English) because they are used in dropdowns, too. // They are used to associate a custom field with an entity type. @@ -282,6 +272,8 @@ // TODO: Translate the following. // 'title.success' => 'Success', 'title.login' => 'Prijava', +// TODO: translate the follolwing. +// 'title.2fa' => 'Two Factor Authentication', 'title.groups' => 'Timovi', // TODO: change "teams" to "groups". // TODO: translate the following. // 'title.add_group' => 'Adding Group', @@ -375,22 +367,6 @@ // 'title.edit_file' => 'Editing File', // 'title.delete_file' => 'Deleting File', // 'title.download_file' => 'Downloading File', -// 'title.work' => 'Work', -// 'title.add_work' => 'Adding Work', -// 'title.edit_work' => 'Editing Work', -// 'title.delete_work' => 'Deleting Work', -// 'title.active_work' => 'Active Work', // Active work items this group outsources to other groups. -// 'title.available_work' => 'Available Work', // Available work items from other organizations. -// 'title.inactive_work' => 'Inactive Work', // Inactive work items this group was outsourcing to other groups. -// 'title.pending_work' => 'Pending Work', // Work items pending moderator approval. -// 'title.offer' => 'Offer', -// 'title.add_offer' => 'Adding Offer', -// 'title.edit_offer' => 'Editing Offer', -// 'title.delete_offer' => 'Deleting Offer', -// 'title.active_offers' => 'Active Offers', // Active offers this group makes available to other groups. -// 'title.available_offers' => 'Available Offers', // Available offers from other organizations. -// 'title.inactive_offers' => 'Inactive Offers', // Inactive offers for group. -// 'title.pending_offers' => 'Pending Offers', // Offers pending moderator approval. // Section for common strings inside combo boxes on forms. Strings shared between forms shall be placed here. // Strings that are used in a single form must go to the specific form section. @@ -423,7 +399,6 @@ 'dropdown.delete' => 'obriši', 'dropdown.do_not_delete' => 'nemoj obrisati', // TODO: translate the following. -// 'dropdown.pending_approval' => 'pending approval', // 'dropdown.approved' => 'approved', // 'dropdown.not_approved' => 'not approved', // 'dropdown.paid' => 'paid', @@ -442,6 +417,16 @@ // 'form.login.about' => 'Anuko Time Tracker is an open source time tracking system.', 'form.login.about' => 'Anuko Time Tracker je jednostavan i lak za korišćenje za praćenje radnog vremena.', +// Email subject and body for two-factor authentication. +// TODO: translate the following. +// 'email.2fa_code.subject' => 'Anuko Time Tracker two-factor authentication code', +// 'email.2fa_code.body' => "Dear User,\n\nYour two-factor authentication code is:\n\n%s\n\n", + +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// 'form.2fa.hint' => 'Check your email for 2FA code and enter it here.', +// 'form.2fa.2fa_code' => '2FA code', + // Izmena forme za lozinku. Pogledajte primer na https://timetracker.anuko.com/password_reset.php. 'form.reset_password.message' => 'Zahtev za izmenu lozinke je poslat mejlom.', 'form.reset_password.email_subject' => 'Anuko Time Tracker zahtev za izmenu lozinke', @@ -483,10 +468,13 @@ // 'form.reports.include_pending' => 'pending', 'form.reports.select_period' => 'Odaberi vremenski raspon', 'form.reports.set_period' => 'ili podesi datum', +// TODO: translate the following. +// 'form.reports.note_containing' => 'Note containing', 'form.reports.show_fields' => 'Prikaži polja u izveštaju', // TODO: translate the following. // 'form.reports.time_fields' => 'Time fields', // 'form.reports.user_fields' => 'User fields', +// 'form.reports.project_fields' => 'Project fields', 'form.reports.group_by' => 'Grupiši po', 'form.reports.group_by_no' => '--- nemoj grupisati ---', 'form.reports.group_by_date' => 'datum', @@ -499,6 +487,7 @@ // (after generating a report at https://timetracker.anuko.com/reports.php). 'form.report.export' => 'Izvoz', // TODO: translate the following. +// 'form.report.per_hour' => 'Per hour', // 'form.report.assign_to_invoice' => 'Assign to invoice', // 'form.report.assign_to_timesheet' => 'Assign to timesheet', @@ -537,6 +526,7 @@ // Korisnička forma. Pogledajte primer na https://timetracker.anuko.com/users.php // TODO: translate the following. +// 'form.users.uncompleted_entry_today' => 'User has an uncompleted time entry today', // 'form.users.uncompleted_entry' => 'User has an uncompleted time entry', 'form.users.role' => 'Funkcija', 'form.users.manager' => 'Menadžer', @@ -597,13 +587,19 @@ 'form.group_edit.type_duration' => 'trajanje', // TODO: translate the following. // 'form.group_edit.punch_mode' => 'Punch mode', +// 'form.group_edit.one_uncompleted' => 'One uncompleted', // 'form.group_edit.allow_overlap' => 'Allow overlap', // 'form.group_edit.future_entries' => 'Future entries', // 'form.group_edit.uncompleted_indicators' => 'Uncompleted indicators', // 'form.group_edit.confirm_save' => 'Confirm saving', -// 'form.group_edit.allow_ip' => 'Allow IP', // 'form.group_edit.advanced_settings' => 'Advanced settings', +// Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. +// TODO: Translate the following. +// 'form.group_advanced_edit.allow_ip' => 'Allow IP', +// 'form.group_advanced_edit.password_complexity' => 'Password complexity', +// 'form.group_advanced_edit.2fa' => 'Two factor authentication', + // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php // TODO: translate the following. // 'form.group_delete.hint' => 'Are you sure you want to delete the entire group?', @@ -672,21 +668,7 @@ // 'form.display_options.note_on_separate_row' => 'Note on separate row', // 'form.display_options.not_complete_days' => 'Not complete days', // 'form.display_options.inactive_projects' => 'Inactive projects', +// 'form.display_options.cost_per_hour' => 'Cost per hour', // 'form.display_options.custom_css' => 'Custom CSS', - -// Work plugin strings. See example at https://timetracker.anuko.com/work.php -// TODO: translate the following. -// 'work.error.work_not_available' => 'Work item is not available.', -// 'work.error.offer_not_available' => 'Offer is not available.', -// 'work.type.one_time' => 'one time', // Work type is "one time job" for well defined work ("do exactly this"). -// 'work.type.ongoing' => 'ongoing', // Work type is "ongoing" for complex jobs (billed by the hour, multiple contractors, etc.) -// 'work.label.own_work' => 'Own work', -// 'work.label.own_offers' => 'Own offers', -// 'work.label.offers' => 'Offers', -// 'work.button.send_message' => 'Send message', -// 'work.button.make_offer' => 'Make offer', -// 'work.button.accept' => 'Accept', -// 'work.button.decline' => 'Decline', -// 'work.title.send_message' => 'Sending Message', -// 'work.msg.message_sent' => 'Message sent.', +// 'form.display_options.custom_translation' => 'Custom translation', ); diff --git a/WEB-INF/resources/sv.lang.php b/WEB-INF/resources/sv.lang.php index bd025f84b..dba0fd6f2 100644 --- a/WEB-INF/resources/sv.lang.php +++ b/WEB-INF/resources/sv.lang.php @@ -2,7 +2,8 @@ /* Copyright (c) Anuko International Ltd. https://www.anuko.com License: See license.txt */ -// Note: escape apostrophes with THREE backslashes, like here: choisir l\\\'option. +// Note: escape apostrophes with THREE backslashes, like here: 'choisir l\\\'option'. +// Alternatively: use one backslash and surround by double-quotes: "choisir l\'option". // Other characters (such as double-quotes in http links, etc.) do not have to be escaped. $i18n_language = 'Swedish (Svenska)'; @@ -65,6 +66,9 @@ // TODO: translate the following. // 'error.record' => 'Select record.', 'error.auth' => 'Ogiltigt användarnamn eller lösenord.', +// TODO: translate the following. +// 'error.2fa_code' => 'Invalid 2FA code.', +// 'error.weak_password' => 'Weak password.', 'error.user_exists' => 'Det finns redan en användare med det här användarnamnet.', // TODO: translate the following. // 'error.object_exists' => 'Object with this name already exists.', @@ -93,7 +97,6 @@ // 'error.user_count' => 'Limit on user count.', // 'error.expired' => 'Expiration date reached.', // 'error.file_storage' => 'File storage server error.', // See comment in English file. -// 'error.remote_work' => 'Remote work server error.', // See comment in English file. // Warning messages. // TODO: translate the following. @@ -206,9 +209,6 @@ 'label.ldap_hint' => 'Fyll i ditt användarnamn och lösenord för Windows i fälten nedan.', 'label.required_fields' => '* - Obligatoriska fält', 'label.on_behalf' => 'agerar som', -'label.role_manager' => '(Ansvarig)', -'label.role_comanager' => '(Delansvarig)', -'label.role_admin' => '(Administratör)', 'label.page' => 'Sida', 'label.condition' => 'Villkor', // TODO: translate the following. @@ -236,6 +236,7 @@ // 'label.mark_paid' => 'Mark paid', // 'label.week_note' => 'Week note', // 'label.week_list' => 'Week list', +// 'label.weekends' => 'Weekends', // 'label.work_units' => 'Work units', // 'label.work_units_short' => 'Units', 'label.totals_only' => 'Visa endast summeringar', @@ -254,17 +255,6 @@ // 'label.file' => 'File', 'label.active_users' => 'Aktiva användare', 'label.inactive_users' => 'Inaktiva användare', -// TODO: translate the following. -// 'label.details' => 'Details', -// 'label.budget' => 'Budget', -// 'label.work' => 'Work', // Table column header for work items, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// 'label.offer' => 'Offer', // Table column header for offers, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// 'label.contractor' => 'Contractor', // Table column header for offers (contractor is someone who offers to do work). - // Technically, it is either an org name or a combination of org and group names - // because both work items and offers are owned by Time Tracker groups of users. -// 'label.how_to_pay' => 'How to pay', // Label for the "How to pay" field on offers, which allows contractors to specify - // how to pay them, for example: paypal email, check by mail to a specific address, etc. -// 'label.moderator_comment' => 'Moderator comment', // Label for "Moderator comment" field that explains something. // Entity names. We use lower case (in English) because they are used in dropdowns, too. // They are used to associate a custom field with an entity type. @@ -278,6 +268,8 @@ // TODO: Translate the following. // 'title.success' => 'Success', 'title.login' => 'Logga in', +// TODO: translate the follolwing. +// 'title.2fa' => 'Two Factor Authentication', 'title.groups' => 'Grupper', // TODO: translate the following. // 'title.add_group' => 'Adding Group', @@ -375,22 +367,6 @@ // 'title.edit_file' => 'Editing File', // 'title.delete_file' => 'Deleting File', // 'title.download_file' => 'Downloading File', -// 'title.work' => 'Work', -// 'title.add_work' => 'Adding Work', -// 'title.edit_work' => 'Editing Work', -// 'title.delete_work' => 'Deleting Work', -// 'title.active_work' => 'Active Work', // Active work items this group outsources to other groups. -// 'title.available_work' => 'Available Work', // Available work items from other organizations. -// 'title.inactive_work' => 'Inactive Work', // Inactive work items this group was outsourcing to other groups. -// 'title.pending_work' => 'Pending Work', // Work items pending moderator approval. -// 'title.offer' => 'Offer', -// 'title.add_offer' => 'Adding Offer', -// 'title.edit_offer' => 'Editing Offer', -// 'title.delete_offer' => 'Deleting Offer', -// 'title.active_offers' => 'Active Offers', // Active offers this group makes available to other groups. -// 'title.available_offers' => 'Available Offers', // Available offers from other organizations. -// 'title.inactive_offers' => 'Inactive Offers', // Inactive offers for group. -// 'title.pending_offers' => 'Pending Offers', // Offers pending moderator approval. // Section for common strings inside combo boxes on forms. Strings shared between forms shall be placed here. // Strings that are used in a single form must go to the specific form section. @@ -421,7 +397,6 @@ 'dropdown.delete' => 'Ta bort', 'dropdown.do_not_delete' => 'Ta inte bort', // TODO: translate the following. -// 'dropdown.pending_approval' => 'pending approval', // 'dropdown.approved' => 'approved', // 'dropdown.not_approved' => 'not approved', // 'dropdown.paid' => 'paid', @@ -440,6 +415,16 @@ // 'form.login.about' => 'Anuko Time Tracker is an open source time tracking system.', 'form.login.about' => 'Anuko Time Tracker är en lättanvänd applikation byggd med öppen källkod som enkelt låter dig spåra och hålla koll på arbetstider.', +// Email subject and body for two-factor authentication. +// TODO: translate the following. +// 'email.2fa_code.subject' => 'Anuko Time Tracker two-factor authentication code', +// 'email.2fa_code.body' => "Dear User,\n\nYour two-factor authentication code is:\n\n%s\n\n", + +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// 'form.2fa.hint' => 'Check your email for 2FA code and enter it here.', +// 'form.2fa.2fa_code' => '2FA code', + // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. 'form.reset_password.message' => 'Begäran om att återställa lösenordet skickades via e-post.', 'form.reset_password.email_subject' => 'Återställning av lösenord för Anuko Time Tracker begärd', @@ -481,10 +466,13 @@ // 'form.reports.include_pending' => 'pending', 'form.reports.select_period' => 'Välj intervall', 'form.reports.set_period' => 'eller ställ in datum', +// TODO: translate the following. +// 'form.reports.note_containing' => 'Note containing', 'form.reports.show_fields' => 'Visa fält', // TODO: translate the following. // 'form.reports.time_fields' => 'Time fields', // 'form.reports.user_fields' => 'User fields', +// 'form.reports.project_fields' => 'Project fields', 'form.reports.group_by' => 'Gruppera efter', 'form.reports.group_by_no' => '--- Ingen gruppering ---', 'form.reports.group_by_date' => 'Datum', @@ -497,6 +485,7 @@ // (after generating a report at https://timetracker.anuko.com/reports.php). 'form.report.export' => 'Exportera som', // TODO: translate the following. +// 'form.report.per_hour' => 'Per hour', // 'form.report.assign_to_invoice' => 'Assign to invoice', // 'form.report.assign_to_timesheet' => 'Assign to timesheet', @@ -534,6 +523,8 @@ 'form.tasks.inactive_tasks' => 'Inaktiva arbetsuppgifter', // Users form. See example at https://timetracker.anuko.com/users.php +// TODO: translate the following. +// 'form.users.uncompleted_entry_today' => 'User has an uncompleted time entry today', 'form.users.uncompleted_entry' => 'Användaren har en oavslutad tidsregistrering', 'form.users.role' => 'Roll', 'form.users.manager' => 'Ansvarig', @@ -594,14 +585,20 @@ 'form.group_edit.type_duration' => 'Varaktighet', // TODO: translate the following. // 'form.group_edit.punch_mode' => 'Punch mode', +// 'form.group_edit.one_uncompleted' => 'One uncompleted', // 'form.group_edit.allow_overlap' => 'Allow overlap', // 'form.group_edit.future_entries' => 'Future entries', 'form.group_edit.uncompleted_indicators' => 'Indikatorer för oavslutad registrering', // TODO: translate the following. // 'form.group_edit.confirm_save' => 'Confirm saving', -// 'form.group_edit.allow_ip' => 'Allow IP', // 'form.group_edit.advanced_settings' => 'Advanced settings', +// Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. +// TODO: Translate the following. +// 'form.group_advanced_edit.allow_ip' => 'Allow IP', +// 'form.group_advanced_edit.password_complexity' => 'Password complexity', +// 'form.group_advanced_edit.2fa' => 'Two factor authentication', + // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php // TODO: translate the following. // 'form.group_delete.hint' => 'Are you sure you want to delete the entire group?', @@ -669,21 +666,7 @@ // 'form.display_options.note_on_separate_row' => 'Note on separate row', // 'form.display_options.not_complete_days' => 'Not complete days', // 'form.display_options.inactive_projects' => 'Inactive projects', +// 'form.display_options.cost_per_hour' => 'Cost per hour', // 'form.display_options.custom_css' => 'Custom CSS', - -// Work plugin strings. See example at https://timetracker.anuko.com/work.php -// TODO: translate the following. -// 'work.error.work_not_available' => 'Work item is not available.', -// 'work.error.offer_not_available' => 'Offer is not available.', -// 'work.type.one_time' => 'one time', // Work type is "one time job" for well defined work ("do exactly this"). -// 'work.type.ongoing' => 'ongoing', // Work type is "ongoing" for complex jobs (billed by the hour, multiple contractors, etc.) -// 'work.label.own_work' => 'Own work', -// 'work.label.own_offers' => 'Own offers', -// 'work.label.offers' => 'Offers', -// 'work.button.send_message' => 'Send message', -// 'work.button.make_offer' => 'Make offer', -// 'work.button.accept' => 'Accept', -// 'work.button.decline' => 'Decline', -// 'work.title.send_message' => 'Sending Message', -// 'work.msg.message_sent' => 'Message sent.', +// 'form.display_options.custom_translation' => 'Custom translation', ); diff --git a/WEB-INF/resources/tr.lang.php b/WEB-INF/resources/tr.lang.php index 5ac60a72d..f2531ee76 100644 --- a/WEB-INF/resources/tr.lang.php +++ b/WEB-INF/resources/tr.lang.php @@ -2,7 +2,8 @@ /* Copyright (c) Anuko International Ltd. https://www.anuko.com License: See license.txt */ -// Note: escape apostrophes with THREE backslashes, like here: choisir l\\\'option. +// Note: escape apostrophes with THREE backslashes, like here: 'choisir l\\\'option'. +// Alternatively: use one backslash and surround by double-quotes: "choisir l\'option". // Other characters (such as double-quotes in http links, etc.) do not have to be escaped. // Note to translators: Use proper capitalization rules for your language. @@ -81,6 +82,8 @@ // 'error.record' => 'Select record.', 'error.auth' => 'Hatalı kullanıcı adı veya parola.', // TODO: translate the following. +// 'error.2fa_code' => 'Invalid 2FA code.', +// 'error.weak_password' => 'Weak password.', // 'error.user_exists' => 'User with this login already exists.', // 'error.object_exists' => 'Object with this name already exists.', // 'error.invoice_exists' => 'Invoice with this number already exists.', @@ -104,7 +107,6 @@ // 'error.user_count' => 'Limit on user count.', // 'error.expired' => 'Expiration date reached.', // 'error.file_storage' => 'File storage server error.', // See comment in English file. -// 'error.remote_work' => 'Remote work server error.', // See comment in English file. // Warning messages. // TODO: translate the following. @@ -230,9 +232,6 @@ // 'label.ldap_hint' => 'Type your Windows login and password in the fields below.', 'label.required_fields' => '* zorunlu bilgi', 'label.on_behalf' => 'adına', -'label.role_manager' => '(yönetici)', -'label.role_comanager' => '(yardımcı yönetici)', -'label.role_admin' => '(sistem yönetici)', // TODO: translate the following. // 'label.page' => 'Page', // 'label.condition' => 'Condition', @@ -259,6 +258,7 @@ // 'label.mark_paid' => 'Mark paid', // 'label.week_note' => 'Week note', // 'label.week_list' => 'Week list', +// 'label.weekends' => 'Weekends', // 'label.work_units' => 'Work units', // 'label.work_units_short' => 'Units', 'label.totals_only' => 'Sadece toplamlar', @@ -277,16 +277,6 @@ // 'label.file' => 'File', // 'label.active_users' => 'Active Users', // 'label.inactive_users' => 'Inactive Users', -// 'label.details' => 'Details', -// 'label.budget' => 'Budget', -// 'label.work' => 'Work', // Table column header for work items, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// 'label.offer' => 'Offer', // Table column header for offers, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// 'label.contractor' => 'Contractor', // Table column header for offers (contractor is someone who offers to do work). - // Technically, it is either an org name or a combination of org and group names - // because both work items and offers are owned by Time Tracker groups of users. -// 'label.how_to_pay' => 'How to pay', // Label for the "How to pay" field on offers, which allows contractors to specify - // how to pay them, for example: paypal email, check by mail to a specific address, etc. -// 'label.moderator_comment' => 'Moderator comment', // Label for "Moderator comment" field that explains something. // Entity names. We use lower case (in English) because they are used in dropdowns, too. // They are used to associate a custom field with an entity type. @@ -305,7 +295,9 @@ // 'title.error' => 'Error', // 'title.success' => 'Success', 'title.login' => 'Giriş', -'title.groups' => 'Ekipler', // TODO: change "teams" to "groups". +// TODO: translate the follolwing. +// 'title.2fa' => 'Two Factor Authentication', + 'title.groups' => 'Ekipler', // TODO: change "teams" to "groups". // TODO: translate the following. // 'title.add_group' => 'Adding Group', // 'title.edit_group' => 'Editing Group', @@ -403,22 +395,6 @@ // 'title.edit_file' => 'Editing File', // 'title.delete_file' => 'Deleting File', // 'title.download_file' => 'Downloading File', -// 'title.work' => 'Work', -// 'title.add_work' => 'Adding Work', -// 'title.edit_work' => 'Editing Work', -// 'title.delete_work' => 'Deleting Work', -// 'title.active_work' => 'Active Work', // Active work items this group outsources to other groups. -// 'title.available_work' => 'Available Work', // Available work items from other organizations. -// 'title.inactive_work' => 'Inactive Work', // Inactive work items this group was outsourcing to other groups. -// 'title.pending_work' => 'Pending Work', // Work items pending moderator approval. -// 'title.offer' => 'Offer', -// 'title.add_offer' => 'Adding Offer', -// 'title.edit_offer' => 'Editing Offer', -// 'title.delete_offer' => 'Deleting Offer', -// 'title.active_offers' => 'Active Offers', // Active offers this group makes available to other groups. -// 'title.available_offers' => 'Available Offers', // Available offers from other organizations. -// 'title.inactive_offers' => 'Inactive Offers', // Inactive offers for group. -// 'title.pending_offers' => 'Pending Offers', // Offers pending moderator approval. // Section for common strings inside combo boxes on forms. Strings shared between forms shall be placed here. // Strings that are used in a single form must go to the specific form section. @@ -451,7 +427,6 @@ // 'dropdown.status_inactive' => 'inactive', // 'dropdown.delete' => 'delete', // 'dropdown.do_not_delete' => 'do not delete', -// 'dropdown.pending_approval' => 'pending approval', // 'dropdown.approved' => 'approved', // 'dropdown.not_approved' => 'not approved', // 'dropdown.paid' => 'paid', @@ -464,6 +439,16 @@ // TODO: translate the following. // 'form.login.about' => 'Anuko Time Tracker is an open source time tracking system.', +// Email subject and body for two-factor authentication. +// TODO: translate the following. +// 'email.2fa_code.subject' => 'Anuko Time Tracker two-factor authentication code', +// 'email.2fa_code.body' => "Dear User,\n\nYour two-factor authentication code is:\n\n%s\n\n", + +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// 'form.2fa.hint' => 'Check your email for 2FA code and enter it here.', +// 'form.2fa.2fa_code' => '2FA code', + // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. 'form.reset_password.message' => 'Parola sıfırlama talebi yollandı.', // TODO: add "by email" to match the English string. 'form.reset_password.email_subject' => 'Anuko Time Tracker parola sıfırlama talebi', @@ -509,10 +494,13 @@ // 'form.reports.include_pending' => 'pending', 'form.reports.select_period' => 'Zaman aralığını seç', 'form.reports.set_period' => 'ya da tarihleri belirle', +// TODO: translate the following. +// 'form.reports.note_containing' => 'Note containing', 'form.reports.show_fields' => 'Alanları göster', // TODO: translate the following. // 'form.reports.time_fields' => 'Time fields', // 'form.reports.user_fields' => 'User fields', +// 'form.reports.project_fields' => 'Project fields', 'form.reports.group_by' => 'Gruplandırma kıstas', 'form.reports.group_by_no' => '--- gruplama yok ---', 'form.reports.group_by_date' => 'tarih', @@ -527,6 +515,7 @@ // (after generating a report at https://timetracker.anuko.com/reports.php). // TODO: translate the following. // 'form.report.export' => 'Export', +// 'form.report.per_hour' => 'Per hour', // 'form.report.assign_to_invoice' => 'Assign to invoice', // 'form.report.assign_to_timesheet' => 'Assign to timesheet', @@ -567,6 +556,7 @@ // Users form. See example at https://timetracker.anuko.com/users.php // TODO: translate the following. +// 'form.users.uncompleted_entry_today' => 'User has an uncompleted time entry today', // 'form.users.uncompleted_entry' => 'User has an uncompleted time entry', 'form.users.role' => 'Rol', // TODO: translate the following. @@ -631,13 +621,19 @@ // 'form.group_edit.type_start_finish' => 'start and finish', // 'form.group_edit.type_duration' => 'duration', // 'form.group_edit.punch_mode' => 'Punch mode', +// 'form.group_edit.one_uncompleted' => 'One uncompleted', // 'form.group_edit.allow_overlap' => 'Allow overlap', // 'form.group_edit.future_entries' => 'Future entries', // 'form.group_edit.uncompleted_indicators' => 'Uncompleted indicators', // 'form.group_edit.confirm_save' => 'Confirm saving', -// 'form.group_edit.allow_ip' => 'Allow IP', // 'form.group_edit.advanced_settings' => 'Advanced settings', +// Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. +// TODO: Translate the following. +// 'form.group_advanced_edit.allow_ip' => 'Allow IP', +// 'form.group_advanced_edit.password_complexity' => 'Password complexity', +// 'form.group_advanced_edit.2fa' => 'Two factor authentication', + // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php // TODO: translate the following. // 'form.group_delete.hint' => 'Are you sure you want to delete the entire group?', @@ -705,21 +701,7 @@ // 'form.display_options.note_on_separate_row' => 'Note on separate row', // 'form.display_options.not_complete_days' => 'Not complete days', // 'form.display_options.inactive_projects' => 'Inactive projects', +// 'form.display_options.cost_per_hour' => 'Cost per hour', // 'form.display_options.custom_css' => 'Custom CSS', - -// Work plugin strings. See example at https://timetracker.anuko.com/work.php -// TODO: translate the following. -// 'work.error.work_not_available' => 'Work item is not available.', -// 'work.error.offer_not_available' => 'Offer is not available.', -// 'work.type.one_time' => 'one time', // Work type is "one time job" for well defined work ("do exactly this"). -// 'work.type.ongoing' => 'ongoing', // Work type is "ongoing" for complex jobs (billed by the hour, multiple contractors, etc.) -// 'work.label.own_work' => 'Own work', -// 'work.label.own_offers' => 'Own offers', -// 'work.label.offers' => 'Offers', -// 'work.button.send_message' => 'Send message', -// 'work.button.make_offer' => 'Make offer', -// 'work.button.accept' => 'Accept', -// 'work.button.decline' => 'Decline', -// 'work.title.send_message' => 'Sending Message', -// 'work.msg.message_sent' => 'Message sent.', +// 'form.display_options.custom_translation' => 'Custom translation', ); diff --git a/WEB-INF/resources/zh-cn.lang.php b/WEB-INF/resources/zh-cn.lang.php index 6c6451214..d30515863 100644 --- a/WEB-INF/resources/zh-cn.lang.php +++ b/WEB-INF/resources/zh-cn.lang.php @@ -2,7 +2,8 @@ /* Copyright (c) Anuko International Ltd. https://www.anuko.com License: See license.txt */ -// Note: escape apostrophes with THREE backslashes, like here: choisir l\\\'option. +// Note: escape apostrophes with THREE backslashes, like here: 'choisir l\\\'option'. +// Alternatively: use one backslash and surround by double-quotes: "choisir l\'option". // Other characters (such as double-quotes in http links, etc.) do not have to be escaped. $i18n_language = 'Chinese (简体中文)'; @@ -68,6 +69,9 @@ // 'error.report' => 'Select report.', // 'error.record' => 'Select record.', 'error.auth' => '不正确的用户名或密码。', +// TODO: translate the following. +// 'error.2fa_code' => 'Invalid 2FA code.', +// 'error.weak_password' => 'Weak password.', 'error.user_exists' => '该用户登录信息已经存在。', // TODO: translate the following. // 'error.object_exists' => 'Object with this name already exists.', @@ -94,7 +98,6 @@ // 'error.user_count' => 'Limit on user count.', // 'error.expired' => 'Expiration date reached.', // 'error.file_storage' => 'File storage server error.', // See comment in English file. -// 'error.remote_work' => 'Remote work server error.', // See comment in English file. // Warning messages. // TODO: translate the following. @@ -217,9 +220,6 @@ 'label.ldap_hint' => '在下面的栏目输入您的Windows用户名密码。', 'label.required_fields' => '* 必填栏目', 'label.on_behalf' => '代表', -'label.role_manager' => '(经理)', -'label.role_comanager' => '(合作经理人)', -'label.role_admin' => '(管理员)', 'label.page' => '页码', // TODO: translate the following. // 'label.condition' => 'Condition', @@ -246,6 +246,7 @@ // 'label.mark_paid' => 'Mark paid', // 'label.week_note' => 'Week note', // 'label.week_list' => 'Week list', +// 'label.weekends' => 'Weekends', // 'label.work_units' => 'Work units', // 'label.work_units_short' => 'Units', // 'label.totals_only' => 'Totals only', @@ -263,16 +264,6 @@ // 'label.file' => 'File', // 'label.active_users' => 'Active Users', // 'label.inactive_users' => 'Inactive Users', -// 'label.details' => 'Details', -// 'label.budget' => 'Budget', -// 'label.work' => 'Work', // Table column header for work items, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// 'label.offer' => 'Offer', // Table column header for offers, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// 'label.contractor' => 'Contractor', // Table column header for offers (contractor is someone who offers to do work). - // Technically, it is either an org name or a combination of org and group names - // because both work items and offers are owned by Time Tracker groups of users. -// 'label.how_to_pay' => 'How to pay', // Label for the "How to pay" field on offers, which allows contractors to specify - // how to pay them, for example: paypal email, check by mail to a specific address, etc. -// 'label.moderator_comment' => 'Moderator comment', // Label for "Moderator comment" field that explains something. // Entity names. We use lower case (in English) because they are used in dropdowns, too. // They are used to associate a custom field with an entity type. @@ -286,6 +277,8 @@ // 'title.error' => 'Error', // 'title.success' => 'Success', 'title.login' => '登录', +// TODO: translate the follolwing. +// 'title.2fa' => 'Two Factor Authentication', 'title.groups' => '团队', // TODO: change "teams" to "groups". // TODO: translate the following. // 'title.add_group' => 'Adding Group', @@ -385,22 +378,6 @@ // 'title.edit_file' => 'Editing File', // 'title.delete_file' => 'Deleting File', // 'title.download_file' => 'Downloading File', -// 'title.work' => 'Work', -// 'title.add_work' => 'Adding Work', -// 'title.edit_work' => 'Editing Work', -// 'title.delete_work' => 'Deleting Work', -// 'title.active_work' => 'Active Work', // Active work items this group outsources to other groups. -// 'title.available_work' => 'Available Work', // Available work items from other organizations. -// 'title.inactive_work' => 'Inactive Work', // Inactive work items this group was outsourcing to other groups. -// 'title.pending_work' => 'Pending Work', // Work items pending moderator approval. -// 'title.offer' => 'Offer', -// 'title.add_offer' => 'Adding Offer', -// 'title.edit_offer' => 'Editing Offer', -// 'title.delete_offer' => 'Deleting Offer', -// 'title.active_offers' => 'Active Offers', // Active offers this group makes available to other groups. -// 'title.available_offers' => 'Available Offers', // Available offers from other organizations. -// 'title.inactive_offers' => 'Inactive Offers', // Inactive offers for group. -// 'title.pending_offers' => 'Pending Offers', // Offers pending moderator approval. // Section for common strings inside combo boxes on forms. Strings shared between forms shall be placed here. // Strings that are used in a single form must go to the specific form section. @@ -432,7 +409,6 @@ // 'dropdown.status_inactive' => 'inactive', // 'dropdown.delete' => 'delete', // 'dropdown.do_not_delete' => 'do not delete', -// 'dropdown.pending_approval' => 'pending approval', // 'dropdown.approved' => 'approved', // 'dropdown.not_approved' => 'not approved', // 'dropdown.paid' => 'paid', @@ -446,6 +422,16 @@ // 'form.login.about' => 'Anuko Time Tracker is an open source time tracking system.', 'form.login.about' => 'Anuko Time Tracker 是一种简单、易用、开放源代码的实时跟踪系统。', +// Email subject and body for two-factor authentication. +// TODO: translate the following. +// 'email.2fa_code.subject' => 'Anuko Time Tracker two-factor authentication code', +// 'email.2fa_code.body' => "Dear User,\n\nYour two-factor authentication code is:\n\n%s\n\n", + +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// 'form.2fa.hint' => 'Check your email for 2FA code and enter it here.', +// 'form.2fa.2fa_code' => '2FA code', + // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. 'form.reset_password.message' => '密码重设请求已经发送。', // TODO: Add "by email" to match the English string. 'form.reset_password.email_subject' => 'Anuko时间追踪器密码重设请求', @@ -486,10 +472,13 @@ // 'form.reports.include_pending' => 'pending', 'form.reports.select_period' => '选择时间段', 'form.reports.set_period' => '或设定日期', +// TODO: translate the following. +// 'form.reports.note_containing' => 'Note containing', 'form.reports.show_fields' => '显示栏目', // TODO: translate the following. // 'form.reports.time_fields' => 'Time fields', // 'form.reports.user_fields' => 'User fields', +// 'form.reports.project_fields' => 'Project fields', 'form.reports.group_by' => '分组方式', 'form.reports.group_by_no' => '--- 没有分组 ---', 'form.reports.group_by_date' => '日期', @@ -504,6 +493,7 @@ // (after generating a report at https://timetracker.anuko.com/reports.php). // TODO: translate the following. // 'form.report.export' => 'Export', +// 'form.report.per_hour' => 'Per hour', // 'form.report.assign_to_invoice' => 'Assign to invoice', // 'form.report.assign_to_timesheet' => 'Assign to timesheet', @@ -544,6 +534,7 @@ // Users form. See example at https://timetracker.anuko.com/users.php // TODO: translate the following. +// 'form.users.uncompleted_entry_today' => 'User has an uncompleted time entry today', // 'form.users.uncompleted_entry' => 'User has an uncompleted time entry', 'form.users.role' => '角色', 'form.users.manager' => '经理', @@ -605,13 +596,19 @@ // 'form.group_edit.type_start_finish' => 'start and finish', // 'form.group_edit.type_duration' => 'duration', // 'form.group_edit.punch_mode' => 'Punch mode', +// 'form.group_edit.one_uncompleted' => 'One uncompleted', // 'form.group_edit.allow_overlap' => 'Allow overlap', // 'form.group_edit.future_entries' => 'Future entries', // 'form.group_edit.uncompleted_indicators' => 'Uncompleted indicators', // 'form.group_edit.confirm_save' => 'Confirm saving', -// 'form.group_edit.allow_ip' => 'Allow IP', // 'form.group_edit.advanced_settings' => 'Advanced settings', +// Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. +// TODO: Translate the following. +// 'form.group_advanced_edit.allow_ip' => 'Allow IP', +// 'form.group_advanced_edit.password_complexity' => 'Password complexity', +// 'form.group_advanced_edit.2fa' => 'Two factor authentication', + // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php // TODO: translate the following. // 'form.group_delete.hint' => 'Are you sure you want to delete the entire group?', @@ -679,21 +676,7 @@ // 'form.display_options.note_on_separate_row' => 'Note on separate row', // 'form.display_options.not_complete_days' => 'Not complete days', // 'form.display_options.inactive_projects' => 'Inactive projects', +// 'form.display_options.cost_per_hour' => 'Cost per hour', // 'form.display_options.custom_css' => 'Custom CSS', - -// Work plugin strings. See example at https://timetracker.anuko.com/work.php -// TODO: translate the following. -// 'work.error.work_not_available' => 'Work item is not available.', -// 'work.error.offer_not_available' => 'Offer is not available.', -// 'work.type.one_time' => 'one time', // Work type is "one time job" for well defined work ("do exactly this"). -// 'work.type.ongoing' => 'ongoing', // Work type is "ongoing" for complex jobs (billed by the hour, multiple contractors, etc.) -// 'work.label.own_work' => 'Own work', -// 'work.label.own_offers' => 'Own offers', -// 'work.label.offers' => 'Offers', -// 'work.button.send_message' => 'Send message', -// 'work.button.make_offer' => 'Make offer', -// 'work.button.accept' => 'Accept', -// 'work.button.decline' => 'Decline', -// 'work.title.send_message' => 'Sending Message', -// 'work.msg.message_sent' => 'Message sent.', +// 'form.display_options.custom_translation' => 'Custom translation', ); diff --git a/WEB-INF/resources/zh-tw.lang.php b/WEB-INF/resources/zh-tw.lang.php index d16bf4cb7..890244f66 100644 --- a/WEB-INF/resources/zh-tw.lang.php +++ b/WEB-INF/resources/zh-tw.lang.php @@ -2,7 +2,8 @@ /* Copyright (c) Anuko International Ltd. https://www.anuko.com License: See license.txt */ -// Note: escape apostrophes with THREE backslashes, like here: choisir l\\\'option. +// Note: escape apostrophes with THREE backslashes, like here: 'choisir l\\\'option'. +// Alternatively: use one backslash and surround by double-quotes: "choisir l\'option". // Other characters (such as double-quotes in http links, etc.) do not have to be escaped. $i18n_language = 'Chinese (簡體中文)'; @@ -71,6 +72,9 @@ // 'error.report' => 'Select report.', // 'error.record' => 'Select record.', 'error.auth' => '不正確的用戶名或密碼。', +// TODO: translate the following. +// 'error.2fa_code' => 'Invalid 2FA code.', +// 'error.weak_password' => 'Weak password.', 'error.user_exists' => '該使用者登錄資訊已經存在。', // TODO: translate the following. // 'error.object_exists' => 'Object with this name already exists.', @@ -98,7 +102,6 @@ // 'error.user_count' => 'Limit on user count.', // 'error.expired' => 'Expiration date reached.', // 'error.file_storage' => 'File storage server error.', // See comment in English file. -// 'error.remote_work' => 'Remote work server error.', // See comment in English file. // Warning messages. // TODO: translate the following. @@ -222,9 +225,6 @@ 'label.ldap_hint' => '在下麵的欄目輸入您的Windows用戶名密碼。', 'label.required_fields' => '* 必填欄目', 'label.on_behalf' => '代表', -'label.role_manager' => '(經理)', -'label.role_comanager' => '(合作經理人)', -'label.role_admin' => '(管理員)', // TODO: translate the following. // 'label.page' => 'Page', // 'label.condition' => 'Condition', @@ -251,6 +251,7 @@ // 'label.mark_paid' => 'Mark paid', // 'label.week_note' => 'Week note', // 'label.week_list' => 'Week list', +// 'label.weekends' => 'Weekends', // 'label.work_units' => 'Work units', // 'label.work_units_short' => 'Units', // 'label.totals_only' => 'Totals only', @@ -268,16 +269,6 @@ // 'label.file' => 'File', // 'label.active_users' => 'Active Users', // 'label.inactive_users' => 'Inactive Users', -// 'label.details' => 'Details', -// 'label.budget' => 'Budget', -// 'label.work' => 'Work', // Table column header for work items, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// 'label.offer' => 'Offer', // Table column header for offers, see https://www.anuko.com/time_tracker/what_is/work_plugin.htm -// 'label.contractor' => 'Contractor', // Table column header for offers (contractor is someone who offers to do work). - // Technically, it is either an org name or a combination of org and group names - // because both work items and offers are owned by Time Tracker groups of users. -// 'label.how_to_pay' => 'How to pay', // Label for the "How to pay" field on offers, which allows contractors to specify - // how to pay them, for example: paypal email, check by mail to a specific address, etc. -// 'label.moderator_comment' => 'Moderator comment', // Label for "Moderator comment" field that explains something. // Entity names. We use lower case (in English) because they are used in dropdowns, too. // They are used to associate a custom field with an entity type. @@ -291,6 +282,8 @@ // 'title.error' => 'Error', // 'title.success' => 'Success', 'title.login' => '登錄', +// TODO: translate the follolwing. +// 'title.2fa' => 'Two Factor Authentication', 'title.groups' => '團隊', // TODO: change "teams" to "groups". // TODO: translate the following. // 'title.add_group' => 'Adding Group', @@ -389,22 +382,6 @@ // 'title.edit_file' => 'Editing File', // 'title.delete_file' => 'Deleting File', // 'title.download_file' => 'Downloading File', -// 'title.work' => 'Work', -// 'title.add_work' => 'Adding Work', -// 'title.edit_work' => 'Editing Work', -// 'title.delete_work' => 'Deleting Work', -// 'title.active_work' => 'Active Work', // Active work items this group outsources to other groups. -// 'title.available_work' => 'Available Work', // Available work items from other organizations. -// 'title.inactive_work' => 'Inactive Work', // Inactive work items this group was outsourcing to other groups. -// 'title.pending_work' => 'Pending Work', // Work items pending moderator approval. -// 'title.offer' => 'Offer', -// 'title.add_offer' => 'Adding Offer', -// 'title.edit_offer' => 'Editing Offer', -// 'title.delete_offer' => 'Deleting Offer', -// 'title.active_offers' => 'Active Offers', // Active offers this group makes available to other groups. -// 'title.available_offers' => 'Available Offers', // Available offers from other organizations. -// 'title.inactive_offers' => 'Inactive Offers', // Inactive offers for group. -// 'title.pending_offers' => 'Pending Offers', // Offers pending moderator approval. // Section for common strings inside combo boxes on forms. Strings shared between forms shall be placed here. // Strings that are used in a single form must go to the specific form section. @@ -436,7 +413,6 @@ // 'dropdown.status_inactive' => 'inactive', // 'dropdown.delete' => 'delete', // 'dropdown.do_not_delete' => 'do not delete', -// 'dropdown.pending_approval' => 'pending approval', // 'dropdown.approved' => 'approved', // 'dropdown.not_approved' => 'not approved', // 'dropdown.paid' => 'paid', @@ -449,6 +425,16 @@ // TODO: translate the following. // 'form.login.about' => 'Anuko Time Tracker is an open source time tracking system.', +// Email subject and body for two-factor authentication. +// TODO: translate the following. +// 'email.2fa_code.subject' => 'Anuko Time Tracker two-factor authentication code', +// 'email.2fa_code.body' => "Dear User,\n\nYour two-factor authentication code is:\n\n%s\n\n", + +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// 'form.2fa.hint' => 'Check your email for 2FA code and enter it here.', +// 'form.2fa.2fa_code' => '2FA code', + // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. 'form.reset_password.message' => '密碼重設請求已經發送。', // TODO: Add "by email" to match the English string. 'form.reset_password.email_subject' => 'Anuko時間追蹤器密碼重設請求', @@ -489,10 +475,13 @@ // 'form.reports.include_pending' => 'pending', 'form.reports.select_period' => '選擇時間段', 'form.reports.set_period' => '或設定日期', +// TODO: translate the following. +// 'form.reports.note_containing' => 'Note containing', 'form.reports.show_fields' => '顯示欄目', // TODO: translate the following. // 'form.reports.time_fields' => 'Time fields', // 'form.reports.user_fields' => 'User fields', +// 'form.reports.project_fields' => 'Project fields', 'form.reports.group_by' => '分組方式', 'form.reports.group_by_no' => '--- 沒有分組 ---', 'form.reports.group_by_date' => '日期', @@ -507,6 +496,7 @@ // (after generating a report at https://timetracker.anuko.com/reports.php). // TODO: translate the following. // 'form.report.export' => 'Export', +// 'form.report.per_hour' => 'Per hour', // 'form.report.assign_to_invoice' => 'Assign to invoice', // 'form.report.assign_to_timesheet' => 'Assign to timesheet', @@ -547,6 +537,7 @@ // Users form. See example at https://timetracker.anuko.com/users.php // TODO: translate the following. +// 'form.users.uncompleted_entry_today' => 'User has an uncompleted time entry today', // 'form.users.uncompleted_entry' => 'User has an uncompleted time entry', 'form.users.role' => '角色', 'form.users.manager' => '經理', @@ -608,13 +599,19 @@ // 'form.group_edit.type_start_finish' => 'start and finish', // 'form.group_edit.type_duration' => 'duration', // 'form.group_edit.punch_mode' => 'Punch mode', +// 'form.group_edit.one_uncompleted' => 'One uncompleted', // 'form.group_edit.allow_overlap' => 'Allow overlap', // 'form.group_edit.future_entries' => 'Future entries', // 'form.group_edit.uncompleted_indicators' => 'Uncompleted indicators', // 'form.group_edit.confirm_save' => 'Confirm saving', -// 'form.group_edit.allow_ip' => 'Allow IP', // 'form.group_edit.advanced_settings' => 'Advanced settings', +// Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. +// TODO: Translate the following. +// 'form.group_advanced_edit.allow_ip' => 'Allow IP', +// 'form.group_advanced_edit.password_complexity' => 'Password complexity', +// 'form.group_advanced_edit.2fa' => 'Two factor authentication', + // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php // TODO: translate the following. // 'form.group_delete.hint' => 'Are you sure you want to delete the entire group?', @@ -682,21 +679,7 @@ // 'form.display_options.note_on_separate_row' => 'Note on separate row', // 'form.display_options.not_complete_days' => 'Not complete days', // 'form.display_options.inactive_projects' => 'Inactive projects', +// 'form.display_options.cost_per_hour' => 'Cost per hour', // 'form.display_options.custom_css' => 'Custom CSS', - -// Work plugin strings. See example at https://timetracker.anuko.com/work.php -// TODO: translate the following. -// 'work.error.work_not_available' => 'Work item is not available.', -// 'work.error.offer_not_available' => 'Offer is not available.', -// 'work.type.one_time' => 'one time', // Work type is "one time job" for well defined work ("do exactly this"). -// 'work.type.ongoing' => 'ongoing', // Work type is "ongoing" for complex jobs (billed by the hour, multiple contractors, etc.) -// 'work.label.own_work' => 'Own work', -// 'work.label.own_offers' => 'Own offers', -// 'work.label.offers' => 'Offers', -// 'work.button.send_message' => 'Send message', -// 'work.button.make_offer' => 'Make offer', -// 'work.button.accept' => 'Accept', -// 'work.button.decline' => 'Decline', -// 'work.title.send_message' => 'Sending Message', -// 'work.msg.message_sent' => 'Message sent.', +// 'form.display_options.custom_translation' => 'Custom translation', ); diff --git a/WEB-INF/templates/2fa.tpl b/WEB-INF/templates/2fa.tpl new file mode 100644 index 000000000..d425cbc11 --- /dev/null +++ b/WEB-INF/templates/2fa.tpl @@ -0,0 +1,30 @@ +{* Copyright (c) Anuko International Ltd. https://www.anuko.com +License: See license.txt *} + +

{$i18n.form.2fa.hint}
+{$forms.twoFactorAuthForm.open} + + + + + + + + + + + + + + + + + + + + + + +
{$forms.twoFactorAuthForm.login.control}
{$forms.twoFactorAuthForm.password.control}
{$forms.twoFactorAuthForm.auth_code.control}
{$forms.twoFactorAuthForm.btn_login.control}
+{$forms.twoFactorAuthForm.close} + diff --git a/WEB-INF/templates/cf_custom_fields.tpl b/WEB-INF/templates/cf_custom_fields.tpl index 70253cb3f..812943db8 100644 --- a/WEB-INF/templates/cf_custom_fields.tpl +++ b/WEB-INF/templates/cf_custom_fields.tpl @@ -19,6 +19,8 @@ License: See license.txt *} {$i18n.entity.time} {elseif CustomFields::ENTITY_USER == $field['entity_type']} {$i18n.entity.user} + {elseif CustomFields::ENTITY_PROJECT == $field['entity_type']} + {$i18n.entity.project} {else} {/if} diff --git a/WEB-INF/templates/charts.tpl b/WEB-INF/templates/charts.tpl index ff15ea8f2..27d335ffb 100644 --- a/WEB-INF/templates/charts.tpl +++ b/WEB-INF/templates/charts.tpl @@ -19,24 +19,69 @@ function adjustTodayLinks() { } } } + +// handleFavReportSelection - manages visibility of controls that are not relevant +// when a fav report is selected. +function handleFavReportSelection() { + var selectedFavReportId = document.getElementById("favorite_report").value; + + var userLabel = document.getElementById("user_label"); + var userControl = document.getElementById("user_control"); + var intervalLabel = document.getElementById("interval_label"); + var intervalControl = document.getElementById("interval_control"); + var smallScreenCalendarDiv = document.getElementById("small_screen_calendar"); + var largeScreenCalendarDiv = document.getElementById("large_screen_calendar"); + + if (selectedFavReportId == -1) { + if (userLabel != null) { + userLabel.style.display = ""; + } + if (userControl != null) { + userControl.style.display = ""; + } + if (intervalLabel != null) { + intervalLabel.style.display = ""; + } + if (intervalControl != null) { + intervalControl.style.display = ""; + } + if (smallScreenCalendarDiv != null) { + smallScreenCalendarDiv.style.display = ""; + } + if (largeScreenCalendarDiv != null) { + largeScreenCalendarDiv.style.display = ""; + } + } else { + if (userLabel != null) { + userLabel.style.display = "none"; + } + if (userControl != null) { + userControl.style.display = "none"; + } + if (intervalLabel != null) { + intervalLabel.style.display = "none"; + } + if (intervalControl != null) { + intervalControl.style.display = "none"; + } + if (smallScreenCalendarDiv != null) { + smallScreenCalendarDiv.style.display = "none"; + } + if (largeScreenCalendarDiv != null) { + largeScreenCalendarDiv.style.display = "none"; + } + } +} {$forms.chartForm.open} -
{$forms.chartForm.date.control}
+
{$forms.chartForm.date.control}
- -{if $user_dropdown} - + + - - - - -{/if} - - - - + + {if $chart_selector} @@ -47,6 +92,20 @@ function adjustTodayLinks() { {/if} +{if isset($user_dropdown)} + + + + + + +{/if} + + + + + +
{$forms.chartForm.date.control}
{$forms.chartForm.date.control}
{$forms.chartForm.user.control}
{$forms.chartForm.interval.control}{$forms.chartForm.favorite_report.control}
{$forms.chartForm.user.control}
{$forms.chartForm.interval.control}
diff --git a/WEB-INF/templates/display_options.tpl b/WEB-INF/templates/display_options.tpl index 6486c0b25..0d0418af9 100644 --- a/WEB-INF/templates/display_options.tpl +++ b/WEB-INF/templates/display_options.tpl @@ -49,12 +49,25 @@ License: See license.txt *} {$i18n.label.what_is_it} + + + + {$forms.displayOptionsForm.report_cost_per_hour.control} + {$i18n.label.what_is_it} + {$i18n.label.what_is_it} + +
{$i18n.form.display_options.custom_css} {$forms.displayOptionsForm.custom_css.control} {$i18n.label.what_is_it}
+
+ {$i18n.form.display_options.custom_translation} + {$forms.displayOptionsForm.custom_translation.control} + {$i18n.label.what_is_it} +
{$forms.displayOptionsForm.btn_save.control}
{$forms.displayOptionsForm.close} diff --git a/WEB-INF/templates/expenses.tpl b/WEB-INF/templates/expenses.tpl index 5b1da05fc..859ae3506 100644 --- a/WEB-INF/templates/expenses.tpl +++ b/WEB-INF/templates/expenses.tpl @@ -119,7 +119,7 @@ function recalculateCost() {
{$forms.expensesForm.date.control}
-{if $user_dropdown} +{if isset($user_dropdown)} diff --git a/WEB-INF/templates/group_advanced_edit.tpl b/WEB-INF/templates/group_advanced_edit.tpl index 1f55adf77..82eac702c 100644 --- a/WEB-INF/templates/group_advanced_edit.tpl +++ b/WEB-INF/templates/group_advanced_edit.tpl @@ -24,15 +24,33 @@ License: See license.txt *} - + - + + + + + + + + + + + + +
{$forms.expensesForm.date.control}
{$forms.groupAdvancedForm.allow_ip.control} {$i18n.label.what_is_it} {$i18n.label.what_is_it}
{$forms.groupAdvancedForm.password_complexity.control} + {$i18n.label.what_is_it} + {$i18n.label.what_is_it} +
{$forms.groupAdvancedForm.2fa.control} + {$i18n.label.what_is_it} + {$i18n.label.what_is_it} +
{$i18n.label.required_fields}
diff --git a/WEB-INF/templates/group_edit.tpl b/WEB-INF/templates/group_edit.tpl index 05a134dd9..909b485e6 100644 --- a/WEB-INF/templates/group_edit.tpl +++ b/WEB-INF/templates/group_edit.tpl @@ -111,6 +111,15 @@ function chLocation(newLocation) { document.location = newLocation; }
+ + + + {$forms.groupForm.one_uncompleted.control} + {$i18n.label.what_is_it} + {$i18n.label.what_is_it} + + +
diff --git a/WEB-INF/templates/header.tpl b/WEB-INF/templates/header.tpl index e7df23ea6..af6151acb 100644 --- a/WEB-INF/templates/header.tpl +++ b/WEB-INF/templates/header.tpl @@ -63,9 +63,6 @@ {$i18n.menu.groups} {$i18n.menu.options} - {if isTrue('WORK_SERVER_ADMINISTRATION')} - {$i18n.label.work} - {/if}
@@ -117,7 +114,7 @@ {if $user->exists() && $user->isPluginEnabled('iv') && ($user->can('manage_invoices') || $user->can('view_client_invoices'))} {$i18n.title.invoices} {/if} - {if ($user->exists() && $user->isPluginEnabled('ch') && ($user->can('view_own_charts') || $user->can('view_charts'))) && + {if ($user->exists() && $user->isPluginEnabled('ch') && ($user->can('view_own_charts') || $user->can('view_charts') || $user->can('view_all_charts'))) && (constant('MODE_PROJECTS') == $user->getTrackingMode() || constant('MODE_PROJECTS_AND_TASKS') == $user->getTrackingMode() || $user->isPluginEnabled('cl'))} {$i18n.menu.charts} @@ -134,9 +131,6 @@ {if $user->isPluginEnabled('cl') && ($user->can('view_own_clients') || $user->can('manage_clients'))} {$i18n.menu.clients} {/if} - {if $user->isPluginEnabled('wk') && ($user->can('update_work') || $user->can('bid_on_work') || $user->can('manage_work')) && $user->exists()} - {$i18n.title.work} - {/if} {if $user->can('export_data')} {$i18n.menu.export} {/if} diff --git a/WEB-INF/templates/plugins.tpl b/WEB-INF/templates/plugins.tpl index 5c1e4c943..e4402f62d 100644 --- a/WEB-INF/templates/plugins.tpl +++ b/WEB-INF/templates/plugins.tpl @@ -210,12 +210,6 @@ function handlePluginCheckboxes() { {$forms.pluginsForm.attachments.control} {$i18n.label.what_is_it}
- - - - {$forms.pluginsForm.work.control} {$i18n.label.what_is_it} - -
{$forms.pluginsForm.btn_save.control}
{$forms.pluginsForm.close} diff --git a/WEB-INF/templates/project_add.tpl b/WEB-INF/templates/project_add.tpl index 8cc43fcaa..7b1fd835c 100644 --- a/WEB-INF/templates/project_add.tpl +++ b/WEB-INF/templates/project_add.tpl @@ -15,6 +15,17 @@ License: See license.txt *} {$forms.projectForm.description.control}
+{if isset($custom_fields) && $custom_fields->projectFields} + {foreach $custom_fields->projectFields as $projectField} + {assign var="control_name" value='project_field_'|cat:$projectField['id']} + + + + {$forms.projectForm.$control_name.control} + +
+ {/foreach} +{/if} {if $show_files} diff --git a/WEB-INF/templates/project_edit.tpl b/WEB-INF/templates/project_edit.tpl index 3ecc85a84..4e3b02e79 100644 --- a/WEB-INF/templates/project_edit.tpl +++ b/WEB-INF/templates/project_edit.tpl @@ -15,6 +15,18 @@ License: See license.txt *} {$forms.projectForm.description.control}
+{if isset($custom_fields) && $custom_fields->projectFields} + {foreach $custom_fields->projectFields as $projectField} + {assign var="control_name" value='project_field_'|cat:$projectField['id']} + + + + {$forms.projectForm.$control_name.control} + +
+ {/foreach} +{/if} +
diff --git a/WEB-INF/templates/puncher.tpl b/WEB-INF/templates/puncher.tpl index 8060c2ff5..98bd5ca24 100644 --- a/WEB-INF/templates/puncher.tpl +++ b/WEB-INF/templates/puncher.tpl @@ -48,11 +48,11 @@ function stopTimer() { } -{if $uncompleted} +{if $uncompleted_today} -{* Copyright (c) Anuko International Ltd. https://www.anuko.com -License: See license.txt *} - {$forms.reportForm.open} @@ -358,6 +424,15 @@ License: See license.txt *} {/if} + + + + + +
{$i18n.label.fav_report}:
{$forms.reportForm.note_containing.control} + {$i18n.label.what_is_it} + {$i18n.label.what_is_it} +
@@ -507,6 +582,19 @@ License: See license.txt *} {/foreach} {/if} +{if $show_project && isset($custom_fields) && $custom_fields->projectFields} + + {foreach $custom_fields->projectFields as $projectField} + {assign var="control_name" value='project_field_'|cat:$projectField['id']} + {assign var="checkbox_control_name" value='show_project_field_'|cat:$projectField['id']} + + + + + + + {/foreach} +{/if}
{$i18n.form.reports.project_fields}
{$forms.reportForm.$control_name.control} {$forms.reportForm.$checkbox_control_name.control}
diff --git a/WEB-INF/templates/site_map.tpl b/WEB-INF/templates/site_map.tpl index 6848fc52e..8688f7677 100644 --- a/WEB-INF/templates/site_map.tpl +++ b/WEB-INF/templates/site_map.tpl @@ -4,9 +4,6 @@
- {if isTrue('WORK_SERVER_ADMINISTRATION')} - - {/if}
{* end of sub menu for admin *} {* main menu for admin *} @@ -40,7 +37,7 @@ {if $user->exists() && $user->isPluginEnabled('iv') && ($user->can('manage_invoices') || $user->can('view_client_invoices'))} {/if} - {if ($user->exists() && $user->isPluginEnabled('ch') && ($user->can('view_own_charts') || $user->can('view_charts'))) && + {if ($user->exists() && $user->isPluginEnabled('ch') && ($user->can('view_own_charts') || $user->can('view_charts') || $user->can('view_all_charts'))) && (constant('MODE_PROJECTS') == $user->getTrackingMode() || constant('MODE_PROJECTS_AND_TASKS') == $user->getTrackingMode() || $user->isPluginEnabled('cl'))} diff --git a/WEB-INF/templates/time.tpl b/WEB-INF/templates/time.tpl index 6d5c036da..fa24e8062 100644 --- a/WEB-INF/templates/time.tpl +++ b/WEB-INF/templates/time.tpl @@ -3,6 +3,14 @@ License: See license.txt *} {include file="time_script.tpl"} + + {if $show_navigation}
{$i18n.label.day_view} @@ -15,7 +23,7 @@ License: See license.txt *}
{$forms.timeRecordForm.date.control}
-{if $user_dropdown} +{if isset($user_dropdown)} @@ -113,6 +121,9 @@ License: See license.txt *}
{$forms.timeRecordForm.date.control}
{if $time_records} + + +
@@ -149,7 +160,7 @@ License: See license.txt *} {if $show_client} {/if} - {* record custom fileds *} + {* record custom fields *} {if $show_record_custom_fields && isset($custom_fields) && $custom_fields->timeFields} {foreach $custom_fields->timeFields as $timeField} {assign var="control_name" value='time_field_'|cat:$timeField['id']} @@ -177,17 +188,14 @@ License: See license.txt *} {/if} {/if} - - - - - -
{$record.client|escape}{$i18n.label.files} + {if $record.approved || $record.timesheet_id || $record.invoice_id}   {else} - {$i18n.label.edit} {if ($record.duration == '0:00' && $record.start <> '')} - - - - + {/if} + {$i18n.label.edit} {/if} diff --git a/WEB-INF/templates/time_script.tpl b/WEB-INF/templates/time_script.tpl index 67c5062cc..0dafae8cd 100644 --- a/WEB-INF/templates/time_script.tpl +++ b/WEB-INF/templates/time_script.tpl @@ -12,7 +12,6 @@ // project_ids[143] = "325,370,390,400"; // Comma-separated list of project ids for client. // project_names[325] = "Time Tracker"; // Project name. // task_ids[325] = "100,101,302,303,304"; // Comma-separated list ot task ids for project. -// task_names[100] = "Coding"; // Task name. // template_ids[325] = "17,21"; // Comma-separated list ot template ids for project. NOT YET IMPLEMENTED. // Prepare an array of project ids for clients. @@ -38,11 +37,6 @@ var task_ids = new Array(); {foreach $project_list as $project} task_ids[{$project.id}] = "{$project.tasks}"; {/foreach} -// Prepare an array of task names. -var task_names = new Array(); -{foreach $task_list as $task} - task_names[{$task.id}] = "{$task.name|escape:'javascript'}"; -{/foreach} // Prepare an array of template ids for projects. var template_ids = new Array(); @@ -143,7 +137,6 @@ function fillProjectDropdown(id) { // tasks associated with a selected project (project id is passed here as id). function fillTaskDropdown(id) { var str_ids = task_ids[id]; - var dropdown = document.getElementById("task"); if (dropdown == null) return; // Nothing to do. @@ -155,20 +148,20 @@ function fillTaskDropdown(id) { // Add mandatory top option. dropdown.options[0] = new Option(empty_label_task, '', true); - // Populate the dropdown from the task_names array. + // Populate the Task dropdown. if (str_ids) { var ids = new Array(); ids = str_ids.split(","); var len = ids.length; + // Iterate through $task_list because it is sorted by upper(name). var idx = 1; - for (var i = 0; i < len; i++) { - var t_id = ids[i]; - if (task_names[t_id]) { - dropdown.options[idx] = new Option(task_names[t_id], t_id); + {foreach $task_list as $task} + if (ids.includes("{$task.id}")) { + dropdown.options[idx] = new Option("{$task.name|escape:'javascript'}", {$task.id}); idx++; } - } + {/foreach} // If a previously selected item is still in dropdown - select it. if (dropdown.options.length > 0) { @@ -256,20 +249,34 @@ function formDisable(formField) { var x; if (((formFieldValue != "") && (formFieldName == "start")) || ((formFieldValue != "") && (formFieldName == "finish"))) { + // Either start or finish field not empty. x = eval("document.timeRecordForm.duration"); x.value = ""; x.disabled = true; x.style.background = "#e9e9e9"; + return; } if (((formFieldValue == "") && (formFieldName == "start") && (document.timeRecordForm.finish.value == "")) || ((formFieldValue == "") && (formFieldName == "finish") && (document.timeRecordForm.start.value == ""))) { + // Both start and finish fields are emtpy. x = eval("document.timeRecordForm.duration"); x.value = ""; x.disabled = false; x.style.background = "white"; + return; + } + + if (((formFieldValue == "") && (formFieldName == "start")) || ((formFieldValue == "") && (formFieldName == "finish"))) { + // Either start or finish field is empty. + x = eval("document.timeRecordForm.duration"); + x.value = ""; + x.disabled = true; + x.style.background = "#e9e9e9"; + return; } if ((formFieldValue != "") && (formFieldName == "duration")) { + // Duration field is not empty. x = eval("document.timeRecordForm.start"); x.value = ""; x.disabled = true; @@ -278,9 +285,11 @@ function formDisable(formField) { x.value = ""; x.disabled = true; x.style.background = "#e9e9e9"; + return; } if ((formFieldValue == "") && (formFieldName == "duration")) { + // Duration field is empty. x = eval("document.timeRecordForm.start"); x.disabled = false; x.style.background = "white"; diff --git a/WEB-INF/templates/timesheets.tpl b/WEB-INF/templates/timesheets.tpl index e5f9abdb6..2bd927369 100644 --- a/WEB-INF/templates/timesheets.tpl +++ b/WEB-INF/templates/timesheets.tpl @@ -7,7 +7,7 @@ License: See license.txt *} {$forms.timesheetsForm.open} -{if $user_dropdown} +{if isset($user_dropdown)} diff --git a/WEB-INF/templates/user_edit.tpl b/WEB-INF/templates/user_edit.tpl index 744e8c40e..e4d916a5c 100644 --- a/WEB-INF/templates/user_edit.tpl +++ b/WEB-INF/templates/user_edit.tpl @@ -57,7 +57,11 @@ function setRate(element) { // handleClientRole - manages visibility and content of controls related to client role, // also hides and unselects projects when client role is selected. function handleClientRole() { - var selectedRoleId = document.getElementById("role").value; + var roleControl = document.getElementById("role"); + if (roleControl == null) { + return; // Role control not available, nothing to do... + } + var selectedRoleId = roleControl.value; var clientControl = document.getElementById("client"); var clientBlock = document.getElementById("client_block"); var nonClientBlock = document.getElementById("non_client_block"); diff --git a/WEB-INF/templates/users.tpl b/WEB-INF/templates/users.tpl index c8c2d0046..2a226238d 100644 --- a/WEB-INF/templates/users.tpl +++ b/WEB-INF/templates/users.tpl @@ -23,7 +23,13 @@ License: See license.txt *} diff --git a/WEB-INF/templates/week.tpl b/WEB-INF/templates/week.tpl index 41568f601..6032d4eb5 100644 --- a/WEB-INF/templates/week.tpl +++ b/WEB-INF/templates/week.tpl @@ -28,7 +28,7 @@ function fillDropdowns() {
{$forms.weekTimeForm.date.control}
{if isset($uncompleted_indicators) && $uncompleted_indicators} - + {if $u.has_uncompleted_entry_today} + + {elseif $u.has_uncompleted_entry} + + {else} + + {/if} {/if} {$u.name|escape}
-{if $user_dropdown} +{if isset($user_dropdown)} @@ -130,7 +130,7 @@ function fillDropdowns() { {foreach $time_records as $record} - {* record custom fileds *} + {* record custom fields *} {if $show_record_custom_fields && isset($custom_fields) && $custom_fields->timeFields} {foreach $custom_fields->timeFields as $timeField} {assign var="control_name" value='time_field_'|cat:$timeField['id']} diff --git a/WEB-INF/templates/week_view.tpl b/WEB-INF/templates/week_view.tpl index 3226e0c44..9cd7b90a2 100644 --- a/WEB-INF/templates/week_view.tpl +++ b/WEB-INF/templates/week_view.tpl @@ -39,6 +39,15 @@ License: See license.txt *} + + + + + +
{$forms.weekTimeForm.date.control}
{$record.date}
{$forms.weekViewForm.weekends.control} + {$i18n.label.what_is_it} + {$i18n.label.what_is_it} +
{$forms.weekViewForm.btn_save.control}
{$forms.weekViewForm.close} diff --git a/WEB-INF/templates/work/admin_offer_edit.tpl b/WEB-INF/templates/work/admin_offer_edit.tpl deleted file mode 100644 index bd9a6542b..000000000 --- a/WEB-INF/templates/work/admin_offer_edit.tpl +++ /dev/null @@ -1,70 +0,0 @@ -{$forms.offerForm.open} - - - - -
- -{if $work_id} - - - - - - - - - -{/if} - - - - - - - - - - - - -{if $show_files} - - - - -{/if} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
{$i18n.label.work}:{$work_name}
{$forms.offerForm.work_description.control}
 
{$i18n.label.offer} (*):{$forms.offerForm.offer_name.control}
{$i18n.label.description}:{$forms.offerForm.description.control}
{$i18n.label.details}:{$forms.offerForm.details.control}
{$i18n.label.file}:{$forms.offerForm.newfile.control}
{$i18n.label.currency}:{$forms.offerForm.currency.control}
{$i18n.label.budget} (*):{$forms.offerForm.budget.control}
{$i18n.label.how_to_pay} (*):{$forms.offerForm.payment_info.control}
{$i18n.label.status}:{$forms.offerForm.status.control}
{$i18n.label.moderator_comment}:{$forms.offerForm.moderator_comment.control}
{$i18n.label.required_fields}
 
{$forms.offerForm.btn_approve.control} {$forms.offerForm.btn_save.control} {$forms.offerForm.btn_disapprove.control}
-
-{$forms.offerForm.close} diff --git a/WEB-INF/templates/work/admin_work.tpl b/WEB-INF/templates/work/admin_work.tpl deleted file mode 100644 index 13f37e911..000000000 --- a/WEB-INF/templates/work/admin_work.tpl +++ /dev/null @@ -1,104 +0,0 @@ - - - - - - -
-{if $pending_work} - - - - - - - - - - - {foreach $pending_work as $work_item} - - - - - - - - - {/foreach} -
{$i18n.title.pending_work}
{$i18n.label.work}{$i18n.label.description}{$i18n.label.client}{$i18n.label.budget}
{$work_item.subject|escape}{$work_item.description|escape}{$work_item.group_name|escape} ({$work_item.site_id}.{$work_item.group_id}){$work_item.amount_with_currency}{$i18n.label.edit}{$i18n.label.delete}
-{/if} - -
- -{if $pending_offers} - - - - - - - - - - - {foreach $pending_offers as $offer} - - - - - - - - - {/foreach} -
{$i18n.title.pending_offers}
{$i18n.label.offer}{$i18n.label.description}{$i18n.label.contractor}{$i18n.label.budget}
{$offer.subject|escape}{$offer.description|escape}{$offer.group_name|escape} ({$offer.site_id}.{$offer.group_id}){$offer.amount_with_currency}{$i18n.label.edit}{$i18n.label.delete}
-{/if} - -
- - - -{if $available_work} - - - - - - - {foreach $available_work as $work_item} - - - - - - - {/foreach} -{/if} -
{$i18n.title.available_work}
{$i18n.label.work}{$i18n.label.description}{$i18n.label.client}{$i18n.label.budget}
{$work_item.subject|escape}{$work_item.description|escape}{$work_item.group_name|escape}{$work_item.amount_with_currency}
- -
- - - -{if $available_offers} - - - - - - - {foreach $available_offers as $offer} - - - - - - - {/foreach} -{/if} -
{$i18n.title.available_offers}
{$i18n.label.offer}{$i18n.label.description}{$i18n.label.contractor}{$i18n.label.budget}
{$offer.subject|escape}{$offer.description|escape}{$offer.group_name|escape}{$offer.amount_with_currency}
- -
diff --git a/WEB-INF/templates/work/admin_work_edit.tpl b/WEB-INF/templates/work/admin_work_edit.tpl deleted file mode 100644 index 1ae59a6fe..000000000 --- a/WEB-INF/templates/work/admin_work_edit.tpl +++ /dev/null @@ -1,67 +0,0 @@ -{$forms.workForm.open} - -{if $offer} - - - -{/if} - - - -
- - - - - -
{$i18n.label.offer}: {$offer.subject}
{$i18n.label.contractor}: {$offer.group_name}
{$i18n.label.description}: {$offer.descr_short|escape}
{$i18n.label.budget}: {$offer.amount_with_currency}
-
- - - - - - - - - - - - - -{if $show_files} - - - - -{/if} - - - - - - - - - - - - - - - - - - - - - - - - - - - -
{$i18n.label.work} (*):{$forms.workForm.work_name.control}
{$i18n.label.description}:{$forms.workForm.description.control}
{$i18n.label.details}:{$forms.workForm.details.control}
{$i18n.label.file}:{$forms.workForm.newfile.control}
{$i18n.label.currency}:{$forms.workForm.currency.control}
{$i18n.label.budget} (*):{$forms.workForm.budget.control}
{$i18n.label.status}:{$forms.workForm.status.control}
{$i18n.label.moderator_comment}:{$forms.workForm.moderator_comment.control}
{$i18n.label.required_fields}
 
{$forms.workForm.btn_save.control}
-
-{$forms.workForm.close} diff --git a/WEB-INF/templates/work/footer_old.tpl b/WEB-INF/templates/work/footer_old.tpl deleted file mode 100644 index 611067550..000000000 --- a/WEB-INF/templates/work/footer_old.tpl +++ /dev/null @@ -1,29 +0,0 @@ -
-

 

- - - - - -
{$i18n.footer.contribute_msg}
-
- - - - -
Anuko Time Tracker {constant('APP_VERSION')} | Copyright © Anuko | - {$i18n.footer.credits} | - {$i18n.footer.license} | - {$i18n.footer.improve} -
-
- -
- - - diff --git a/WEB-INF/templates/work/header.tpl b/WEB-INF/templates/work/header.tpl deleted file mode 100644 index 75a0d586f..000000000 --- a/WEB-INF/templates/work/header.tpl +++ /dev/null @@ -1,207 +0,0 @@ - - - - - - -{if $i18n.language.rtl} - -{/if} - Time Tracker{if $title} - {$title}{/if} - - - - - - - -{assign var="tab_width" value="700"} - - - - - - + +
- - - - -{if $user->custom_logo} - - -
-{else} - -{/if} - - - - -
- - - -{if $user->custom_logo} - -{else} - -{/if} - - -
-
- - -{if $authenticated} - {if $user->can('administer_site')} - - - - - -
  - {$i18n.menu.logout} - {$i18n.menu.forum} - {$i18n.menu.help} -
- - - - - - - - - - {else} - - - - - -
  - {$i18n.menu.logout} - {if $user->exists() && $user->can('manage_own_settings')} - {$i18n.menu.profile} - {/if} - {if $user->can('manage_basic_settings')} - {$i18n.menu.group} - {/if} - {if $user->can('manage_features')} - {$i18n.menu.plugins} - {/if} - {$i18n.menu.forum} - {$i18n.menu.help} -
- - - - - - - - - - {/if} -{else} - - - - - -
  - {$i18n.menu.login} - {if isTrue('MULTIORG_MODE') && constant('AUTH_MODULE') == 'db'} - {$i18n.menu.register} - {/if} - {$i18n.menu.forum} - {$i18n.menu.help} -
-{/if} -
- - -{if $title} - - - {* No need to escape as it is done in the class. *} -
{$title}{if $timestring}: {$timestring}{/if}
{$user->getUserPartForHeader()}
-{/if} - - - -{if $err->yes()} - - - - -
- {foreach $err->getErrors() as $error} - {$error.message}
{* No need to escape as they are not coming from user and may contain a link. *} - {/foreach} -
-{/if} - - - -{if $msg->yes()} - - - - -
- {foreach $msg->getErrors() as $message} - {$message.message}
{* No need to escape. *} - {/foreach} -
-{/if} - diff --git a/WEB-INF/templates/work/index.tpl b/WEB-INF/templates/work/index.tpl deleted file mode 100644 index c4f193521..000000000 --- a/WEB-INF/templates/work/index.tpl +++ /dev/null @@ -1,5 +0,0 @@ -{include file="work/header.tpl"} - -{if $content_page_name}{include file="$content_page_name"}{/if} - -{include file="work/footer_old.tpl"} diff --git a/WEB-INF/templates/work/offer_add.tpl b/WEB-INF/templates/work/offer_add.tpl deleted file mode 100644 index 80e07d803..000000000 --- a/WEB-INF/templates/work/offer_add.tpl +++ /dev/null @@ -1,74 +0,0 @@ -{$forms.offerForm.open} - -{if $work_item} - - - -{/if} - - - - -
- - - - -
{$i18n.label.work}: {$work_item.subject}
{$i18n.label.description}: {$work_item.descr_short|escape}
{$i18n.label.budget}: {$work_item.amount_with_currency}
-
- -{if $work_id} - - - - - - - - - -{/if} - - - - - - - - - - - - -{if $show_files} - - - - -{/if} - - - - - - - - - - - - - - - - - - - - - - - -
{$i18n.label.work}:{$work_name}
{$forms.offerForm.work_description.control}
 
{$i18n.label.offer} (*):{$forms.offerForm.offer_name.control}
{$i18n.label.description}:{$forms.offerForm.description.control}
{$i18n.label.details}:{$forms.offerForm.details.control}
{$i18n.label.file}:{$forms.offerForm.newfile.control}
{$i18n.label.currency}:{$forms.offerForm.currency.control}
{$i18n.label.budget} (*):{$forms.offerForm.budget.control} {$i18n.label.what_is_it}
{$i18n.label.how_to_pay} (*):{$forms.offerForm.payment_info.control} {$i18n.label.what_is_it}
{$i18n.label.required_fields}
 
{$forms.offerForm.btn_add.control}
-
-{$forms.offerForm.close} diff --git a/WEB-INF/templates/work/offer_delete.tpl b/WEB-INF/templates/work/offer_delete.tpl deleted file mode 100644 index 7d8954cfb..000000000 --- a/WEB-INF/templates/work/offer_delete.tpl +++ /dev/null @@ -1,20 +0,0 @@ -{$forms.offerDeleteForm.open} - - - - -
- - - - - - - - - - - -
{$offer_to_delete|escape}
 
{$forms.offerDeleteForm.btn_delete.control}  {$forms.offerDeleteForm.btn_cancel.control}
-
-{$forms.offerDeleteForm.close} diff --git a/WEB-INF/templates/work/offer_edit.tpl b/WEB-INF/templates/work/offer_edit.tpl deleted file mode 100644 index 5c1e89a33..000000000 --- a/WEB-INF/templates/work/offer_edit.tpl +++ /dev/null @@ -1,67 +0,0 @@ -{$forms.offerForm.open} - -{if $work_item} - - - -{/if} - - - - -
- - - - -
{$i18n.label.work}: {$work_item.subject}
{$i18n.label.description}: {$work_item.descr_short|escape}
{$i18n.label.budget}: {$work_item.amount_with_currency}
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -{if $show_moderator_comment} - - - - -{/if} - - - - - - - - - - - -
{$i18n.label.offer} (*):{$forms.offerForm.offer_name.control}
{$i18n.label.description}:{$forms.offerForm.description.control}
{$i18n.label.details}:{$forms.offerForm.details.control}
{$i18n.label.currency}:{$forms.offerForm.currency.control}
{$i18n.label.budget} (*):{$forms.offerForm.budget.control} {$i18n.label.what_is_it}
{$i18n.label.how_to_pay} (*):{$forms.offerForm.payment_info.control} {$i18n.label.what_is_it}
{$i18n.label.status}:{$forms.offerForm.status.control}
{$i18n.label.moderator_comment}:{$forms.offerForm.moderator_comment.control}
{$i18n.label.required_fields}
 
{$forms.offerForm.btn_save.control}
-
-{$forms.offerForm.close} diff --git a/WEB-INF/templates/work/offer_message.tpl b/WEB-INF/templates/work/offer_message.tpl deleted file mode 100644 index c97a3db91..000000000 --- a/WEB-INF/templates/work/offer_message.tpl +++ /dev/null @@ -1,32 +0,0 @@ - - -{$forms.messageForm.open} - -{if $offer} - - - -{/if} - - - -
- - - - -
{$i18n.label.offer}: {$offer.subject}
{$i18n.label.description}: {$offer.descr_short|escape}
{$i18n.label.budget}: {$offer.amount_with_currency}
-
- - - - - - - - -
{$i18n.work.label.message}:{$forms.messageForm.message_body.control}
{$forms.messageForm.btn_send.control}
-
-{$forms.messageForm.close} diff --git a/WEB-INF/templates/work/offer_view.tpl b/WEB-INF/templates/work/offer_view.tpl deleted file mode 100644 index 3d9d7d0c5..000000000 --- a/WEB-INF/templates/work/offer_view.tpl +++ /dev/null @@ -1,47 +0,0 @@ - - -{$forms.offerForm.open} - - - - -
- - - - - - - - - - - - - - - - - - - - - -
{$i18n.label.contractor}:{$forms.offerForm.contractor.control}
{$i18n.label.offer}:{$forms.offerForm.offer_name.control}
{$i18n.label.description}:{$forms.offerForm.description.control}
{$i18n.label.details}:{$forms.offerForm.details.control}
{$i18n.label.budget}:{$forms.offerForm.budget.control}
-
-{$forms.offerForm.close} - - - - - -
- - - - - -
-
diff --git a/WEB-INF/templates/work/offer_view_own.tpl b/WEB-INF/templates/work/offer_view_own.tpl deleted file mode 100644 index d00ae7ea1..000000000 --- a/WEB-INF/templates/work/offer_view_own.tpl +++ /dev/null @@ -1,42 +0,0 @@ - - -{$forms.offerForm.open} - -{if $work_item} - - - -{/if} - - - - -
- - - - -
{$i18n.label.work}: {$work_item.subject}
{$i18n.label.description}: {$work_item.descr_short|escape}
{$i18n.label.budget}: {$work_item.amount_with_currency}
-
- - - - - - - - - - - - - - - - - -
{$i18n.label.offer}:{$forms.offerForm.offer_name.control}
{$i18n.label.description}:{$forms.offerForm.description.control}
{$i18n.label.details}:{$forms.offerForm.details.control}
{$i18n.label.budget}:{$forms.offerForm.budget.control}
-
-{$forms.offerForm.close} diff --git a/WEB-INF/templates/work/work.tpl b/WEB-INF/templates/work/work.tpl deleted file mode 100644 index 67235b64f..000000000 --- a/WEB-INF/templates/work/work.tpl +++ /dev/null @@ -1,140 +0,0 @@ - - - - - - -
-{if $own_work_items} - - - - - - - - - - - - {foreach $own_work_items as $work_item} - - - - - - - {if $work_item.can_edit} - - {else} - - {/if} - {if $work_item.can_delete} - - {else} - - {/if} - - {/foreach} -
{$i18n.work.label.own_work}
{$i18n.label.work}{$i18n.label.description}{$i18n.label.status}{$i18n.work.label.offers}{$i18n.label.budget}
{$work_item.subject|escape}{$work_item.description|escape}{$work_item.status_label}{if $work_item.num_offers}{$work_item.num_offers}{/if}{$work_item.amount_with_currency}{$i18n.label.edit}{$i18n.label.delete}
- - - - - -

-
-
-{/if} - - - -{if $available_work_items} - - - - - - - {foreach $available_work_items as $work_item} - - - - - - - {/foreach} -{/if} -
{$i18n.title.available_work}
{$i18n.label.work}{$i18n.label.description}{$i18n.label.client}{$i18n.label.budget}
{$work_item.subject|escape}{$work_item.description|escape}{$work_item.group_name|escape}{$work_item.amount_with_currency}
- - - - - -

-
-
- -{if $own_offers} - - - - - - - - - - - {foreach $own_offers as $offer} - - - - - - - - - {/foreach} -
{$i18n.work.label.own_offers}
{$i18n.label.offer}{$i18n.label.description}{$i18n.label.status}{$i18n.label.budget}
{$offer.subject|escape}{$offer.description|escape}{$offer.status_label}{$offer.amount_with_currency}{$i18n.label.edit}{$i18n.label.delete}
- - - - - -

-
-
-{/if} - - - -{if $available_offers} - - - - - - - {foreach $available_offers as $offer} - - - - - - - {/foreach} -{/if} -
{$i18n.title.available_offers}
{$i18n.label.offer}{$i18n.label.description}{$i18n.label.contractor}{$i18n.label.budget}
{$offer.subject|escape}{$offer.description|escape}{$offer.group_name|escape}{$offer.amount_with_currency}
- - - - - -

-
-
- -
diff --git a/WEB-INF/templates/work/work_add.tpl b/WEB-INF/templates/work/work_add.tpl deleted file mode 100644 index eca0ba799..000000000 --- a/WEB-INF/templates/work/work_add.tpl +++ /dev/null @@ -1,62 +0,0 @@ -{$forms.workForm.open} - -{if $offer} - - - -{/if} - - - -
- - - - -
{$i18n.label.offer}: {$offer.subject}
{$i18n.label.description}: {$offer.descr_short|escape}
{$i18n.label.budget}: {$offer.amount_with_currency}
-
- - - - - - - - - - - - - - - - - -{if $show_files} - - - - -{/if} - - - - - - - - - - - - - - - - - - - -
{$i18n.label.work} (*):{$forms.workForm.work_name.control}
{$i18n.label.type}:{$forms.workForm.work_type.control}
{$i18n.label.description}:{$forms.workForm.description.control}
{$i18n.label.details}:{$forms.workForm.details.control}
{$i18n.label.file}:{$forms.workForm.newfile.control}
{$i18n.label.currency}:{$forms.workForm.currency.control}
{$i18n.label.budget} (*):{$forms.workForm.budget.control} {$i18n.label.what_is_it}
{$i18n.label.required_fields}
 
{$forms.workForm.btn_add.control}
-
-{$forms.workForm.close} diff --git a/WEB-INF/templates/work/work_delete.tpl b/WEB-INF/templates/work/work_delete.tpl deleted file mode 100644 index 2831b0f15..000000000 --- a/WEB-INF/templates/work/work_delete.tpl +++ /dev/null @@ -1,20 +0,0 @@ -{$forms.workDeleteForm.open} - - - - -
- - - - - - - - - - - -
{$work_to_delete|escape}
 
{$forms.workDeleteForm.btn_delete.control}  {$forms.workDeleteForm.btn_cancel.control}
-
-{$forms.workDeleteForm.close} diff --git a/WEB-INF/templates/work/work_edit.tpl b/WEB-INF/templates/work/work_edit.tpl deleted file mode 100644 index 7165d5e19..000000000 --- a/WEB-INF/templates/work/work_edit.tpl +++ /dev/null @@ -1,73 +0,0 @@ -{$forms.workForm.open} - -{if $offer} - - - -{/if} - - - -
- - - - - -
{$i18n.label.offer}: {$offer.subject}
{$i18n.label.contractor}: {$offer.group_name}
{$i18n.label.description}: {$offer.descr_short|escape}
{$i18n.label.budget}: {$offer.amount_with_currency}
-
- - - - - - - - - - - - - - - - - -{if $show_files} - - - - -{/if} - - - - - - - - - - - - -{if $show_moderator_comment} - - - - -{/if} - - - - - - - - - - - -
{$i18n.label.work} (*):{$forms.workForm.work_name.control}
{$i18n.label.type}:{$forms.workForm.work_type.control}
{$i18n.label.description}:{$forms.workForm.description.control}
{$i18n.label.details}:{$forms.workForm.details.control}
{$i18n.label.file}:{$forms.workForm.newfile.control}
{$i18n.label.currency}:{$forms.workForm.currency.control}
{$i18n.label.budget} (*):{$forms.workForm.budget.control} {$i18n.label.what_is_it}
{$i18n.label.status}:{$forms.workForm.status.control}
{$i18n.label.moderator_comment}:{$forms.workForm.moderator_comment.control}
{$i18n.label.required_fields}
 
{$forms.workForm.btn_save.control}
-
-{$forms.workForm.close} diff --git a/WEB-INF/templates/work/work_message.tpl b/WEB-INF/templates/work/work_message.tpl deleted file mode 100644 index 3f237659c..000000000 --- a/WEB-INF/templates/work/work_message.tpl +++ /dev/null @@ -1,32 +0,0 @@ - - -{$forms.messageForm.open} - -{if $work_item} - - - -{/if} - - - -
- - - - -
{$i18n.label.work}: {$work_item.subject}
{$i18n.label.description}: {$work_item.descr_short|escape}
{$i18n.label.budget}: {$work_item.amount_with_currency}
-
- - - - - - - - -
{$i18n.work.label.message}:{$forms.messageForm.message_body.control}
{$forms.messageForm.btn_send.control}
-
-{$forms.messageForm.close} diff --git a/WEB-INF/templates/work/work_offer_view.tpl b/WEB-INF/templates/work/work_offer_view.tpl deleted file mode 100644 index e2afe0003..000000000 --- a/WEB-INF/templates/work/work_offer_view.tpl +++ /dev/null @@ -1,57 +0,0 @@ -{$forms.offerForm.open} - -{if $work_item} - - - -{/if} - - - -
- - - - - -
{$i18n.label.work}: {$work_item.subject}
{$i18n.label.client}: {$work_item.group_name}
{$i18n.label.description}: {$work_item.descr_short|escape}
{$i18n.label.budget}: {$work_item.amount_with_currency}
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
{$i18n.label.contractor}:{$forms.offerForm.contractor.control}
{$i18n.label.offer}:{$forms.offerForm.offer_name.control}
{$i18n.label.description}:{$forms.offerForm.description.control}
{$i18n.label.details}:{$forms.offerForm.details.control}
{$i18n.label.budget}:{$forms.offerForm.budget.control}
{$i18n.label.status}:{$forms.offerForm.status.control}
 
{$forms.offerForm.btn_accept.control} {$forms.offerForm.btn_decline.control}
{$i18n.label.comment}:{$forms.offerForm.client_comment.control}
-
-{$forms.offerForm.close} diff --git a/WEB-INF/templates/work/work_offers.tpl b/WEB-INF/templates/work/work_offers.tpl deleted file mode 100644 index ea0d8816f..000000000 --- a/WEB-INF/templates/work/work_offers.tpl +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - -
- - - - - -
{$i18n.label.work}: {$work_item.subject}
{$i18n.label.client}: {$work_item.group_name}
{$i18n.label.description}: {$work_item.descr_short|escape}
{$i18n.label.budget}: {$work_item.amount_with_currency}
-
- -{if $work_item_offers} - - - - - - - - {foreach $work_item_offers as $offer} - - - - - - - {/foreach} -
{$i18n.label.contractor}{$i18n.label.description}{$i18n.label.status}{$i18n.label.budget}
{$offer.group_name|escape}{$offer.description|escape}{$offer.status_label}{$offer.amount_with_currency}
-{/if} - -
diff --git a/WEB-INF/templates/work/work_view.tpl b/WEB-INF/templates/work/work_view.tpl deleted file mode 100644 index 468cb9ddd..000000000 --- a/WEB-INF/templates/work/work_view.tpl +++ /dev/null @@ -1,47 +0,0 @@ - - -{$forms.workForm.open} - - - - -
- - - - - - - - - - - - - - - - - - - - - -
{$i18n.label.client}:{$forms.workForm.client.control}
{$i18n.label.work}:{$forms.workForm.work_name.control}
{$i18n.label.description}:{$forms.workForm.description.control}
{$i18n.label.details}:{$forms.workForm.details.control}
{$i18n.label.budget}:{$forms.workForm.budget.control}
-
-{$forms.workForm.close} - - - - - -
- - - - - -
-
diff --git a/WEB-INF/templates/work/work_view_own.tpl b/WEB-INF/templates/work/work_view_own.tpl deleted file mode 100644 index 61e270b4b..000000000 --- a/WEB-INF/templates/work/work_view_own.tpl +++ /dev/null @@ -1,38 +0,0 @@ -{$forms.workForm.open} - -{if $offer} - - - -{/if} - - - -
- - - - - -
{$i18n.label.offer}: {$offer.subject}
{$i18n.label.contractor}: {$offer.group_name}
{$i18n.label.description}: {$offer.descr_short|escape}
{$i18n.label.budget}: {$offer.amount_with_currency}
-
- - - - - - - - - - - - - - - - - -
{$i18n.label.work}:{$forms.workForm.work_name.control}
{$i18n.label.description}:{$forms.workForm.description.control}
{$i18n.label.details}:{$forms.workForm.details.control}
{$i18n.label.budget}:{$forms.workForm.budget.control}
-
-{$forms.workForm.close} diff --git a/admin_group_delete.php b/admin_group_delete.php index 0cea02b32..909b2347a 100644 --- a/admin_group_delete.php +++ b/admin_group_delete.php @@ -13,7 +13,7 @@ } $group_id = (int)$request->getParameter('id'); $group_name = ttAdmin::getGroupName($group_id); -if (!($group_id && $group_name)) { +if (!($group_id && $group_name != null)) { header('Location: access_denied.php'); exit(); } diff --git a/admin_group_edit.php b/admin_group_edit.php index 81588ebec..496c7b8fb 100644 --- a/admin_group_edit.php +++ b/admin_group_edit.php @@ -14,7 +14,7 @@ } $group_id = (int)$request->getParameter('id'); $group_name = ttAdmin::getGroupName($group_id); -if (!($group_id && $group_name)) { +if (!($group_id && $group_name != null)) { header('Location: access_denied.php'); exit(); } diff --git a/admin_options.php b/admin_options.php index 84a0ed3a5..aed852320 100644 --- a/admin_options.php +++ b/admin_options.php @@ -48,7 +48,7 @@ // If we change login, it must be unique. if ($cl_login != $user->login && ttUserHelper::getUserByLogin($cl_login)) $err->add($i18n->get('error.user_exists')); - if (!$auth->isPasswordExternal() && ($cl_password1 || $cl_password2)) { + if (!$auth->isPasswordExternal() && ($cl_password1 != null || $cl_password2 != null)) { if (!ttValidString($cl_password1)) $err->add($i18n->get('error.field'), $i18n->get('label.password')); if (!ttValidString($cl_password2)) diff --git a/cf_custom_field_add.php b/cf_custom_field_add.php index 6cfd87256..87d46bcd5 100644 --- a/cf_custom_field_add.php +++ b/cf_custom_field_add.php @@ -15,7 +15,6 @@ header('Location: feature_disabled.php'); exit(); } -$fields = CustomFields::getFields(); // End of access checks. if ($request->isPost()) { @@ -29,7 +28,8 @@ $form->addInput(array('type'=>'text','maxlength'=>'100','name'=>'name','value'=>'')); $form->addInput(array('type'=>'combobox','name'=>'entity', 'data'=>array(CustomFields::ENTITY_TIME=>$i18n->get('entity.time'), - CustomFields::ENTITY_USER=>$i18n->get('entity.user')) + CustomFields::ENTITY_USER=>$i18n->get('entity.user'), + CustomFields::ENTITY_PROJECT=>$i18n->get('entity.project')) )); $form->addInput(array('type'=>'combobox','name'=>'type', 'data'=>array(CustomFields::TYPE_TEXT=>$i18n->get('label.type_text'), @@ -41,6 +41,7 @@ if ($request->isPost()) { // Validate user input. if (!ttValidString($cl_field_name)) $err->add($i18n->get('error.field'), $i18n->get('label.thing_name')); + if (CustomFields::getFieldByName($cl_field_name, $cl_entity_type) != null) $err->add($i18n->get('error.object_exists')); if ($err->no()) { $res = CustomFields::insertField($cl_field_name, $cl_entity_type, $cl_field_type, $cl_required); diff --git a/cf_dropdown_option_delete.php b/cf_dropdown_option_delete.php index e6eeeeb75..50c685014 100644 --- a/cf_dropdown_option_delete.php +++ b/cf_dropdown_option_delete.php @@ -17,7 +17,7 @@ } $cl_id = (int)$request->getParameter('id'); $option = CustomFields::getOptionName($cl_id); -if (!$option) { +if ($option == null) { header('Location: access_denied.php'); exit(); } diff --git a/cf_dropdown_option_edit.php b/cf_dropdown_option_edit.php index eee02f63c..2477b057c 100644 --- a/cf_dropdown_option_edit.php +++ b/cf_dropdown_option_edit.php @@ -17,7 +17,7 @@ } $cl_id = (int)$request->getParameter('id'); $cl_name = CustomFields::getOptionName($cl_id); -if (!$cl_name) { +if ($cl_name == null) { header('Location: access_denied.php'); exit(); } diff --git a/charts.php b/charts.php index 1bf6d34fa..9dbb95c70 100644 --- a/charts.php +++ b/charts.php @@ -6,15 +6,18 @@ require_once('initialize.php'); import('form.Form'); -import('DateAndTime'); +import('ttDate'); import('ttChartHelper'); import('ttUserConfig'); import('PieChartEx'); import('ttUserHelper'); import('ttTeamHelper'); +import('ttFavReportHelper'); + +define('ALL_USERS_OPTION_ID', -1); // An identifier for "all users" seclection in User dropdown. // Access checks. -if (!(ttAccessAllowed('view_own_charts') || ttAccessAllowed('view_charts'))) { +if (!(ttAccessAllowed('view_own_charts') || ttAccessAllowed('view_charts') || ttAccessAllowed('view_all_charts'))) { header('Location: access_denied.php'); exit(); } @@ -26,7 +29,7 @@ header('Location: access_denied.php'); // Nobody to display a chart for. exit(); } -if ($user->behalf_id && (!$user->can('view_charts') || !$user->checkBehalfId())) { +if ($user->behalf_id && (!($user->can('view_charts') || $user->can('view_all_charts')) || !$user->checkBehalfId())) { header('Location: access_denied.php'); // Trying on behalf, but no right or wrong user. exit(); } @@ -34,21 +37,34 @@ header('Location: access_denied.php'); // Trying as self, but no right for self, and noone to view on behalf. exit(); } +$userDropdownSelectionId = (int)$request->getParameter('user'); // Reused below access checks. if ($request->isPost() && $request->getParameter('user')) { - if (!$user->isUserValid((int)$request->getParameter('user'))) { + if ($userDropdownSelectionId == constant('ALL_USERS_OPTION_ID') && !ttAccessAllowed('view_all_charts')) { + header('Location: access_denied.php'); // All users option is only for users with view_all_charts access right. + exit(); + } + if ($userDropdownSelectionId != constant('ALL_USERS_OPTION_ID') && !$user->isUserValid($userDropdownSelectionId)) { header('Location: access_denied.php'); // Wrong user id on post. exit(); } } +$date = $request->getParameter('date'); +if ($date && !ttValidDbDateFormatDate($date)) { + header('Location: access_denied.php'); + exit(); +} // End of access checks. -// Determine user for which we display this page. +// Determine user for whom we display this page. $userChanged = (int)$request->getParameter('user_changed'); -if ($request->isPost() && $userChanged) { - $user_id = (int)$request->getParameter('user'); - $user->setOnBehalfUser($user_id); -} else { - $user_id = $user->getUser(); +if ($request->isPost() && $userChanged ) { + if ($userDropdownSelectionId != constant('ALL_USERS_OPTION_ID')) { + $user->setOnBehalfUser($userDropdownSelectionId); + } +} +if ($request->isGet()) { + $userDropdownSelectionId = $user->getUser(); + // Note that this may change to ALL_USERS_OPTION_ID below from session. } $uc = new ttUserConfig(); @@ -57,52 +73,105 @@ // Initialize and store date in session. $cl_date = $request->getParameter('date', @$_SESSION['date']); if(!$cl_date) { - $now = new DateAndTime(DB_DATEFORMAT); - $cl_date = $now->toString(DB_DATEFORMAT); + $now = new ttDate(); + $cl_date = $now->toString(); } $_SESSION['date'] = $cl_date; if ($request->isPost()) { - $cl_interval = (int)$request->getParameter('interval'); - if (!$cl_interval) $cl_interval = INTERVAL_THIS_MONTH; - $_SESSION['chart_interval'] = $cl_interval; - $uc->setValue(SYSC_CHART_INTERVAL, $cl_interval); + $cl_fav_report = (int)$request->getParameter('favorite_report'); + if (!$cl_fav_report) $cl_fav_report = -1; + $_SESSION['fav_report'] = $cl_fav_report; $cl_type = (int)$request->getParameter('type'); if (!$cl_type) $cl_type = ttChartHelper::adjustType($cl_type); $_SESSION['chart_type'] = $cl_type; $uc->setValue(SYSC_CHART_TYPE, $cl_type); -} else { - // Initialize chart interval. - $cl_interval = @$_SESSION['chart_interval']; - if (!$cl_interval) $cl_interval = $uc->getValue(SYSC_CHART_INTERVAL); + + // Remember all users selection in session. + $_SESSION['chart_all_users'] = $userDropdownSelectionId == constant('ALL_USERS_OPTION_ID') ? true : false; + + $cl_interval = (int)$request->getParameter('interval'); if (!$cl_interval) $cl_interval = INTERVAL_THIS_MONTH; $_SESSION['chart_interval'] = $cl_interval; + $uc->setValue(SYSC_CHART_INTERVAL, $cl_interval); +} else { + // Initialize fav report selector. + $cl_fav_report = @$_SESSION['fav_report']; + if (!$cl_fav_report) $cl_fav_report = -1; + $_SESSION['fav_report'] = $cl_fav_report; // Initialize chart type. $cl_type = @$_SESSION['chart_type']; if (!$cl_type) $cl_type = $uc->getValue(SYSC_CHART_TYPE); $cl_type = ttChartHelper::adjustType($cl_type); $_SESSION['chart_type'] = $cl_type; + + // Set user selection to all users, if necessary. + $allUsersSetInSession = @$_SESSION['chart_all_users']; + if ($allUsersSetInSession) + $userDropdownSelectionId = constant('ALL_USERS_OPTION_ID'); + + // Initialize chart interval. + $cl_interval = @$_SESSION['chart_interval']; + if (!$cl_interval) $cl_interval = $uc->getValue(SYSC_CHART_INTERVAL); + if (!$cl_interval) $cl_interval = INTERVAL_THIS_MONTH; + $_SESSION['chart_interval'] = $cl_interval; } // Elements of chartForm. $chart_form = new Form('chartForm'); $largeScreenCalendarRowSpan = 1; // Number of rows calendar spans on large screens. +// Fav report control. +$report_list = ttFavReportHelper::getReports(); +$chart_form->addInput(array('type'=>'combobox', + 'name'=>'favorite_report', + 'onchange'=>'handleFavReportSelection();this.form.submit();', + 'data'=>$report_list, + 'value' => $cl_fav_report, + 'datakeys'=>array('id','name'), + 'empty'=>array('-1'=>$i18n->get('dropdown.no')))); +$largeScreenCalendarRowSpan += 2; + +// Chart type options. +$chart_selector = (MODE_PROJECTS_AND_TASKS == $tracking_mode || $user->isPluginEnabled('cl')); +if ($chart_selector) { + $types = array(); + if (MODE_PROJECTS == $tracking_mode || MODE_PROJECTS_AND_TASKS == $tracking_mode) + $types[CHART_PROJECTS] = $i18n->get('dropdown.projects'); + if (MODE_PROJECTS_AND_TASKS == $tracking_mode) + $types[CHART_TASKS] = $i18n->get('dropdown.tasks'); + if ($user->isPluginEnabled('cl')) + $types[CHART_CLIENTS] = $i18n->get('dropdown.clients'); + + // Add chart type dropdown. + $chart_form->addInput(array('type' => 'combobox', + 'onchange' => 'this.form.submit();', + 'name' => 'type', + 'value' => $cl_type, + 'data' => $types + )); + $largeScreenCalendarRowSpan += 2; +} + // User dropdown. Changes the user "on behalf" of whom we are working. -if ($user->can('view_charts')) { +if ($user->can('view_charts') || $user->can('view_all_charts')) { $rank = $user->getMaxRankForGroup($user->getGroup()); if ($user->can('view_own_charts')) $options = array('status'=>ACTIVE,'max_rank'=>$rank,'include_self'=>true,'self_first'=>true); else $options = array('status'=>ACTIVE,'max_rank'=>$rank); $user_list = $user->getUsers($options); + // Add the --- all --- option to dropdown. + if ($user->can('view_all_charts')) { + $user_list[] = array('id'=>'-1','group_id'=>$user->getGroup(), 'name'=>$i18n->get('dropdown.all'), 'rights'=>''); + } if (count($user_list) >= 1) { $chart_form->addInput(array('type'=>'combobox', 'onchange'=>'this.form.user_changed.value=1;this.form.submit();', 'name'=>'user', - 'value'=>$user_id, + 'value'=>$userDropdownSelectionId, 'data'=>$user_list, 'datakeys'=>array('id','name'), )); @@ -117,6 +186,7 @@ $intervals[INTERVAL_THIS_DAY] = $i18n->get('dropdown.selected_day'); $intervals[INTERVAL_THIS_WEEK] = $i18n->get('dropdown.selected_week'); $intervals[INTERVAL_THIS_MONTH] = $i18n->get('dropdown.selected_month'); +$intervals[INTERVAL_PREVIOUS_MONTH] = $i18n->get('dropdown.previous_month'); $intervals[INTERVAL_THIS_YEAR] = $i18n->get('dropdown.selected_year'); $intervals[INTERVAL_ALL_TIME] = $i18n->get('dropdown.all_time'); @@ -129,32 +199,14 @@ )); $largeScreenCalendarRowSpan += 2; -// Chart type options. -$chart_selector = (MODE_PROJECTS_AND_TASKS == $tracking_mode || $user->isPluginEnabled('cl')); -if ($chart_selector) { - $types = array(); - if (MODE_PROJECTS == $tracking_mode || MODE_PROJECTS_AND_TASKS == $tracking_mode) - $types[CHART_PROJECTS] = $i18n->get('dropdown.projects'); - if (MODE_PROJECTS_AND_TASKS == $tracking_mode) - $types[CHART_TASKS] = $i18n->get('dropdown.tasks'); - if ($user->isPluginEnabled('cl')) - $types[CHART_CLIENTS] = $i18n->get('dropdown.clients'); - - // Add chart type dropdown. - $chart_form->addInput(array('type' => 'combobox', - 'onchange' => 'this.form.submit();', - 'name' => 'type', - 'value' => $cl_type, - 'data' => $types - )); - $largeScreenCalendarRowSpan += 2; -} - // Calendar. $chart_form->addInput(array('type'=>'calendar','name'=>'date','value'=>$cl_date)); // calendar // Get data for our chart. -$totals = ttChartHelper::getTotals($user_id, $cl_type, $cl_date, $cl_interval); +if ($cl_fav_report == -1) + $totals = ttChartHelper::getTotals($userDropdownSelectionId, $cl_type, $cl_date, $cl_interval); +else + $totals = ttChartHelper::getTotalsForFavReport($cl_fav_report, $cl_type); $smarty->assign('totals', $totals); // Prepare chart for drawing. @@ -202,7 +254,7 @@ $smarty->assign('large_screen_calendar_row_span', $largeScreenCalendarRowSpan); $smarty->assign('img_file_name', $img_ref); $smarty->assign('chart_selector', $chart_selector); -$smarty->assign('onload', 'onLoad="adjustTodayLinks()"'); +$smarty->assign('onload', 'onLoad="adjustTodayLinks();handleFavReportSelection();"'); $smarty->assign('forms', array($chart_form->getName() => $chart_form->toArray())); $smarty->assign('title', $i18n->get('title.charts')); $smarty->assign('content_page_name', 'charts.tpl'); diff --git a/client_edit.php b/client_edit.php index 349834181..665fda15e 100644 --- a/client_edit.php +++ b/client_edit.php @@ -64,6 +64,7 @@ if (!ttValidString($cl_name)) $err->add($i18n->get('error.field'), $i18n->get('label.client_name')); if (!ttValidString($cl_address, true)) $err->add($i18n->get('error.field'), $i18n->get('label.client_address')); if (!ttValidFloat($cl_tax, true)) $err->add($i18n->get('error.field'), $i18n->get('label.tax')); + if (!ttValidStatus($cl_status)) $err->add($i18n->get('error.field'), $i18n->get('label.status')); if ($err->no()) { if ($request->getParameter('btn_save')) { diff --git a/dbinstall.php b/dbinstall.php index 2dfe30d66..44fede0cd 100644 --- a/dbinstall.php +++ b/dbinstall.php @@ -91,13 +91,22 @@ function ttGenerateKeys() { } // Check if PHP version is good enough. - // $required_version = '5.2.1'; // Something in TCPDF library does not work below this one. - $required_version = '5.4.0'; // Week view (week.php) requires 5.4 because of []-way of referencing arrays. - // This needs further investigation as we use [] elsewhere without obvious problems. - if (version_compare(phpversion(), $required_version, '>=')) { - echo('PHP version: '.phpversion().', good enough.
'); + $required_version = '7.0'; // smarty 4.3.0 that we embed uses null coalescing operator (introduced in php 7). + + // Print a warning about php <= 7.4 because we are no longer testing there. + if (version_compare(phpversion(), '7.4', '<')) { + echo('Warning: This app is no longer tested with PHP version: '.phpversion().'.
'); + } + + // Print a warning about php >= 8.2 because of insufficient testing there. + if (version_compare(phpversion(), '8.2', '>=')) { + echo('Error: This app was not tested with PHP version: '.phpversion().'.
'); } else { - echo('Error: PHP version is not high enough: '.phpversion().'. Required: '.$required_version.'.
'); + if (version_compare(phpversion(), $required_version, '>=')) { + echo('PHP version: '.phpversion().', good enough.
'); + } else { + echo('Error: PHP version is not high enough: '.phpversion().'. Required: '.$required_version.'.
'); + } } // Depending on DSN, require either mysqli or mysql extensions. @@ -171,13 +180,17 @@ function ttGenerateKeys() { echo('There are no tables in database. Execute step 1 - Create database structure.
'); } - $sql = "select param_value from tt_site_config where param_name = 'version_db'"; - $res = $conn->query($sql); - if (is_a($res, 'MDB2_Error')) { - echo('Error: database schema version query failed. '.$res->getMessage().'
'); - } else { - $val = $res->fetchRow(); - echo('Database version is: '.$val['param_value'].'.'); + try { + $sql = "select param_value from tt_site_config where param_name = 'version_db'"; + $res = $conn->query($sql); + if (is_a($res, 'MDB2_Error')) { + echo('Error: database schema version query failed. '.$res->getMessage().'
'); + } else { + $val = $res->fetchRow(); + echo('Database version is: '.$val['param_value'].'.'); + } + } catch (Exception $e) { + echo('Caught exception: '.$e->getMessage().'
'); } $conn->disconnect(); @@ -187,7 +200,7 @@ function ttGenerateKeys() { if ($_POST) { print "Processing...
\n"; - if ($_POST["crstructure"]) { + if (array_key_exists('crstructure', $_POST)) { $sqlQuery = join("\n", file("mysql.sql")); $sqlQuery = str_replace("TYPE=MyISAM","",$sqlQuery); $queries = explode(";",$sqlQuery); @@ -201,7 +214,7 @@ function ttGenerateKeys() { } } - if ($_POST["convert5to7"]) { + if (array_key_exists('convert5to7', $_POST)) { ttExecute("alter table `activity_log` CHANGE al_comment al_comment text"); ttExecute("CREATE TABLE `sysconfig` (`sysc_id` int(11) unsigned NOT NULL auto_increment,`sysc_name` varchar(32) NOT NULL default '',`sysc_value` varchar(70) default NULL, PRIMARY KEY (`sysc_id`), UNIQUE KEY `sysc_id` (`sysc_id`), UNIQUE KEY `sysc_name` (`sysc_name`))"); ttExecute("alter table `companies` add c_locktime int(4) default -1"); @@ -215,7 +228,7 @@ function ttGenerateKeys() { ttExecute("alter table users drop `u_aprojects`"); } - if ($_POST["convert7to133"]) { + if (array_key_exists('convert7to133', $_POST)) { ttExecute("ALTER TABLE users ADD COLUMN u_lang VARCHAR(20) DEFAULT NULL"); ttExecute("ALTER TABLE users ADD COLUMN u_email VARCHAR(100) DEFAULT NULL"); ttExecute("ALTER TABLE `activity_log` drop `al_proof`"); @@ -230,7 +243,7 @@ function ttGenerateKeys() { // The update_projects function updates p_activities field in the projects table so that we could // improve performance of the application by using this field instead of activity_bind table. - if ($_POST["update_projects"]) { + if (array_key_exists('update_projects', $_POST)) { $mdb2 = getConnection(); // $sql = "select p_id from projects where p_status = 1 and p_activities is NULL"; $sql = "select p_id from projects where p_status = 1"; @@ -267,15 +280,15 @@ function ttGenerateKeys() { } } - if ($_POST["convert133to1340"]) { + if (array_key_exists('convert133to1340', $_POST)) { ttExecute("ALTER TABLE companies ADD COLUMN c_show_pie smallint(2) DEFAULT 1"); ttExecute("ALTER TABLE companies ADD COLUMN c_pie_mode smallint(2) DEFAULT 1"); ttExecute("ALTER TABLE companies ADD COLUMN c_lang varchar(20) default NULL"); } // The update_companies function sets up c_show_pie, c_pie_mode, and c_lang - // fields in the companies table from the corresponding manager fields. - if ($_POST["update_companies"]) { + // fields in the companies table from the corresponding manager fields. + if (array_key_exists('update_companies', $_POST)) { $mdb2 = getConnection(); // Get all active managers. $sql = "select u_company_id, u_show_pie, u_pie_mode, u_lang from users @@ -302,7 +315,7 @@ function ttGenerateKeys() { } } - if ($_POST["convert1340to1485"]) { + if (array_key_exists('convert1340to1485', $_POST)) { ttExecute("ALTER TABLE users DROP u_show_pie"); ttExecute("ALTER TABLE users DROP u_pie_mode"); ttExecute("ALTER TABLE users DROP u_lang"); @@ -407,7 +420,7 @@ function ttGenerateKeys() { } // The update_to_team_id function sets team_id field projects, activities, and clients tables. - if ($_POST["update_to_team_id"]) { + if (array_key_exists('update_to_team_id', $_POST)) { $mdb2 = getConnection(); // Update projects. @@ -504,7 +517,7 @@ function ttGenerateKeys() { print "Updated $clients_updated clients...
\n"; } - if ($_POST["convert1485to1579"]) { + if (array_key_exists('convert1485to1579', $_POST)) { ttExecute("ALTER TABLE tt_fav_reports MODIFY id int(11) NOT NULL auto_increment"); ttExecute("RENAME TABLE clients TO tt_clients"); ttExecute("ALTER TABLE tt_clients CHANGE clnt_id id int(11) NOT NULL AUTO_INCREMENT"); @@ -611,7 +624,7 @@ function ttGenerateKeys() { ttExecute("create index invoice_idx on tt_log(invoice_id)"); } - if ($_POST["convert1579to1600"]) { + if (array_key_exists('convert1579to1600', $_POST)) { ttExecute("ALTER TABLE tt_invoices ADD COLUMN date date NOT NULL"); ttExecute("ALTER TABLE tt_teams ADD COLUMN custom_logo tinyint(4) default '0'"); ttExecute("ALTER TABLE tt_tasks ADD COLUMN description varchar(255) default NULL"); @@ -629,7 +642,7 @@ function ttGenerateKeys() { } // The update_clients function updates projects field in tt_clients table. - if ($_POST["update_clients"]) { + if (array_key_exists('update_clients', $_POST)) { $mdb2 = getConnection(); $sql = "select id from tt_clients where status = 1 or status = 0"; $res = $mdb2->query($sql); @@ -668,7 +681,7 @@ function ttGenerateKeys() { } // The update_custom_fields function updates option_id field in tt_custom_field_log table. - if ($_POST['update_custom_fields']) { + if (array_key_exists('update_custom_fields', $_POST)) { $mdb2 = getConnection(); $sql = "update tt_custom_field_log set option_id = value where option_id is null and value is not null and field_id in (select id from tt_custom_fields where type = 2)"; $affected = $mdb2->exec($sql); @@ -679,7 +692,7 @@ function ttGenerateKeys() { } // The update_tracking_mode function sets the tracking_mode field in tt_teams table to 2 (== MODE_PROJECTS_AND_TASKS). - if ($_POST['update_tracking_mode']) { + if (array_key_exists('update_tracking_mode', $_POST)) { $mdb2 = getConnection(); $sql = "update tt_teams set tracking_mode = 2 where tracking_mode = 0"; $affected = $mdb2->exec($sql); @@ -689,7 +702,7 @@ function ttGenerateKeys() { print "Updated $affected teams...
\n"; } - if ($_POST["convert1600to11400"]) { + if (array_key_exists('convert1600to11400', $_POST)) { ttExecute("DROP TABLE IF EXISTS tt_invoice_headers"); ttExecute("ALTER TABLE tt_fav_reports ADD COLUMN `client_id` int(11) default NULL"); ttExecute("ALTER TABLE tt_fav_reports ADD COLUMN `cf_1_option_id` int(11) default NULL"); @@ -737,7 +750,7 @@ function ttGenerateKeys() { ttExecute("ALTER TABLE `tt_log` ADD `paid` tinyint(4) NULL default '0' AFTER `billable`"); } - if ($_POST["convert11400to11744"]) { + if (array_key_exists('convert11400to11744', $_POST)) { ttExecute("ALTER TABLE `tt_teams` DROP `address`"); ttExecute("ALTER TABLE `tt_fav_reports` ADD `report_spec` text default NULL AFTER `user_id`"); ttExecute("ALTER TABLE `tt_fav_reports` ADD `paid_status` tinyint(4) default NULL AFTER `invoice`"); @@ -780,7 +793,7 @@ function ttGenerateKeys() { } // The update_role_id function assigns a role_id to users, who don't have it. - if ($_POST['update_role_id']) { + if (array_key_exists('update_role_id', $_POST)) { import('I18n'); $mdb2 = getConnection(); @@ -819,7 +832,7 @@ function ttGenerateKeys() { print "Updated $users_updated users...
\n"; } - if ($_POST["convert11744to11797"]) { + if (array_key_exists('convert11744to11797', $_POST)) { ttExecute("update `tt_roles` inner join `tt_site_config` sc on (sc.param_name = 'version_db' and sc.param_value = '1.17.44') set rights = replace(rights, 'override_punch_mode,override_date_lock', 'override_punch_mode,override_own_punch_mode,override_date_lock')"); ttExecute("UPDATE `tt_site_config` SET param_value = '1.17.48' where param_name = 'version_db' and param_value = '1.17.44'"); ttExecute("update `tt_users` inner join `tt_site_config` sc on (sc.param_name = 'version_db' and sc.param_value = '1.17.48') set role_id = (select id from tt_roles where team_id = 0 and `rank` = 512) where role = 324"); @@ -924,7 +937,7 @@ function ttGenerateKeys() { } // The update_group_id function updates group_id field in tt_log and tt_expense_items tables. - if ($_POST["update_group_id"]) { + if (array_key_exists('update_group_id', $_POST)) { $mdb2 = getConnection(); $sql = "(select distinct user_id from tt_log where group_id is null) union distinct (select distinct user_id from tt_expense_items where group_id is null)"; @@ -970,7 +983,7 @@ function ttGenerateKeys() { print "Updated $tt_expense_items_updated tt_expense_items records...
\n"; } - if ($_POST["convert11797to11900"]) { + if (array_key_exists('convert11797to11900', $_POST)) { ttExecute("ALTER TABLE `tt_fav_reports` CHANGE `group_by` `group_by1` varchar(20) default NULL"); ttExecute("ALTER TABLE `tt_fav_reports` ADD `group_by2` varchar(20) default NULL AFTER `group_by1`"); ttExecute("ALTER TABLE `tt_fav_reports` ADD `group_by3` varchar(20) default NULL AFTER `group_by2`"); @@ -1137,7 +1150,7 @@ function ttGenerateKeys() { ttExecute("UPDATE `tt_site_config` SET param_value = '1.19.0', modified = now() where param_name = 'version_db' and param_value = '1.18.61'"); } - if ($_POST["convert11900to11929"]) { + if (array_key_exists('convert11900to12203', $_POST)) { ttExecute("CREATE TABLE `tt_work_currencies` (`id` int(10) unsigned NOT NULL,`name` varchar(10) NOT NULL,PRIMARY KEY (`id`))"); ttExecute("create unique index currency_idx on tt_work_currencies(`name`)"); ttExecute("INSERT INTO `tt_work_currencies` (`id`, `name`) VALUES ('1', 'USD'), ('2', 'CAD'), ('3', 'AUD'), ('4', 'EUR'), ('5', 'NZD')"); @@ -1172,9 +1185,24 @@ function ttGenerateKeys() { ttExecute("UPDATE `tt_site_config` SET param_value = '1.19.23', modified = now() where param_name = 'version_db' and param_value = '1.19.22'"); ttExecute("ALTER TABLE `tt_groups` ADD `entities_modified` datetime default NULL AFTER `modified_by`"); ttExecute("UPDATE `tt_site_config` SET param_value = '1.19.29', modified = now() where param_name = 'version_db' and param_value = '1.19.23'"); + ttExecute("DROP TABLE `tt_work_currencies`"); + ttExecute("DROP TABLE `tt_work_categories`"); + ttExecute("update `tt_roles` inner join `tt_site_config` sc on (sc.param_name = 'version_db' and sc.param_value = '1.19.29') set rights = replace(rights, 'swap_roles,update_work', 'swap_roles') where `rank` >= 12"); + ttExecute("update `tt_roles` inner join `tt_site_config` sc on (sc.param_name = 'version_db' and sc.param_value = '1.19.29') set rights = replace(rights, 'view_all_reports,manage_work,bid_on_work', 'view_all_reports') where `rank` >= 68"); + ttExecute("UPDATE `tt_site_config` SET param_value = '1.20.0', modified = now() where param_name = 'version_db' and param_value = '1.19.29'"); + ttExecute("update `tt_roles` inner join `tt_site_config` sc on (sc.param_name = 'version_db' and sc.param_value = '1.20.0') set rights = replace(rights, 'manage_basic_settings,view_all_reports', 'manage_basic_settings,view_all_charts,view_all_reports') where `rank` >= 68"); + ttExecute("UPDATE `tt_site_config` SET param_value = '1.21.0', modified = now() where param_name = 'version_db' and param_value = '1.20.0'"); + ttExecute("update `tt_roles` inner join `tt_site_config` sc on (sc.param_name = 'version_db' and sc.param_value = '1.21.0') set rights = replace(rights, 'view_client_unapproved,delete_group', 'view_client_unapproved,override_2fa,delete_group') where `rank` = 512"); + ttExecute("update `tt_roles` inner join `tt_site_config` sc on (sc.param_name = 'version_db' and sc.param_value = '1.21.0') set rights = replace(rights, ',manage_work', '')"); + ttExecute("update `tt_roles` inner join `tt_site_config` sc on (sc.param_name = 'version_db' and sc.param_value = '1.21.0') set rights = replace(rights, ',update_work', '')"); + ttExecute("UPDATE `tt_site_config` SET param_value = '1.21.2', modified = now() where param_name = 'version_db' and param_value = '1.21.0'"); + ttExecute("ALTER TABLE tt_fav_reports ADD COLUMN `note_containing` varchar(80) default NULL after `paid_status`"); + ttExecute("UPDATE `tt_site_config` SET param_value = '1.21.7', modified = now() where param_name = 'version_db' and param_value = '1.21.2'"); + ttExecute("ALTER TABLE `tt_groups` ADD `custom_translation` text default NULL AFTER `custom_css`"); + ttExecute("UPDATE `tt_site_config` SET param_value = '1.22.3', modified = now() where param_name = 'version_db' and param_value = '1.21.7'"); } - if ($_POST["cleanup"]) { + if (array_key_exists('cleanup', $_POST)) { $mdb2 = getConnection(); $inactive_orgs = ttOrgHelper::getInactiveOrgs(); @@ -1185,6 +1213,13 @@ function ttGenerateKeys() { $res = ttOrgHelper::deleteOrg($inactive_orgs[$i]); } + // Delete items that were marked as deleted by users. + // This is currently commented out to ease up recovery of accidental deletes by users. + // These statements can be executed manually in MySQL console to shrink database a bit. + // ttExecute("delete from tt_log where status is null"); + // ttExecute("delete from tt_custom_field_log where status is null"); + // ttExecute("delete from tt_expense_items where status is null"); + ttExecute("OPTIMIZE TABLE tt_client_project_binds"); ttExecute("OPTIMIZE TABLE tt_clients"); ttExecute("OPTIMIZE TABLE tt_config"); @@ -1196,7 +1231,8 @@ function ttGenerateKeys() { ttExecute("OPTIMIZE TABLE tt_expense_items"); ttExecute("OPTIMIZE TABLE tt_fav_reports"); ttExecute("OPTIMIZE TABLE tt_invoices"); - ttExecute("OPTIMIZE TABLE tt_log"); + ttExecute("OPTIMIZE TABLE tt_log"); // This locks the production table for 4 minutes. Impossible to login, etc. + // TODO: what should we do about it? ttExecute("OPTIMIZE TABLE tt_monthly_quotas"); ttExecute("OPTIMIZE TABLE tt_templates"); ttExecute("OPTIMIZE TABLE tt_project_template_binds"); @@ -1222,7 +1258,7 @@ function ttGenerateKeys() {

DB Install

-
Create database structure (v1.19.29) + Create database structure (v1.22.3)
(applies only to new installations, do not execute when updating)
@@ -1271,8 +1307,8 @@ function ttGenerateKeys() {
Update database structure (v1.19 to v1.19.29)Update database structure (v1.19 to v1.22.3)
diff --git a/default.css b/default.css index dddc5eb3f..d1a77ebd8 100644 --- a/default.css +++ b/default.css @@ -145,6 +145,20 @@ div.page-hint { border-collapse: collapse; } +/* For printing large tables that do not fit into one page, we have to change the display +property to "block" and also use "break-before: avoid;". This eliminates a page break between +header and large table on print previews. I could not accomplish this with inline-block +because break-before is apparently ignored for inline-block elements. +inline-block above in itself is needed for nice horizontal centering on screen. +A side effect of this is that printed tables are not horizontally centered. */ +@media print +{ + .x-scrollable-table { display: block; break-before: avoid; break-after: auto; } + /* Not sure if we need this, keep commented out for now. + .x-scrollable-table tr { page-break-inside:avoid; page-break-after:auto; } + .x-scrollable-table td { page-break-inside:avoid; page-break-after:auto; } */ +} + /* th in x-scrollable-table */ .x-scrollable-table th { white-space: nowrap; @@ -363,7 +377,7 @@ input[type="text"].short-text-field { } /* textareas for fields */ -#description, #address, #item_name, #comment, #custom_css, #content { +#description, #address, #item_name, #comment, #custom_css, #custom_translation, #content { width: 220px; border-radius: 4px; border: 1px solid #c9c9c9; @@ -371,7 +385,7 @@ input[type="text"].short-text-field { } /* textareas with focus */ -#description:focus, #address:focus, #item_name:focus, #comment:focus, #custom_css:focus, #content:focus { +#description:focus, #address:focus, #item_name:focus, #comment:focus, #custom_css:focus, #custom_translation:focus, #content:focus { background-color: yellow; border: 1px solid black; outline: none; @@ -811,8 +825,10 @@ div.table-divider { height: 30px; } border: 1px solid rgba(0, 0, 0, .1); border-radius: 50%; background-color: rgba(0, 0, 0, .1); + margin-right: .25em; } -.uncompleted-entry.active { background-color: red; } +.uncompleted-entry.active-today { background-color: red; } +.uncompleted-entry.active { background-color: purple; } .table_icon { height: 16px; diff --git a/display_options.php b/display_options.php index 6e0ccc548..e0b7bc987 100644 --- a/display_options.php +++ b/display_options.php @@ -21,14 +21,18 @@ $cl_record_custom_fields = (bool)$request->getParameter('record_custom_fields'); $cl_report_note_on_separate_row = (bool)$request->getParameter('report_note_on_separate_row'); $cl_report_inactive_projects = (bool)($request->getParameter('report_inactive_projects')); + $cl_report_cost_per_hour = (bool)$request->getParameter('report_cost_per_hour'); $cl_custom_css = trim($request->getParameter('custom_css')); + $cl_custom_translation = trim($request->getParameter('custom_translation')); } else { $cl_time_note_on_separate_row = $config->getDefinedValue('time_note_on_separate_row'); $cl_time_not_complete_days = $config->getDefinedValue('time_not_complete_days'); $cl_record_custom_fields = $config->getDefinedValue('record_custom_fields'); $cl_report_note_on_separate_row = $config->getDefinedValue('report_note_on_separate_row'); $cl_report_inactive_projects = $config->getDefinedValue('report_inactive_projects'); + $cl_report_cost_per_hour = $config->getDefinedValue('report_cost_per_hour'); $cl_custom_css = $user->getCustomCss(); + $cl_custom_translation = $user->getCustomTranslation(); } $form = new Form('displayOptionsForm'); @@ -49,14 +53,17 @@ // Reports. $form->addInput(array('type'=>'checkbox','name'=>'report_note_on_separate_row','value'=>$cl_report_note_on_separate_row)); $form->addInput(array('type'=>'checkbox','name'=>'report_inactive_projects','value'=>$cl_report_inactive_projects)); +$form->addInput(array('type'=>'checkbox','name'=>'report_cost_per_hour','value'=>$cl_report_cost_per_hour)); // TODO: add PDF break controller here. $form->addInput(array('type'=>'textarea','name'=>'custom_css','value'=>$cl_custom_css)); +$form->addInput(array('type'=>'textarea','name'=>'custom_translation','value'=>$cl_custom_translation)); $form->addInput(array('type'=>'submit','name'=>'btn_save','value'=>$i18n->get('button.save'))); if ($request->isPost()){ // Validate user input. if (!ttValidCss($cl_custom_css)) $err->add($i18n->get('error.field'), $i18n->get('form.display_options.custom_css')); + if (!ttValidTranslation($cl_custom_translation)) $err->add($i18n->get('error.field'), $i18n->get('form.display_options.custom_translation')); // Finished validating user input. if ($err->no()) { @@ -66,9 +73,11 @@ $config->setDefinedValue('record_custom_fields', $cl_record_custom_fields); $config->setDefinedValue('report_note_on_separate_row', $cl_report_note_on_separate_row); $config->setDefinedValue('report_inactive_projects', $cl_report_inactive_projects); + $config->setDefinedValue('report_cost_per_hour', $cl_report_cost_per_hour); if ($user->updateGroup(array( 'config' => $config->getConfig(), - 'custom_css' => $cl_custom_css))) { + 'custom_css' => $cl_custom_css, + 'custom_translation' => $cl_custom_translation))) { header('Location: success.php'); exit(); } else diff --git a/expense_delete.php b/expense_delete.php index 6c5cb791f..6dbf421a9 100644 --- a/expense_delete.php +++ b/expense_delete.php @@ -4,7 +4,7 @@ require_once('initialize.php'); import('form.Form'); -import('DateAndTime'); +import('ttDate'); import('ttExpenseHelper'); // Access checks. @@ -30,7 +30,7 @@ if ($request->getParameter('delete_button')) { // Delete button pressed. // Determine if it is okay to delete the record. - $item_date = new DateAndTime(DB_DATEFORMAT, $expense_item['date']); + $item_date = new ttDate($expense_item['date']); if ($user->isDateLocked($item_date)) $err->add($i18n->get('error.range_locked')); diff --git a/expense_edit.php b/expense_edit.php index 271a588ac..98098a19f 100644 --- a/expense_edit.php +++ b/expense_edit.php @@ -5,9 +5,9 @@ require_once('initialize.php'); import('form.Form'); import('ttGroupHelper'); -import('DateAndTime'); import('ttTimeHelper'); import('ttExpenseHelper'); +import('ttDate'); // Access checks. if (!(ttAccessAllowed('track_own_expenses') || ttAccessAllowed('track_expenses'))) { @@ -27,9 +27,17 @@ header('Location: access_denied.php'); exit(); } +if ($request->isPost()) { + // Validate that browser_today parameter is in correct format. + $browser_today = $request->getParameter('browser_today'); + if ($browser_today && !ttValidDbDateFormatDate($browser_today)) { + header('Location: access_denied.php'); + exit(); + } +} // End of access checks. -$item_date = new DateAndTime(DB_DATEFORMAT, $expense_item['date']); +$item_date = new ttDate($expense_item['date']); $confirm_save = $user->getConfigOption('confirm_save'); $trackingMode = $user->getTrackingMode(); $show_project = MODE_PROJECTS == $trackingMode || MODE_PROJECTS_AND_TASKS == $trackingMode; @@ -142,12 +150,14 @@ if (!ttValidDate($cl_date)) $err->add($i18n->get('error.field'), $i18n->get('label.date')); // This is a new date for the expense item. - $new_date = new DateAndTime($user->getDateFormat(), $cl_date); + $new_date = new ttDate($cl_date, $user->getDateFormat()); // Prohibit creating entries in future. if (!$user->isOptionEnabled('future_entries')) { - $browser_today = new DateAndTime(DB_DATEFORMAT, $request->getParameter('browser_today', null)); - if ($new_date->after($browser_today)) + $browser_today = new ttDate($request->getParameter('browser_today', null)); + $server_tomorrow = new ttDate(); + $server_tomorrow->incrementDay(); + if ($new_date->after($browser_today) || $new_date->after($server_tomorrow)) $err->add($i18n->get('error.future_date')); } if (!ttTimeHelper::canAdd()) $err->add($i18n->get('error.expired')); @@ -170,9 +180,9 @@ // Now, an update. if ($err->no()) { - if (ttExpenseHelper::update(array('id'=>$cl_id,'date'=>$new_date->toString(DB_DATEFORMAT), + if (ttExpenseHelper::update(array('id'=>$cl_id,'date'=>$new_date->toString(), 'client_id'=>$cl_client,'project_id'=>$cl_project,'name'=>$cl_item_name,'cost'=>$cl_cost,'paid'=>$cl_paid))) { - header('Location: expenses.php?date='.$new_date->toString(DB_DATEFORMAT)); + header('Location: expenses.php?date='.$new_date->toString()); exit(); } } @@ -186,9 +196,9 @@ // Now, a new insert. if ($err->no()) { - if (ttExpenseHelper::insert(array('date'=>$new_date->toString(DB_DATEFORMAT),'client_id'=>$cl_client, + if (ttExpenseHelper::insert(array('date'=>$new_date->toString(),'client_id'=>$cl_client, 'project_id'=>$cl_project,'name'=>$cl_item_name,'cost'=>$cl_cost,'status'=>1))) { - header('Location: expenses.php?date='.$new_date->toString(DB_DATEFORMAT)); + header('Location: expenses.php?date='.$new_date->toString()); exit(); } else $err->add($i18n->get('error.db')); diff --git a/expenses.php b/expenses.php index 1914a253e..3cbbd8bec 100644 --- a/expenses.php +++ b/expenses.php @@ -6,7 +6,7 @@ import('form.Form'); import('ttUserHelper'); import('ttGroupHelper'); -import('DateAndTime'); +import('ttDate'); import('ttTimeHelper'); import('ttExpenseHelper'); import('ttFileHelper'); @@ -33,11 +33,26 @@ exit(); } if ($request->isPost() && $request->getParameter('user')) { - if (!$user->isUserValid((int)$request->getParameter('user'))) { + $posted_user_id = $request->getParameter('user'); + if (!ttValidInteger($posted_user_id) && !$user->isUserValid($posted_user_id)) { header('Location: access_denied.php'); // Wrong user id on post. exit(); } } +// If we are passed in a date, make sure it is in correct format. +$date = $request->getParameter('date'); +if ($date && !ttValidDbDateFormatDate($date)) { + header('Location: access_denied.php'); + exit(); +} +if ($request->isPost()) { + // Validate that browser_today parameter is in correct format. + $browser_today = $request->getParameter('browser_today'); + if ($browser_today && !ttValidDbDateFormatDate($browser_today)) { + header('Location: access_denied.php'); + exit(); + } +} // End of access checks. // Determine user for which we display this page. @@ -51,13 +66,12 @@ // Initialize and store date in session. $cl_date = $request->getParameter('date', @$_SESSION['date']); -$selected_date = new DateAndTime(DB_DATEFORMAT, $cl_date); -if($selected_date->isError()) - $selected_date = new DateAndTime(DB_DATEFORMAT); +$selected_date = new ttDate($cl_date); if(!$cl_date) - $cl_date = $selected_date->toString(DB_DATEFORMAT); + $cl_date = $selected_date->toString(); $_SESSION['date'] = $cl_date; + $tracking_mode = $user->getTrackingMode(); $show_project = MODE_PROJECTS == $tracking_mode || MODE_PROJECTS_AND_TASKS == $tracking_mode; $showFiles = $user->isPluginEnabled('at'); @@ -175,6 +189,10 @@ // Submit. if ($request->isPost()) { if ($request->getParameter('btn_submit')) { + // Use the "limit" plugin if we have one. Ignore include errors. + // The "limit" plugin is not required for normal operation of Time Tracker. + @include('plugins/limit/access_check.php'); + // Validate user input. if ($user->isPluginEnabled('cl') && $user->isOptionEnabled('client_required') && !$cl_client) $err->add($i18n->get('error.client')); @@ -185,11 +203,12 @@ // Prohibit creating entries in future. if (!$user->isOptionEnabled('future_entries')) { - $browser_today = new DateAndTime(DB_DATEFORMAT, $request->getParameter('browser_today', null)); - if ($selected_date->after($browser_today)) + $browser_today = new ttDate($request->getParameter('browser_today', null)); + $server_tomorrow = new ttDate(); + $server_tomorrow->incrementDay(); + if ($selected_date->after($browser_today) || $selected_date->after($server_tomorrow)) $err->add($i18n->get('error.future_date')); } - if (!ttTimeHelper::canAdd()) $err->add($i18n->get('error.expired')); // Finished validating input data. // Prohibit creating entries in locked range. diff --git a/group_advanced_edit.php b/group_advanced_edit.php index d08017576..26134c789 100644 --- a/group_advanced_edit.php +++ b/group_advanced_edit.php @@ -16,17 +16,22 @@ // End of access checks. $group = ttGroupHelper::getGroupAttrs($user->getGroup()); +$config = $user->getConfigHelper(); if ($request->isPost()) { $cl_group = trim($request->getParameter('group_name')); $cl_description = trim($request->getParameter('description')); $cl_bcc_email = trim($request->getParameter('bcc_email')); $cl_allow_ip = trim($request->getParameter('allow_ip')); + $cl_password_complexity = trim($request->getParameter('password_complexity')); + $cl_2fa = (bool) $request->getParameter('2fa'); } else { $cl_group = $group['name']; $cl_description = $group['description']; $cl_bcc_email = $group['bcc_email']; $cl_allow_ip = $group['allow_ip']; + $cl_password_complexity = $group['password_complexity']; + $cl_2fa = $config->getDefinedValue('2fa'); } $form = new Form('groupAdvancedForm'); @@ -34,6 +39,8 @@ $form->addInput(array('type'=>'textarea','name'=>'description','value'=>$cl_description)); $form->addInput(array('type'=>'text','maxlength'=>'100','name'=>'bcc_email','value'=>$cl_bcc_email)); $form->addInput(array('type'=>'text','maxlength'=>'100','name'=>'allow_ip','value'=>$cl_allow_ip)); +$form->addInput(array('type'=>'text','maxlength'=>'100','name'=>'password_complexity','value'=>$cl_password_complexity)); +$form->addInput(array('type'=>'checkbox','name'=>'2fa','value'=>$cl_2fa)); $form->addInput(array('type'=>'submit','name'=>'btn_save','value'=>$i18n->get('button.save'))); @@ -43,15 +50,22 @@ if (!ttValidString($cl_group)) $err->add($i18n->get('error.field'), $i18n->get('label.group_name')); if (!ttValidString($cl_description, true)) $err->add($i18n->get('error.field'), $i18n->get('label.description')); if (!ttValidEmail($cl_bcc_email, true)) $err->add($i18n->get('error.field'), $i18n->get('label.bcc')); - if (!ttValidIP($cl_allow_ip, true)) $err->add($i18n->get('error.field'), $i18n->get('form.group_edit.allow_ip')); + if (!ttValidIP($cl_allow_ip, true)) $err->add($i18n->get('error.field'), $i18n->get('form.group_advanced_edit.allow_ip')); + if (!ttValidPasswordComplexity($cl_password_complexity, true)) $err->add($i18n->get('error.field'), $i18n->get('form.group_advanced_edit.password_complexity')); // Finished validating user input. if ($err->no()) { + // Update config. + $config->setDefinedValue('2fa', $cl_2fa); + + // Update group. if ($user->updateGroup(array( 'name' => $cl_group, 'description' => $cl_description, 'bcc_email' => $cl_bcc_email, - 'allow_ip' => $cl_allow_ip))) { + 'allow_ip' => $cl_allow_ip, + 'password_complexity' => $cl_password_complexity, + 'config' => $config->getConfig()))) { header('Location: success.php'); exit(); } else diff --git a/group_edit.php b/group_edit.php index ab3065d52..dbfca56bb 100644 --- a/group_edit.php +++ b/group_edit.php @@ -66,6 +66,7 @@ $cl_project_required = $request->getParameter('project_required'); $cl_record_type = $request->getParameter('record_type'); $cl_punch_mode = (bool)$request->getParameter('punch_mode'); + $cl_one_uncompleted = (bool)$request->getParameter('one_uncompleted'); $cl_allow_overlap = (bool)$request->getParameter('allow_overlap'); $cl_future_entries = (bool)$request->getParameter('future_entries'); $cl_uncompleted_indicators = (bool)$request->getParameter('uncompleted_indicators'); @@ -82,6 +83,7 @@ $cl_project_required = $group['project_required']; $cl_record_type = $group['record_type']; $cl_punch_mode = $config->getDefinedValue('punch_mode'); + $cl_one_uncompleted = $config->getDefinedValue('one_uncompleted'); $cl_allow_overlap = $config->getDefinedValue('allow_overlap'); $cl_future_entries = $config->getDefinedValue('future_entries'); $cl_uncompleted_indicators = $config->getDefinedValue('uncompleted_indicators'); @@ -166,6 +168,9 @@ // Punch mode checkbox. $form->addInput(array('type'=>'checkbox','name'=>'punch_mode','value'=>$cl_punch_mode)); +// One uncompleted. +$form->addInput(array('type'=>'checkbox','name'=>'one_uncompleted','value'=>$cl_one_uncompleted)); + // Allow overlap checkbox. $form->addInput(array('type'=>'checkbox','name'=>'allow_overlap','value'=>$cl_allow_overlap)); @@ -200,6 +205,7 @@ if ($err->no()) { // Update config. $config->setDefinedValue('punch_mode', $cl_punch_mode); + $config->setDefinedValue('one_uncompleted', $cl_one_uncompleted); $config->setDefinedValue('allow_overlap', $cl_allow_overlap); $config->setDefinedValue('future_entries', $cl_future_entries); $config->setDefinedValue('uncompleted_indicators', $cl_uncompleted_indicators); diff --git a/groups.php b/groups.php index a7bf2dfe6..ca01a5f7c 100644 --- a/groups.php +++ b/groups.php @@ -11,9 +11,16 @@ header('Location: access_denied.php'); exit(); } -if ($request->isPost() && !$user->isGroupValid($request->getParameter('group'))) { - header('Location: access_denied.php'); // Wrong group id in post. - exit(); +if ($request->isPost()) { + $group_id = $request->getParameter('group'); + if (!ttValidInteger($group_id)) { + header('Location: access_denied.php'); // Protection against sql injection. + exit(); + } + if (!$user->isGroupValid($group_id)) { + header('Location: access_denied.php'); // Wrong group id in post. + exit(); + } } // End of access checks. diff --git a/initialize.php b/initialize.php index b8f7a204f..18946798c 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,16 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.19.29.5596"); +// Disable mysqli fatal error behaviour when using php8.1 or greater. +// See https://php.watch/versions/8.1/mysqli-error-mode +if (version_compare(phpversion(), '8.1', '>=')) { + if (function_exists('mysqli_report')) + mysqli_report(MYSQLI_REPORT_OFF); + else + die("mysqli_report function is not available."); // No point to continue as mysqli will not work. +} + +define("APP_VERSION", "1.22.22.5818"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); @@ -108,6 +117,11 @@ define('CHARSET', 'utf-8'); +// Definitions of max counts of utf8mb4 characters for various varchar database fields. +define('MAX_NAME_CHARS', 80); +define('MAX_DESCR_CHARS', 255); +define('MAX_CURRENCY_CHARS', 7); + date_default_timezone_set(@date_default_timezone_get()); // Initialize global objects that are needed for the application. diff --git a/invoice_view.php b/invoice_view.php index b03b719e4..b1fcf91f9 100644 --- a/invoice_view.php +++ b/invoice_view.php @@ -3,7 +3,7 @@ License: See license.txt */ require_once('initialize.php'); -import('DateAndTime'); +import('ttDate'); import('ttInvoiceHelper'); import('ttClientHelper'); import('form.Form'); @@ -25,7 +25,7 @@ } // End of access checks. -$invoice_date = new DateAndTime(DB_DATEFORMAT, $invoice['date']); +$invoice_date = new ttDate($invoice['date']); $client = ttClientHelper::getClient($invoice['client_id'], true); if (!$client) // In case client was deleted. $client = ttClientHelper::getDeletedClient($invoice['client_id']); diff --git a/invoices.php b/invoices.php index 3f6438b96..d34e1220e 100644 --- a/invoices.php +++ b/invoices.php @@ -76,7 +76,7 @@ if (!ttInvoiceHelper::validSortOrder($sort_order_2)) $err->add($i18n->get('error.field'), $i18n->get('label.sort')); if ($sort_option_1 == $sort_option_2) $err->add($i18n->get('error.field'), $i18n->get('label.sort')); - if($request->getParameter('sorting_changed')) { + if ($err->no() && $request->getParameter('sorting_changed')) { // User changed sorting. Get invoices sorted accordingly. $sort_options = array('sort_option_1'=>$sort_option_1, 'sort_order_1'=>$sort_order_1, diff --git a/login.php b/login.php index 09c7af141..2d9dde044 100644 --- a/login.php +++ b/login.php @@ -6,6 +6,18 @@ import('form.Form'); import('ttOrgHelper'); import('ttUser'); +import('ttUserHelper'); + +// Access checks. +if ($request->isPost()) { + // Validate that browser_today parameter is in correct format. + $browser_today = $request->getParameter('browser_today'); + if ($browser_today && !ttValidDbDateFormatDate($browser_today)) { + header('Location: access_denied.php'); + exit(); + } +} +// End of access checks. $cl_login = $request->getParameter('login'); if ($cl_login == null && $request->isGet()) $cl_login = @$_COOKIE[LOGIN_COOKIE_NAME]; @@ -22,32 +34,103 @@ 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')); + $loginSucceeded = $use2FA = false; + if ($err->no()) { // Use the "limit" plugin if we have one. Ignore include errors. // The "limit" plugin is not required for normal operation of Time Tracker. @include('plugins/limit/access_check.php'); - if ($auth->doLogin($cl_login, $cl_password)) { - // Set current user date (as determined by user browser) into session. - $current_user_date = $request->getParameter('browser_today', null); - if ($current_user_date) - $_SESSION['date'] = $current_user_date; - + // Check user login. + $loginSucceeded = $auth->doLogin($cl_login, $cl_password); + if ($loginSucceeded) { // Remember user login in a cookie. setcookie(LOGIN_COOKIE_NAME, $cl_login, time() + COOKIE_EXPIRE, '/'); + } else { + $err->add($i18n->get('error.auth')); + } + } - $user = new ttUser(null, $auth->getUserId()); - // Redirect, depending on user role. - if ($user->can('administer_site')) { - header('Location: admin_groups.php'); - } elseif ($user->isClient()) { - header('Location: reports.php'); - } else { - header('Location: time.php'); - } - exit(); + // Do we have to use 2FA? + if ($err->no() && $loginSucceeded) { + $user = new ttUser(null, $auth->getUserId()); + + if ($user->initialized) { + // Determine if we have to additionally use two-factor authentication. + $config = $user->getConfigHelper(); + $use2FA = $config->getDefinedValue('2fa'); + } else { + $err->add($i18n->get('error.db')); + } + } + + // If we have to use 2FA, email auth code to user and redirect to 2fa.php. + if ($err->no() && $use2FA && !$user->can('override_2fa')) { + // To keep things simple, we use the same code as for password resets. + $cryptographically_strong = true; + $random_bytes = openssl_random_pseudo_bytes(16, $cryptographically_strong); + if ($random_bytes === false) die ("openssl_random_pseudo_bytes function call failed..."); + $temp_ref = bin2hex($random_bytes); + ttUserHelper::saveTmpRef($temp_ref, $user->id); + + // For user languague in email. + $user_i18n = null; + if ($user->lang != $i18n->lang) { + $user_i18n = new I18n(); + $user_i18n->load($user->lang); } else - $err->add($i18n->get('error.auth')); + $user_i18n = &$i18n; + + // Where do we email to? + $receiver = null; + if ($user->email) + $receiver = $user->email; + else { + if (ttValidEmail($user->login)) + $receiver = $user->login; + } + if (!$receiver) $err->add($user_i18n->get('error.no_email')); + + // Send 2FA_code email to user. + if ($receiver) { + import('mail.Mailer'); + $mailer = new Mailer(); + $mailer->setCharSet(CHARSET); + $mailer->setSender(SENDER); + $mailer->setReceiver("$receiver"); + + $subject = $user_i18n->get('email.2fa_code.subject'); + $body = sprintf($user_i18n->get('email.2fa_code.body'), $temp_ref); + + $mailer->setMailMode(MAIL_MODE); + if (!$mailer->send($subject, $body)) + $err->add($i18n->get('error.mail_send')); + } + + $auth->doLogout(); + + // Redirect to 2fa.php if we have no errors. + if ($err->no()) { + header('Location: 2fa.php'); + exit(); + } + } + + if ($err->no() && $loginSucceeded) { + // Set current user date (as determined by user browser) into session. + $current_user_date = $request->getParameter('browser_today', null); + if ($current_user_date) + $_SESSION['date'] = $current_user_date; + + // Redirect, depending on user role. + if ($user->can('administer_site')) { + header('Location: admin_groups.php'); + } elseif ($user->isClient()) { + header('Location: reports.php'); + } else { + header('Location: time.php'); + } + exit(); } } // isPost @@ -55,7 +138,7 @@ $err->add($i18n->get('error.no_groups')); // Determine whether to show login hint. It is currently used only for Windows LDAP authentication. -$show_hint = ('ad' == $GLOBALS['AUTH_MODULE_PARAMS']['type']); +$show_hint = ('ad' == isset($GLOBALS['AUTH_MODULE_PARAMS']['type']) ? $GLOBALS['AUTH_MODULE_PARAMS']['type'] : null); $smarty->assign('forms', array($form->getName()=>$form->toArray())); $smarty->assign('show_hint', $show_hint); diff --git a/mysql.sql b/mysql.sql index 7a87b3766..f614d0ac8 100644 --- a/mysql.sql +++ b/mysql.sql @@ -39,6 +39,7 @@ CREATE TABLE `tt_groups` ( `custom_logo` tinyint(4) default 0, # whether to use a custom logo or not `config` text default NULL, # miscellaneous group configuration settings `custom_css` text default NULL, # custom css for group + `custom_translation` text default NULL, # custom translation for group `created` datetime default NULL, # creation timestamp `created_ip` varchar(45) default NULL, # creator ip `created_by` int(11) default NULL, # creator user_id @@ -78,7 +79,7 @@ create unique index role_idx on tt_roles(group_id, `rank`, status); # Insert site-wide roles - site administrator and top manager. INSERT INTO `tt_roles` (`group_id`, `name`, `rank`, `rights`) VALUES (0, 'Site administrator', 1024, 'administer_site'); -INSERT INTO `tt_roles` (`group_id`, `name`, `rank`, `rights`) VALUES (0, 'Top manager', 512, 'track_own_time,track_own_expenses,view_own_reports,view_own_charts,view_own_projects,view_own_tasks,manage_own_settings,view_users,view_client_reports,view_client_invoices,track_time,track_expenses,view_reports,approve_reports,approve_timesheets,view_charts,view_own_clients,override_punch_mode,override_own_punch_mode,override_date_lock,override_own_date_lock,swap_roles,update_work,manage_own_account,manage_users,manage_projects,manage_tasks,manage_custom_fields,manage_clients,manage_invoices,override_allow_ip,manage_basic_settings,view_all_reports,manage_work,bid_on_work,manage_features,manage_advanced_settings,manage_roles,export_data,approve_all_reports,approve_own_timesheets,manage_subgroups,view_client_unapproved,delete_group'); +INSERT INTO `tt_roles` (`group_id`, `name`, `rank`, `rights`) VALUES (0, 'Top manager', 512, 'track_own_time,track_own_expenses,view_own_reports,view_own_charts,view_own_projects,view_own_tasks,manage_own_settings,view_users,view_client_reports,view_client_invoices,track_time,track_expenses,view_reports,approve_reports,approve_timesheets,view_charts,view_own_clients,override_punch_mode,override_own_punch_mode,override_date_lock,override_own_date_lock,swap_roles,manage_own_account,manage_users,manage_projects,manage_tasks,manage_custom_fields,manage_clients,manage_invoices,override_allow_ip,manage_basic_settings,view_all_charts,view_all_reports,manage_features,manage_advanced_settings,manage_roles,export_data,approve_all_reports,approve_own_timesheets,manage_subgroups,view_client_unapproved,override_2fa,delete_group'); # @@ -86,9 +87,9 @@ INSERT INTO `tt_roles` (`group_id`, `name`, `rank`, `rights`) VALUES (0, 'Top ma # CREATE TABLE `tt_users` ( `id` int(11) NOT NULL auto_increment, # user id - `login` varchar(50) COLLATE utf8mb4_bin NOT NULL,# user login + `login` varchar(80) COLLATE utf8mb4_bin NOT NULL,# user login `password` varchar(50) default NULL, # password hash - `name` varchar(100) default NULL, # user name + `name` varchar(80) default NULL, # user name `group_id` int(11) NOT NULL, # group id `org_id` int(11) default NULL, # organization id `role_id` int(11) default NULL, # role id @@ -273,6 +274,7 @@ CREATE TABLE `tt_fav_reports` ( `invoice` tinyint(4) default NULL, # whether to include invoiced, not invoiced, or all records `timesheet` tinyint(4) default NULL, # include records with a specific timesheet status, or all records `paid_status` tinyint(4) default NULL, # whether to include paid, not paid, or all records + `note_containing` varchar(80) default NULL, # include only records with notes containing this text `users` text default NULL, # Comma-separated list of user ids. Nothing here means "all" users. `period` tinyint(4) default NULL, # selected period type for report `period_start` date default NULL, # period start @@ -608,42 +610,6 @@ CREATE TABLE `tt_files` ( ); -# -# Structure for table tt_work_currencies. -# This table keeps currencies supported by remote work plugin. -# -CREATE TABLE `tt_work_currencies` ( - `id` int(10) unsigned NOT NULL, # currency id - `name` varchar(10) NOT NULL, # currency name (USD, CAD, etc.) - PRIMARY KEY (`id`) -); - -# Create an index that guarantees unique work currencies. -create unique index currency_idx on tt_work_currencies(`name`); - -INSERT INTO `tt_work_currencies` (`id`, `name`) VALUES ('1', 'USD'), ('2', 'CAD'), ('3', 'AUD'), ('4', 'EUR'), ('5', 'NZD'); - - -# -# Structure for table tt_work_categories. -# This table keeps work categories supported by remote work plugin. -# -CREATE TABLE `tt_work_categories` ( - `id` int(10) unsigned NOT NULL, # Category id. - `parents` text default NULL, # Comma-separated list of parent ids associated with this category. - `name` varchar(64) NOT NULL, # English category name. - `name_localized` text default NULL, # Comma-separated list of localized category names in other languages. - # Example: es:Codificación,ru:Кодирование. - PRIMARY KEY (`id`) -); - -# Insert some default categories. Table content to be updated at run time, though. -INSERT INTO `tt_work_categories` (`id`, `parents`, `name`, `name_localized`) VALUES ('1', NULL, 'Coding', 'es:Codificación,ru:Кодирование'); -INSERT INTO `tt_work_categories` (`id`, `parents`, `name`, `name_localized`) VALUES ('2', NULL, 'Other', 'es:Otra,ru:Другое'); -INSERT INTO `tt_work_categories` (`id`, `parents`, `name`, `name_localized`) VALUES ('3', '1', 'PHP', NULL); -INSERT INTO `tt_work_categories` (`id`, `parents`, `name`, `name_localized`) VALUES ('4', '1', 'C/C++', NULL); - - # # Structure for table tt_site_config. This table stores configuration data # for Time Tracker site as a whole. @@ -657,4 +623,4 @@ CREATE TABLE `tt_site_config` ( PRIMARY KEY (`param_name`) ); -INSERT INTO `tt_site_config` (`param_name`, `param_value`, `created`) VALUES ('version_db', '1.19.29', now()); # TODO: change when structure changes. +INSERT INTO `tt_site_config` (`param_name`, `param_value`, `created`) VALUES ('version_db', '1.22.3', now()); # TODO: change when structure changes. diff --git a/password_change.php b/password_change.php index 992bdd87b..d395b6396 100644 --- a/password_change.php +++ b/password_change.php @@ -49,6 +49,9 @@ if (!ttValidString($cl_password2)) $err->add($i18n->get('error.field'), $i18n->get('label.confirm_password')); if ($cl_password1 !== $cl_password2) $err->add($i18n->get('error.not_equal'), $i18n->get('label.password'), $i18n->get('label.confirm_password')); + // Check password complexity. + if (!ttCheckPasswordComplexity($cl_password1)) + $err->add($i18n->get('error.weak_password')); if ($err->no()) { // Use the "limit" plugin if we have one. Ignore include errors. diff --git a/plugins.php b/plugins.php index 95b6d4b9b..a4372694a 100644 --- a/plugins.php +++ b/plugins.php @@ -32,11 +32,10 @@ $cl_timesheets = (bool)$request->getParameter('timesheets'); $cl_templates = (bool)$request->getParameter('templates'); $cl_attachments = (bool)$request->getParameter('attachments'); - $cl_work = (bool)$request->getParameter('work'); } else { // Note: we get here in get, and also in post when group changes. // Which plugins do we have enabled in currently selected group? - $plugins = explode(',', $user->getPlugins()); + $plugins = explode(',', $user->getPlugins() ?? ''); $cl_charts = in_array('ch', $plugins); $cl_puncher = in_array('pu', $plugins); $cl_clients = in_array('cl', $plugins); @@ -55,7 +54,6 @@ $cl_timesheets = in_array('ts', $plugins); $cl_templates = in_array('tp', $plugins); $cl_attachments = in_array('at', $plugins); - $cl_work = in_array('wk', $plugins); } $form = new Form('pluginsForm'); @@ -79,7 +77,6 @@ $form->addInput(array('type'=>'checkbox','name'=>'timesheets','value'=>$cl_timesheets)); $form->addInput(array('type'=>'checkbox','name'=>'templates','value'=>$cl_templates,'onchange'=>'handlePluginCheckboxes()')); $form->addInput(array('type'=>'checkbox','name'=>'attachments','value'=>$cl_attachments,'onchange'=>'handlePluginCheckboxes()')); -$form->addInput(array('type'=>'checkbox','name'=>'work','value'=>$cl_work,'onchange'=>'handlePluginCheckboxes()')); // Submit button. $form->addInput(array('type'=>'submit','name'=>'btn_save','value'=>$i18n->get('button.save'))); @@ -124,8 +121,6 @@ $plugins .= ',tp'; if ($cl_attachments) $plugins .= ',at'; - if ($cl_work) - $plugins .= ',wk'; $plugins = trim($plugins, ','); // Prepare a new config string. diff --git a/plugins/CustomFields.class.php b/plugins/CustomFields.class.php index 6350a6784..b561af804 100644 --- a/plugins/CustomFields.class.php +++ b/plugins/CustomFields.class.php @@ -1,31 +1,6 @@ quote($option_name); + $sql = "select id from tt_custom_field_options". + " where field_id = $field_id and group_id = $group_id and org_id = $org_id and value = ".$mdb2->quote($option_name). + " and status is not null"; $res = $mdb2->query($sql); if (is_a($res, 'PEAR_Error')) return false; @@ -245,6 +221,8 @@ static function getOptionName($id) { global $user; $mdb2 = getConnection(); + $id = (int) $id; // Just in case. Better safe than sorry as it is used in sql. + $group_id = $user->getGroup(); $org_id = $user->org_id; @@ -253,10 +231,10 @@ static function getOptionName($id) { $res = $mdb2->query($sql); if (!is_a($res, 'PEAR_Error')) { $val = $res->fetchRow(); - $name = $val['value']; - return $name; + if (isset($val['value'])) + return $val['value']; } - return false; + return null; } // getFields returns an array of custom fields for group. @@ -300,6 +278,27 @@ static function getField($id) { return false; } + // getFieldByName returns a custom field by name and its entity type. + static function getFieldByName($fieldName, $entityType) { + global $user; + $mdb2 = getConnection(); + + $entityType = (int) $entityType; // Just in case. + $group_id = $user->getGroup(); + $org_id = $user->org_id; + + $sql = "select label, type, required from tt_custom_fields" . + " where label = ". $mdb2->quote($fieldName) . " and entity_type = $entityType". + " and group_id = $group_id and org_id = $org_id and status is not null"; + $res = $mdb2->query($sql); + if (!is_a($res, 'PEAR_Error')) { + $val = $res->fetchRow(); + if (isset($val)) + return $val; + } + return null; + } + // getFieldIdForOption returns field id from an associated option id. static function getFieldIdForOption($option_id) { global $user; @@ -373,18 +372,27 @@ static function deleteField($field_id) { $org_id = $user->org_id; // Mark log entries as deleted. TODO: why are we doing this? Research impact. + // The impact is quite severe: an accidental delete of a custom field makes manual recovery problematic. + // Therefore, not doing this anymore as of Oct 7, 2021. + /* $sql = "update tt_custom_field_log set status = null" . " where field_id = $field_id and group_id = $group_id and org_id = $org_id"; $affected = $mdb2->exec($sql); if (is_a($affected, 'PEAR_Error')) return false; + */ // Mark field options as deleted. + // Same comment as above applies. + // An accidental delete of a custom field makes manual recovery problematic. + // Therefore, not doing this anymore as of Oct 7, 2021. + /* $sql = "update tt_custom_field_options set status = null" . " where field_id = $field_id and group_id = $group_id and org_id = $org_id"; $affected = $mdb2->exec($sql); if (is_a($affected, 'PEAR_Error')) return false; + */ // Mark custom field as deleted. $sql = "update tt_custom_fields set status = null" . diff --git a/plugins/MonthlyQuota.class.php b/plugins/MonthlyQuota.class.php index f742fd918..f37cd7885 100644 --- a/plugins/MonthlyQuota.class.php +++ b/plugins/MonthlyQuota.class.php @@ -1,30 +1,6 @@ getUserQuota($selected_date->mYear, $selected_date->mMonth); - $workdaysInMonth = $this->getNumWorkdays($selected_date->mMonth, $selected_date->mYear); + $monthQuotaMinutes = $this->getUserQuota($selected_date->year, $selected_date->month); + $workdaysInMonth = $this->getNumWorkdays($selected_date->month, $selected_date->year); // Iterate from 1st up to selected date. $workdaysFrom1st = 0; - for ($i = 1; $i <= $selected_date->mDate; $i++) { - $date = ttTimeHelper::dateInDatabaseFormat($selected_date->mYear, $selected_date->mMonth, $i); + for ($i = 1; $i <= $selected_date->day; $i++) { + $date = ttTimeHelper::dateInDatabaseFormat($selected_date->year, $selected_date->month, $i); if (!ttTimeHelper::isWeekend($date) && !ttTimeHelper::isHoliday($date)) { $workdaysFrom1st++; } diff --git a/plugins/work_constants.php b/plugins/work_constants.php deleted file mode 100644 index 207ca5d35..000000000 --- a/plugins/work_constants.php +++ /dev/null @@ -1,36 +0,0 @@ -add($i18n->get('error.field'), $i18n->get('label.confirm_password')); if ($cl_password1 !== $cl_password2) $err->add($i18n->get('error.not_equal'), $i18n->get('label.password'), $i18n->get('label.confirm_password')); + // Check password complexity. + if (!ttCheckPasswordComplexity($cl_password1)) + $err->add($i18n->get('error.weak_password')); } if (!ttValidEmail($cl_email, true)) $err->add($i18n->get('error.field'), $i18n->get('label.email')); // Finished validating user input. diff --git a/project_add.php b/project_add.php index 35b45c97a..859fdc259 100644 --- a/project_add.php +++ b/project_add.php @@ -19,6 +19,13 @@ } // End of access checks. +// Use custom fields plugin if it is enabled. +if ($user->isPluginEnabled('cf')) { + require_once('plugins/CustomFields.class.php'); + $custom_fields = new CustomFields(); + $smarty->assign('custom_fields', $custom_fields); +} + $showFiles = $user->isPluginEnabled('at'); $users = ttGroupHelper::getActiveUsers(); foreach ($users as $user_item) @@ -35,6 +42,18 @@ $cl_description = trim($request->getParameter('description')); $cl_users = $request->getParameter('users', array()); $cl_tasks = $request->getParameter('tasks', array()); + // If we have project custom fields - collect input. + if (isset($custom_fields) && $custom_fields->projectFields) { + foreach ($custom_fields->projectFields as $projectField) { + $control_name = 'project_field_'.$projectField['id']; + $projectCustomFields[$projectField['id']] = array('field_id' => $projectField['id'], + 'control_name' => $control_name, + 'label' => $projectField['label'], + 'type' => $projectField['type'], + 'required' => $projectField['required'], + 'value' => trim($request->getParameter($control_name))); + } + } } else { foreach ($users as $user_item) $cl_users[] = $user_item['id']; @@ -45,6 +64,20 @@ $form = new Form('projectForm'); $form->addInput(array('type'=>'text','maxlength'=>'100','name'=>'project_name','value'=>$cl_name)); $form->addInput(array('type'=>'textarea','name'=>'description','value'=>$cl_description)); +// If we have custom fields - add controls for them. +if (isset($custom_fields) && $custom_fields->projectFields) { + foreach ($custom_fields->projectFields as $projectField) { + $field_name = 'project_field_'.$projectField['id']; + if ($projectField['type'] == CustomFields::TYPE_TEXT) { + $form->addInput(array('type'=>'text','name'=>$field_name,'value'=>$projectCustomFields[$projectField['id']]['value'])); + } elseif ($projectField['type'] == CustomFields::TYPE_DROPDOWN) { + $form->addInput(array('type'=>'combobox','name'=>$field_name, + 'data'=>CustomFields::getOptions($projectField['id']), + 'value'=>$projectCustomFields[$projectField['id']]['value'], + 'empty'=>array(''=>$i18n->get('dropdown.select')))); + } + } +} if ($showFiles) $form->addInput(array('type'=>'upload','name'=>'newfile','value'=>$i18n->get('button.submit'))); $form->addInput(array('type'=>'checkboxgroup','name'=>'users','data'=>$all_users,'layout'=>'H','value'=>$cl_users)); @@ -56,26 +89,37 @@ // Validate user input. if (!ttValidString($cl_name)) $err->add($i18n->get('error.field'), $i18n->get('label.thing_name')); if (!ttValidString($cl_description, true)) $err->add($i18n->get('error.field'), $i18n->get('label.description')); + // Validate input in project custom fields. + if (isset($custom_fields) && $custom_fields->projectFields) { + foreach ($projectCustomFields as $projectField) { + // Validation is the same for text and dropdown fields. + if (!ttValidString($projectField['value'], !$projectField['required'])) $err->add($i18n->get('error.field'), htmlspecialchars($projectField['label'])); + } + } if (!ttGroupHelper::validateCheckboxGroupInput($cl_users, 'tt_users')) $err->add($i18n->get('error.field'), $i18n->get('label.users')); if (!ttGroupHelper::validateCheckboxGroupInput($cl_tasks, 'tt_tasks')) $err->add($i18n->get('error.field'), $i18n->get('label.tasks')); if ($err->no()) { if (!ttProjectHelper::getProjectByName($cl_name)) { - $id = ttProjectHelper::insert(array('name' => $cl_name, + $project_id = ttProjectHelper::insert(array('name' => $cl_name, 'description' => $cl_description, 'users' => $cl_users, 'tasks' => $cl_tasks, 'status' => ACTIVE)); - + // Insert project custom fields if we have them. + $result = true; + if ($project_id && isset($custom_fields) && $custom_fields->projectFields) { + $result = $custom_fields->insertEntityFields(CustomFields::ENTITY_PROJECT, $project_id, $projectCustomFields); + } // Put a new file in storage if we have it. - if ($id && $showFiles && $_FILES['newfile']['name']) { + if ($project_id && $showFiles && $_FILES['newfile']['name']) { $fileHelper = new ttFileHelper($err); $fields = array('entity_type'=>'project', - 'entity_id' => $id, + 'entity_id' => $project_id, 'file_name' => $_FILES['newfile']['name']); $fileHelper->putFile($fields); } - if ($id) { + if ($project_id && $result) { header('Location: projects.php'); exit(); } else diff --git a/project_edit.php b/project_edit.php index 917b2e974..a0190ff83 100644 --- a/project_edit.php +++ b/project_edit.php @@ -24,6 +24,13 @@ } // End of access checks. +// Use custom fields plugin if it is enabled. +if ($user->isPluginEnabled('cf')) { + require_once('plugins/CustomFields.class.php'); + $custom_fields = new CustomFields(); + $smarty->assign('custom_fields', $custom_fields); +} + $users = ttGroupHelper::getActiveUsers(); foreach ($users as $user_item) $all_users[$user_item['id']] = $user_item['name']; @@ -39,9 +46,33 @@ $cl_status = $request->getParameter('status'); $cl_users = $request->getParameter('users', array()); $cl_tasks = $request->getParameter('tasks', array()); + // If we have project custom fields - collect input. + if (isset($custom_fields) && $custom_fields->projectFields) { + foreach ($custom_fields->projectFields as $projectField) { + $control_name = 'project_field_'.$projectField['id']; + $projectCustomFields[$projectField['id']] = array('field_id' => $projectField['id'], + 'control_name' => $control_name, + 'label' => $projectField['label'], + 'type' => $projectField['type'], + 'required' => $projectField['required'], + 'value' => trim($request->getParameter($control_name))); + } + } } else { $cl_name = $project['name']; $cl_description = $project['description']; + // If we have project custom fields - collect values from database. + if (isset($custom_fields) && $custom_fields->projectFields) { + foreach ($custom_fields->projectFields as $projectField) { + $control_name = 'project_field_'.$projectField['id']; + $projectCustomFields[$projectField['id']] = array('field_id' => $projectField['id'], + 'control_name' => $control_name, + 'label' => $projectField['label'], + 'type' => $projectField['type'], + 'required' => $projectField['required'], + 'value' => $custom_fields->getEntityFieldValue(CustomFields::ENTITY_PROJECT, $cl_project_id, $projectField['id'], $projectField['type'])); + } + } $cl_status = $project['status']; $cl_users = ttProjectHelper::getAssignedUsers($cl_project_id); $cl_tasks = explode(',', $project['tasks']); @@ -51,6 +82,20 @@ $form->addInput(array('type'=>'hidden','name'=>'id','value'=>$cl_project_id)); $form->addInput(array('type'=>'text','maxlength'=>'100','name'=>'project_name','value'=>$cl_name)); $form->addInput(array('type'=>'textarea','name'=>'description','value'=>$cl_description)); +// If we have custom fields - add controls for them. +if (isset($custom_fields) && $custom_fields->projectFields) { + foreach ($custom_fields->projectFields as $projectField) { + $field_name = 'project_field_'.$projectField['id']; + if ($projectField['type'] == CustomFields::TYPE_TEXT) { + $form->addInput(array('type'=>'text','name'=>$field_name,'value'=>$projectCustomFields[$projectField['id']]['value'])); + } elseif ($projectField['type'] == CustomFields::TYPE_DROPDOWN) { + $form->addInput(array('type'=>'combobox','name'=>$field_name, + 'data'=>CustomFields::getOptions($projectField['id']), + 'value'=>$projectCustomFields[$projectField['id']]['value'], + 'empty'=>array(''=>$i18n->get('dropdown.select')))); + } + } +} $form->addInput(array('type'=>'combobox','name'=>'status','value'=>$cl_status, 'data'=>array(ACTIVE=>$i18n->get('dropdown.status_active'),INACTIVE=>$i18n->get('dropdown.status_inactive')))); $form->addInput(array('type'=>'checkboxgroup','name'=>'users','data'=>$all_users,'layout'=>'H','value'=>$cl_users)); @@ -63,6 +108,14 @@ // Validate user input. if (!ttValidString($cl_name)) $err->add($i18n->get('error.field'), $i18n->get('label.thing_name')); if (!ttValidString($cl_description, true)) $err->add($i18n->get('error.field'), $i18n->get('label.description')); + // Validate input in project custom fields. + if (isset($custom_fields) && $custom_fields->projectFields) { + foreach ($projectCustomFields as $projectField) { + // Validation is the same for text and dropdown fields. + if (!ttValidString($projectField['value'], !$projectField['required'])) $err->add($i18n->get('error.field'), htmlspecialchars($projectField['label'])); + } + } + if (!ttValidStatus($cl_status)) $err->add($i18n->get('error.field'), $i18n->get('label.status')); if (!ttGroupHelper::validateCheckboxGroupInput($cl_users, 'tt_users')) $err->add($i18n->get('error.field'), $i18n->get('label.users')); if (!ttGroupHelper::validateCheckboxGroupInput($cl_tasks, 'tt_tasks')) $err->add($i18n->get('error.field'), $i18n->get('label.tasks')); @@ -71,28 +124,39 @@ $existing_project = ttProjectHelper::getProjectByName($cl_name); if (!$existing_project || ($cl_project_id == $existing_project['id'])) { // Update project information. - if (ttProjectHelper::update(array( + $result = ttProjectHelper::update(array( 'id' => $cl_project_id, 'name' => $cl_name, 'description' => $cl_description, 'status' => $cl_status, 'users' => $cl_users, - 'tasks' => $cl_tasks))) { + 'tasks' => $cl_tasks)); + // Update project custom fields if we have them. + if ($result && isset($custom_fields) && $custom_fields->projectFields) { + $result = $custom_fields->updateEntityFields(CustomFields::ENTITY_PROJECT, $cl_project_id, $projectCustomFields); + } + if ($result) { header('Location: projects.php'); exit(); - } else + } else $err->add($i18n->get('error.db')); - } else + } else $err->add($i18n->get('error.object_exists')); } if ($request->getParameter('btn_copy')) { if (!ttProjectHelper::getProjectByName($cl_name)) { - if (ttProjectHelper::insert(array('name' => $cl_name, + $project_id = ttProjectHelper::insert(array('name' => $cl_name, 'description' => $cl_description, 'users' => $cl_users, 'tasks' => $cl_tasks, - 'status' => ACTIVE))) { + 'status' => ACTIVE)); + // Insert project custom fields if we have them. + $result = true; + if ($project_id && isset($custom_fields) && $custom_fields->projectFields) { + $result = $custom_fields->insertEntityFields(CustomFields::ENTITY_PROJECT, $project_id, $projectCustomFields); + } + if ($project_id && $result) { header('Location: projects.php'); exit(); } else diff --git a/puncher.php b/puncher.php index 5d6bca7c0..26b371fcd 100644 --- a/puncher.php +++ b/puncher.php @@ -8,7 +8,7 @@ import('ttGroupHelper'); import('ttClientHelper'); import('ttTimeHelper'); -import('DateAndTime'); +import('ttDate'); // Access check. if (!ttAccessAllowed('track_own_time')) { @@ -19,6 +19,26 @@ header('Location: feature_disabled.php'); exit(); } +// If we are passed in browser_today, make sure it is in correct format. +$browser_today = null; // Reused below beyond access checks. +if ($request->isPost()) { + // Validate that browser_today parameter is in correct format. + $browser_today = $request->getParameter('browser_today'); + if ($browser_today && !ttValidDbDateFormatDate($browser_today)) { + header('Location: access_denied.php'); + exit(); + } +} +// If we are passed in browser_time, make sure it is in correct format. +$browser_time = null; // Reused below beyond access checks. +if ($request->isPost()) { + // Validate that browser_time parameter is in correct format. + $browser_time = $request->getParameter('browser_time'); + if ($browser_time && !ttValidTime($browser_time)) { + header('Location: access_denied.php'); + exit(); + } +} // End of access checks. $showClient = $user->isPluginEnabled('cl'); @@ -28,16 +48,11 @@ $showTask = MODE_PROJECTS_AND_TASKS == $trackingMode; $taskRequired = false; if ($showTask) $taskRequired = $user->getConfigOption('task_required'); +$oneUncompleted = $user->getConfigOption('one_uncompleted'); -// Initialize and store date in session. -$cl_date = $request->getParameter('date', @$_SESSION['date']); -$selected_date = new DateAndTime(DB_DATEFORMAT, $cl_date); -if($selected_date->isError()) - $selected_date = new DateAndTime(DB_DATEFORMAT); -if(!$cl_date) - $cl_date = $selected_date->toString(DB_DATEFORMAT); -$_SESSION['date'] = $cl_date; -// TODO: for timer page we may limit the day to today only. +// Initialize $cl_date. +$date_today = new ttDate($browser_today); // Initialize to browser today if we are passed it in, otherwise server today. +$cl_date = $date_today->toString(); // Use custom fields plugin if it is enabled. if ($user->isPluginEnabled('cf')) { @@ -46,13 +61,16 @@ $smarty->assign('custom_fields', $custom_fields); } -// Obtain uncompleted record. Assumption is that only 1 uncompleted record is allowed. +// Obtain first uncompleted record for today. If there are multiples, we operate with first found. +$uncompletedToday = ttTimeHelper::getFirstUncompletedForDate($user->getUser(), $date_today->toString()); +$enable_controls = ($uncompletedToday == null); + +// Obtain first found uncompleted record, not necessarily for today. Used to prohibit entry in "One uncompleted" mode. $uncompleted = ttTimeHelper::getUncompleted($user->getUser()); -$enable_controls = ($uncompleted == null); // Initialize variables. -$cl_start = trim($request->getParameter('browser_time')); -$cl_finish = trim($request->getParameter('browser_time')); +$cl_start = $browser_time; +$cl_finish = $browser_time; $cl_duration = $cl_note = null; // Disabled controls are not posted. Therefore, && $enable_controls condition in several places below. // This allows us to get values from session when controls are disabled and reset to null when not. @@ -187,11 +205,11 @@ $form->addInput(array('type'=>'hidden','name'=>'browser_time','value'=>'')); // User current time, which gets filled in on button click. // Start and stop buttons. -$enable_start = $uncompleted ? false : true; -if (!$uncompleted) - $form->addInput(array('type'=>'submit','name'=>'btn_start','onclick'=>'browser_time.value=get_time()','value'=>$i18n->get('button.start'),'enable'=>$enable_start)); +$enable_start = $uncompletedToday ? false : true; +if (!$uncompletedToday) + $form->addInput(array('type'=>'submit','name'=>'btn_start','onclick'=>'browser_today.value=get_date();browser_time.value=get_time()','value'=>$i18n->get('button.start'),'enable'=>$enable_start)); else - $form->addInput(array('type'=>'submit','name'=>'btn_stop','onclick'=>'browser_time.value=get_time()','value'=>$i18n->get('button.stop'),'enable'=>!$enable_start)); + $form->addInput(array('type'=>'submit','name'=>'btn_stop','onclick'=>'browser_today.value=get_date();browser_time.value=get_time()','value'=>$i18n->get('button.stop'),'enable'=>!$enable_start)); // Submit. if ($request->isPost()) { @@ -219,18 +237,20 @@ // Prohibit creating entries in future. if (!$user->isOptionEnabled('future_entries')) { - $browser_today = new DateAndTime(DB_DATEFORMAT, $request->getParameter('browser_today', null)); - if ($selected_date->after($browser_today)) + // $date_today is already browser_today, just check if the date we are using is after server tomorrow. + $server_tomorrow = new ttDate(); + $server_tomorrow->incrementDay(); + if ($date_today->after($server_tomorrow)) $err->add($i18n->get('error.future_date')); } // Prohibit creating time entries in locked interval. - if ($user->isDateLocked($selected_date)) + if ($user->isDateLocked($date_today)) $err->add($i18n->get('error.range_locked')); // Prohibit creating another uncompleted record. - if ($err->no() && $uncompleted) { - $err->add($i18n->get('error.uncompleted_exists')." ".$i18n->get('error.goto_uncompleted').""); + if ($err->no() && $uncompleted && $oneUncompleted) { + $err->add($i18n->get('error.uncompleted_exists')." ".$i18n->get('error.goto_uncompleted').""); } // Prohibit creating an overlapping record. @@ -266,7 +286,7 @@ } if ($request->getParameter('btn_stop')) { // Stop button clicked. We need to finish an uncompleted record in progress. - $record = ttTimeHelper::getRecord($uncompleted['id']); + $record = ttTimeHelper::getRecord($uncompletedToday['id']); // Can we complete this record? if (ttTimeHelper::isValidInterval($record['start'], $cl_finish) // finish time is greater than start time @@ -294,17 +314,16 @@ } } // isPost -$week_total = ttTimeHelper::getTimeForWeek($cl_date); +$week_total = ttTimeHelper::getTimeForWeek($date_today); $timeRecords = ttTimeHelper::getRecords($cl_date); $smarty->assign('week_total', $week_total); -$smarty->assign('uncompleted', $uncompleted); +$smarty->assign('uncompleted_today', $uncompletedToday); $smarty->assign('show_client', $showClient); $smarty->assign('show_billable', $showBillable); $smarty->assign('show_project', $showProject); $smarty->assign('show_task', $showTask); $smarty->assign('task_required', $taskRequired); -$smarty->assign('time_records', ttTimeHelper::getRecords($cl_date)); $smarty->assign('day_total', ttTimeHelper::getTimeForDay($cl_date)); $smarty->assign('time_records', $timeRecords); $smarty->assign('show_record_custom_fields', $user->isOptionEnabled('record_custom_fields')); @@ -314,7 +333,7 @@ $smarty->assign('task_list', $task_list); $smarty->assign('forms', array($form->getName()=>$form->toArray())); $smarty->assign('onload', 'onLoad="fillDropdowns()"'); -$smarty->assign('timestring', $selected_date->toString($user->date_format)); +$smarty->assign('timestring', $date_today->toString($user->getDateFormat())); $smarty->assign('title', $i18n->get('title.puncher')); $smarty->assign('content_page_name', 'puncher.tpl'); $smarty->display('index.tpl'); diff --git a/readme.txt b/readme.txt index 421a18cdb..90cdc970d 100644 --- a/readme.txt +++ b/readme.txt @@ -1,9 +1,9 @@ Anuko Time Tracker Copyright (c) Anuko (https://www.anuko.com) -Project home page: https://www.anuko.com/time_tracker/index.htm +Project home page: https://www.anuko.com/time-tracker/index.htm Forum: https://www.anuko.com/forum/viewforum.php?f=4 -Info for developers: https://www.anuko.com/time_tracker/info_for_developers.htm +Info for developers: https://www.anuko.com/time-tracker/info-for-developers.htm Free hosting of Time Tracker for individuals and small groups is available at https://timetracker.anuko.com Unless otherwise noted, files in this archive are protected by the LIBERAL FREEWARE LICENSE. @@ -12,7 +12,7 @@ Read the file license.txt for details. INSTALLATION INSTRUCTIONS -Documentation is available at https://www.anuko.com/time_tracker/install_guide/index.htm +Documentation is available at https://www.anuko.com/time-tracker/install-guide/index.htm The general installation procedure looks like this: @@ -40,7 +40,7 @@ The general installation procedure looks like this: UPGRADE FROM EARLIER VERSIONS -See https://www.anuko.com/time_tracker/upgrade.htm +See https://www.anuko.com/time-tracker/install-guide/upgrade.htm BLANK PAGES IN TIME TRACKER @@ -62,4 +62,4 @@ Support is available on per-incident basis - see https://www.anuko.com/support.h CHANGE LOG -Change log is available at https://www.anuko.com/time_tracker/change_log/index.htm \ No newline at end of file +Change log is available at https://www.anuko.com/time-tracker/change-log/index.htm \ No newline at end of file diff --git a/register.php b/register.php index 35fbee9cc..6f54ccf5b 100644 --- a/register.php +++ b/register.php @@ -36,8 +36,8 @@ } $form = new Form('groupForm'); -$form->addInput(array('type'=>'text','maxlength'=>'200','name'=>'group_name','value'=>$cl_group_name)); -$form->addInput(array('type'=>'text','maxlength'=>'7','name'=>'currency','value'=>$cl_currency)); +$form->addInput(array('type'=>'text','name'=>'group_name','value'=>$cl_group_name)); +$form->addInput(array('type'=>'text','name'=>'currency','value'=>$cl_currency)); // Prepare an array of available languages. $lang_files = I18n::getLangFileList(); @@ -57,14 +57,16 @@ $longname_lang = mu_sort($longname_lang, 'name'); $form->addInput(array('type'=>'combobox','name'=>'lang','data'=>$longname_lang,'datakeys'=>array('id','name'),'value'=>$cl_lang)); -$form->addInput(array('type'=>'text','maxlength'=>'100','name'=>'manager_name','value'=>$cl_manager_name)); -$form->addInput(array('type'=>'text','maxlength'=>'100','name'=>'manager_login','value'=>$cl_manager_login)); +$form->addInput(array('type'=>'text','name'=>'manager_name','value'=>$cl_manager_name)); +$form->addInput(array('type'=>'text','name'=>'manager_login','value'=>$cl_manager_login)); $form->addInput(array('type'=>'password','maxlength'=>'30','name'=>'password1','value'=>$cl_password1)); $form->addInput(array('type'=>'password','maxlength'=>'30','name'=>'password2','value'=>$cl_password2)); $form->addInput(array('type'=>'text','maxlength'=>'100','name'=>'manager_email','value'=>$cl_manager_email)); $form->addInput(array('type'=>'submit','name'=>'btn_submit','value'=>$i18n->get('button.submit'))); if ($request->isPost()) { + // Note: user input validation is done in ttRegistrator constructor. + // Create fields array for ttRegistrator instance. $fields = array( 'user_name' => $cl_manager_name, diff --git a/report.php b/report.php index bd2576f14..b8a549b69 100644 --- a/report.php +++ b/report.php @@ -18,6 +18,7 @@ // End of access checks. $config = new ttConfigHelper($user->getConfig()); +$show_cost_per_hour = $config->getDefinedValue('report_cost_per_hour') && ($user->can('manage_invoices') || $user->isClient()); if ($user->isPluginEnabled('ap')) { $cl_mark_approved_select_option = $request->getParameter('mark_approved_select_options', ($request->isPost() ? null : @$_SESSION['mark_approved_select_option'])); @@ -58,7 +59,7 @@ // If we are in post, load the bean from session, as the constructor does it only in get. if ($request->isPost()) $bean->loadBean(); -$client_id = $bean->getAttribute('client'); +$client_id = (int)$bean->getAttribute('client'); $options = ttReportHelper::getReportOptions($bean); // Do we need to show checkboxes? We show them in the following 4 situations: @@ -298,7 +299,11 @@ if ($bean->getAttribute('chduration')) $colspan++; if (!$user->getConfigOption('report_note_on_separate_row') && $bean->getAttribute('chnote')) $colspan++; if ($bean->getAttribute('chunits')) $colspan++; -if ($bean->getAttribute('chcost')) $colspan++; +if ($bean->getAttribute('chcost')) { + if ($show_cost_per_hour) + $colspan++; + $colspan++; +} if ($bean->getAttribute('chapproved')) $colspan++; if ($bean->getAttribute('chpaid')) $colspan++; if ($bean->getAttribute('chip')) $colspan++; @@ -316,6 +321,7 @@ $smarty->assign('report_items', $report_items); $smarty->assign('subtotals', $subtotals); $smarty->assign('totals', $totals); +$smarty->assign('show_cost_per_hour', $show_cost_per_hour); $smarty->assign('note_on_separate_row', $user->getConfigOption('report_note_on_separate_row')); $smarty->assign('colspan', $colspan); $smarty->assign('bean', $bean); diff --git a/reports.php b/reports.php index c1df04387..aabeec08f 100644 --- a/reports.php +++ b/reports.php @@ -5,13 +5,13 @@ require_once('initialize.php'); import('form.Form'); import('form.ActionForm'); -import('DateAndTime'); import('ttGroupHelper'); -import('Period'); import('ttProjectHelper'); import('ttFavReportHelper'); import('ttClientHelper'); import('ttReportHelper'); +import('ttDate'); +import('ttPeriod'); // Access check. if (!(ttAccessAllowed('view_own_reports') || ttAccessAllowed('view_reports') || ttAccessAllowed('view_all_reports') || ttAccessAllowed('view_client_reports'))) { @@ -84,9 +84,16 @@ if (count($project_list) == 0) $showProject = false; } if ($showProject) { - $form->addInput(array('type'=>'combobox', - 'onchange'=>'fillTaskDropdown(this.value);selectAssignedUsers(this.value);', - 'name'=>'project', + $form->addInput(array('type'=>'multipleselectcombobox', + 'onchange'=>'fillTaskDropdown();selectAssignedUsers();', + 'name'=>'project', // Multiple select now, but the name without "[]" in the end because we can't use it in reports.tpl. + // Example: {$forms.reportForm.project.control}, we can't use {$forms.reportForm.project[].control}. + // This creates an ugly complication: + // Form.class.php addInput adds "[]" to a multiple select combobox element name. + // When we verify bean in a form post, the name is "project". + // But if bean is loaded from session, the name is "project[]". + // It is unclear how to deal with this properly. + //'multiple'=>true, 'data'=>$project_list, 'datakeys'=>array('id','name'), 'empty'=>array(''=>$i18n->get('dropdown.all')))); @@ -229,16 +236,19 @@ $form->addInput(array('type'=>'combobox', 'name'=>'period', 'data'=>array(INTERVAL_THIS_MONTH=>$i18n->get('dropdown.current_month'), - INTERVAL_LAST_MONTH=>$i18n->get('dropdown.previous_month'), + INTERVAL_PREVIOUS_MONTH=>$i18n->get('dropdown.previous_month'), INTERVAL_THIS_WEEK=>$i18n->get('dropdown.current_week'), - INTERVAL_LAST_WEEK=>$i18n->get('dropdown.previous_week'), + INTERVAL_PREVIOUS_WEEK=>$i18n->get('dropdown.previous_week'), INTERVAL_THIS_DAY=>$i18n->get('dropdown.current_day'), - INTERVAL_LAST_DAY=>$i18n->get('dropdown.previous_day')), + INTERVAL_PREVIOUS_DAY=>$i18n->get('dropdown.previous_day')), 'empty'=>array(''=>$i18n->get('dropdown.select')))); // Add controls for start and end dates. $form->addInput(array('type'=>'datefield','maxlength'=>'20','name'=>'start_date')); $form->addInput(array('type'=>'datefield','maxlength'=>'20','name'=>'end_date')); +// Add control for notes containing. +$form->addInput(array('type'=>'text','maxlength'=>'80','name'=>'note_containing')); + // Add checkboxes for "Show fields" block. if ($showClient) $form->addInput(array('type'=>'checkbox','name'=>'chclient')); @@ -312,6 +322,23 @@ } } +// If we have projects custom fields - add controls for them. +if ($showProject && isset($custom_fields) && $custom_fields->projectFields) { + foreach ($custom_fields->projectFields as $projectField) { + $field_name = 'project_field_'.$projectField['id']; + $checkbox_field_name = 'show_'.$field_name; + if ($projectField['type'] == CustomFields::TYPE_TEXT) { + $form->addInput(array('type'=>'text','name'=>$field_name,)); + } elseif ($projectField['type'] == CustomFields::TYPE_DROPDOWN) { + $form->addInput(array('type'=>'combobox','name'=>$field_name, + 'data'=>CustomFields::getOptions($projectField['id']), + 'empty'=>array(''=>$i18n->get('dropdown.all')))); + } + // Also add a checkbox (to print the field or not). + $form->addInput(array('type'=>'checkbox','name'=>$checkbox_field_name)); + } +} + // Add group by control. $group_by_options['no_grouping'] = $i18n->get('form.reports.group_by_no'); $group_by_options['date'] = $i18n->get('form.reports.group_by_date'); @@ -337,6 +364,13 @@ $group_by_options[$field_name] = $userField['label']; } } +// If we have project custom fields - add group by options for them. +if (isset($custom_fields) && $custom_fields->projectFields) { + foreach ($custom_fields->projectFields as $projectField) { + $field_name = 'project_field_'.$projectField['id']; + $group_by_options[$field_name] = $projectField['label']; + } +} $group_by_options_size = sizeof($group_by_options); $form->addInput(array('type'=>'combobox','onchange'=>'handleCheckboxes();','name'=>'group_by1','data'=>$group_by_options)); if ($group_by_options_size > 2) $form->addInput(array('type'=>'combobox','onchange'=>'handleCheckboxes();','name'=>'group_by2','data'=>$group_by_options)); @@ -357,9 +391,9 @@ if ($request->isGet() && !$bean->isSaved()) { // No previous form data were found in session. Use the following default values. $form->setValueByElement('users_active', array_keys((array)$user_list_active)); - $period = new Period(INTERVAL_THIS_MONTH, new DateAndTime($user->getDateFormat())); - $form->setValueByElement('start_date', $period->getStartDate()); - $form->setValueByElement('end_date', $period->getEndDate()); + $period = new ttPeriod(new ttDate(), INTERVAL_THIS_MONTH); + $form->setValueByElement('start_date', $period->getStartDate($user->getDateFormat())); + $form->setValueByElement('end_date', $period->getEndDate($user->getDateFormat())); $form->setValueByElement('chclient', '1'); $form->setValueByElement('chstart', '1'); @@ -390,83 +424,90 @@ $form->getElement('btn_delete')->setEnabled(false); if ($request->isPost()) { - if((!$bean->getAttribute('btn_generate') && ($request->getParameter('fav_report_changed')))) { - // User changed favorite report. We need to load new values into the form. - if ($bean->getAttribute('favorite_report')) { - // This loads new favorite report options into the bean (into our form). - ttFavReportHelper::loadReport($bean); - - // If user selected no favorite report - mark all user checkboxes (most probable scenario). - if ($bean->getAttribute('favorite_report') == -1) { - $form->setValueByElement('users_active', array_keys($user_list_active)); - $form->setValueByElement('users_inactive', false); + // Verify bean. Do not proceed with bogus post data. + if (!ttReportHelper::verifyBean($bean)) $err->add($i18n->get('error.sys')); + + if($err->no()) { + if (!$bean->getAttribute('btn_generate') && $request->getParameter('fav_report_changed')) { + // User changed favorite report. We need to load new values into the form. + if ($bean->getAttribute('favorite_report')) { + // This loads new favorite report options into the bean (into our form). + ttFavReportHelper::loadReport($bean); + + // If user selected no favorite report - mark all user checkboxes (most probable scenario). + if ($bean->getAttribute('favorite_report') == -1) { + $form->setValueByElement('users_active', array_keys($user_list_active)); + $form->setValueByElement('users_inactive', false); + } + + // Save form data in session for future use. + $bean->saveBean(); + header('Location: reports.php'); + exit(); } + } elseif ($bean->getAttribute('btn_save')) { + // User clicked the Save button. We need to save form options as new favorite report. + + // We check new_fav_report here, rather than in ttReportHelper::verifyBean to display a specific error, instead of 'error.sys'. + if (!ttValidString($bean->getAttribute('new_fav_report'))) $err->add($i18n->get('error.field'), $i18n->get('form.reports.save_as_favorite')); - // Save form data in session for future use. - $bean->saveBean(); - header('Location: reports.php'); - exit(); - } - } elseif ($bean->getAttribute('btn_save')) { - // User clicked the Save button. We need to save form options as new favorite report. - if (!ttValidString($bean->getAttribute('new_fav_report'))) $err->add($i18n->get('error.field'), $i18n->get('form.reports.save_as_favorite')); - - if ($err->no()) { - $id = ttFavReportHelper::saveReport($bean); - if (!$id) - $err->add($i18n->get('error.db')); if ($err->no()) { - $bean->setAttribute('favorite_report', $id); + $id = ttFavReportHelper::saveReport($bean); + if (!$id) + $err->add($i18n->get('error.db')); + if ($err->no()) { + $bean->setAttribute('favorite_report', $id); + $bean->saveBean(); + header('Location: reports.php'); + exit(); + } + } + } elseif($bean->getAttribute('btn_delete')) { + // Delete button pressed. User wants to delete a favorite report. + if ($bean->getAttribute('favorite_report')) { + ttFavReportHelper::deleteReport($bean->getAttribute('favorite_report')); + // Load default report. + $bean->setAttribute('favorite_report',''); + $bean->setAttribute('new_fav_report', $report_list[0]['name']); + ttFavReportHelper::loadReport($bean); + $form->setValueByElement('users', array_keys($user_list)); $bean->saveBean(); header('Location: reports.php'); exit(); } - } - } elseif($bean->getAttribute('btn_delete')) { - // Delete button pressed. User wants to delete a favorite report. - if ($bean->getAttribute('favorite_report')) { - ttFavReportHelper::deleteReport($bean->getAttribute('favorite_report')); - // Load default report. - $bean->setAttribute('favorite_report',''); - $bean->setAttribute('new_fav_report', $report_list[0]['name']); - ttFavReportHelper::loadReport($bean); - $form->setValueByElement('users', array_keys($user_list)); - $bean->saveBean(); - header('Location: reports.php'); - exit(); - } - } else { - // Generate button pressed. Check some values. - if (!$bean->getAttribute('period')) { - $start_date = new DateAndTime($user->getDateFormat(), $bean->getAttribute('start_date')); - - if ($start_date->isError() || !$bean->getAttribute('start_date')) - $err->add($i18n->get('error.field'), $i18n->get('label.start_date')); - - $end_date = new DateAndTime($user->getDateFormat(), $bean->getAttribute('end_date')); - if ($end_date->isError() || !$bean->getAttribute('end_date')) - $err->add($i18n->get('error.field'), $i18n->get('label.end_date')); + } else { + // Generate button pressed. Check some values. + if (!$bean->getAttribute('period')) { + // Validate start date. + if (!ttValidDate($bean->getAttribute('start_date'))) $err->add($i18n->get('error.field'), $i18n->get('label.start_date')); + // Validate end date. + if (!ttValidDate($bean->getAttribute('end_date'))) $err->add($i18n->get('error.field'), $i18n->get('label.end_date')); + // Make sure that the end date is equal or after the start date. + if ($err->no()) { + $startDate = new ttDate($bean->getAttribute('start_date'), $user->getDateFormat()); + $endDate = new ttDate($bean->getAttribute('end_date'), $user->getDateFormat()); + if ($startDate->compare($endDate) > 0) { + $err->add($i18n->get('error.interval'), $i18n->get('label.end_date'), $i18n->get('label.start_date')); + } + } + } + $group_by1 = $bean->getAttribute('group_by1'); + $group_by2 = $bean->getAttribute('group_by2'); + $group_by3 = $bean->getAttribute('group_by3'); + if (($group_by3 != null && $group_by3 != 'no_grouping') && ($group_by3 == $group_by1 || $group_by3 == $group_by2)) + $err->add($i18n->get('error.field'), $i18n->get('form.reports.group_by')); + if (($group_by2 != null && $group_by2 != 'no_grouping') && ($group_by2 == $group_by1 || $group_by3 == $group_by2)) + $err->add($i18n->get('error.field'), $i18n->get('form.reports.group_by')); + // TODO: review the above checks and possibly move some of them to verifyBean that we call when entering isPost above. - if ($start_date->compare($end_date) > 0) - $err->add($i18n->get('error.interval'), $i18n->get('label.end_date'), $i18n->get('label.start_date')); - } - $group_by1 = $bean->getAttribute('group_by1'); - $group_by2 = $bean->getAttribute('group_by2'); - $group_by3 = $bean->getAttribute('group_by3'); - if (($group_by3 != null && $group_by3 != 'no_grouping') && ($group_by3 == $group_by1 || $group_by3 == $group_by2)) - $err->add($i18n->get('error.field'), $i18n->get('form.reports.group_by')); - if (($group_by2 != null && $group_by2 != 'no_grouping') && ($group_by2 == $group_by1 || $group_by3 == $group_by2)) - $err->add($i18n->get('error.field'), $i18n->get('form.reports.group_by')); - // Check remaining values. - if (!ttReportHelper::verifyBean($bean)) $err->add($i18n->get('error.sys')); - - if ($err->no()) { - $bean->saveBean(); - // Now we can go ahead and create a report. - header('Location: report.php'); - exit(); + if ($err->no()) { + $bean->saveBean(); + // Now we can go ahead and create a report. + header('Location: report.php'); + exit(); + } } - } + } // if($err->no()) } // isPost $smarty->assign('client_list', $client_list); diff --git a/role_edit.php b/role_edit.php index 5192ee62d..f2aecadee 100644 --- a/role_edit.php +++ b/role_edit.php @@ -58,6 +58,7 @@ if (!ttValidString($cl_name)) $err->add($i18n->get('error.field'), $i18n->get('label.thing_name')); if (!ttValidString($cl_description, true)) $err->add($i18n->get('error.field'), $i18n->get('label.description')); if ($cl_rank >= $user->rank || $cl_rank < 0) $err->add($i18n->get('error.field'), $i18n->get('form.roles.rank')); + if (!ttValidStatus($cl_status)) $err->add($i18n->get('error.field'), $i18n->get('label.status')); if ($err->no()) { $existing_role = ttRoleHelper::getRoleByName($cl_name); diff --git a/task_add.php b/task_add.php index 3d8dcc1c7..4a3353703 100644 --- a/task_add.php +++ b/task_add.php @@ -32,15 +32,15 @@ } $form = new Form('taskForm'); -$form->addInput(array('type'=>'text','maxlength'=>'100','name'=>'name','value'=>$cl_name)); +$form->addInput(array('type'=>'text','name'=>'name','value'=>$cl_name)); $form->addInput(array('type'=>'textarea','name'=>'description','value'=>$cl_description)); $form->addInput(array('type'=>'checkboxgroup','name'=>'projects','layout'=>'H','data'=>$projects,'datakeys'=>array('id','name'),'value'=>$cl_projects)); $form->addInput(array('type'=>'submit','name'=>'btn_submit','value'=>$i18n->get('button.add'))); if ($request->isPost()) { // Validate user input. - if (!ttValidString($cl_name)) $err->add($i18n->get('error.field'), $i18n->get('label.thing_name')); - if (!ttValidString($cl_description, true)) $err->add($i18n->get('error.field'), $i18n->get('label.description')); + if (!ttValidString($cl_name, false, MAX_NAME_CHARS)) $err->add($i18n->get('error.field'), $i18n->get('label.thing_name')); + if (!ttValidString($cl_description, true, MAX_DESCR_CHARS)) $err->add($i18n->get('error.field'), $i18n->get('label.description')); if ($err->no()) { if (!ttTaskHelper::getTaskByName($cl_name)) { diff --git a/task_edit.php b/task_edit.php index b2b61b80b..1eb8c7f5b 100644 --- a/task_edit.php +++ b/task_edit.php @@ -44,7 +44,7 @@ $form = new Form('taskForm'); $form->addInput(array('type'=>'hidden','name'=>'id','value'=>$cl_task_id)); -$form->addInput(array('type'=>'text','maxlength'=>'100','name'=>'name','value'=>$cl_name)); +$form->addInput(array('type'=>'text','name'=>'name','value'=>$cl_name)); $form->addInput(array('type'=>'textarea','name'=>'description','value'=>$cl_description)); $form->addInput(array('type'=>'combobox','name'=>'status','value'=>$cl_status, 'data'=>array(ACTIVE=>$i18n->get('dropdown.status_active'),INACTIVE=>$i18n->get('dropdown.status_inactive')))); @@ -54,8 +54,9 @@ if ($request->isPost()) { // Validate user input. - if (!ttValidString($cl_name)) $err->add($i18n->get('error.field'), $i18n->get('label.thing_name')); - if (!ttValidString($cl_description, true)) $err->add($i18n->get('error.field'), $i18n->get('label.description')); + if (!ttValidString($cl_name, false, MAX_NAME_CHARS)) $err->add($i18n->get('error.field'), $i18n->get('label.thing_name')); + if (!ttValidString($cl_description, true, MAX_DESCR_CHARS)) $err->add($i18n->get('error.field'), $i18n->get('label.description')); + if (!ttValidStatus($cl_status)) $err->add($i18n->get('error.field'), $i18n->get('label.status')); if ($err->no()) { if ($request->getParameter('btn_save')) { diff --git a/template_edit.php b/template_edit.php index 2f3c69727..2ff87b721 100644 --- a/template_edit.php +++ b/template_edit.php @@ -64,6 +64,7 @@ if (!ttValidString($cl_name)) $err->add($i18n->get('error.field'), $i18n->get('label.thing_name')); if (!ttValidString($cl_description, true)) $err->add($i18n->get('error.field'), $i18n->get('label.description')); if (!ttValidString($cl_content)) $err->add($i18n->get('error.field'), $i18n->get('label.template')); + if (!ttValidStatus($cl_status)) $err->add($i18n->get('error.field'), $i18n->get('label.status')); if (!ttGroupHelper::validateCheckboxGroupInput($cl_projects, 'tt_projects')) $err->add($i18n->get('error.field'), $i18n->get('label.projects')); // Finished validating user input. diff --git a/time.php b/time.php index 1bee1129c..ee90770b8 100644 --- a/time.php +++ b/time.php @@ -10,7 +10,7 @@ import('ttClientHelper'); import('ttTimeHelper'); import('ttFileHelper'); -import('DateAndTime'); +import('ttDate'); // Access checks. if (!(ttAccessAllowed('track_own_time') || ttAccessAllowed('track_time'))) { @@ -32,6 +32,44 @@ exit(); } } +// If we are passed in a date, make sure it is in correct format. +$date = $request->getParameter('date'); +if ($date && !ttValidDbDateFormatDate($date)) { + header('Location: access_denied.php'); + exit(); +} +if ($request->isPost()) { + // Validate that browser_today parameter is in correct format. + $browser_today = $request->getParameter('browser_today'); + if ($browser_today && !ttValidDbDateFormatDate($browser_today)) { + header('Location: access_denied.php'); + exit(); + } +} +// Additional checks for hidden controls used for completion of uncompleted records. +// browser_date, browser_time, record_id. +if ($request->isPost() && $request->getParameter('btn_stop')) { + $recordId = (int) $request->getParameter('record_id'); + $time_rec = ttTimeHelper::getRecord($recordId); + if (!$time_rec) { + // We are passed a bogus record id. + header('Location: access_denied.php'); + exit(); + } + + // Validate that browser_date parameter is in correct format. + $browser_date = $request->getParameter('browser_date'); + if ($browser_date && !ttValidDbDateFormatDate($browser_date)) { + header('Location: access_denied.php'); + exit(); + } + // Validate that browser_time parameter is in correct format. + $browser_time = $request->getParameter('browser_time'); + if ($browser_time && !ttValidTime($browser_time)) { + header('Location: access_denied.php'); + exit(); + } +} // End of access checks. // Determine user for whom we display this page. @@ -57,14 +95,13 @@ $showDuration = TYPE_DURATION == $recordType || TYPE_ALL == $recordType; $showFiles = $user->isPluginEnabled('at'); $showRecordCustomFields = $user->isOptionEnabled('record_custom_fields'); +$oneUncompleted = $config->getDefinedValue('one_uncompleted'); // Initialize and store date in session. $cl_date = $request->getParameter('date', @$_SESSION['date']); -$selected_date = new DateAndTime(DB_DATEFORMAT, $cl_date); -if($selected_date->isError()) - $selected_date = new DateAndTime(DB_DATEFORMAT); +$selected_date = new ttDate($cl_date); if(!$cl_date) - $cl_date = $selected_date->toString(DB_DATEFORMAT); + $cl_date = $selected_date->toString(); $_SESSION['date'] = $cl_date; // Use custom fields plugin if it is enabled. @@ -99,13 +136,13 @@ if ($user->isPluginEnabled('mq')){ require_once('plugins/MonthlyQuota.class.php'); $quota = new MonthlyQuota(); - $month_quota_minutes = $quota->getUserQuota($selected_date->mYear, $selected_date->mMonth); + $month_quota_minutes = $quota->getUserQuota($selected_date->year, $selected_date->month); $quota_minutes_from_1st = $quota->getUserQuotaFrom1st($selected_date); $month_total = ttTimeHelper::getTimeForMonth($selected_date); $month_total_minutes = ttTimeHelper::toMinutes($month_total); $balance_left = $quota_minutes_from_1st - $month_total_minutes; $minutes_left = $month_quota_minutes - $month_total_minutes; - + $smarty->assign('month_total', $month_total); $smarty->assign('month_quota', ttTimeHelper::toAbsDuration($month_quota_minutes)); $smarty->assign('over_balance', $balance_left < 0); @@ -115,10 +152,10 @@ } // Initialize variables. -$cl_start = trim($request->getParameter('start')); -$cl_finish = trim($request->getParameter('finish')); -$cl_duration = trim($request->getParameter('duration')); -$cl_note = trim($request->getParameter('note')); +$cl_start = is_null($request->getParameter('start')) ? null : trim($request->getParameter('start')); +$cl_finish = is_null($request->getParameter('finish')) ? null : trim($request->getParameter('finish')); +$cl_duration = is_null($request->getParameter('duration')) ? null : trim($request->getParameter('duration')); +$cl_note = is_null($request->getParameter('note')) ? null : trim($request->getParameter('note')); $cl_billable = 1; if ($showBillable) { if ($request->isPost()) { @@ -147,7 +184,7 @@ 'label' => $timeField['label'], 'type' => $timeField['type'], 'required' => $timeField['required'], - 'value' => trim($cl_control_name)); + 'value' => is_null($cl_control_name) ? null : trim($cl_control_name)); } } @@ -328,6 +365,11 @@ if ($request->isPost()) { if ($request->getParameter('btn_submit')) { // Submit button clicked. + + // Use the "limit" plugin if we have one. Ignore include errors. + // The "limit" plugin is not required for normal operation of Time Tracker. + @include('plugins/limit/access_check.php'); + // Validate user input. if ($showClient && $user->isOptionEnabled('client_required') && !$cl_client) $err->add($i18n->get('error.client')); @@ -344,7 +386,7 @@ if ($showTask && $taskRequired) { if (!$cl_task) $err->add($i18n->get('error.task')); } - if (strlen($cl_duration) == 0) { + if ($cl_duration == null) { if ($cl_start || $cl_finish) { if (!ttTimeHelper::isValidTime($cl_start)) $err->add($i18n->get('error.field'), $i18n->get('label.start')); @@ -370,13 +412,14 @@ if ($user->isPluginEnabled('tp') && !ttValidTemplateText($cl_note)) { $err->add($i18n->get('error.field'), $i18n->get('label.note')); } - if (!ttTimeHelper::canAdd()) $err->add($i18n->get('error.expired')); // Finished validating user input. // Prohibit creating entries in future. if (!$user->isOptionEnabled('future_entries')) { - $browser_today = new DateAndTime(DB_DATEFORMAT, $request->getParameter('browser_today', null)); - if ($selected_date->after($browser_today)) + $browser_today = new ttDate($request->getParameter('browser_today', null)); + $server_tomorrow = new ttDate(); + $server_tomorrow->incrementDay(); + if ($selected_date->after($browser_today) || $selected_date->after($server_tomorrow)) $err->add($i18n->get('error.future_date')); } @@ -385,7 +428,7 @@ $err->add($i18n->get('error.range_locked')); // Prohibit creating another uncompleted record. - if ($err->no()) { + if ($err->no() && $oneUncompleted) { if (($not_completed_rec = ttTimeHelper::getUncompleted($user_id)) && (($cl_finish == '') && ($cl_duration == ''))) $err->add($i18n->get('error.uncompleted_exists')." ".$i18n->get('error.goto_uncompleted').""); } @@ -486,6 +529,7 @@ $smarty->assign('client_list', $client_list); $smarty->assign('project_list', $project_list); $smarty->assign('task_list', $task_list); +$smarty->assign('one_uncompleted', $oneUncompleted); $smarty->assign('forms', array($form->getName()=>$form->toArray())); $smarty->assign('onload', 'onLoad="fillDropdowns();prepopulateNote();adjustTodayLinks()"'); $smarty->assign('timestring', $selected_date->toString($user->getDateFormat())); diff --git a/time_delete.php b/time_delete.php index 9c028dabb..cd0af11ab 100644 --- a/time_delete.php +++ b/time_delete.php @@ -6,7 +6,7 @@ import('form.Form'); import('ttUserHelper'); import('ttTimeHelper'); -import('DateAndTime'); +import('ttDate'); // Access checks. if (!(ttAccessAllowed('track_own_time') || ttAccessAllowed('track_time'))) { @@ -33,7 +33,7 @@ if ($request->getParameter('delete_button')) { // Delete button pressed. // Determine if it's okay to delete the record. - $item_date = new DateAndTime(DB_DATEFORMAT, $time_rec['date']); + $item_date = new ttDate($time_rec['date']); // Determine if the record is uncompleted. $uncompleted = ($time_rec['duration'] == '0:00'); diff --git a/time_edit.php b/time_edit.php index d34ae1ca6..35037dcaf 100644 --- a/time_edit.php +++ b/time_edit.php @@ -9,7 +9,7 @@ import('ttClientHelper'); import('ttTimeHelper'); import('ttConfigHelper'); -import('DateAndTime'); +import('ttDate'); // Access checks. if (!(ttAccessAllowed('track_own_time') || ttAccessAllowed('track_time'))) { @@ -24,6 +24,14 @@ header('Location: access_denied.php'); exit(); } +if ($request->isPost()) { + // Validate that browser_today parameter is in correct format. + $browser_today = $request->getParameter('browser_today'); + if ($browser_today && !ttValidDbDateFormatDate($browser_today)) { + header('Location: access_denied.php'); + exit(); + } +} // End of access checks. $user_id = $user->getUser(); @@ -40,6 +48,7 @@ $recordType = $user->getRecordType(); $showStart = TYPE_START_FINISH == $recordType || TYPE_ALL == $recordType; $showDuration = TYPE_DURATION == $recordType || TYPE_ALL == $recordType; +$oneUncompleted = $config->getDefinedValue('one_uncompleted'); // Use custom fields plugin if it is enabled. if ($user->isPluginEnabled('cf')) { @@ -48,15 +57,15 @@ $smarty->assign('custom_fields', $custom_fields); } -$item_date = new DateAndTime(DB_DATEFORMAT, $time_rec['date']); +$item_date = new ttDate($time_rec['date']); $confirm_save = $user->getConfigOption('confirm_save'); // Initialize variables. $cl_start = $cl_finish = $cl_duration = $cl_date = $cl_note = $cl_project = $cl_task = $cl_billable = null; if ($request->isPost()) { - $cl_start = trim($request->getParameter('start')); - $cl_finish = trim($request->getParameter('finish')); - $cl_duration = trim($request->getParameter('duration')); + $cl_start = is_null($request->getParameter('start')) ? null : trim($request->getParameter('start')); + $cl_finish = is_null($request->getParameter('finish')) ? null : trim($request->getParameter('finish')); + $cl_duration = is_null($request->getParameter('duration')) ? null : trim($request->getParameter('duration')); $cl_date = $request->getParameter('date'); $cl_note = trim($request->getParameter('note')); // If we have time custom fields - collect input. @@ -108,8 +117,8 @@ // Add an info message to the form if we are editing an uncompleted record. if (strlen($cl_start) > 0 && $cl_start == $cl_finish && $cl_duration == '0:00') { - $cl_finish = ''; - $cl_duration = ''; + $cl_finish = null; + $cl_duration = null; $msg->add($i18n->get('form.time_edit.uncompleted')); } } @@ -283,10 +292,8 @@ if ($showTask && $taskRequired) { if (!$cl_task) $err->add($i18n->get('error.task')); } - if (!$cl_duration) { - if ('0' == $cl_duration) - $err->add($i18n->get('error.field'), $i18n->get('label.duration')); - elseif ($cl_start || $cl_finish) { + if ($cl_duration == null) { + if ($cl_start || $cl_finish) { if (!ttTimeHelper::isValidTime($cl_start)) $err->add($i18n->get('error.field'), $i18n->get('label.start')); if ($cl_finish) { @@ -316,12 +323,16 @@ // Finished validating user input. // This is a new date for the time record. - $new_date = new DateAndTime($user->getDateFormat(), $cl_date); + $new_date = null; + if ($err->no()) + $new_date = new ttDate($cl_date, $user->getDateFormat()); // Prohibit creating entries in future. - if (!$user->isOptionEnabled('future_entries')) { - $browser_today = new DateAndTime(DB_DATEFORMAT, $request->getParameter('browser_today', null)); - if ($new_date->after($browser_today)) + if ($err->no() && !$user->isOptionEnabled('future_entries')) { + $browser_today = new ttDate($request->getParameter('browser_today', null)); + $server_tomorrow = new ttDate(); + $server_tomorrow->incrementDay(); + if ($new_date->after($browser_today) || $new_date->after($server_tomorrow)) $err->add($i18n->get('error.future_date')); } @@ -343,8 +354,8 @@ $err->add($i18n->get('error.range_locked')); // 3) Prohibit saving uncompleted unlocked entries when another uncompleted entry exists. - $uncompleted = ($cl_finish == '' && $cl_duration == ''); - if ($uncompleted) { + $uncompleted = ($cl_finish == null && $cl_duration == null); + if ($uncompleted && $oneUncompleted) { $not_completed_rec = ttTimeHelper::getUncompleted($user_id); if ($not_completed_rec && ($time_rec['id'] <> $not_completed_rec['id'])) { // We have another not completed record. @@ -355,7 +366,7 @@ // Prohibit creating an overlapping record. if ($err->no()) { - if (ttTimeHelper::overlaps($user_id, $new_date->toString(DB_DATEFORMAT), $cl_start, $cl_finish, $cl_id)) + if (ttTimeHelper::overlaps($user_id, $new_date->toString(), $cl_start, $cl_finish, $cl_id)) $err->add($i18n->get('error.overlap')); } @@ -363,7 +374,7 @@ if ($err->no()) { $res = ttTimeHelper::update(array( 'id'=>$cl_id, - 'date'=>$new_date->toString(DB_DATEFORMAT), + 'date'=>$new_date->toString(), 'client'=>$cl_client, 'project'=>$cl_project, 'task'=>$cl_task, @@ -380,9 +391,10 @@ } if ($res) { - header('Location: time.php?date='.$new_date->toString(DB_DATEFORMAT)); + header('Location: time.php?date='.$new_date->toString()); exit(); } + $err->add($i18n->get('error.db')); } } @@ -400,7 +412,7 @@ // 2) Prohibit saving uncompleted unlocked entries when another uncompleted entry exists. $uncompleted = ($cl_finish == '' && $cl_duration == ''); - if ($uncompleted) { + if ($uncompleted && $oneUncompleted) { $not_completed_rec = ttTimeHelper::getUncompleted($user_id); if ($not_completed_rec) { // We have another not completed record. @@ -411,7 +423,7 @@ // Prohibit creating an overlapping record. if ($err->no()) { - if (ttTimeHelper::overlaps($user_id, $new_date->toString(DB_DATEFORMAT), $cl_start, $cl_finish)) + if (ttTimeHelper::overlaps($user_id, $new_date->toString(), $cl_start, $cl_finish)) $err->add($i18n->get('error.overlap')); } @@ -419,7 +431,7 @@ if ($err->no()) { $id = ttTimeHelper::insert(array( - 'date'=>$new_date->toString(DB_DATEFORMAT), + 'date'=>$new_date->toString(), 'client'=>$cl_client, 'project'=>$cl_project, 'task'=>$cl_task, @@ -436,7 +448,7 @@ $res = $custom_fields->insertTimeFields($id, $timeCustomFields); } if ($id && $res) { - header('Location: time.php?date='.$new_date->toString(DB_DATEFORMAT)); + header('Location: time.php?date='.$new_date->toString()); exit(); } $err->add($i18n->get('error.db')); diff --git a/timesheet_edit.php b/timesheet_edit.php index 164cc61bb..3e46733ae 100644 --- a/timesheet_edit.php +++ b/timesheet_edit.php @@ -51,6 +51,7 @@ // Validate user input. if (!ttValidString($cl_name)) $err->add($i18n->get('error.field'), $i18n->get('label.thing_name')); if (!ttValidString($cl_comment, true)) $err->add($i18n->get('error.field'), $i18n->get('label.comment')); + if (!ttValidStatus($cl_status)) $err->add($i18n->get('error.field'), $i18n->get('label.status')); if ($request->getParameter('btn_save')) { if ($err->no()) { diff --git a/tofile.php b/tofile.php index 530d2f7df..0134885f8 100644 --- a/tofile.php +++ b/tofile.php @@ -20,6 +20,8 @@ $custom_fields = new CustomFields(); } +$show_cost_per_hour = $user->getConfigOption('report_cost_per_hour') && ($user->can('manage_invoices') || $user->isClient()); + // Report settings are stored in session bean before we get here. $bean = new ActionForm('reportBean', new Form('reportForm'), $request); @@ -104,6 +106,14 @@ } if ($bean->getAttribute('chclient')) print "\t\n"; if ($bean->getAttribute('chproject')) print "\t\n"; + // Project custom fields. + if (isset($custom_fields) && $custom_fields->projectFields) { + foreach ($custom_fields->projectFields as $projectField) { + $field_name = 'project_field_'.$projectField['id']; + $checkbox_control_name = 'show_'.$field_name; + if ($bean->getAttribute($checkbox_control_name)) print "\t<$field_name>\n"; + } + } if ($bean->getAttribute('chtask')) print "\t\n"; // Time custom fields. if (isset($custom_fields) && $custom_fields->timeFields) { @@ -123,6 +133,11 @@ } if ($bean->getAttribute('chunits')) print "\t\n"; if ($bean->getAttribute('chnote')) print "\t\n"; + if ($bean->getAttribute('chcost') && $show_cost_per_hour) { + print "\t\n"; + } if ($bean->getAttribute('chcost')) { print "\tcan('manage_invoices') || $user->isClient()) @@ -199,6 +214,14 @@ } if ($bean->getAttribute('chclient')) print ',"'.$i18n->get('label.client').'"'; if ($bean->getAttribute('chproject')) print ',"'.$i18n->get('label.project').'"'; + // Project custom field labels. + if (isset($custom_fields) && $custom_fields->projectFields) { + foreach ($custom_fields->projectFields as $projectField) { + $field_name = 'project_field_'.$projectField['id']; + $checkbox_control_name = 'show_'.$field_name; + if ($bean->getAttribute($checkbox_control_name)) print ',"'.ttNeutralizeForCsv($projectField['label']).'"'; + } + } if ($bean->getAttribute('chtask')) print ',"'.$i18n->get('label.task').'"'; // Time custom field labels. if (isset($custom_fields) && $custom_fields->timeFields) { @@ -213,7 +236,11 @@ if ($bean->getAttribute('chduration')) print ',"'.$i18n->get('label.duration').'"'; if ($bean->getAttribute('chunits')) print ',"'.$i18n->get('label.work_units_short').'"'; if ($bean->getAttribute('chnote')) print ',"'.$i18n->get('label.note').'"'; - if ($bean->getAttribute('chcost')) print ',"'.$i18n->get('label.cost').'"'; + if ($bean->getAttribute('chcost')) { + if ($show_cost_per_hour) + print ',"'.$i18n->get('form.report.per_hour').'"'; + print ',"'.$i18n->get('label.cost').'"'; + } if ($bean->getAttribute('chapproved')) print ',"'.$i18n->get('label.approved').'"'; if ($bean->getAttribute('chpaid')) print ',"'.$i18n->get('label.paid').'"'; if ($bean->getAttribute('chip')) print ',"'.$i18n->get('label.ip').'"'; @@ -235,6 +262,14 @@ } if ($bean->getAttribute('chclient')) print ',"'.ttNeutralizeForCsv($item['client']).'"'; if ($bean->getAttribute('chproject')) print ',"'.ttNeutralizeForCsv($item['project']).'"'; + // Project custom fields. + if (isset($custom_fields) && $custom_fields->projectFields) { + foreach ($custom_fields->projectFields as $projectField) { + $field_name = 'project_field_'.$projectField['id']; + $checkbox_control_name = 'show_'.$field_name; + if ($bean->getAttribute($checkbox_control_name)) print ',"'.ttNeutralizeForCsv($item[$field_name]).'"'; + } + } if ($bean->getAttribute('chtask')) print ',"'.ttNeutralizeForCsv($item['task']).'"'; // Time custom fields. if (isset($custom_fields) && $custom_fields->timeFields) { @@ -255,10 +290,13 @@ if ($bean->getAttribute('chunits')) print ',"'.$item['units'].'"'; if ($bean->getAttribute('chnote')) print ',"'.ttNeutralizeForCsv($item['note']).'"'; if ($bean->getAttribute('chcost')) { - if ($user->can('manage_invoices') || $user->isClient()) + if ($user->can('manage_invoices') || $user->isClient()) { + if ($show_cost_per_hour) + print ',"'.$item['cost_per_hour'].'"'; print ',"'.$item['cost'].'"'; - else + } else { print ',"'.$item['expense'].'"'; + } } if ($bean->getAttribute('chapproved')) print ',"'.$item['approved'].'"'; if ($bean->getAttribute('chpaid')) print ',"'.$item['paid'].'"'; diff --git a/topdf.php b/topdf.php index 85ff8d84e..6393d5f55 100644 --- a/topdf.php +++ b/topdf.php @@ -38,6 +38,7 @@ $config = new ttConfigHelper($user->getConfig()); $show_note_column = $bean->getAttribute('chnote') && !$config->getDefinedValue('report_note_on_separate_row'); $show_note_row = $bean->getAttribute('chnote') && $config->getDefinedValue('report_note_on_separate_row'); +$show_cost_per_hour = $config->getDefinedValue('report_cost_per_hour') && ($user->can('manage_invoices') || $user->isClient()); // There are 2 variations of report: totals only, or normal. Totals only means that the report // is grouped by either date, user, client, project, or task and user only needs to see subtotals by group. @@ -54,6 +55,7 @@ $totals = ttReportHelper::getTotals($options); // Totals for the entire report. // Assign variables that are used to print subtotals. +$print_subtotals = $first_pass = false; if ($items && $grouping) { $print_subtotals = true; $first_pass = true; @@ -138,6 +140,14 @@ } if ($bean->getAttribute('chclient')) { $colspan++; $html .= ''.$i18n->get('label.client').''; } if ($bean->getAttribute('chproject')) { $colspan++; $html .= ''.$i18n->get('label.project').''; } + // Project custom field labels. + if (isset($custom_fields) && $custom_fields->projectFields) { + foreach ($custom_fields->projectFields as $projectField) { + $field_name = 'project_field_'.$projectField['id']; + $checkbox_control_name = 'show_'.$field_name; + if ($bean->getAttribute($checkbox_control_name)) { $colspan++; $html .= ''.htmlspecialchars($projectField['label']).''; } + } + } if ($bean->getAttribute('chtask')) { $colspan++; $html .= ''.$i18n->get('label.task').''; } // Time custom field labels. if (isset($custom_fields) && $custom_fields->timeFields) { @@ -152,6 +162,7 @@ if ($bean->getAttribute('chduration')) { $colspan++; $html .= "".$i18n->get('label.duration').''; } if ($bean->getAttribute('chunits')) { $colspan++; $html .= "".$i18n->get('label.work_units_short').''; } if ($show_note_column) { $colspan++; $html .= ''.$i18n->get('label.note').''; } + if ($bean->getAttribute('chcost') && $show_cost_per_hour) { $colspan++; $html .= "".$i18n->get('form.report.per_hour').''; } if ($bean->getAttribute('chcost')) { $colspan++; $html .= "".$i18n->get('label.cost').''; } if ($bean->getAttribute('chapproved')) { $colspan++; $html .= "".$i18n->get('label.approved').''; } if ($bean->getAttribute('chpaid')) { $colspan++; $html .= "".$i18n->get('label.paid').''; } @@ -192,6 +203,14 @@ $html .= htmlspecialchars($subtotals[$prev_grouped_by]['project']); $html .= ''; } + // Project custom fields. + if (isset($custom_fields) && $custom_fields->projectFields) { + foreach ($custom_fields->projectFields as $projectField) { + $field_name = 'project_field_'.$projectField['id']; + $checkbox_control_name = 'show_'.$field_name; + if ($bean->getAttribute($checkbox_control_name)) $html .= ''; + } + } if ($bean->getAttribute('chtask')) { $html .= ''; $html .= htmlspecialchars($subtotals[$prev_grouped_by]['task']); @@ -210,6 +229,7 @@ if ($bean->getAttribute('chduration')) $html .= "".$subtotals[$prev_grouped_by]['time'].''; if ($bean->getAttribute('chunits')) $html .= "".$subtotals[$prev_grouped_by]['units'].''; if ($show_note_column) $html .= ''; + if ($bean->getAttribute('chcost') && $show_cost_per_hour) $html .= ''; if ($bean->getAttribute('chcost')) { $html .= ""; if ($user->can('manage_invoices') || $user->isClient()) @@ -252,6 +272,14 @@ } if ($bean->getAttribute('chclient')) $html .= ''.htmlspecialchars($item['client']).''; if ($bean->getAttribute('chproject')) $html .= ''.htmlspecialchars($item['project']).''; + // Project custom fields. + if (isset($custom_fields) && $custom_fields->projectFields) { + foreach ($custom_fields->projectFields as $projectField) { + $field_name = 'project_field_'.$projectField['id']; + $checkbox_control_name = 'show_'.$field_name; + if ($bean->getAttribute($checkbox_control_name)) $html .= ''.htmlspecialchars($item[$field_name]).''; + } + } if ($bean->getAttribute('chtask')) $html .= ''.htmlspecialchars($item['task']).''; // Time custom fields. if (isset($custom_fields) && $custom_fields->timeFields) { @@ -266,6 +294,11 @@ if ($bean->getAttribute('chduration')) $html .= "".$item['duration'].''; if ($bean->getAttribute('chunits')) $html .= "".$item['units'].''; if ($show_note_column) $html .= ''.htmlspecialchars($item['note']).''; + if ($bean->getAttribute('chcost') && $show_cost_per_hour) { + $html .= ""; + $html .= $item['cost_per_hour']; + $html .= ''; + } if ($bean->getAttribute('chcost')) { $html .= ""; if ($user->can('manage_invoices') || $user->isClient()) @@ -332,6 +365,14 @@ $html .= htmlspecialchars($subtotals[$prev_grouped_by]['project']); $html .= ''; } + // Project custom fields. + if (isset($custom_fields) && $custom_fields->projectFields) { + foreach ($custom_fields->projectFields as $projectField) { + $field_name = 'project_field_'.$projectField['id']; + $checkbox_control_name = 'show_'.$field_name; + if ($bean->getAttribute($checkbox_control_name)) $html .= ''; + } + } if ($bean->getAttribute('chtask')) { $html .= ''; $html .= htmlspecialchars($subtotals[$prev_grouped_by]['task']); @@ -350,6 +391,7 @@ if ($bean->getAttribute('chduration')) $html .= "".$subtotals[$prev_grouped_by]['time'].''; if ($bean->getAttribute('chunits')) $html .= "".$subtotals[$prev_grouped_by]['units'].''; if ($show_note_column) $html .= ''; + if ($bean->getAttribute('chcost') && $show_cost_per_hour) $html .= ''; if ($bean->getAttribute('chcost')) { $html .= ""; if ($user->can('manage_invoices') || $user->isClient()) @@ -381,6 +423,14 @@ } if ($bean->getAttribute('chclient')) $html .= ''; if ($bean->getAttribute('chproject')) $html .= ''; + // Project custom fields. + if (isset($custom_fields) && $custom_fields->projectFields) { + foreach ($custom_fields->projectFields as $projectField) { + $field_name = 'project_field_'.$projectField['id']; + $checkbox_control_name = 'show_'.$field_name; + if ($bean->getAttribute($checkbox_control_name)) $html .= ''; + } + } if ($bean->getAttribute('chtask')) $html .= ''; // Time custom fields. if (isset($custom_fields) && $custom_fields->timeFields) { @@ -395,6 +445,7 @@ if ($bean->getAttribute('chduration')) $html .= "".$totals['time'].''; if ($bean->getAttribute('chunits')) $html .= "".$totals['units'].''; if ($show_note_column) $html .= ''; + if ($bean->getAttribute('chcost') && $show_cost_per_hour) $html .= ''; if ($bean->getAttribute('chcost')) { $html .= "".htmlspecialchars($user->currency).' '; if ($user->can('manage_invoices') || $user->isClient()) diff --git a/user_add.php b/user_add.php index 8d584afb7..e75e0901d 100644 --- a/user_add.php +++ b/user_add.php @@ -158,6 +158,9 @@ function render(&$table, $value, $row, $column, $selected = false) { if (!ttValidString($cl_password2)) $err->add($i18n->get('error.field'), $i18n->get('label.confirm_password')); if ($cl_password1 !== $cl_password2) $err->add($i18n->get('error.not_equal'), $i18n->get('label.password'), $i18n->get('label.confirm_password')); + // Check password complexity. + if (!ttCheckPasswordComplexity($cl_password1)) + $err->add($i18n->get('error.weak_password')); } if (!ttValidEmail($cl_email, true)) $err->add($i18n->get('error.field'), $i18n->get('label.email')); // Require selection of a client for a client role. diff --git a/user_edit.php b/user_edit.php index 10aaebbb0..0633e8a1f 100644 --- a/user_edit.php +++ b/user_edit.php @@ -190,12 +190,16 @@ function render(&$table, $value, $row, $column, $selected = false) { if (!ttValidString($cl_password2)) $err->add($i18n->get('error.field'), $i18n->get('label.confirm_password')); if ($cl_password1 !== $cl_password2) $err->add($i18n->get('error.not_equal'), $i18n->get('label.password'), $i18n->get('label.confirm_password')); + // Check password complexity. + if (!ttCheckPasswordComplexity($cl_password1)) + $err->add($i18n->get('error.weak_password')); } if (!ttValidEmail($cl_email, true)) $err->add($i18n->get('error.field'), $i18n->get('label.email')); // Require selection of a client for a client role. if ($user->isPluginEnabled('cl') && ttRoleHelper::isClientRole($cl_role_id) && !$cl_client_id) $err->add($i18n->get('error.client')); + if (!ttValidStatus($cl_status)) $err->add($i18n->get('error.field'), $i18n->get('label.status')); if (!ttValidFloat($cl_quota_percent, true)) $err->add($i18n->get('error.field'), $i18n->get('label.quota')); - // Validate input in user custom fields. + // Validate input in user custom fields. if (isset($custom_fields) && $custom_fields->userFields) { foreach ($userCustomFields as $userField) { // Validation is the same for text and dropdown fields. diff --git a/users.php b/users.php index 16a422390..38d4e1bc3 100644 --- a/users.php +++ b/users.php @@ -30,8 +30,11 @@ $uncompleted_indicators = $user->getConfigOption('uncompleted_indicators'); if ($uncompleted_indicators) { + $today = new ttDate(); // Server today. + $todayString = $today->toString(); // Check each active user if they have an uncompleted time entry. foreach ($active_users as $key => $active_user) { + $active_users[$key]['has_uncompleted_entry_today'] = (bool) ttTimeHelper::getFirstUncompletedForDate($active_user['id'], $todayString); $active_users[$key]['has_uncompleted_entry'] = (bool) ttTimeHelper::getUncompleted($active_user['id']); } $smarty->assign('uncompleted_indicators', true); diff --git a/week.php b/week.php index 7687f9598..3c2384b91 100644 --- a/week.php +++ b/week.php @@ -12,7 +12,7 @@ import('ttWeekViewHelper'); import('ttClientHelper'); import('ttTimeHelper'); -import('DateAndTime'); +import('ttDate'); // Access checks. if (!(ttAccessAllowed('track_own_time') || ttAccessAllowed('track_time'))) { @@ -38,6 +38,20 @@ exit(); } } +// If we are passed in a date, make sure it is in correct format. +$date = $request->getParameter('date'); +if ($date && !ttValidDbDateFormatDate($date)) { + header('Location: access_denied.php'); + exit(); +} +if ($request->isPost()) { + // Validate that browser_today parameter is in correct format. + $browser_today = $request->getParameter('browser_today'); + if ($browser_today && !ttValidDbDateFormatDate($browser_today)) { + header('Location: access_denied.php'); + exit(); + } +} // End of access checks. // Determine user for whom we display this page. @@ -59,6 +73,7 @@ if ($showTask) $taskRequired = $user->getConfigOption('task_required'); $showWeekNote = $user->isOptionEnabled('week_note'); $showWeekNotes = $user->isOptionEnabled('week_notes'); +$showWeekends = $user->isOptionEnabled('weekends'); $recordType = $user->getRecordType(); $showStart = TYPE_START_FINISH == $recordType || TYPE_ALL == $recordType; $showFiles = $user->isPluginEnabled('at'); @@ -66,11 +81,9 @@ // Initialize and store date in session. $cl_date = $request->getParameter('date', @$_SESSION['date']); -$selected_date = new DateAndTime(DB_DATEFORMAT, $cl_date); -if($selected_date->isError()) - $selected_date = new DateAndTime(DB_DATEFORMAT); +$selected_date = new ttDate($cl_date); if(!$cl_date) - $cl_date = $selected_date->toString(DB_DATEFORMAT); + $cl_date = $selected_date->toString(); $_SESSION['date'] = $cl_date; // Determine selected week start and end dates. @@ -81,10 +94,10 @@ $startWeekBias = $weekStartDay - 7; else $startWeekBias = $weekStartDay; -$startDate = new DateAndTime(); -$startDate->setTimestamp(mktime(0,0,0,$t_arr[4]+1,$t_arr[3]-$t_arr[6]+$startWeekBias,$t_arr[5])); -$endDate = new DateAndTime(); -$endDate->setTimestamp(mktime(0,0,0,$t_arr[4]+1,$t_arr[3]-$t_arr[6]+6+$startWeekBias,$t_arr[5])); +$startDate = new ttDate(); +$startDate->setFromUnixTimestamp(mktime(0,0,0,$t_arr[4]+1,$t_arr[3]-$t_arr[6]+$startWeekBias,$t_arr[5])); +$endDate = new ttDate(); +$endDate->setFromUnixTimestamp(mktime(0,0,0,$t_arr[4]+1,$t_arr[3]-$t_arr[6]+6+$startWeekBias,$t_arr[5])); // The above is needed to set date range (timestring) in page title. // Use custom fields plugin if it is enabled. @@ -98,7 +111,7 @@ if ($user->isPluginEnabled('mq')){ require_once('plugins/MonthlyQuota.class.php'); $quota = new MonthlyQuota(); - $month_quota_minutes = $quota->getUserQuota($selected_date->mYear, $selected_date->mMonth); + $month_quota_minutes = $quota->getUserQuota($selected_date->year, $selected_date->month); $month_total = ttTimeHelper::getTimeForMonth($selected_date); $minutes_left = $month_quota_minutes - ttTimeHelper::toMinutes($month_total); @@ -144,15 +157,36 @@ // Get the data we need to display week view. // Get column headers, which are day numbers in month. -$dayHeaders = ttWeekViewHelper::getDayHeadersForWeek($startDate->toString(DB_DATEFORMAT)); -$lockedDays = ttWeekViewHelper::getLockedDaysForWeek($startDate->toString(DB_DATEFORMAT)); +$dayHeaders = ttWeekViewHelper::getDayHeadersForWeek($startDate->toString()); +$lockedDays = ttWeekViewHelper::getLockedDaysForWeek($startDate->toString()); +// If we are not showing weekends, reduce the above arrays to 5 days only. +$weekend_start_idx = $weekend_end_idx = 0; +if (!$showWeekends) { + if (defined('WEEKEND_START_DAY')) { + $weekend_start_idx = (7 + WEEKEND_START_DAY - $weekStartDay) % 7; + $weekend_end_idx = (7 + WEEKEND_START_DAY + 1 - $weekStartDay) % 7; + } else { + $weekend_start_idx = 6 - $weekStartDay; + $weekend_end_idx = (7 - $weekStartDay) % 7; + } + if ($weekend_end_idx > $weekend_start_idx) { + array_splice($dayHeaders, $weekend_start_idx, 2); + array_splice($lockedDays, $weekend_start_idx, 2); + } else { + array_splice($dayHeaders, $weekend_start_idx, 1); + array_splice($dayHeaders, $weekend_end_idx, 1); + array_splice($lockedDays, $weekend_start_idx, 1); + array_splice($lockedDays, $weekend_end_idx, 1); + } +} + // Get already existing records. -$records = ttWeekViewHelper::getRecordsForInterval($startDate->toString(DB_DATEFORMAT), $endDate->toString(DB_DATEFORMAT), $showFiles); +$records = ttWeekViewHelper::getRecordsForInterval($startDate->toString(), $endDate->toString(), $showFiles); // Build data array for the table. Format is described in ttWeekViewHelper::getDataForWeekView function. if ($records) $dataArray = ttWeekViewHelper::getDataForWeekView($records, $dayHeaders); else - $dataArray = ttWeekViewHelper::prePopulateFromPastWeeks($startDate->toString(DB_DATEFORMAT), $dayHeaders); + $dataArray = ttWeekViewHelper::prePopulateFromPastWeeks($startDate->toString(), $dayHeaders); // Build day totals (total durations for each day in week). $dayTotals = ttWeekViewHelper::getDayTotals($dataArray, $dayHeaders); @@ -199,7 +233,7 @@ function render(&$table, $value, $row, $column, $selected = false) { $field = new TextField($field_name); // Disable control if the date is locked. global $lockedDays; - if ($lockedDays[$column-1]) + if ($lockedDays[$column]) $field->setEnabled(false); $field->setFormName($table->getFormName()); $field->setStyle('width: 60px;'); // TODO: need to style everything properly, eventually. @@ -213,7 +247,7 @@ function render(&$table, $value, $row, $column, $selected = false) { $field->setValue($table->getValueAt($row,$column)['duration']); // Duration for even rows. } else { $field->setValue($table->getValueAt($row,$column)['note']); // Comment for odd rows. - $field->setTitle($table->getValueAt($row,$column)['note']); // Tooltip to help view the entire comment. + $field->setTitle(htmlspecialchars($table->getValueAt($row,$column)['note'])); // Tooltip to help view the entire comment. } } else { $field->setValue($table->getValueAt($row,$column)['duration']); @@ -261,8 +295,16 @@ function render(&$table, $value, $row, $column, $selected = false) { $table->setData($dataArray); // Add columns to table. $table->addColumn(new TableColumn('label', '', new LabelCellRenderer(), $dayTotals['label'])); -for ($i = 0; $i < 7; $i++) { - $table->addColumn(new TableColumn($dayHeaders[$i], $dayHeaders[$i], new WeekViewCellRenderer(), $dayTotals[$dayHeaders[$i]])); + +if ($showWeekends) { + for ($i = 0; $i < 7; $i++) { + $table->addColumn(new TableColumn($dayHeaders[$i], $dayHeaders[$i], new WeekViewCellRenderer(), $dayTotals[$dayHeaders[$i]])); + } +} +else { + for ($i = 0; $i < 5; $i++) { + $table->addColumn(new TableColumn($dayHeaders[$i], $dayHeaders[$i], new WeekViewCellRenderer(), $dayTotals[$dayHeaders[$i]])); + } } $table->setInteractive(false); $form->addInputElement($table); @@ -369,7 +411,7 @@ function render(&$table, $value, $row, $column, $selected = false) { $form->addInput(array('type'=>'calendar','name'=>'date','value'=>$cl_date)); // A hidden control for today's date from user's browser. -$form->addInput(array('type'=>'hidden','name'=>'browser_today','value'=>'get_date()')); // User current date, which gets filled in on btn_submit click. +$form->addInput(array('type'=>'hidden','name'=>'browser_today','value'=>'')); // User current date, which gets filled in on btn_submit click. // Submit button. $form->addInput(array('type'=>'submit','name'=>'btn_submit','onclick'=>'browser_today.value=get_date()','value'=>$i18n->get('button.submit'))); @@ -444,9 +486,6 @@ function render(&$table, $value, $row, $column, $selected = false) { continue; // Posted value is different. if ($existingDuration == null) { - // Skip inserting 0 duration values. - if (0 == ttTimeHelper::toMinutes($postedDuration)) - continue; // Insert a new record. $fields = array(); $fields['row_id'] = $dataArray[$rowNumber]['row_id']; @@ -475,7 +514,7 @@ function render(&$table, $value, $row, $column, $selected = false) { } } $fields['day_header'] = $dayHeader; - $fields['start_date'] = $startDate->toString(DB_DATEFORMAT); // To be able to determine date for the entry using $dayHeader. + $fields['start_date'] = $startDate->toString(); // To be able to determine date for the entry using $dayHeader. $fields['duration'] = $postedDuration; $fields['browser_today'] = $request->getParameter('browser_today', null); if ($showWeekNotes) { @@ -487,7 +526,7 @@ function render(&$table, $value, $row, $column, $selected = false) { } } $result = ttWeekViewHelper::insertDurationFromWeekView($fields, $custom_fields, $err); - } elseif ($postedDuration == null || 0 == ttTimeHelper::toMinutes($postedDuration)) { + } elseif ($postedDuration == null) { // Delete an already existing record here. $result = ttTimeHelper::delete($dataArray[$rowNumber][$dayHeader]['tt_log_id']); } else { diff --git a/week_view.php b/week_view.php index 1718afd4b..7db10226a 100644 --- a/week_view.php +++ b/week_view.php @@ -18,11 +18,13 @@ $cl_week_note = (bool)$request->getParameter('week_note'); $cl_week_list = (bool)$request->getParameter('week_list'); $cl_notes = (bool)$request->getParameter('notes'); + $cl_weekends = (bool)$request->getParameter('weekends'); } else { $cl_week_menu = $config->getDefinedValue('week_menu'); $cl_week_note = $config->getDefinedValue('week_note'); $cl_week_list = $config->getDefinedValue('week_list'); $cl_notes = $config->getDefinedValue('week_notes'); + $cl_weekends = $config->getDefinedValue('weekends'); } $form = new Form('weekViewForm'); @@ -30,6 +32,7 @@ $form->addInput(array('type'=>'checkbox','name'=>'week_note','value'=>$cl_week_note)); $form->addInput(array('type'=>'checkbox','name'=>'week_list','value'=>$cl_week_list)); $form->addInput(array('type'=>'checkbox','name'=>'notes','value'=>$cl_notes)); +$form->addInput(array('type'=>'checkbox','name'=>'weekends','value'=>$cl_weekends)); $form->addInput(array('type'=>'submit','name'=>'btn_save','value'=>$i18n->get('button.save'))); if ($request->isPost()){ @@ -38,6 +41,7 @@ $config->setDefinedValue('week_note', $cl_week_note); $config->setDefinedValue('week_list', $cl_week_list); $config->setDefinedValue('week_notes', $cl_notes); + $config->setDefinedValue('weekends', $cl_weekends); if (!$user->updateGroup(array('config' => $config->getConfig()))) { $err->add($i18n->get('error.db')); } diff --git a/work/admin_offer_delete.php b/work/admin_offer_delete.php deleted file mode 100644 index a1bd5fac1..000000000 --- a/work/admin_offer_delete.php +++ /dev/null @@ -1,71 +0,0 @@ -getParameter('id'); -$adminWorkHelper = new ttAdminWorkHelper($err); -$offer = $adminWorkHelper->getOffer($cl_offer_id); -if (!$offer) { - header('Location: ../access_denied.php'); - exit(); -} -// End of access checks. - -$offer_to_delete = $offer['subject']; - -$form = new Form('offerDeleteForm'); -$form->addInput(array('type'=>'hidden','name'=>'id','value'=>$cl_offer_id)); -$form->addInput(array('type'=>'submit','name'=>'btn_delete','value'=>$i18n->get('label.delete'))); -$form->addInput(array('type'=>'submit','name'=>'btn_cancel','value'=>$i18n->get('button.cancel'))); - -if ($request->isPost()) { - if ($request->getParameter('btn_delete')) { - if ($adminWorkHelper->deleteOffer($cl_offer_id)) { - header('Location: admin_work.php'); - exit(); - } - } elseif ($request->getParameter('btn_cancel')) { - header('Location: admin_work.php'); - exit(); - } -} // isPost - -$smarty->assign('offer_to_delete', $offer_to_delete); -$smarty->assign('forms', array($form->getName()=>$form->toArray())); -$smarty->assign('onload', 'onLoad="document.offerDeleteForm.btn_cancel.focus()"'); -$smarty->assign('title', $i18n->get('title.delete_offer')); -$smarty->assign('content_page_name', 'work/offer_delete.tpl'); -$smarty->display('work/index.tpl'); diff --git a/work/admin_offer_edit.php b/work/admin_offer_edit.php deleted file mode 100644 index ab795c136..000000000 --- a/work/admin_offer_edit.php +++ /dev/null @@ -1,171 +0,0 @@ -getParameter('id'); -$adminWorkHelper = new ttAdminWorkHelper($err); -$offer = $adminWorkHelper->getOffer($cl_offer_id); -if (!$offer) { - header('Location: ../access_denied.php'); - exit(); -} -// End of access checks. - -// Is this offer associated with a work item? -$work_id = $offer['work_id']; -if ($work_id) { - $work_item = $adminWorkHelper->getWorkItem($work_id); - if (!$work_item) $err->add($i18n->get('work.error.work_not_available')); -} - -$existingStatus = $offer['status']; -$currencies = ttWorkHelper::getCurrencies(); - -if ($request->isPost()) { - $cl_name = trim($request->getParameter('offer_name')); - $cl_description = trim($request->getParameter('description')); - $cl_details = trim($request->getParameter('details')); - $cl_currency_id = $request->getParameter('currency'); - if (!$cl_currency_id && $work_item) $cl_currency_id = ttWorkHelper::getCurrencyID($work_item['currency']); - $cl_budget = $request->getParameter('budget'); - $cl_payment_info = $request->getParameter('payment_info'); - // $cl_status = $request->getParameter('status'); - $cl_moderator_comment = $request->getParameter('moderator_comment'); -} else { - $cl_name = $offer['subject']; - $cl_description = $offer['descr_short']; - $cl_details = $offer['descr_long']; - $currency = $offer['currency']; - $cl_currency_id = ttWorkHelper::getCurrencyID($offer['currency']); - $cl_budget = $offer['amount']; - $cl_payment_info = $offer['payment_info']; - $status = $offer['status']; - $status_label = $offer['status_label']; - $cl_moderator_comment = $offer['moderator_comment']; -} - -$form = new Form('offerForm'); -$form->addInput(array('type'=>'hidden','name'=>'id','value'=>$cl_offer_id)); -if ($work_id) { - $form->addInput(array('type'=>'textarea','name'=>'work_description','style'=>'width: 400px; height: 80px;','value'=>$work_item['descr_short'])); - $form->getElement('work_description')->setEnabled(false); -} -$form->addInput(array('type'=>'text','name'=>'offer_name','style'=>'width: 400px;','value'=>$cl_name)); -if ($work_id) $form->getElement('offer_name')->setEnabled(false); -$form->addInput(array('type'=>'textarea','name'=>'description','maxlength'=>'512','style'=>'width: 400px; height: 80px;','value'=>$cl_description)); -$form->addInput(array('type'=>'textarea','name'=>'details','style'=>'width: 400px; height: 200px;','value'=>$cl_details)); -$form->addInput(array('type'=>'combobox','name'=>'currency','data'=>$currencies,'datakeys'=>array('id','name'),'value'=>$cl_currency_id)); -if ($work_id) $form->getElement('currency')->setEnabled(false); -$form->addInput(array('type'=>'floatfield','maxlength'=>'10','name'=>'budget','format'=>'.2','value'=>$cl_budget)); -$form->addInput(array('type'=>'textarea','name'=>'payment_info','maxlength'=>'256','style'=>'width: 400px; height: 40px;vertical-align: middle','value'=>$cl_payment_info)); -$form->addInput(array('type'=>'text','name'=>'status','style'=>'width: 400px;','value'=>$status_label)); -$form->getElement('status')->setEnabled(false); - -// Prepare status choices. -/* -$status_options = array(); -$status_options[STATUS_PENDING_APPROVAL] = $i18n->get('dropdown.pending_approval'); -$status_options[STATUS_DISAPPROVED] = $i18n->get('dropdown.not_approved'); -$status_options[STATUS_APPROVED] = $i18n->get('dropdown.approved'); - -$form->addInput(array('type'=>'combobox','name'=>'status','value'=>$cl_status,'data'=>$status_options)); -*/ -$form->addInput(array('type'=>'textarea','name'=>'moderator_comment','style'=>'width: 400px; height: 80px;','value'=>$cl_moderator_comment)); -$form->addInput(array('type'=>'submit','name'=>'btn_save','value'=>$i18n->get('button.save'))); -if ($status == STATUS_PENDING_APPROVAL) { - $form->addInput(array('type'=>'submit','name'=>'btn_approve','value'=>$i18n->get('button.approve'))); - $form->addInput(array('type'=>'submit','name'=>'btn_disapprove','value'=>$i18n->get('button.disapprove'))); -} - -if ($request->isPost()) { - // Validate user input. - if (!ttValidString($cl_name)) $err->add($i18n->get('error.field'), $i18n->get('label.work')); - if (!ttValidString($cl_description, true)) $err->add($i18n->get('error.field'), $i18n->get('label.description')); - if (!ttValidString($cl_details, true)) $err->add($i18n->get('error.field'), $i18n->get('label.details')); - if (!ttValidString($cl_budget)) $err->add($i18n->get('error.field'), $i18n->get('label.budget')); - if (!ttValidString($cl_payment_info)) $err->add($i18n->get('error.field'), $i18n->get('label.how_to_pay')); - if (!ttValidString($cl_moderator_comment, true)) $err->add($i18n->get('error.field'), $i18n->get('label.moderator_comment')); - - // Ensure user email exists (required for workflow). - if (!$user->getEmail()) $err->add($i18n->get('error.no_email')); - - $fields = array('offer_id'=>$cl_offer_id, - 'subject'=>$cl_name, - 'descr_short' => $cl_description, - 'descr_long' => $cl_details, - 'currency' => ttWorkHelper::getCurrencyName($cl_currency_id), - 'amount' => $cl_budget, - 'payment_info' => $cl_payment_info, - 'moderator_comment' => $cl_moderator_comment); - - if ($err->no()) { - if ($request->getParameter('btn_approve')) { - // Approve offer. - if ($adminWorkHelper->approveOffer($fields)) { - header('Location: admin_work.php'); - exit(); - } - } - - if ($request->getParameter('btn_save')) { - // Update offer without changing its status. - if ($adminWorkHelper->updateOffer($fields)) { - header('Location: admin_work.php'); - exit(); - } - } - - if ($request->getParameter('btn_disapprove')) { - // Dispprove offer. - if ($adminWorkHelper->disapproveOffer($fields)) { - header('Location: admin_work.php'); - exit(); - } - } - } -} // isPost - -if ($work_id) { - $smarty->assign('work_id', $work_id); - $smarty->assign('work_name', $work_item['subject']); - $smarty->assign('work_description', $work_item['descr_short']); -} -$smarty->assign('forms', array($form->getName()=>$form->toArray())); -$smarty->assign('title', $i18n->get('title.edit_offer')); -$smarty->assign('content_page_name', 'work/admin_offer_edit.tpl'); -$smarty->display('work/index.tpl'); diff --git a/work/admin_work.php b/work/admin_work.php deleted file mode 100644 index 86a029a4a..000000000 --- a/work/admin_work.php +++ /dev/null @@ -1,53 +0,0 @@ -getItems(); -$pending_work = $adminItems['pending_work']; -$pending_offers = $adminItems['pending_offers']; -$available_work = $adminItems['available_work']; -$available_offers = $adminItems['available_offers']; - -$smarty->assign('pending_work', $pending_work); -$smarty->assign('pending_offers', $pending_offers); -$smarty->assign('available_work', $available_work); -$smarty->assign('available_offers', $available_offers); -$smarty->assign('title', $i18n->get('title.work')); -$smarty->assign('content_page_name', 'work/admin_work.tpl'); -$smarty->display('work/index.tpl'); diff --git a/work/admin_work_delete.php b/work/admin_work_delete.php deleted file mode 100644 index 11b599430..000000000 --- a/work/admin_work_delete.php +++ /dev/null @@ -1,71 +0,0 @@ -getParameter('id'); -$adminWorkHelper = new ttAdminWorkHelper($err); -$work_item = $adminWorkHelper->getWorkItem($cl_work_id); -if (!$work_item) { - header('Location: ../access_denied.php'); - exit(); -} -// End of access checks. - -$work_to_delete = $work_item['subject']; - -$form = new Form('workDeleteForm'); -$form->addInput(array('type'=>'hidden','name'=>'id','value'=>$cl_work_id)); -$form->addInput(array('type'=>'submit','name'=>'btn_delete','value'=>$i18n->get('label.delete'))); -$form->addInput(array('type'=>'submit','name'=>'btn_cancel','value'=>$i18n->get('button.cancel'))); - -if ($request->isPost()) { - if ($request->getParameter('btn_delete')) { - if ($adminWorkHelper->deleteWorkItem($cl_work_id)) { - header('Location: admin_work.php'); - exit(); - } - } elseif ($request->getParameter('btn_cancel')) { - header('Location: admin_work.php'); - exit(); - } -} // isPost - -$smarty->assign('work_to_delete', $work_to_delete); -$smarty->assign('forms', array($form->getName()=>$form->toArray())); -$smarty->assign('onload', 'onLoad="document.workDeleteForm.btn_cancel.focus()"'); -$smarty->assign('title', $i18n->get('title.delete_work')); -$smarty->assign('content_page_name', 'work/work_delete.tpl'); -$smarty->display('work/index.tpl'); diff --git a/work/admin_work_edit.php b/work/admin_work_edit.php deleted file mode 100644 index 51169325c..000000000 --- a/work/admin_work_edit.php +++ /dev/null @@ -1,168 +0,0 @@ -getParameter('id'); -$adminWorkHelper = new ttAdminWorkHelper($err); -$work_item = $adminWorkHelper->getWorkItem($cl_work_id); -if (!$work_item) { - header('Location: ../access_denied.php'); - exit(); -} -// Do we have offer_id? -$offer_id = $work_item['offer_id']; -if ($offer_id) { - $offer = $adminWorkHelper->getOffer($offer_id); - if (!$offer) { - header('Location: ../access_denied.php'); - exit(); - } -} -// End of access checks. - -$existingStatus = $work_item['status']; -$currencies = ttWorkHelper::getCurrencies(); - -if ($request->isPost()) { - $cl_name = trim($request->getParameter('work_name')); - $cl_description = trim($request->getParameter('description')); - $cl_details = trim($request->getParameter('details')); - $cl_currency_id = $request->getParameter('currency'); - $cl_budget = $request->getParameter('budget'); - $cl_status = $request->getParameter('status'); - $cl_moderator_comment = $request->getParameter('moderator_comment'); -} else { - $cl_name = $work_item['subject']; - $cl_description = $work_item['descr_short']; - $cl_details = $work_item['descr_long']; - $currency = $work_item['currency']; - $cl_currency_id = ttWorkHelper::getCurrencyID($work_item['currency']); - $cl_budget = $work_item['amount']; - $cl_status = $work_item['status']; - $cl_moderator_comment = $work_item['moderator_comment']; -} - -$form = new Form('workForm'); -$form->addInput(array('type'=>'hidden','name'=>'id','value'=>$cl_work_id)); -$form->addInput(array('type'=>'text','name'=>'work_name','maxlength'=>'128','style'=>'width: 400px;','value'=>$cl_name)); -$form->addInput(array('type'=>'textarea','name'=>'description','maxlength'=>'512','style'=>'width: 400px; height: 80px;','value'=>$cl_description)); -$form->addInput(array('type'=>'textarea','name'=>'details','style'=>'width: 400px; height: 200px;','value'=>$cl_details)); -$form->addInput(array('type'=>'combobox','name'=>'currency','data'=>$currencies,'datakeys'=>array('id','name'),'value'=>$cl_currency_id)); -$form->addInput(array('type'=>'floatfield','maxlength'=>'10','name'=>'budget','format'=>'.2','value'=>$cl_budget)); -// Disable some controls for work on an available offer. -if ($offer) { - $form->getElement('work_name')->setEnabled(false); - // $form->getElement('work_type')->setEnabled(false); - $form->getElement('currency')->setEnabled(false); - $form->getElement('budget')->setEnabled(false); -} - -// Prepare status choices. -$status_options = array(); -$status_options[STATUS_PENDING_APPROVAL] = $i18n->get('dropdown.pending_approval'); -$status_options[STATUS_DISAPPROVED] = $i18n->get('dropdown.not_approved'); -$status_options[STATUS_APPROVED] = $i18n->get('dropdown.approved'); - -$form->addInput(array('type'=>'combobox','name'=>'status','value'=>$cl_status,'data'=>$status_options)); -$form->addInput(array('type'=>'textarea','name'=>'moderator_comment','style'=>'width: 400px; height: 80px;','value'=>$cl_moderator_comment)); -$form->addInput(array('type'=>'submit','name'=>'btn_save','value'=>$i18n->get('button.save'))); - -if ($request->isPost()) { - // Validate user input. - if (!ttValidString($cl_name)) $err->add($i18n->get('error.field'), $i18n->get('label.work')); - if (!ttValidString($cl_description, true)) $err->add($i18n->get('error.field'), $i18n->get('label.description')); - if (!ttValidString($cl_details, true)) $err->add($i18n->get('error.field'), $i18n->get('label.details')); - if (!ttValidString($cl_budget)) $err->add($i18n->get('error.field'), $i18n->get('label.budget')); - if (!ttValidString($cl_moderator_comment, true)) $err->add($i18n->get('error.field'), $i18n->get('label.moderator_comment')); - - // Ensure user email exists (required for workflow). - if (!$user->getEmail()) $err->add($i18n->get('error.no_email')); - - if ($err->no()) { - if ($request->getParameter('btn_save')) { - $fields = array('work_id'=>$cl_work_id, - 'subject'=>$cl_name, - 'descr_short' => $cl_description, - 'descr_long' => $cl_details, - 'currency' => ttWorkHelper::getCurrencyName($cl_currency_id), - 'amount' => $cl_budget, - 'moderator_comment' => $cl_moderator_comment); - - // Do things differently, depending on status control value. - if ($existingStatus == $cl_status) { - // Status not changed. Update work information. - if ($adminWorkHelper->updateWorkItem($fields)) { - header('Location: admin_work.php'); - exit(); - } - } else if ($cl_status == STATUS_DISAPPROVED) { - // Status changed to "not approved". Disapprove work. - if ($offer_id) { - if ($adminWorkHelper->disapproveWorkItemOnOffer($fields)) { - header('Location: admin_work.php'); - exit(); - } - } else { - if ($adminWorkHelper->disapproveWorkItem($fields)) { - header('Location: admin_work.php'); - exit(); - } - } - } else if ($cl_status == STATUS_APPROVED) { - // Status changed to "approved". Approve work. - if ($offer_id) { - if ($adminWorkHelper->approveWorkItemOnOffer($fields)) { - header('Location: admin_work.php'); - exit(); - } - } else { - if ($adminWorkHelper->approveWorkItem($fields)) { - header('Location: admin_work.php'); - exit(); - } - } - } - } - } -} // isPost - -$smarty->assign('offer', $offer); -$smarty->assign('forms', array($form->getName()=>$form->toArray())); -$smarty->assign('title', $i18n->get('title.edit_work')); -$smarty->assign('content_page_name', 'work/admin_work_edit.tpl'); -$smarty->display('work/index.tpl'); diff --git a/work/offer_add.php b/work/offer_add.php deleted file mode 100644 index bafdae216..000000000 --- a/work/offer_add.php +++ /dev/null @@ -1,120 +0,0 @@ -isPluginEnabled('wk')) { - header('Location: ../feature_disabled.php'); - exit(); -} -// Do we have work_id? -$cl_work_id = (int)$request->getParameter('work_id'); -if ($cl_work_id) { - $workHelper = new ttWorkHelper($err); - $work_item = $workHelper->getAvailableWorkItem($cl_work_id); - if (!$work_item) { - header('Location: ../access_denied.php'); - exit(); - } -} -// End of access checks. -if ($work_item) $cl_name = $work_item['subject']; - -if ($request->isPost()) { - if (!$work_item) - $cl_name = trim($request->getParameter('offer_name')); - $cl_description = trim($request->getParameter('description')); - $cl_details = trim($request->getParameter('details')); - $cl_currency_id = $request->getParameter('currency'); - if (!$cl_currency_id && $work_item) $cl_currency_id = ttWorkHelper::getCurrencyID($work_item['currency']); - $cl_budget = $request->getParameter('budget'); - $cl_payment_info = $request->getParameter('payment_info'); -} else { - if ($work_item) { - $cl_currency_id = ttWorkHelper::getCurrencyID($work_item['currency']); - } -} - -$form = new Form('offerForm'); -if ($cl_work_id) { - $form->addInput(array('type'=>'hidden','name'=>'work_id','value'=>$cl_work_id)); - $form->addInput(array('type'=>'textarea','name'=>'work_description','style'=>'width: 400px; height: 80px;','value'=>$work_item['descr_short'])); - $form->getElement('work_description')->setEnabled(false); -} -$form->addInput(array('type'=>'text','name'=>'offer_name','maxlength'=>'128','style'=>'width: 400px;','value'=>$cl_name)); -if ($work_item) $form->getElement('offer_name')->setEnabled(false); -$form->addInput(array('type'=>'textarea','name'=>'description','maxlength'=>'512','style'=>'width: 400px; height: 80px;','value'=>$cl_description)); -$form->addInput(array('type'=>'textarea','name'=>'details','style'=>'width: 400px; height: 200px;','value'=>$cl_details)); -// Add a dropdown for currency. -$currencies = ttWorkHelper::getCurrencies(); -$form->addInput(array('type'=>'combobox','name'=>'currency','data'=>$currencies,'datakeys'=>array('id','name'),'value'=>$cl_currency_id)); -if ($work_item) $form->getElement('currency')->setEnabled(false); // Do not allow changing currency for offers on existing work items. -$form->addInput(array('type'=>'floatfield','maxlength'=>'10','name'=>'budget','format'=>'.2','value'=>$cl_budget)); -$form->addInput(array('type'=>'textarea','name'=>'payment_info','maxlength'=>'256','style'=>'width: 400px; height: 40px;vertical-align: middle','value'=>$cl_payment_info)); -$form->addInput(array('type'=>'submit','name'=>'btn_add','value'=>$i18n->get('button.add'))); - -if ($request->isPost()) { - // Validate user input. - if (!ttValidString($cl_name)) $err->add($i18n->get('error.field'), $i18n->get('label.offer')); - if (!ttValidString($cl_description, true)) $err->add($i18n->get('error.field'), $i18n->get('label.description')); - if (!ttValidString($cl_details, true)) $err->add($i18n->get('error.field'), $i18n->get('label.details')); - if (!ttValidString($cl_budget)) $err->add($i18n->get('error.field'), $i18n->get('label.budget')); - if (!ttValidString($cl_payment_info)) $err->add($i18n->get('error.field'), $i18n->get('label.how_to_pay')); - - // Ensure user email exists (required for workflow). - if (!$user->getEmail()) $err->add($i18n->get('error.no_email')); - - if ($err->no()) { - $workHelper = new ttWorkHelper($err); - $fields = array('work_id'=>$cl_work_id, - 'subject'=>$cl_name, - 'descr_short' => $cl_description, - 'descr_long' => $cl_details, - 'currency' => ttWorkHelper::getCurrencyName($cl_currency_id), - 'amount' => $cl_budget, - 'payment_info' => $cl_payment_info); - if ($workHelper->putOwnOffer($fields)) { - header('Location: work.php'); - exit(); - } - } -} // isPost - -$smarty->assign('work_item', $work_item); -$smarty->assign('forms', array($form->getName()=>$form->toArray())); -$smarty->assign('onload', 'onLoad="document.offerForm.work_name.focus()"'); -$smarty->assign('title', $i18n->get('title.add_offer')); -$smarty->assign('content_page_name', 'work/offer_add.tpl'); -$smarty->display('work/index.tpl'); diff --git a/work/offer_delete.php b/work/offer_delete.php deleted file mode 100644 index fd3d2e7d1..000000000 --- a/work/offer_delete.php +++ /dev/null @@ -1,75 +0,0 @@ -isPluginEnabled('wk')) { - header('Location: ../feature_disabled.php'); - exit(); -} -$cl_offer_id = (int)$request->getParameter('id'); -$workHelper = new ttWorkHelper($err); -$offer = $workHelper->getOwnOffer($cl_offer_id); -if (!$offer) { - header('Location: ../access_denied.php'); - exit(); -} -// End of access checks. - -$offer_to_delete = $offer['subject']; - -$form = new Form('offerDeleteForm'); -$form->addInput(array('type'=>'hidden','name'=>'id','value'=>$cl_offer_id)); -$form->addInput(array('type'=>'submit','name'=>'btn_delete','value'=>$i18n->get('label.delete'))); -$form->addInput(array('type'=>'submit','name'=>'btn_cancel','value'=>$i18n->get('button.cancel'))); - -if ($request->isPost()) { - if ($request->getParameter('btn_delete')) { - if ($workHelper->deleteOwnOffer($cl_offer_id)) { - header('Location: work.php'); - exit(); - } - } elseif ($request->getParameter('btn_cancel')) { - header('Location: work.php'); - exit(); - } -} // isPost - -$smarty->assign('offer_to_delete', $offer_to_delete); -$smarty->assign('forms', array($form->getName()=>$form->toArray())); -$smarty->assign('onload', 'onLoad="document.offerDeleteForm.btn_cancel.focus()"'); -$smarty->assign('title', $i18n->get('title.delete_offer')); -$smarty->assign('content_page_name', 'work/offer_delete.tpl'); -$smarty->display('work/index.tpl'); diff --git a/work/offer_edit.php b/work/offer_edit.php deleted file mode 100644 index bd2172210..000000000 --- a/work/offer_edit.php +++ /dev/null @@ -1,135 +0,0 @@ -isPluginEnabled('wk')) { - header('Location: ../feature_disabled.php'); - exit(); -} -$cl_offer_id = (int)$request->getParameter('id'); -$workHelper = new ttWorkHelper($err); -$offer = $workHelper->getOwnOffer($cl_offer_id); -if (!$offer) { - header('Location: ../access_denied.php'); - exit(); -} -// End of access checks. - -// Is this offer associated with a work item? -$work_id = $offer['work_id']; -if ($work_id) { - $work_item = $workHelper->getAvailableWorkItem($work_id); - if (!$work_item) $err->add($i18n->get('work.error.work_not_available')); -} - -$currencies = ttWorkHelper::getCurrencies(); - -if ($request->isPost()) { - $cl_name = trim($request->getParameter('offer_name')); - $cl_description = trim($request->getParameter('description')); - $cl_details = trim($request->getParameter('details')); - $cl_currency_id = $request->getParameter('currency'); - if (!$cl_currency_id && $work_item) $cl_currency_id = ttWorkHelper::getCurrencyID($work_item['currency']); - $cl_budget = $request->getParameter('budget'); - $cl_payment_info = $request->getParameter('payment_info'); -} else { - $cl_name = $offer['subject']; - $cl_description = $offer['descr_short']; - $cl_details = $offer['descr_long']; - $cl_currency_id = ttWorkHelper::getCurrencyID($offer['currency']); - $cl_budget = $offer['amount']; - $cl_payment_info = $offer['payment_info']; - $cl_status = $offer['status_label']; - $cl_moderator_comment = $offer['moderator_comment']; -} - -$show_moderator_comment = $cl_moderator_comment != null; - -$form = new Form('offerForm'); -$form->addInput(array('type'=>'hidden','name'=>'id','value'=>$cl_offer_id)); -if ($work_id) { - $form->addInput(array('type'=>'textarea','name'=>'work_description','style'=>'width: 400px; height: 80px;','value'=>$work_item['descr_short'])); - $form->getElement('work_description')->setEnabled(false); -} -$form->addInput(array('type'=>'text','name'=>'offer_name','maxlength'=>'128','style'=>'width: 400px;','value'=>$cl_name)); -if ($work_id) $form->getElement('offer_name')->setEnabled(false); -$form->addInput(array('type'=>'textarea','name'=>'description','maxlength'=>'512','style'=>'width: 400px; height: 80px;','value'=>$cl_description)); -$form->addInput(array('type'=>'textarea','name'=>'details','style'=>'width: 400px; height: 200px;','value'=>$cl_details)); -$form->addInput(array('type'=>'combobox','name'=>'currency','data'=>$currencies,'datakeys'=>array('id','name'),'value'=>$cl_currency_id)); -if ($work_id) $form->getElement('currency')->setEnabled(false); -$form->addInput(array('type'=>'floatfield','maxlength'=>'10','name'=>'budget','format'=>'.2','value'=>$cl_budget)); -$form->addInput(array('type'=>'textarea','name'=>'payment_info','maxlength'=>'256','style'=>'width: 400px; height: 40px; vertical-align: middle','value'=>$cl_payment_info)); -$form->addInput(array('type'=>'text','name'=>'status','style'=>'width: 400px;','value'=>$cl_status)); -$form->getElement('status')->setEnabled(false); -$form->addInput(array('type'=>'textarea','name'=>'moderator_comment','style'=>'width: 400px; height: 80px;','value'=>$cl_moderator_comment)); -$form->getElement('moderator_comment')->setEnabled(false); -$form->addInput(array('type'=>'submit','name'=>'btn_save','value'=>$i18n->get('button.save'))); - -if ($request->isPost()) { - // Validate user input. - if (!ttValidString($cl_name)) $err->add($i18n->get('error.field'), $i18n->get('label.offer')); - if (!ttValidString($cl_description, true)) $err->add($i18n->get('error.field'), $i18n->get('label.description')); - if (!ttValidString($cl_details, true)) $err->add($i18n->get('error.field'), $i18n->get('label.details')); - if (!ttValidString($cl_budget)) $err->add($i18n->get('error.field'), $i18n->get('label.budget')); - if (!ttValidString($cl_payment_info)) $err->add($i18n->get('error.field'), $i18n->get('label.how_to_pay')); - - // Ensure user email exists (required for workflow). - if (!$user->getEmail()) $err->add($i18n->get('error.no_email')); - - if ($err->no()) { - if ($request->getParameter('btn_save')) { - // Update offer information. - $fields = array('offer_id'=>$cl_offer_id, - 'subject'=>$cl_name, - 'descr_short' => $cl_description, - 'descr_long' => $cl_details, - 'currency' => ttWorkHelper::getCurrencyName($cl_currency_id), - 'amount' => $cl_budget, - 'payment_info' => $cl_payment_info); - if ($workHelper->updateOwnOffer($fields)) { - header('Location: work.php'); - exit(); - } - } - } -} // isPost - -$smarty->assign('work_item', $work_item); -$smarty->assign('show_moderator_comment', $show_moderator_comment); -$smarty->assign('forms', array($form->getName()=>$form->toArray())); -$smarty->assign('title', $i18n->get('title.edit_offer')); -$smarty->assign('content_page_name', 'work/offer_edit.tpl'); -$smarty->display('work/index.tpl'); diff --git a/work/offer_message.php b/work/offer_message.php deleted file mode 100644 index ff324808e..000000000 --- a/work/offer_message.php +++ /dev/null @@ -1,90 +0,0 @@ -isPluginEnabled('wk')) { - header('Location: ../feature_disabled.php'); - exit(); -} -$cl_offer_id = (int)$request->getParameter('offer_id'); -if (!$cl_offer_id) { - header('Location: ../access_denied.php'); - exit(); -} -$workHelper = new ttWorkHelper($err); -$offer = $workHelper->getAvailableOffer($cl_offer_id); -if (!$offer) { - header('Location: ../access_denied.php'); - exit(); -} -// End of access checks. - -$cl_name = $offer['subject']; - -if ($request->isPost()) { - $cl_message_body = trim($request->getParameter('message_body')); -} - -$form = new Form('messageForm'); -$form->addInput(array('type'=>'hidden','name'=>'offer_id','value'=>$cl_offer_id)); -$form->addInput(array('type'=>'textarea','name'=>'message_body','style'=>'width: 400px; height: 80px;vertical-align: middle','value'=>$cl_message_body)); -$form->addInput(array('type'=>'submit','name'=>'btn_send','value'=>$i18n->get('work.button.send_message'))); - -if ($request->isPost()) { - // Validate user input. - if (!ttValidString($cl_message_body)) $err->add($i18n->get('error.field'), $i18n->get('work.label.message')); - - // Ensure user email exists (required for workflow). - if (!$user->getEmail()) $err->add($i18n->get('error.no_email')); - - if ($err->no()) { - $workHelper = new ttWorkHelper($err); - $fields = array('offer_id'=>$cl_offer_id, - 'message_body' => $cl_message_body); - if ($workHelper->sendMessageToOfferOwner($fields)) { - $msg->add($i18n->get('work.msg.message_sent')); - // header('Location: work.php'); // TODO: where to redirect? - // exit(); - } - } -} // isPost - -$smarty->assign('offer', $offer); -$smarty->assign('forms', array($form->getName()=>$form->toArray())); -$smarty->assign('onload', 'onLoad="document.offerForm.work_name.focus()"'); -$smarty->assign('title', $i18n->get('work.title.send_message')); -$smarty->assign('content_page_name', 'work/offer_message.tpl'); -$smarty->display('work/index.tpl'); diff --git a/work/offer_view.php b/work/offer_view.php deleted file mode 100644 index 1e045bf20..000000000 --- a/work/offer_view.php +++ /dev/null @@ -1,81 +0,0 @@ -isPluginEnabled('wk')) { - header('Location: ../feature_disabled.php'); - exit(); -} -if (!ttAccessAllowed('manage_work')) { - header('Location: ../access_denied.php'); - exit(); -} -$offer_id = (int)$request->getParameter('id'); -$workHelper = new ttWorkHelper($err); -$offer = $workHelper->getAvailableOffer($offer_id); -if (!$offer) { - header('Location: ../access_denied.php'); - exit(); -} -// End of access checks. - -// Is this offer associated with a work item? -$work_id = $offer['work_id']; -if ($work_id) { - $work_item = $workHelper->getAvailableWorkItem($work_id); - if (!$work_item) $err->add($i18n->get('work.error.work_not_available')); -} - -$cl_contractor = $offer['group_name']; -$cl_name = $offer['subject']; -$cl_description = $offer['descr_short']; -$cl_details = $offer['descr_long']; -$cl_budget = $offer['amount_with_currency']; - -$form = new Form('offerForm'); -$form->addInput(array('type'=>'text','name'=>'contractor','value'=>$cl_contractor)); -$form->getElement('contractor')->setEnabled(false); -$form->addInput(array('type'=>'text','name'=>'offer_name','style'=>'width: 400px;','value'=>$cl_name)); -$form->getElement('offer_name')->setEnabled(false); -$form->addInput(array('type'=>'textarea','name'=>'description','style'=>'width: 400px; height: 80px;','value'=>$cl_description)); -$form->getElement('description')->setEnabled(false); -$form->addInput(array('type'=>'textarea','name'=>'details','style'=>'width: 400px; height: 200px;','value'=>$cl_details)); -$form->getElement('details')->setEnabled(false); -$form->addInput(array('type'=>'text','name'=>'budget','value'=>$cl_budget)); -$form->getElement('budget')->setEnabled(false); - - -$smarty->assign('offer_id', $offer_id); -$smarty->assign('forms', array($form->getName()=>$form->toArray())); -$smarty->assign('title', $i18n->get('title.offer')); -$smarty->assign('content_page_name', 'work/offer_view.tpl'); -$smarty->display('work/index.tpl'); diff --git a/work/offer_view_own.php b/work/offer_view_own.php deleted file mode 100644 index 57b1b95b3..000000000 --- a/work/offer_view_own.php +++ /dev/null @@ -1,77 +0,0 @@ -isPluginEnabled('wk')) { - header('Location: ../feature_disabled.php'); - exit(); -} -if (!ttAccessAllowed('manage_work')) { - header('Location: ../access_denied.php'); - exit(); -} -$offer_id = (int)$request->getParameter('id'); -$workHelper = new ttWorkHelper($err); -$offer = $workHelper->getOwnOffer($offer_id); -if (!$offer) { - header('Location: ../access_denied.php'); - exit(); -} -// End of access checks. - -// Is this offer associated with a work item? -$work_id = $offer['work_id']; -if ($work_id) { - $work_item = $workHelper->getAvailableWorkItem($work_id); - if (!$work_item) $err->add($i18n->get('work.error.work_not_available')); -} - -$cl_name = $offer['subject']; -$cl_description = $offer['descr_short']; -$cl_details = $offer['descr_long']; -$cl_budget = $offer['amount_with_currency']; - -$form = new Form('offerForm'); -$form->addInput(array('type'=>'text','name'=>'offer_name','style'=>'width: 400px;','value'=>$cl_name)); -$form->getElement('offer_name')->setEnabled(false); -$form->addInput(array('type'=>'textarea','name'=>'description','style'=>'width: 400px; height: 80px;','value'=>$cl_description)); -$form->getElement('description')->setEnabled(false); -$form->addInput(array('type'=>'textarea','name'=>'details','style'=>'width: 400px; height: 200px;','value'=>$cl_details)); -$form->getElement('details')->setEnabled(false); -$form->addInput(array('type'=>'text','name'=>'budget','value'=>$cl_budget)); -$form->getElement('budget')->setEnabled(false); - -$smarty->assign('work_item', $work_item); -$smarty->assign('forms', array($form->getName()=>$form->toArray())); -$smarty->assign('title', $i18n->get('title.offer')); -$smarty->assign('content_page_name', 'work/offer_view_own.tpl'); -$smarty->display('work/index.tpl'); diff --git a/work/work.php b/work/work.php deleted file mode 100644 index b043690b4..000000000 --- a/work/work.php +++ /dev/null @@ -1,65 +0,0 @@ -isPluginEnabled('wk')) { - header('Location: ../feature_disabled.php'); - exit(); -} -// End of access checks. - -$workHelper = new ttWorkHelper($err); -$groupItems = $workHelper->getGroupItems(); // All group items. -if($user->can('manage_work')) { - $own_work_items = $groupItems['own_work_items']; // Own work items this group is outsourcing. - $available_offers = $groupItems['available_offers']; // Available offers from other organizations. -} -if($user->can('bid_on_work')) { - $available_work_items = $groupItems['available_work_items']; // Currently available work items from other orgs. - $own_offers = $groupItems['own_offers']; // Own offers this group makes available to other groups. -} -if($user->can('update_work')) { - // $in_progress_work = ttWorkHelper::getInProgressWork(); // Work items in progress for other groups. - // $completed_work = ttWorkHelper::getCompletedWork(); // Completed work items for other groups. -} -// TODO: review access rights for the code above. - -$smarty->assign('own_work_items', $own_work_items); -$smarty->assign('available_work_items', $available_work_items); -$smarty->assign('own_offers', $own_offers); -$smarty->assign('available_offers', $available_offers); -$smarty->assign('title', $i18n->get('title.work')); -$smarty->assign('content_page_name', 'work/work.tpl'); -$smarty->display('work/index.tpl'); diff --git a/work/work_add.php b/work/work_add.php deleted file mode 100644 index df12c9155..000000000 --- a/work/work_add.php +++ /dev/null @@ -1,122 +0,0 @@ -isPluginEnabled('wk')) { - header('Location: ../feature_disabled.php'); - exit(); -} -// Do we have offer_id? -$cl_offer_id = (int)$request->getParameter('offer_id'); -if ($cl_offer_id) { - $workHelper = new ttWorkHelper($err); - $offer = $workHelper->getAvailableOffer($cl_offer_id); - if (!$offer) { - header('Location: ../access_denied.php'); - exit(); - } -} -// End of access checks. - -if ($request->isPost()) { - $cl_name = trim($request->getParameter('work_name')); - $cl_work_type = $request->getParameter('work_type'); - $cl_description = trim($request->getParameter('description')); - $cl_details = trim($request->getParameter('details')); - $cl_currency_id = $request->getParameter('currency'); - $cl_budget = $request->getParameter('budget'); -} -// Override some fields for work being created on an available offer. -if ($offer) { - $cl_name = $offer['subject']; - $cl_work_type = 0; // one-time work - $cl_currency_id = ttWorkHelper::getCurrencyID($offer['currency']); - $cl_budget = $offer['amount']; -} - -$form = new Form('workForm'); -if ($offer) - $form->addInput(array('type'=>'hidden','name'=>'offer_id','value'=>$cl_offer_id)); -$form->addInput(array('type'=>'text','name'=>'work_name','maxlength'=>'128','style'=>'width: 400px;','value'=>$cl_name)); -$WORK_TYPE_OPTIONS = array('0'=>$i18n->get('work.type.one_time'),'1'=>$i18n->get('work.type.ongoing')); -$form->addInput(array('type'=>'combobox','name'=>'work_type','data'=>$WORK_TYPE_OPTIONS,'value'=>$cl_work_type)); -$form->addInput(array('type'=>'textarea','name'=>'description','maxlength'=>'512','style'=>'width: 400px; height: 80px;','value'=>$cl_description)); -$form->addInput(array('type'=>'textarea','name'=>'details','style'=>'width: 400px; height: 200px;','value'=>$cl_details)); -// Add a dropdown for currency. -$currencies = ttWorkHelper::getCurrencies(); -$form->addInput(array('type'=>'combobox','name'=>'currency','data'=>$currencies,'datakeys'=>array('id','name'),'value'=>$cl_currency_id)); -$form->addInput(array('type'=>'floatfield','maxlength'=>'10','name'=>'budget','format'=>'.2','value'=>$cl_budget)); -$form->addInput(array('type'=>'submit','name'=>'btn_add','value'=>$i18n->get('button.add'))); -// Disable some controls for work being created on an available offer. -if ($offer) { - $form->getElement('work_name')->setEnabled(false); - $form->getElement('work_type')->setEnabled(false); - $form->getElement('currency')->setEnabled(false); - $form->getElement('budget')->setEnabled(false); -} - -if ($request->isPost()) { - // Validate user input. - if (!ttValidString($cl_name)) $err->add($i18n->get('error.field'), $i18n->get('label.work')); - if (!ttValidString($cl_description, true)) $err->add($i18n->get('error.field'), $i18n->get('label.description')); - if (!ttValidString($cl_details, true)) $err->add($i18n->get('error.field'), $i18n->get('label.details')); - if (!ttValidString($cl_budget)) $err->add($i18n->get('error.field'), $i18n->get('label.budget')); - - // Ensure user email exists (required for workflow). - if (!$user->getEmail()) $err->add($i18n->get('error.no_email')); - - if ($err->no()) { - $workHelper = new ttWorkHelper($err); - $fields = array('offer_id' => $cl_offer_id, - 'subject' => $cl_name, - 'type' => $cl_work_type, - 'descr_short' => $cl_description, - 'descr_long' => $cl_details, - 'currency' => ttWorkHelper::getCurrencyName($cl_currency_id), - 'amount' => $cl_budget); - if ($workHelper->putOwnWorkItem($fields)) { - header('Location: work.php'); - exit(); - } - } -} // isPost - -$smarty->assign('offer', $offer); -$smarty->assign('forms', array($form->getName()=>$form->toArray())); -$smarty->assign('onload', 'onLoad="document.workForm.work_name.focus()"'); -$smarty->assign('title', $i18n->get('title.add_work')); -$smarty->assign('content_page_name', 'work/work_add.tpl'); -$smarty->display('work/index.tpl'); diff --git a/work/work_delete.php b/work/work_delete.php deleted file mode 100644 index 1d94603ab..000000000 --- a/work/work_delete.php +++ /dev/null @@ -1,75 +0,0 @@ -isPluginEnabled('wk')) { - header('Location: ../feature_disabled.php'); - exit(); -} -$cl_work_id = (int)$request->getParameter('id'); -$workHelper = new ttWorkHelper($err); -$work_item = $workHelper->getOwnWorkItem($cl_work_id); -if (!$work_item) { - header('Location: ../access_denied.php'); - exit(); -} -// End of access checks. - -$work_to_delete = $work_item['subject']; - -$form = new Form('workDeleteForm'); -$form->addInput(array('type'=>'hidden','name'=>'id','value'=>$cl_work_id)); -$form->addInput(array('type'=>'submit','name'=>'btn_delete','value'=>$i18n->get('label.delete'))); -$form->addInput(array('type'=>'submit','name'=>'btn_cancel','value'=>$i18n->get('button.cancel'))); - -if ($request->isPost()) { - if ($request->getParameter('btn_delete')) { - if ($workHelper->deleteOwnWorkItem($cl_work_id)) { - header('Location: work.php'); - exit(); - } - } elseif ($request->getParameter('btn_cancel')) { - header('Location: work.php'); - exit(); - } -} // isPost - -$smarty->assign('work_to_delete', $work_to_delete); -$smarty->assign('forms', array($form->getName()=>$form->toArray())); -$smarty->assign('onload', 'onLoad="document.workDeleteForm.btn_cancel.focus()"'); -$smarty->assign('title', $i18n->get('title.delete_work')); -$smarty->assign('content_page_name', 'work/work_delete.tpl'); -$smarty->display('work/index.tpl'); diff --git a/work/work_edit.php b/work/work_edit.php deleted file mode 100644 index 7e27106b8..000000000 --- a/work/work_edit.php +++ /dev/null @@ -1,151 +0,0 @@ -isPluginEnabled('wk')) { - header('Location: ../feature_disabled.php'); - exit(); -} -$cl_work_id = (int)$request->getParameter('id'); -$workHelper = new ttWorkHelper($err); -$work_item = $workHelper->getOwnWorkItem($cl_work_id); -if (!$work_item) { - header('Location: ../access_denied.php'); - exit(); -} -// Do we have offer_id? -$offer_id = $work_item['offer_id']; -if ($offer_id) { - $offer = $workHelper->getAvailableOffer($offer_id); - if (!$offer) { - header('Location: ../access_denied.php'); - exit(); - } -} -// End of access checks. - -$currencies = ttWorkHelper::getCurrencies(); - -if ($request->isPost()) { - $cl_name = trim($request->getParameter('work_name')); - $cl_work_type = $request->getParameter('work_type'); - $cl_description = trim($request->getParameter('description')); - $cl_details = trim($request->getParameter('details')); - $cl_currency_id = $request->getParameter('currency'); - $cl_budget = $request->getParameter('budget'); -} else { - $cl_name = $work_item['subject']; - $cl_work_type = $work_item['type']; - $cl_description = $work_item['descr_short']; - $cl_details = $work_item['descr_long']; - $cl_currency_id = ttWorkHelper::getCurrencyID($work_item['currency']); - $cl_budget = $work_item['amount']; - $cl_status = $work_item['status_label']; - $cl_moderator_comment = $work_item['moderator_comment']; -} -// Override some fields for work on an available offer. -if ($offer) { - $cl_name = $offer['subject']; - $cl_work_type = 0; // one-time work - $cl_currency_id = ttWorkHelper::getCurrencyID($offer['currency']); - $cl_budget = $offer['amount']; -} - -$show_moderator_comment = $cl_moderator_comment != null; - -$form = new Form('workForm'); -$form->addInput(array('type'=>'hidden','name'=>'id','value'=>$cl_work_id)); -$form->addInput(array('type'=>'text','name'=>'work_name','maxlength'=>'128','style'=>'width: 400px;','value'=>$cl_name)); -$WORK_TYPE_OPTIONS = array('0'=>$i18n->get('work.type.one_time'),'1'=>$i18n->get('work.type.ongoing')); -$form->addInput(array('type'=>'combobox','name'=>'work_type','data'=>$WORK_TYPE_OPTIONS,'value'=>$cl_work_type)); -$form->addInput(array('type'=>'textarea','name'=>'description','maxlength'=>'512','style'=>'width: 400px; height: 80px;','value'=>$cl_description)); -$form->addInput(array('type'=>'textarea','name'=>'details','style'=>'width: 400px; height: 200px;','value'=>$cl_details)); -$form->addInput(array('type'=>'combobox','name'=>'currency','data'=>$currencies,'datakeys'=>array('id','name'),'value'=>$cl_currency_id)); -$form->addInput(array('type'=>'floatfield','maxlength'=>'10','name'=>'budget','format'=>'.2','value'=>$cl_budget)); -$form->addInput(array('type'=>'text','name'=>'status','style'=>'width: 400px;','value'=>$cl_status)); -$form->getElement('status')->setEnabled(false); -$form->addInput(array('type'=>'textarea','name'=>'moderator_comment','style'=>'width: 400px; height: 80px;','value'=>$cl_moderator_comment)); -$form->getElement('moderator_comment')->setEnabled(false); -$form->addInput(array('type'=>'submit','name'=>'btn_save','value'=>$i18n->get('button.save'))); -// Disable some controls for work on an available offer. -if ($offer) { - $form->getElement('work_name')->setEnabled(false); - $form->getElement('work_type')->setEnabled(false); - $form->getElement('currency')->setEnabled(false); - $form->getElement('budget')->setEnabled(false); -} - -if ($request->isPost()) { - // Validate user input. - if (!ttValidString($cl_name)) $err->add($i18n->get('error.field'), $i18n->get('label.work')); - if (!ttValidString($cl_description, true)) $err->add($i18n->get('error.field'), $i18n->get('label.description')); - if (!ttValidString($cl_details, true)) $err->add($i18n->get('error.field'), $i18n->get('label.details')); - if (!ttValidString($cl_budget)) $err->add($i18n->get('error.field'), $i18n->get('label.budget')); - - // Ensure user email exists (required for workflow). - if (!$user->getEmail()) $err->add($i18n->get('error.no_email')); - - if ($err->no()) { - if ($request->getParameter('btn_save')) { - // Update work information. - $fields = array('work_id'=>$cl_work_id, - 'type'=>$cl_work_type, - 'subject'=>$cl_name, - 'descr_short' => $cl_description, - 'descr_long' => $cl_details, - 'currency' => ttWorkHelper::getCurrencyName($cl_currency_id), - 'amount' => $cl_budget); - if ($offer_id > 0) { - if ($workHelper->updateOwnWorkItemOnOffer($fields)) { - header('Location: work.php'); - exit(); - } - } else { - if ($workHelper->updateOwnWorkItem($fields)) { - header('Location: work.php'); - exit(); - } - } - } - } -} // isPost - -$smarty->assign('offer', $offer); -$smarty->assign('show_moderator_comment', $show_moderator_comment); -$smarty->assign('forms', array($form->getName()=>$form->toArray())); -$smarty->assign('title', $i18n->get('title.edit_work')); -$smarty->assign('content_page_name', 'work/work_edit.tpl'); -$smarty->display('work/index.tpl'); diff --git a/work/work_message.php b/work/work_message.php deleted file mode 100644 index 91d02b5cb..000000000 --- a/work/work_message.php +++ /dev/null @@ -1,90 +0,0 @@ -isPluginEnabled('wk')) { - header('Location: ../feature_disabled.php'); - exit(); -} -$cl_work_id = (int)$request->getParameter('work_id'); -if (!$cl_work_id) { - header('Location: ../access_denied.php'); - exit(); -} -$workHelper = new ttWorkHelper($err); -$work_item = $workHelper->getAvailableWorkItem($cl_work_id); -if (!$work_item) { - header('Location: ../access_denied.php'); - exit(); -} -// End of access checks. - -$cl_name = $work_item['subject']; - -if ($request->isPost()) { - $cl_message_body = trim($request->getParameter('message_body')); -} - -$form = new Form('messageForm'); -$form->addInput(array('type'=>'hidden','name'=>'work_id','value'=>$cl_work_id)); -$form->addInput(array('type'=>'textarea','name'=>'message_body','style'=>'width: 400px; height: 80px;vertical-align: middle','value'=>$cl_message_body)); -$form->addInput(array('type'=>'submit','name'=>'btn_send','value'=>$i18n->get('work.button.send_message'))); - -if ($request->isPost()) { - // Validate user input. - if (!ttValidString($cl_message_body)) $err->add($i18n->get('error.field'), $i18n->get('work.label.message')); - - // Ensure user email exists (required for workflow). - if (!$user->getEmail()) $err->add($i18n->get('error.no_email')); - - if ($err->no()) { - $workHelper = new ttWorkHelper($err); - $fields = array('work_id'=>$cl_work_id, - 'message_body' => $cl_message_body); - if ($workHelper->sendMessageToWorkOwner($fields)) { - $msg->add($i18n->get('work.msg.message_sent')); - // header('Location: work.php'); // TODO: where to redirect? - // exit(); - } - } -} // isPost - -$smarty->assign('work_item', $work_item); -$smarty->assign('forms', array($form->getName()=>$form->toArray())); -$smarty->assign('onload', 'onLoad="document.offerForm.work_name.focus()"'); -$smarty->assign('title', $i18n->get('work.title.send_message')); -$smarty->assign('content_page_name', 'work/work_message.tpl'); -$smarty->display('work/index.tpl'); diff --git a/work/work_offer_view.php b/work/work_offer_view.php deleted file mode 100644 index c0dd5ee1a..000000000 --- a/work/work_offer_view.php +++ /dev/null @@ -1,123 +0,0 @@ -isPluginEnabled('wk')) { - header('Location: ../feature_disabled.php'); - exit(); -} -if (!ttAccessAllowed('manage_work')) { - header('Location: ../access_denied.php'); - exit(); -} -$cl_offer_id = (int)$request->getParameter('id'); -$workHelper = new ttWorkHelper($err); -$offer = $workHelper->getOwnWorkItemOffer($cl_offer_id); -if (!$offer) { - header('Location: ../access_denied.php'); - exit(); -} -// End of access checks. - -// Get an associated work item. -$work_item = $workHelper->getOwnWorkItem($offer['work_id']); -if (!$work_item) $err->add($i18n->get('work.error.work_not_available')); - -if ($request->isPost()) { - $cl_client_comment = $request->getParameter('client_comment'); -} else { - $cl_client_comment = $offer['client_comment']; -} - -$cl_contractor = $offer['group_name']; -$cl_name = $offer['subject']; -$cl_description = $offer['descr_short']; -$cl_details = $offer['descr_long']; -$cl_budget = $offer['amount_with_currency']; -$cl_status = $offer['status_label']; - -$form = new Form('offerForm'); -$form->addInput(array('type'=>'hidden','name'=>'id','value'=>$cl_offer_id)); -$form->addInput(array('type'=>'text','name'=>'contractor','value'=>$cl_contractor)); -$form->getElement('contractor')->setEnabled(false); -$form->addInput(array('type'=>'text','name'=>'offer_name','style'=>'width: 400px;','value'=>$cl_name)); -$form->getElement('offer_name')->setEnabled(false); -$form->addInput(array('type'=>'textarea','name'=>'description','style'=>'width: 400px; height: 80px;','value'=>$cl_description)); -$form->getElement('description')->setEnabled(false); -$form->addInput(array('type'=>'textarea','name'=>'details','style'=>'width: 400px; height: 200px;','value'=>$cl_details)); -$form->getElement('details')->setEnabled(false); -$form->addInput(array('type'=>'text','name'=>'budget','value'=>$cl_budget)); -$form->getElement('budget')->setEnabled(false); -$form->addInput(array('type'=>'text','name'=>'status','style'=>'width: 400px;','value'=>$cl_status)); -$form->getElement('status')->setEnabled(false); -if ($offer['status'] == STATUS_APPROVED) { - $form->addInput(array('type'=>'submit','name'=>'btn_accept','value'=>$i18n->get('work.button.accept'))); - $form->addInput(array('type'=>'submit','name'=>'btn_decline','value'=>$i18n->get('work.button.decline'))); -} -$form->addInput(array('type'=>'textarea','name'=>'client_comment','style'=>'width: 400px; height: 80px;','value'=>$cl_client_comment)); - -if ($request->isPost()) { - // Validate user input. - if (!ttValidString($cl_client_comment, true)) $err->add($i18n->get('error.field'), $i18n->get('label.comment')); - - // Ensure user email exists (required for workflow). - if (!$user->getEmail()) $err->add($i18n->get('error.no_email')); - - if ($err->no()) { - $workHelper = new ttWorkHelper($err); - $fields = array('offer_id'=>$cl_offer_id, - 'client_comment'=>$cl_client_comment); - - if ($request->getParameter('btn_accept')) { - // Accept offer. - if ($workHelper->acceptOwnWorkItemOffer($fields)) { - header('Location: work_offer_view.php?id='.$cl_offer_id); - exit(); - } - } - - if ($request->getParameter('btn_decline')) { - // Decline offer. - if ($workHelper->declineOwnWorkItemOffer($fields)) { - header('Location: work_offer_view.php?id='.$cl_offer_id); - exit(); - } - } - } -} // isPost - -$smarty->assign('work_item', $work_item); -$smarty->assign('forms', array($form->getName()=>$form->toArray())); -$smarty->assign('title', $i18n->get('title.offer')); -$smarty->assign('content_page_name', 'work/work_offer_view.tpl'); -$smarty->display('work/index.tpl'); diff --git a/work/work_offers.php b/work/work_offers.php deleted file mode 100644 index eae9841bb..000000000 --- a/work/work_offers.php +++ /dev/null @@ -1,56 +0,0 @@ -isPluginEnabled('wk')) { - header('Location: ../feature_disabled.php'); - exit(); -} -$work_id = (int)$request->getParameter('id'); -$workHelper = new ttWorkHelper($err); -$work_item = $workHelper->getOwnWorkItem($work_id); -if (!$work_item) { - header('Location: ../access_denied.php'); - exit(); -} -// End of access checks. - -$work_item_offers = $workHelper->getOwnWorkItemOffers($work_id); - -$smarty->assign('work_item', $work_item); -$smarty->assign('work_item_offers', $work_item_offers); -$smarty->assign('title', $i18n->get('work.label.offers')); -$smarty->assign('content_page_name', 'work/work_offers.tpl'); -$smarty->display('work/index.tpl'); diff --git a/work/work_view.php b/work/work_view.php deleted file mode 100644 index cb73f2eed..000000000 --- a/work/work_view.php +++ /dev/null @@ -1,74 +0,0 @@ -isPluginEnabled('wk')) { - header('Location: ../feature_disabled.php'); - exit(); -} -if (!ttAccessAllowed('bid_on_work')) { - header('Location: ../access_denied.php'); - exit(); -} -$cl_work_id = (int)$request->getParameter('id'); -$workHelper = new ttWorkHelper($err); -$work_item = $workHelper->getAvailableWorkItem($cl_work_id); -if (!$work_item) { - header('Location: ../access_denied.php'); - exit(); -} -// End of access checks. - -$cl_client = $work_item['group_name']; -$cl_name = $work_item['subject']; -$cl_description = $work_item['descr_short']; -$cl_details = $work_item['descr_long']; -$cl_budget = $work_item['amount_with_currency']; - -$form = new Form('workForm'); -$form->addInput(array('type'=>'hidden','name'=>'id','value'=>$cl_work_id)); -$form->addInput(array('type'=>'text','name'=>'client','value'=>$cl_client)); -$form->getElement('client')->setEnabled(false); -$form->addInput(array('type'=>'textarea','name'=>'work_name','style'=>'width: 400px;','value'=>$cl_name)); -$form->getElement('work_name')->setEnabled(false); -$form->addInput(array('type'=>'textarea','name'=>'description','style'=>'width: 400px; height: 80px;','value'=>$cl_description)); -$form->getElement('description')->setEnabled(false); -$form->addInput(array('type'=>'textarea','name'=>'details','style'=>'width: 400px; height: 200px;','value'=>$cl_details)); -$form->getElement('details')->setEnabled(false); -$form->addInput(array('type'=>'text','name'=>'budget','value'=>$cl_budget)); -$form->getElement('budget')->setEnabled(false); - -$smarty->assign('work_id', $cl_work_id); -$smarty->assign('forms', array($form->getName()=>$form->toArray())); -$smarty->assign('title', $i18n->get('title.work')); -$smarty->assign('content_page_name', 'work/work_view.tpl'); -$smarty->display('work/index.tpl'); diff --git a/work/work_view_own.php b/work/work_view_own.php deleted file mode 100644 index c4b887e7e..000000000 --- a/work/work_view_own.php +++ /dev/null @@ -1,79 +0,0 @@ -isPluginEnabled('wk')) { - header('Location: ../feature_disabled.php'); - exit(); -} -if (!ttAccessAllowed('manage_work')) { - header('Location: ../access_denied.php'); - exit(); -} -$work_id = (int)$request->getParameter('id'); -$workHelper = new ttWorkHelper($err); -$work_item = $workHelper->getOwnWorkItem($work_id); -if (!$work_item) { - header('Location: ../access_denied.php'); - exit(); -} -// Do we have offer_id? -$offer_id = $work_item['offer_id']; -if ($offer_id) { - $offer = $workHelper->getAvailableOffer($offer_id); - if (!$offer) { - header('Location: ../access_denied.php'); - exit(); - } -} -// End of access checks. - -$cl_name = $work_item['subject']; -$cl_description = $work_item['descr_short']; -$cl_details = $work_item['descr_long']; -$cl_budget = $work_item['amount_with_currency']; - -$form = new Form('workForm'); -$form->addInput(array('type'=>'textarea','name'=>'work_name','style'=>'width: 400px;','value'=>$cl_name)); -$form->getElement('work_name')->setEnabled(false); -$form->addInput(array('type'=>'textarea','name'=>'description','style'=>'width: 400px; height: 80px;','value'=>$cl_description)); -$form->getElement('description')->setEnabled(false); -$form->addInput(array('type'=>'textarea','name'=>'details','style'=>'width: 400px; height: 200px;','value'=>$cl_details)); -$form->getElement('details')->setEnabled(false); -$form->addInput(array('type'=>'text','name'=>'budget','value'=>$cl_budget)); -$form->getElement('budget')->setEnabled(false); - -$smarty->assign('offer', $offer); -$smarty->assign('forms', array($form->getName()=>$form->toArray())); -$smarty->assign('title', $i18n->get('title.work')); -$smarty->assign('content_page_name', 'work/work_view_own.tpl'); -$smarty->display('work/index.tpl');