From 4ce11322cefcf172b8103ae23b3c1c9684dd91ee Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Tue, 15 Mar 2022 15:32:34 +0000 Subject: [PATCH 001/168] Starting work on 2fa. --- WEB-INF/resources/ca.lang.php | 1 + WEB-INF/resources/cs.lang.php | 1 + WEB-INF/resources/da.lang.php | 1 + WEB-INF/resources/de.lang.php | 1 + WEB-INF/resources/en.lang.php | 1 + WEB-INF/resources/es.lang.php | 1 + WEB-INF/resources/et.lang.php | 1 + WEB-INF/resources/fa.lang.php | 1 + WEB-INF/resources/fi.lang.php | 1 + WEB-INF/resources/fr.lang.php | 1 + WEB-INF/resources/gr.lang.php | 1 + WEB-INF/resources/he.lang.php | 1 + WEB-INF/resources/hu.lang.php | 1 + WEB-INF/resources/it.lang.php | 1 + WEB-INF/resources/ja.lang.php | 1 + WEB-INF/resources/ko.lang.php | 1 + WEB-INF/resources/nl.lang.php | 2 ++ WEB-INF/resources/no.lang.php | 1 + WEB-INF/resources/pl.lang.php | 1 + WEB-INF/resources/pt-br.lang.php | 1 + WEB-INF/resources/pt.lang.php | 1 + WEB-INF/resources/ro.lang.php | 1 + WEB-INF/resources/ru.lang.php | 1 + WEB-INF/resources/sk.lang.php | 1 + WEB-INF/resources/sl.lang.php | 1 + WEB-INF/resources/sr.lang.php | 1 + WEB-INF/resources/sv.lang.php | 1 + WEB-INF/resources/tr.lang.php | 1 + WEB-INF/resources/zh-cn.lang.php | 1 + WEB-INF/resources/zh-tw.lang.php | 1 + initialize.php | 2 +- 31 files changed, 32 insertions(+), 1 deletion(-) diff --git a/WEB-INF/resources/ca.lang.php b/WEB-INF/resources/ca.lang.php index f1a31d7d..e862f97f 100644 --- a/WEB-INF/resources/ca.lang.php +++ b/WEB-INF/resources/ca.lang.php @@ -586,6 +586,7 @@ // 'form.group_edit.confirm_save' => 'Confirm saving', // 'form.group_edit.allow_ip' => 'Allow IP', // 'form.group_edit.password_complexity' => 'Password complexity', +// 'form.group_edit.2fa' => 'Two factor authentication', // 'form.group_edit.advanced_settings' => 'Advanced settings', // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php diff --git a/WEB-INF/resources/cs.lang.php b/WEB-INF/resources/cs.lang.php index c54d09d6..d73ef53d 100644 --- a/WEB-INF/resources/cs.lang.php +++ b/WEB-INF/resources/cs.lang.php @@ -598,6 +598,7 @@ // 'form.group_edit.confirm_save' => 'Confirm saving', // 'form.group_edit.allow_ip' => 'Allow IP', // 'form.group_edit.password_complexity' => 'Password complexity', +// 'form.group_edit.2fa' => 'Two factor authentication', // 'form.group_edit.advanced_settings' => 'Advanced settings', // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php diff --git a/WEB-INF/resources/da.lang.php b/WEB-INF/resources/da.lang.php index 84ec205c..4c1f1b24 100644 --- a/WEB-INF/resources/da.lang.php +++ b/WEB-INF/resources/da.lang.php @@ -565,6 +565,7 @@ // 'form.group_edit.confirm_save' => 'Confirm saving', // 'form.group_edit.allow_ip' => 'Allow IP', // 'form.group_edit.password_complexity' => 'Password complexity', +// 'form.group_edit.2fa' => 'Two factor authentication', // 'form.group_edit.advanced_settings' => 'Advanced settings', // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php diff --git a/WEB-INF/resources/de.lang.php b/WEB-INF/resources/de.lang.php index 6a79d397..cbe69a21 100644 --- a/WEB-INF/resources/de.lang.php +++ b/WEB-INF/resources/de.lang.php @@ -540,6 +540,7 @@ 'form.group_edit.allow_ip' => 'Erlaube IP', // TODO: translate the following. // 'form.group_edit.password_complexity' => 'Password complexity', +// 'form.group_edit.2fa' => 'Two factor authentication', // 'form.group_edit.advanced_settings' => 'Advanced settings', // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php diff --git a/WEB-INF/resources/en.lang.php b/WEB-INF/resources/en.lang.php index 3a4b699c..4c2b74b4 100644 --- a/WEB-INF/resources/en.lang.php +++ b/WEB-INF/resources/en.lang.php @@ -522,6 +522,7 @@ 'form.group_edit.confirm_save' => 'Confirm saving', 'form.group_edit.allow_ip' => 'Allow IP', 'form.group_edit.password_complexity' => 'Password complexity', +'form.group_edit.2fa' => 'Two factor authentication', 'form.group_edit.advanced_settings' => 'Advanced settings', // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php diff --git a/WEB-INF/resources/es.lang.php b/WEB-INF/resources/es.lang.php index d622d857..80683acc 100644 --- a/WEB-INF/resources/es.lang.php +++ b/WEB-INF/resources/es.lang.php @@ -600,6 +600,7 @@ // 'form.group_edit.confirm_save' => 'Confirm saving', // 'form.group_edit.allow_ip' => 'Allow IP', // 'form.group_edit.password_complexity' => 'Password complexity', +// 'form.group_edit.2fa' => 'Two factor authentication', // 'form.group_edit.advanced_settings' => 'Advanced settings', // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php diff --git a/WEB-INF/resources/et.lang.php b/WEB-INF/resources/et.lang.php index 6a6d5d02..9c2a1c61 100644 --- a/WEB-INF/resources/et.lang.php +++ b/WEB-INF/resources/et.lang.php @@ -574,6 +574,7 @@ 'form.group_edit.allow_ip' => 'Luba IP', // TODO: translate the following. // 'form.group_edit.password_complexity' => 'Password complexity', +// 'form.group_edit.2fa' => 'Two factor authentication', // 'form.group_edit.advanced_settings' => 'Advanced settings', // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php diff --git a/WEB-INF/resources/fa.lang.php b/WEB-INF/resources/fa.lang.php index bde6ab03..1deb7547 100644 --- a/WEB-INF/resources/fa.lang.php +++ b/WEB-INF/resources/fa.lang.php @@ -587,6 +587,7 @@ // 'form.group_edit.confirm_save' => 'Confirm saving', // 'form.group_edit.allow_ip' => 'Allow IP', // 'form.group_edit.password_complexity' => 'Password complexity', +// 'form.group_edit.2fa' => 'Two factor authentication', // 'form.group_edit.advanced_settings' => 'Advanced settings', // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php diff --git a/WEB-INF/resources/fi.lang.php b/WEB-INF/resources/fi.lang.php index db49d601..82c84df8 100644 --- a/WEB-INF/resources/fi.lang.php +++ b/WEB-INF/resources/fi.lang.php @@ -571,6 +571,7 @@ // 'form.group_edit.confirm_save' => 'Confirm saving', // 'form.group_edit.allow_ip' => 'Allow IP', // 'form.group_edit.password_complexity' => 'Password complexity', +// 'form.group_edit.2fa' => 'Two factor authentication', // 'form.group_edit.advanced_settings' => 'Advanced settings', // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php diff --git a/WEB-INF/resources/fr.lang.php b/WEB-INF/resources/fr.lang.php index 73d9ae28..b39b0aad 100644 --- a/WEB-INF/resources/fr.lang.php +++ b/WEB-INF/resources/fr.lang.php @@ -561,6 +561,7 @@ // 'form.group_edit.confirm_save' => 'Confirm saving', // 'form.group_edit.allow_ip' => 'Allow IP', // 'form.group_edit.password_complexity' => 'Password complexity', +// 'form.group_edit.2fa' => 'Two factor authentication', // 'form.group_edit.advanced_settings' => 'Advanced settings', // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php diff --git a/WEB-INF/resources/gr.lang.php b/WEB-INF/resources/gr.lang.php index d8cf3d68..d30201fc 100644 --- a/WEB-INF/resources/gr.lang.php +++ b/WEB-INF/resources/gr.lang.php @@ -552,6 +552,7 @@ // 'form.group_edit.confirm_save' => 'Confirm saving', // 'form.group_edit.allow_ip' => 'Allow IP', // 'form.group_edit.password_complexity' => 'Password complexity', +// 'form.group_edit.2fa' => 'Two factor authentication', // 'form.group_edit.advanced_settings' => 'Advanced settings', // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php diff --git a/WEB-INF/resources/he.lang.php b/WEB-INF/resources/he.lang.php index 0a607469..19ce3768 100644 --- a/WEB-INF/resources/he.lang.php +++ b/WEB-INF/resources/he.lang.php @@ -591,6 +591,7 @@ // 'form.group_edit.confirm_save' => 'Confirm saving', // 'form.group_edit.allow_ip' => 'Allow IP', // 'form.group_edit.password_complexity' => 'Password complexity', +// 'form.group_edit.2fa' => 'Two factor authentication', // 'form.group_edit.advanced_settings' => 'Advanced settings', // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php diff --git a/WEB-INF/resources/hu.lang.php b/WEB-INF/resources/hu.lang.php index 172e49fa..83b82426 100644 --- a/WEB-INF/resources/hu.lang.php +++ b/WEB-INF/resources/hu.lang.php @@ -590,6 +590,7 @@ // 'form.group_edit.confirm_save' => 'Confirm saving', // 'form.group_edit.allow_ip' => 'Allow IP', // 'form.group_edit.password_complexity' => 'Password complexity', +// 'form.group_edit.2fa' => 'Two factor authentication', // 'form.group_edit.advanced_settings' => 'Advanced settings', // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php diff --git a/WEB-INF/resources/it.lang.php b/WEB-INF/resources/it.lang.php index 8dc20586..acf7cc5b 100644 --- a/WEB-INF/resources/it.lang.php +++ b/WEB-INF/resources/it.lang.php @@ -567,6 +567,7 @@ // 'form.group_edit.confirm_save' => 'Confirm saving', // 'form.group_edit.allow_ip' => 'Allow IP', // 'form.group_edit.password_complexity' => 'Password complexity', +// 'form.group_edit.2fa' => 'Two factor authentication', // 'form.group_edit.advanced_settings' => 'Advanced settings', // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php diff --git a/WEB-INF/resources/ja.lang.php b/WEB-INF/resources/ja.lang.php index cc9edc0d..3f68a935 100644 --- a/WEB-INF/resources/ja.lang.php +++ b/WEB-INF/resources/ja.lang.php @@ -593,6 +593,7 @@ // 'form.group_edit.confirm_save' => 'Confirm saving', // 'form.group_edit.allow_ip' => 'Allow IP', // 'form.group_edit.password_complexity' => 'Password complexity', +// 'form.group_edit.2fa' => 'Two factor authentication', // 'form.group_edit.advanced_settings' => 'Advanced settings', // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php diff --git a/WEB-INF/resources/ko.lang.php b/WEB-INF/resources/ko.lang.php index 18b6ec6c..60b307f7 100644 --- a/WEB-INF/resources/ko.lang.php +++ b/WEB-INF/resources/ko.lang.php @@ -591,6 +591,7 @@ // 'form.group_edit.confirm_save' => 'Confirm saving', // 'form.group_edit.allow_ip' => 'Allow IP', // 'form.group_edit.password_complexity' => 'Password complexity', +// 'form.group_edit.2fa' => 'Two factor authentication', // 'form.group_edit.advanced_settings' => 'Advanced settings', // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php diff --git a/WEB-INF/resources/nl.lang.php b/WEB-INF/resources/nl.lang.php index 5d866e61..f2b0da6f 100644 --- a/WEB-INF/resources/nl.lang.php +++ b/WEB-INF/resources/nl.lang.php @@ -508,6 +508,8 @@ 'form.group_edit.confirm_save' => 'Bevestigen dat je wilt opslaan', 'form.group_edit.allow_ip' => 'Toegestane IP adressen', 'form.group_edit.password_complexity' => 'Wachtwoord complexiteit', +// TODO: translate the following. +// form.group_edit.2fa' => 'Two factor authentication', 'form.group_edit.advanced_settings' => 'Geavanceerde instellingen', // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php diff --git a/WEB-INF/resources/no.lang.php b/WEB-INF/resources/no.lang.php index 1e38bd0c..281f1768 100644 --- a/WEB-INF/resources/no.lang.php +++ b/WEB-INF/resources/no.lang.php @@ -594,6 +594,7 @@ // 'form.group_edit.confirm_save' => 'Confirm saving', // 'form.group_edit.allow_ip' => 'Allow IP', // 'form.group_edit.password_complexity' => 'Password complexity', +// 'form.group_edit.2fa' => 'Two factor authentication', // 'form.group_edit.advanced_settings' => 'Advanced settings', // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php diff --git a/WEB-INF/resources/pl.lang.php b/WEB-INF/resources/pl.lang.php index 2fc57348..b6610eef 100644 --- a/WEB-INF/resources/pl.lang.php +++ b/WEB-INF/resources/pl.lang.php @@ -573,6 +573,7 @@ // 'form.group_edit.confirm_save' => 'Confirm saving', // 'form.group_edit.allow_ip' => 'Allow IP', // 'form.group_edit.password_complexity' => 'Password complexity', +// 'form.group_edit.2fa' => 'Two factor authentication', // 'form.group_edit.advanced_settings' => 'Advanced settings', // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php diff --git a/WEB-INF/resources/pt-br.lang.php b/WEB-INF/resources/pt-br.lang.php index 7f5601ef..334db5ca 100644 --- a/WEB-INF/resources/pt-br.lang.php +++ b/WEB-INF/resources/pt-br.lang.php @@ -508,6 +508,7 @@ 'form.group_edit.allow_ip' => 'Permitir IP', // TODO: translate the following. // 'form.group_edit.password_complexity' => 'Password complexity', +// 'form.group_edit.2fa' => 'Two factor authentication', 'form.group_edit.advanced_settings' => 'Configurações avançadas', // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php. diff --git a/WEB-INF/resources/pt.lang.php b/WEB-INF/resources/pt.lang.php index 2806b843..d3aac4e3 100644 --- a/WEB-INF/resources/pt.lang.php +++ b/WEB-INF/resources/pt.lang.php @@ -572,6 +572,7 @@ // 'form.group_edit.confirm_save' => 'Confirm saving', // 'form.group_edit.allow_ip' => 'Allow IP', // 'form.group_edit.password_complexity' => 'Password complexity', +// 'form.group_edit.2fa' => 'Two factor authentication', // 'form.group_edit.advanced_settings' => 'Advanced settings', // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php diff --git a/WEB-INF/resources/ro.lang.php b/WEB-INF/resources/ro.lang.php index 8619ea27..170642ef 100644 --- a/WEB-INF/resources/ro.lang.php +++ b/WEB-INF/resources/ro.lang.php @@ -594,6 +594,7 @@ // 'form.group_edit.confirm_save' => 'Confirm saving', // 'form.group_edit.allow_ip' => 'Allow IP', // 'form.group_edit.password_complexity' => 'Password complexity', +// 'form.group_edit.2fa' => 'Two factor authentication', // 'form.group_edit.advanced_settings' => 'Advanced settings', // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php diff --git a/WEB-INF/resources/ru.lang.php b/WEB-INF/resources/ru.lang.php index 41c95c84..e186fbef 100644 --- a/WEB-INF/resources/ru.lang.php +++ b/WEB-INF/resources/ru.lang.php @@ -509,6 +509,7 @@ 'form.group_edit.confirm_save' => 'Предупреждать при сохранении', 'form.group_edit.allow_ip' => 'Разрешить доступ с IP', 'form.group_edit.password_complexity' => 'Сложность пароля', +'form.group_edit.2fa' => 'Двухфакторная аутентификация', 'form.group_edit.advanced_settings' => 'Продвинутые настройки', // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php diff --git a/WEB-INF/resources/sk.lang.php b/WEB-INF/resources/sk.lang.php index e45b0651..7fcc9940 100644 --- a/WEB-INF/resources/sk.lang.php +++ b/WEB-INF/resources/sk.lang.php @@ -577,6 +577,7 @@ // 'form.group_edit.confirm_save' => 'Confirm saving', // 'form.group_edit.allow_ip' => 'Allow IP', // 'form.group_edit.password_complexity' => 'Password complexity', +// 'form.group_edit.2fa' => 'Two factor authentication', // 'form.group_edit.advanced_settings' => 'Advanced settings', // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php diff --git a/WEB-INF/resources/sl.lang.php b/WEB-INF/resources/sl.lang.php index 4e519bcf..88aee694 100644 --- a/WEB-INF/resources/sl.lang.php +++ b/WEB-INF/resources/sl.lang.php @@ -567,6 +567,7 @@ // 'form.group_edit.confirm_save' => 'Confirm saving', // 'form.group_edit.allow_ip' => 'Allow IP', // 'form.group_edit.password_complexity' => 'Password complexity', +// 'form.group_edit.2fa' => 'Two factor authentication', // 'form.group_edit.advanced_settings' => 'Advanced settings', // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php diff --git a/WEB-INF/resources/sr.lang.php b/WEB-INF/resources/sr.lang.php index 7aa5f100..2aead0d8 100644 --- a/WEB-INF/resources/sr.lang.php +++ b/WEB-INF/resources/sr.lang.php @@ -573,6 +573,7 @@ // 'form.group_edit.confirm_save' => 'Confirm saving', // 'form.group_edit.allow_ip' => 'Allow IP', // 'form.group_edit.password_complexity' => 'Password complexity', +// 'form.group_edit.2fa' => 'Two factor authentication', // 'form.group_edit.advanced_settings' => 'Advanced settings', // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php diff --git a/WEB-INF/resources/sv.lang.php b/WEB-INF/resources/sv.lang.php index f641ef69..87b062f5 100644 --- a/WEB-INF/resources/sv.lang.php +++ b/WEB-INF/resources/sv.lang.php @@ -571,6 +571,7 @@ // 'form.group_edit.confirm_save' => 'Confirm saving', // 'form.group_edit.allow_ip' => 'Allow IP', // 'form.group_edit.password_complexity' => 'Password complexity', +// 'form.group_edit.2fa' => 'Two factor authentication', // 'form.group_edit.advanced_settings' => 'Advanced settings', // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php diff --git a/WEB-INF/resources/tr.lang.php b/WEB-INF/resources/tr.lang.php index 9691b950..cf2694c4 100644 --- a/WEB-INF/resources/tr.lang.php +++ b/WEB-INF/resources/tr.lang.php @@ -607,6 +607,7 @@ // 'form.group_edit.confirm_save' => 'Confirm saving', // 'form.group_edit.allow_ip' => 'Allow IP', // 'form.group_edit.password_complexity' => 'Password complexity', +// 'form.group_edit.2fa' => 'Two factor authentication', // 'form.group_edit.advanced_settings' => 'Advanced settings', // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php diff --git a/WEB-INF/resources/zh-cn.lang.php b/WEB-INF/resources/zh-cn.lang.php index afe7588a..5686e268 100644 --- a/WEB-INF/resources/zh-cn.lang.php +++ b/WEB-INF/resources/zh-cn.lang.php @@ -582,6 +582,7 @@ // 'form.group_edit.confirm_save' => 'Confirm saving', // 'form.group_edit.allow_ip' => 'Allow IP', // 'form.group_edit.password_complexity' => 'Password complexity', +// 'form.group_edit.2fa' => 'Two factor authentication', // 'form.group_edit.advanced_settings' => 'Advanced settings', // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php diff --git a/WEB-INF/resources/zh-tw.lang.php b/WEB-INF/resources/zh-tw.lang.php index 29431289..62a9e9ce 100644 --- a/WEB-INF/resources/zh-tw.lang.php +++ b/WEB-INF/resources/zh-tw.lang.php @@ -585,6 +585,7 @@ // 'form.group_edit.confirm_save' => 'Confirm saving', // 'form.group_edit.allow_ip' => 'Allow IP', // 'form.group_edit.password_complexity' => 'Password complexity', +// 'form.group_edit.2fa' => 'Two factor authentication', // 'form.group_edit.advanced_settings' => 'Advanced settings', // Deleting Group form. See example at https://timetracker.anuko.com/delete_group.php diff --git a/initialize.php b/initialize.php index 23e3c733..faf98ef3 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.1.5657"); +define("APP_VERSION", "1.21.1.5658"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From 992a7adb6f2001a5639605f8bb1e64dc68d0edd5 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Tue, 15 Mar 2022 16:06:13 +0000 Subject: [PATCH 002/168] Refactoring in translation files. --- WEB-INF/resources/ca.lang.php | 6 ++++++ WEB-INF/resources/cs.lang.php | 6 ++++++ WEB-INF/resources/da.lang.php | 6 ++++++ WEB-INF/resources/de.lang.php | 6 ++++++ WEB-INF/resources/en.lang.php | 5 +++++ WEB-INF/resources/es.lang.php | 6 ++++++ WEB-INF/resources/et.lang.php | 6 ++++++ WEB-INF/resources/fa.lang.php | 6 ++++++ WEB-INF/resources/fi.lang.php | 6 ++++++ WEB-INF/resources/fr.lang.php | 6 ++++++ WEB-INF/resources/gr.lang.php | 6 ++++++ WEB-INF/resources/he.lang.php | 6 ++++++ WEB-INF/resources/hu.lang.php | 6 ++++++ WEB-INF/resources/it.lang.php | 6 ++++++ WEB-INF/resources/ja.lang.php | 6 ++++++ WEB-INF/resources/ko.lang.php | 6 ++++++ WEB-INF/resources/nl.lang.php | 6 ++++++ WEB-INF/resources/no.lang.php | 6 ++++++ WEB-INF/resources/pl.lang.php | 6 ++++++ WEB-INF/resources/pt-br.lang.php | 6 ++++++ WEB-INF/resources/pt.lang.php | 6 ++++++ WEB-INF/resources/ro.lang.php | 6 ++++++ WEB-INF/resources/ru.lang.php | 5 +++++ WEB-INF/resources/sk.lang.php | 6 ++++++ WEB-INF/resources/sl.lang.php | 6 ++++++ WEB-INF/resources/sr.lang.php | 6 ++++++ WEB-INF/resources/sv.lang.php | 6 ++++++ WEB-INF/resources/tr.lang.php | 6 ++++++ WEB-INF/resources/zh-cn.lang.php | 6 ++++++ WEB-INF/resources/zh-tw.lang.php | 6 ++++++ initialize.php | 2 +- 31 files changed, 179 insertions(+), 1 deletion(-) diff --git a/WEB-INF/resources/ca.lang.php b/WEB-INF/resources/ca.lang.php index e862f97f..dbbeb403 100644 --- a/WEB-INF/resources/ca.lang.php +++ b/WEB-INF/resources/ca.lang.php @@ -589,6 +589,12 @@ // 'form.group_edit.2fa' => 'Two factor authentication', // '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?', diff --git a/WEB-INF/resources/cs.lang.php b/WEB-INF/resources/cs.lang.php index d73ef53d..8b5e5006 100644 --- a/WEB-INF/resources/cs.lang.php +++ b/WEB-INF/resources/cs.lang.php @@ -601,6 +601,12 @@ // 'form.group_edit.2fa' => 'Two factor authentication', // '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?', diff --git a/WEB-INF/resources/da.lang.php b/WEB-INF/resources/da.lang.php index 4c1f1b24..6860b8e3 100644 --- a/WEB-INF/resources/da.lang.php +++ b/WEB-INF/resources/da.lang.php @@ -568,6 +568,12 @@ // 'form.group_edit.2fa' => 'Two factor authentication', // '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?', diff --git a/WEB-INF/resources/de.lang.php b/WEB-INF/resources/de.lang.php index cbe69a21..10431c2a 100644 --- a/WEB-INF/resources/de.lang.php +++ b/WEB-INF/resources/de.lang.php @@ -543,6 +543,12 @@ // 'form.group_edit.2fa' => 'Two factor authentication', // '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' => 'Erlaube 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' => 'Sind Sie sicher, dass Sie die gesamte Gruppe löschen möchten?', diff --git a/WEB-INF/resources/en.lang.php b/WEB-INF/resources/en.lang.php index 4c2b74b4..7505043c 100644 --- a/WEB-INF/resources/en.lang.php +++ b/WEB-INF/resources/en.lang.php @@ -525,6 +525,11 @@ 'form.group_edit.2fa' => 'Two factor authentication', '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?', diff --git a/WEB-INF/resources/es.lang.php b/WEB-INF/resources/es.lang.php index 80683acc..0e94644a 100644 --- a/WEB-INF/resources/es.lang.php +++ b/WEB-INF/resources/es.lang.php @@ -603,6 +603,12 @@ // 'form.group_edit.2fa' => 'Two factor authentication', // '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?', diff --git a/WEB-INF/resources/et.lang.php b/WEB-INF/resources/et.lang.php index 9c2a1c61..4177bfff 100644 --- a/WEB-INF/resources/et.lang.php +++ b/WEB-INF/resources/et.lang.php @@ -577,6 +577,12 @@ // 'form.group_edit.2fa' => 'Two factor authentication', // '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?', diff --git a/WEB-INF/resources/fa.lang.php b/WEB-INF/resources/fa.lang.php index 1deb7547..f37c789c 100644 --- a/WEB-INF/resources/fa.lang.php +++ b/WEB-INF/resources/fa.lang.php @@ -590,6 +590,12 @@ // 'form.group_edit.2fa' => 'Two factor authentication', // '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?', diff --git a/WEB-INF/resources/fi.lang.php b/WEB-INF/resources/fi.lang.php index 82c84df8..e124c61a 100644 --- a/WEB-INF/resources/fi.lang.php +++ b/WEB-INF/resources/fi.lang.php @@ -574,6 +574,12 @@ // 'form.group_edit.2fa' => 'Two factor authentication', // '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?', diff --git a/WEB-INF/resources/fr.lang.php b/WEB-INF/resources/fr.lang.php index b39b0aad..503327a9 100644 --- a/WEB-INF/resources/fr.lang.php +++ b/WEB-INF/resources/fr.lang.php @@ -564,6 +564,12 @@ // 'form.group_edit.2fa' => 'Two factor authentication', // '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?', diff --git a/WEB-INF/resources/gr.lang.php b/WEB-INF/resources/gr.lang.php index d30201fc..310d6bd0 100644 --- a/WEB-INF/resources/gr.lang.php +++ b/WEB-INF/resources/gr.lang.php @@ -555,6 +555,12 @@ // 'form.group_edit.2fa' => 'Two factor authentication', // '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?', diff --git a/WEB-INF/resources/he.lang.php b/WEB-INF/resources/he.lang.php index 19ce3768..95175c93 100644 --- a/WEB-INF/resources/he.lang.php +++ b/WEB-INF/resources/he.lang.php @@ -594,6 +594,12 @@ // 'form.group_edit.2fa' => 'Two factor authentication', // '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?', diff --git a/WEB-INF/resources/hu.lang.php b/WEB-INF/resources/hu.lang.php index 83b82426..c4745c0a 100644 --- a/WEB-INF/resources/hu.lang.php +++ b/WEB-INF/resources/hu.lang.php @@ -593,6 +593,12 @@ // 'form.group_edit.2fa' => 'Two factor authentication', // '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?', diff --git a/WEB-INF/resources/it.lang.php b/WEB-INF/resources/it.lang.php index acf7cc5b..5676164e 100644 --- a/WEB-INF/resources/it.lang.php +++ b/WEB-INF/resources/it.lang.php @@ -570,6 +570,12 @@ // 'form.group_edit.2fa' => 'Two factor authentication', // '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?', diff --git a/WEB-INF/resources/ja.lang.php b/WEB-INF/resources/ja.lang.php index 3f68a935..f8f80c4f 100644 --- a/WEB-INF/resources/ja.lang.php +++ b/WEB-INF/resources/ja.lang.php @@ -596,6 +596,12 @@ // 'form.group_edit.2fa' => 'Two factor authentication', // '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?', diff --git a/WEB-INF/resources/ko.lang.php b/WEB-INF/resources/ko.lang.php index 60b307f7..f578659b 100644 --- a/WEB-INF/resources/ko.lang.php +++ b/WEB-INF/resources/ko.lang.php @@ -594,6 +594,12 @@ // 'form.group_edit.2fa' => 'Two factor authentication', // '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?', diff --git a/WEB-INF/resources/nl.lang.php b/WEB-INF/resources/nl.lang.php index f2b0da6f..a3e415b1 100644 --- a/WEB-INF/resources/nl.lang.php +++ b/WEB-INF/resources/nl.lang.php @@ -512,6 +512,12 @@ // form.group_edit.2fa' => 'Two factor authentication', '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', +// TODO: Translate the following. +// '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' => 'Bent u er zeker van dat u de hele groep wilt verwijderen?', diff --git a/WEB-INF/resources/no.lang.php b/WEB-INF/resources/no.lang.php index 281f1768..82fca5f1 100644 --- a/WEB-INF/resources/no.lang.php +++ b/WEB-INF/resources/no.lang.php @@ -597,6 +597,12 @@ // 'form.group_edit.2fa' => 'Two factor authentication', // '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?', diff --git a/WEB-INF/resources/pl.lang.php b/WEB-INF/resources/pl.lang.php index b6610eef..d4a5be37 100644 --- a/WEB-INF/resources/pl.lang.php +++ b/WEB-INF/resources/pl.lang.php @@ -576,6 +576,12 @@ // 'form.group_edit.2fa' => 'Two factor authentication', // '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?', diff --git a/WEB-INF/resources/pt-br.lang.php b/WEB-INF/resources/pt-br.lang.php index 334db5ca..195cbc85 100644 --- a/WEB-INF/resources/pt-br.lang.php +++ b/WEB-INF/resources/pt-br.lang.php @@ -511,6 +511,12 @@ // 'form.group_edit.2fa' => 'Two factor authentication', '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?', diff --git a/WEB-INF/resources/pt.lang.php b/WEB-INF/resources/pt.lang.php index d3aac4e3..fa8c8384 100644 --- a/WEB-INF/resources/pt.lang.php +++ b/WEB-INF/resources/pt.lang.php @@ -575,6 +575,12 @@ // 'form.group_edit.2fa' => 'Two factor authentication', // '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?', diff --git a/WEB-INF/resources/ro.lang.php b/WEB-INF/resources/ro.lang.php index 170642ef..eab876d0 100644 --- a/WEB-INF/resources/ro.lang.php +++ b/WEB-INF/resources/ro.lang.php @@ -597,6 +597,12 @@ // 'form.group_edit.2fa' => 'Two factor authentication', // '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?', diff --git a/WEB-INF/resources/ru.lang.php b/WEB-INF/resources/ru.lang.php index e186fbef..30737a76 100644 --- a/WEB-INF/resources/ru.lang.php +++ b/WEB-INF/resources/ru.lang.php @@ -512,6 +512,11 @@ 'form.group_edit.2fa' => 'Двухфакторная аутентификация', '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' => 'Вы уверены, что хотите удалить всю группу?', diff --git a/WEB-INF/resources/sk.lang.php b/WEB-INF/resources/sk.lang.php index 7fcc9940..ae1a0376 100644 --- a/WEB-INF/resources/sk.lang.php +++ b/WEB-INF/resources/sk.lang.php @@ -580,6 +580,12 @@ // 'form.group_edit.2fa' => 'Two factor authentication', // '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?', diff --git a/WEB-INF/resources/sl.lang.php b/WEB-INF/resources/sl.lang.php index 88aee694..d6499cb1 100644 --- a/WEB-INF/resources/sl.lang.php +++ b/WEB-INF/resources/sl.lang.php @@ -570,6 +570,12 @@ // 'form.group_edit.2fa' => 'Two factor authentication', // '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?', diff --git a/WEB-INF/resources/sr.lang.php b/WEB-INF/resources/sr.lang.php index 2aead0d8..d05a97ee 100644 --- a/WEB-INF/resources/sr.lang.php +++ b/WEB-INF/resources/sr.lang.php @@ -576,6 +576,12 @@ // 'form.group_edit.2fa' => 'Two factor authentication', // '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?', diff --git a/WEB-INF/resources/sv.lang.php b/WEB-INF/resources/sv.lang.php index 87b062f5..80f6657b 100644 --- a/WEB-INF/resources/sv.lang.php +++ b/WEB-INF/resources/sv.lang.php @@ -574,6 +574,12 @@ // 'form.group_edit.2fa' => 'Two factor authentication', // '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?', diff --git a/WEB-INF/resources/tr.lang.php b/WEB-INF/resources/tr.lang.php index cf2694c4..ca1dacae 100644 --- a/WEB-INF/resources/tr.lang.php +++ b/WEB-INF/resources/tr.lang.php @@ -610,6 +610,12 @@ // 'form.group_edit.2fa' => 'Two factor authentication', // '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?', diff --git a/WEB-INF/resources/zh-cn.lang.php b/WEB-INF/resources/zh-cn.lang.php index 5686e268..068ed52a 100644 --- a/WEB-INF/resources/zh-cn.lang.php +++ b/WEB-INF/resources/zh-cn.lang.php @@ -585,6 +585,12 @@ // 'form.group_edit.2fa' => 'Two factor authentication', // '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?', diff --git a/WEB-INF/resources/zh-tw.lang.php b/WEB-INF/resources/zh-tw.lang.php index 62a9e9ce..34e18441 100644 --- a/WEB-INF/resources/zh-tw.lang.php +++ b/WEB-INF/resources/zh-tw.lang.php @@ -588,6 +588,12 @@ // 'form.group_edit.2fa' => 'Two factor authentication', // '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?', diff --git a/initialize.php b/initialize.php index faf98ef3..3389203c 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.1.5658"); +define("APP_VERSION", "1.21.1.5659"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From bc1c36f9fa327aa478b71ecb7beb1a34f3ba405a Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Tue, 15 Mar 2022 16:29:36 +0000 Subject: [PATCH 003/168] Refactoring of translation files. --- WEB-INF/resources/ca.lang.php | 3 --- WEB-INF/resources/cs.lang.php | 3 --- WEB-INF/resources/da.lang.php | 3 --- WEB-INF/resources/de.lang.php | 3 --- WEB-INF/resources/en.lang.php | 2 -- WEB-INF/resources/es.lang.php | 3 --- WEB-INF/resources/et.lang.php | 3 --- WEB-INF/resources/fa.lang.php | 3 --- WEB-INF/resources/fi.lang.php | 3 --- WEB-INF/resources/fr.lang.php | 3 --- WEB-INF/resources/gr.lang.php | 3 --- WEB-INF/resources/he.lang.php | 3 --- WEB-INF/resources/hu.lang.php | 3 --- WEB-INF/resources/it.lang.php | 3 --- WEB-INF/resources/ja.lang.php | 3 --- WEB-INF/resources/ko.lang.php | 3 --- WEB-INF/resources/nl.lang.php | 4 ---- WEB-INF/resources/no.lang.php | 3 --- WEB-INF/resources/pl.lang.php | 3 --- WEB-INF/resources/pt-br.lang.php | 4 ---- WEB-INF/resources/pt.lang.php | 3 --- WEB-INF/resources/ro.lang.php | 3 --- WEB-INF/resources/ru.lang.php | 3 --- WEB-INF/resources/sk.lang.php | 3 --- WEB-INF/resources/sl.lang.php | 3 --- WEB-INF/resources/sr.lang.php | 3 --- WEB-INF/resources/sv.lang.php | 3 --- WEB-INF/resources/tr.lang.php | 3 --- WEB-INF/resources/zh-cn.lang.php | 3 --- WEB-INF/resources/zh-tw.lang.php | 3 --- WEB-INF/templates/group_advanced_edit.tpl | 8 ++++---- group_advanced_edit.php | 4 ++-- initialize.php | 2 +- 33 files changed, 7 insertions(+), 98 deletions(-) diff --git a/WEB-INF/resources/ca.lang.php b/WEB-INF/resources/ca.lang.php index dbbeb403..e1f9a191 100644 --- a/WEB-INF/resources/ca.lang.php +++ b/WEB-INF/resources/ca.lang.php @@ -584,9 +584,6 @@ // '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.2fa' => 'Two factor authentication', // 'form.group_edit.advanced_settings' => 'Advanced settings', // Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. diff --git a/WEB-INF/resources/cs.lang.php b/WEB-INF/resources/cs.lang.php index 8b5e5006..562253c4 100644 --- a/WEB-INF/resources/cs.lang.php +++ b/WEB-INF/resources/cs.lang.php @@ -596,9 +596,6 @@ // '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.2fa' => 'Two factor authentication', // 'form.group_edit.advanced_settings' => 'Advanced settings', // Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. diff --git a/WEB-INF/resources/da.lang.php b/WEB-INF/resources/da.lang.php index 6860b8e3..6070d5ab 100644 --- a/WEB-INF/resources/da.lang.php +++ b/WEB-INF/resources/da.lang.php @@ -563,9 +563,6 @@ '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.password_complexity' => 'Password complexity', -// 'form.group_edit.2fa' => 'Two factor authentication', // 'form.group_edit.advanced_settings' => 'Advanced settings', // Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. diff --git a/WEB-INF/resources/de.lang.php b/WEB-INF/resources/de.lang.php index 10431c2a..f627e77f 100644 --- a/WEB-INF/resources/de.lang.php +++ b/WEB-INF/resources/de.lang.php @@ -537,10 +537,7 @@ '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.password_complexity' => 'Password complexity', -// 'form.group_edit.2fa' => 'Two factor authentication', // 'form.group_edit.advanced_settings' => 'Advanced settings', // Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. diff --git a/WEB-INF/resources/en.lang.php b/WEB-INF/resources/en.lang.php index 7505043c..1cd30f07 100644 --- a/WEB-INF/resources/en.lang.php +++ b/WEB-INF/resources/en.lang.php @@ -520,9 +520,7 @@ '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.2fa' => 'Two factor authentication', 'form.group_edit.advanced_settings' => 'Advanced settings', // Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. diff --git a/WEB-INF/resources/es.lang.php b/WEB-INF/resources/es.lang.php index 0e94644a..9cc4aabd 100644 --- a/WEB-INF/resources/es.lang.php +++ b/WEB-INF/resources/es.lang.php @@ -598,9 +598,6 @@ // '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.2fa' => 'Two factor authentication', // 'form.group_edit.advanced_settings' => 'Advanced settings', // Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. diff --git a/WEB-INF/resources/et.lang.php b/WEB-INF/resources/et.lang.php index 4177bfff..9dd7d02d 100644 --- a/WEB-INF/resources/et.lang.php +++ b/WEB-INF/resources/et.lang.php @@ -571,10 +571,7 @@ '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.password_complexity' => 'Password complexity', -// 'form.group_edit.2fa' => 'Two factor authentication', // 'form.group_edit.advanced_settings' => 'Advanced settings', // Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. diff --git a/WEB-INF/resources/fa.lang.php b/WEB-INF/resources/fa.lang.php index f37c789c..a7560fc4 100644 --- a/WEB-INF/resources/fa.lang.php +++ b/WEB-INF/resources/fa.lang.php @@ -585,9 +585,6 @@ // '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.2fa' => 'Two factor authentication', // 'form.group_edit.advanced_settings' => 'Advanced settings', // Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. diff --git a/WEB-INF/resources/fi.lang.php b/WEB-INF/resources/fi.lang.php index e124c61a..1f483ef1 100644 --- a/WEB-INF/resources/fi.lang.php +++ b/WEB-INF/resources/fi.lang.php @@ -569,9 +569,6 @@ // '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.2fa' => 'Two factor authentication', // 'form.group_edit.advanced_settings' => 'Advanced settings', // Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. diff --git a/WEB-INF/resources/fr.lang.php b/WEB-INF/resources/fr.lang.php index 503327a9..22132849 100644 --- a/WEB-INF/resources/fr.lang.php +++ b/WEB-INF/resources/fr.lang.php @@ -559,9 +559,6 @@ // '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.2fa' => 'Two factor authentication', // 'form.group_edit.advanced_settings' => 'Advanced settings', // Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. diff --git a/WEB-INF/resources/gr.lang.php b/WEB-INF/resources/gr.lang.php index 310d6bd0..f2606c41 100644 --- a/WEB-INF/resources/gr.lang.php +++ b/WEB-INF/resources/gr.lang.php @@ -550,9 +550,6 @@ '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.password_complexity' => 'Password complexity', -// 'form.group_edit.2fa' => 'Two factor authentication', // 'form.group_edit.advanced_settings' => 'Advanced settings', // Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. diff --git a/WEB-INF/resources/he.lang.php b/WEB-INF/resources/he.lang.php index 95175c93..52892dbc 100644 --- a/WEB-INF/resources/he.lang.php +++ b/WEB-INF/resources/he.lang.php @@ -589,9 +589,6 @@ // '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.2fa' => 'Two factor authentication', // 'form.group_edit.advanced_settings' => 'Advanced settings', // Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. diff --git a/WEB-INF/resources/hu.lang.php b/WEB-INF/resources/hu.lang.php index c4745c0a..b880b22f 100644 --- a/WEB-INF/resources/hu.lang.php +++ b/WEB-INF/resources/hu.lang.php @@ -588,9 +588,6 @@ // '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.2fa' => 'Two factor authentication', // 'form.group_edit.advanced_settings' => 'Advanced settings', // Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. diff --git a/WEB-INF/resources/it.lang.php b/WEB-INF/resources/it.lang.php index 5676164e..40bca63e 100644 --- a/WEB-INF/resources/it.lang.php +++ b/WEB-INF/resources/it.lang.php @@ -565,9 +565,6 @@ '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.password_complexity' => 'Password complexity', -// 'form.group_edit.2fa' => 'Two factor authentication', // 'form.group_edit.advanced_settings' => 'Advanced settings', // Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. diff --git a/WEB-INF/resources/ja.lang.php b/WEB-INF/resources/ja.lang.php index f8f80c4f..518c4269 100644 --- a/WEB-INF/resources/ja.lang.php +++ b/WEB-INF/resources/ja.lang.php @@ -591,9 +591,6 @@ // '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.2fa' => 'Two factor authentication', // 'form.group_edit.advanced_settings' => 'Advanced settings', // Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. diff --git a/WEB-INF/resources/ko.lang.php b/WEB-INF/resources/ko.lang.php index f578659b..dde93402 100644 --- a/WEB-INF/resources/ko.lang.php +++ b/WEB-INF/resources/ko.lang.php @@ -589,9 +589,6 @@ // '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.2fa' => 'Two factor authentication', // 'form.group_edit.advanced_settings' => 'Advanced settings', // Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. diff --git a/WEB-INF/resources/nl.lang.php b/WEB-INF/resources/nl.lang.php index a3e415b1..bd1ad69c 100644 --- a/WEB-INF/resources/nl.lang.php +++ b/WEB-INF/resources/nl.lang.php @@ -506,10 +506,6 @@ '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.password_complexity' => 'Wachtwoord complexiteit', -// TODO: translate the following. -// form.group_edit.2fa' => 'Two factor authentication', 'form.group_edit.advanced_settings' => 'Geavanceerde instellingen', // Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. diff --git a/WEB-INF/resources/no.lang.php b/WEB-INF/resources/no.lang.php index 82fca5f1..c38c74dd 100644 --- a/WEB-INF/resources/no.lang.php +++ b/WEB-INF/resources/no.lang.php @@ -592,9 +592,6 @@ // '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.2fa' => 'Two factor authentication', // 'form.group_edit.advanced_settings' => 'Advanced settings', // Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. diff --git a/WEB-INF/resources/pl.lang.php b/WEB-INF/resources/pl.lang.php index d4a5be37..86556229 100644 --- a/WEB-INF/resources/pl.lang.php +++ b/WEB-INF/resources/pl.lang.php @@ -571,9 +571,6 @@ // '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.2fa' => 'Two factor authentication', // 'form.group_edit.advanced_settings' => 'Advanced settings', // Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. diff --git a/WEB-INF/resources/pt-br.lang.php b/WEB-INF/resources/pt-br.lang.php index 195cbc85..bbabafcb 100644 --- a/WEB-INF/resources/pt-br.lang.php +++ b/WEB-INF/resources/pt-br.lang.php @@ -505,10 +505,6 @@ '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', -// TODO: translate the following. -// 'form.group_edit.password_complexity' => 'Password complexity', -// 'form.group_edit.2fa' => 'Two factor authentication', 'form.group_edit.advanced_settings' => 'Configurações avançadas', // Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. diff --git a/WEB-INF/resources/pt.lang.php b/WEB-INF/resources/pt.lang.php index fa8c8384..41e06c0f 100644 --- a/WEB-INF/resources/pt.lang.php +++ b/WEB-INF/resources/pt.lang.php @@ -570,9 +570,6 @@ // '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.2fa' => 'Two factor authentication', // 'form.group_edit.advanced_settings' => 'Advanced settings', // Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. diff --git a/WEB-INF/resources/ro.lang.php b/WEB-INF/resources/ro.lang.php index eab876d0..7f3168d4 100644 --- a/WEB-INF/resources/ro.lang.php +++ b/WEB-INF/resources/ro.lang.php @@ -592,9 +592,6 @@ // '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.2fa' => 'Two factor authentication', // 'form.group_edit.advanced_settings' => 'Advanced settings', // Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. diff --git a/WEB-INF/resources/ru.lang.php b/WEB-INF/resources/ru.lang.php index 30737a76..35966616 100644 --- a/WEB-INF/resources/ru.lang.php +++ b/WEB-INF/resources/ru.lang.php @@ -507,9 +507,6 @@ 'form.group_edit.future_entries' => 'Будущие записи', 'form.group_edit.uncompleted_indicators' => 'Индикаторы незавершения', 'form.group_edit.confirm_save' => 'Предупреждать при сохранении', -'form.group_edit.allow_ip' => 'Разрешить доступ с IP', -'form.group_edit.password_complexity' => 'Сложность пароля', -'form.group_edit.2fa' => 'Двухфакторная аутентификация', 'form.group_edit.advanced_settings' => 'Продвинутые настройки', // Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. diff --git a/WEB-INF/resources/sk.lang.php b/WEB-INF/resources/sk.lang.php index ae1a0376..c52d5460 100644 --- a/WEB-INF/resources/sk.lang.php +++ b/WEB-INF/resources/sk.lang.php @@ -575,9 +575,6 @@ // '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.2fa' => 'Two factor authentication', // 'form.group_edit.advanced_settings' => 'Advanced settings', // Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. diff --git a/WEB-INF/resources/sl.lang.php b/WEB-INF/resources/sl.lang.php index d6499cb1..1302ccd6 100644 --- a/WEB-INF/resources/sl.lang.php +++ b/WEB-INF/resources/sl.lang.php @@ -565,9 +565,6 @@ // '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.2fa' => 'Two factor authentication', // 'form.group_edit.advanced_settings' => 'Advanced settings', // Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. diff --git a/WEB-INF/resources/sr.lang.php b/WEB-INF/resources/sr.lang.php index d05a97ee..ba65b461 100644 --- a/WEB-INF/resources/sr.lang.php +++ b/WEB-INF/resources/sr.lang.php @@ -571,9 +571,6 @@ // '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.2fa' => 'Two factor authentication', // 'form.group_edit.advanced_settings' => 'Advanced settings', // Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. diff --git a/WEB-INF/resources/sv.lang.php b/WEB-INF/resources/sv.lang.php index 80f6657b..bd815b25 100644 --- a/WEB-INF/resources/sv.lang.php +++ b/WEB-INF/resources/sv.lang.php @@ -569,9 +569,6 @@ '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.password_complexity' => 'Password complexity', -// 'form.group_edit.2fa' => 'Two factor authentication', // 'form.group_edit.advanced_settings' => 'Advanced settings', // Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. diff --git a/WEB-INF/resources/tr.lang.php b/WEB-INF/resources/tr.lang.php index ca1dacae..a2f50330 100644 --- a/WEB-INF/resources/tr.lang.php +++ b/WEB-INF/resources/tr.lang.php @@ -605,9 +605,6 @@ // '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.2fa' => 'Two factor authentication', // 'form.group_edit.advanced_settings' => 'Advanced settings', // Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. diff --git a/WEB-INF/resources/zh-cn.lang.php b/WEB-INF/resources/zh-cn.lang.php index 068ed52a..7932da3b 100644 --- a/WEB-INF/resources/zh-cn.lang.php +++ b/WEB-INF/resources/zh-cn.lang.php @@ -580,9 +580,6 @@ // '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.2fa' => 'Two factor authentication', // 'form.group_edit.advanced_settings' => 'Advanced settings', // Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. diff --git a/WEB-INF/resources/zh-tw.lang.php b/WEB-INF/resources/zh-tw.lang.php index 34e18441..78e3b25b 100644 --- a/WEB-INF/resources/zh-tw.lang.php +++ b/WEB-INF/resources/zh-tw.lang.php @@ -583,9 +583,6 @@ // '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.2fa' => 'Two factor authentication', // 'form.group_edit.advanced_settings' => 'Advanced settings', // Advanced Group Settings form. See example at https://timetracker.anuko.com/group_advanced_edit.php. diff --git a/WEB-INF/templates/group_advanced_edit.tpl b/WEB-INF/templates/group_advanced_edit.tpl index 528daf46..4bcaedf4 100644 --- a/WEB-INF/templates/group_advanced_edit.tpl +++ b/WEB-INF/templates/group_advanced_edit.tpl @@ -24,18 +24,18 @@ License: See license.txt *}
- + - + {$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} diff --git a/group_advanced_edit.php b/group_advanced_edit.php index 7683d879..a4cf540e 100644 --- a/group_advanced_edit.php +++ b/group_advanced_edit.php @@ -46,8 +46,8 @@ 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 (!ttValidPasswordComplexity($cl_password_complexity, true)) $err->add($i18n->get('error.field'), $i18n->get('form.group_edit.password_complexity')); + 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()) { diff --git a/initialize.php b/initialize.php index 3389203c..74d3da0a 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.1.5659"); +define("APP_VERSION", "1.21.1.5660"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From 81b2e03236fd74fe773105841b0f26fdcaa5ab59 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Tue, 15 Mar 2022 17:45:26 +0000 Subject: [PATCH 004/168] Implmeneted handling of 2fa setting on advanced group settings page. --- WEB-INF/templates/group_advanced_edit.tpl | 11 +++++++++++ group_advanced_edit.php | 11 ++++++++++- initialize.php | 2 +- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/WEB-INF/templates/group_advanced_edit.tpl b/WEB-INF/templates/group_advanced_edit.tpl index 4bcaedf4..d4bd55a1 100644 --- a/WEB-INF/templates/group_advanced_edit.tpl +++ b/WEB-INF/templates/group_advanced_edit.tpl @@ -41,6 +41,17 @@ License: See license.txt *} {$i18n.label.what_is_it} +{if constant('2FA_DEBUG')} +
+ + + + {$forms.groupAdvancedForm.2fa.control} + {$i18n.label.what_is_it} + {$i18n.label.what_is_it} + + +{/if}
{$i18n.label.required_fields}
diff --git a/group_advanced_edit.php b/group_advanced_edit.php index a4cf540e..26134c78 100644 --- a/group_advanced_edit.php +++ b/group_advanced_edit.php @@ -16,6 +16,7 @@ // End of access checks. $group = ttGroupHelper::getGroupAttrs($user->getGroup()); +$config = $user->getConfigHelper(); if ($request->isPost()) { $cl_group = trim($request->getParameter('group_name')); @@ -23,12 +24,14 @@ $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'); @@ -37,6 +40,7 @@ $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'))); @@ -51,12 +55,17 @@ // 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, - 'password_complexity' => $cl_password_complexity))) { + 'password_complexity' => $cl_password_complexity, + 'config' => $config->getConfig()))) { header('Location: success.php'); exit(); } else diff --git a/initialize.php b/initialize.php index 74d3da0a..1eb308b0 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.1.5660"); +define("APP_VERSION", "1.21.1.5661"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From 178417e79adcf25143ab28b2a4505f625136f3ea Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Wed, 16 Mar 2022 14:54:46 +0000 Subject: [PATCH 005/168] Work in progress on 2fa. --- WEB-INF/resources/ca.lang.php | 1 + WEB-INF/resources/cs.lang.php | 1 + WEB-INF/resources/da.lang.php | 2 ++ WEB-INF/resources/de.lang.php | 2 ++ WEB-INF/resources/en.lang.php | 1 + WEB-INF/resources/es.lang.php | 1 + WEB-INF/resources/et.lang.php | 2 ++ WEB-INF/resources/fa.lang.php | 5 +++-- WEB-INF/resources/fi.lang.php | 2 ++ WEB-INF/resources/fr.lang.php | 2 ++ WEB-INF/resources/gr.lang.php | 2 ++ WEB-INF/resources/he.lang.php | 2 ++ WEB-INF/resources/hu.lang.php | 1 + WEB-INF/resources/it.lang.php | 2 ++ WEB-INF/resources/ja.lang.php | 1 + WEB-INF/resources/ko.lang.php | 1 + WEB-INF/resources/nl.lang.php | 2 ++ WEB-INF/resources/no.lang.php | 2 ++ WEB-INF/resources/pl.lang.php | 2 ++ WEB-INF/resources/pt-br.lang.php | 2 ++ WEB-INF/resources/pt.lang.php | 1 + WEB-INF/resources/ro.lang.php | 1 + WEB-INF/resources/ru.lang.php | 1 + WEB-INF/resources/sk.lang.php | 2 ++ WEB-INF/resources/sl.lang.php | 1 + WEB-INF/resources/sr.lang.php | 2 ++ WEB-INF/resources/sv.lang.php | 2 ++ WEB-INF/resources/tr.lang.php | 1 + WEB-INF/resources/zh-cn.lang.php | 2 ++ WEB-INF/resources/zh-tw.lang.php | 1 + initialize.php | 2 +- login.php | 13 +++++++++++++ 32 files changed, 62 insertions(+), 3 deletions(-) diff --git a/WEB-INF/resources/ca.lang.php b/WEB-INF/resources/ca.lang.php index e1f9a191..6c577de0 100644 --- a/WEB-INF/resources/ca.lang.php +++ b/WEB-INF/resources/ca.lang.php @@ -418,6 +418,7 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => '¿Ha oblidat la seva paraula de pas?', // TODO: translate the following. +// 'form.login.2fa_code' => '2FA code', // 'form.login.about' => 'Anuko Time Tracker is an open source time tracking system.', // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. diff --git a/WEB-INF/resources/cs.lang.php b/WEB-INF/resources/cs.lang.php index 562253c4..feee2237 100644 --- a/WEB-INF/resources/cs.lang.php +++ b/WEB-INF/resources/cs.lang.php @@ -425,6 +425,7 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => 'Zapomenuté heslo?', // TODO: translate the following. +// 'form.login.2fa_code' => '2FA code', // 'form.login.about' => 'Anuko Time Tracker is an open source time tracking system.', // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. diff --git a/WEB-INF/resources/da.lang.php b/WEB-INF/resources/da.lang.php index 6070d5ab..6a598edb 100644 --- a/WEB-INF/resources/da.lang.php +++ b/WEB-INF/resources/da.lang.php @@ -402,6 +402,8 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => 'Har du glemt din adgangskode?', +// TODO: translate the following. +// 'form.login.2fa_code' => '2FA code', 'form.login.about' => 'Anuko Time Tracker er et open source tidsregistrerings system.', // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. diff --git a/WEB-INF/resources/de.lang.php b/WEB-INF/resources/de.lang.php index f627e77f..ad6727fb 100644 --- a/WEB-INF/resources/de.lang.php +++ b/WEB-INF/resources/de.lang.php @@ -389,6 +389,8 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => 'Passwort vergessen?', +// TODO: translate the following. +// 'form.login.2fa_code' => '2FA code', 'form.login.about' => 'Anuko Time Tracker ist ein Open-Source Zeiterfassungssystem.', // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. diff --git a/WEB-INF/resources/en.lang.php b/WEB-INF/resources/en.lang.php index 1cd30f07..8df7601d 100644 --- a/WEB-INF/resources/en.lang.php +++ b/WEB-INF/resources/en.lang.php @@ -371,6 +371,7 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => 'Forgot password?', +'form.login.2fa_code' => '2FA code', 'form.login.about' => 'Anuko Time Tracker is an open source time tracking system.', // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. diff --git a/WEB-INF/resources/es.lang.php b/WEB-INF/resources/es.lang.php index 9cc4aabd..441b4258 100644 --- a/WEB-INF/resources/es.lang.php +++ b/WEB-INF/resources/es.lang.php @@ -422,6 +422,7 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => '¿Olvido su contraseña?', // TODO: translate the following. +// 'form.login.2fa_code' => '2FA code', // 'form.login.about' => 'Anuko Time Tracker is an open source time tracking system.', // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. diff --git a/WEB-INF/resources/et.lang.php b/WEB-INF/resources/et.lang.php index 9dd7d02d..715b68bc 100644 --- a/WEB-INF/resources/et.lang.php +++ b/WEB-INF/resources/et.lang.php @@ -410,6 +410,8 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => 'Unustasid salasõna?', +// TODO: translate the following. +// 'form.login.2fa_code' => '2FA code', 'form.login.about' => 'Anuko Time Tracker on avatud lähtekoodiga ajaarvestussüsteem.', // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. diff --git a/WEB-INF/resources/fa.lang.php b/WEB-INF/resources/fa.lang.php index a7560fc4..f470be33 100644 --- a/WEB-INF/resources/fa.lang.php +++ b/WEB-INF/resources/fa.lang.php @@ -419,8 +419,9 @@ // 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.2fa_code' => '2FA code', +// 'form.login.about' => 'Anuko Time Tracker is an open source time tracking system.', // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. 'form.reset_password.message' => 'درخواست بازیابی رمزعبور به ایمیل فرستاده شد.', diff --git a/WEB-INF/resources/fi.lang.php b/WEB-INF/resources/fi.lang.php index 1f483ef1..db157940 100644 --- a/WEB-INF/resources/fi.lang.php +++ b/WEB-INF/resources/fi.lang.php @@ -407,6 +407,8 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => 'Salasana unohtunut?', +// TODO: translate the following. +// 'form.login.2fa_code' => '2FA code', // TODO: check translation of form.login.about - is open source "vapaan koodin"? 'form.login.about' => 'Anuko Time Tracker on vapaan koodin tuntiseurantaohjelmisto.', diff --git a/WEB-INF/resources/fr.lang.php b/WEB-INF/resources/fr.lang.php index 22132849..d68e4cb4 100644 --- a/WEB-INF/resources/fr.lang.php +++ b/WEB-INF/resources/fr.lang.php @@ -399,6 +399,8 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => 'Mot de passe oublié?', +// TODO: translate the following. +// 'form.login.2fa_code' => '2FA code', 'form.login.about' => 'Anuko Time Tracker est un système de gestion du temps, open source.', // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. diff --git a/WEB-INF/resources/gr.lang.php b/WEB-INF/resources/gr.lang.php index f2606c41..7c8e3956 100644 --- a/WEB-INF/resources/gr.lang.php +++ b/WEB-INF/resources/gr.lang.php @@ -398,6 +398,8 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => 'Ξεχάσατε τον κωδικό πρόσβασης;', +// TODO: translate the following. +// 'form.login.2fa_code' => '2FA code', 'form.login.about' => 'Anuko Time Tracker είναι ένα ανοικτού κώδικα σύστημα παρακολούθησης χρόνου.', // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. diff --git a/WEB-INF/resources/he.lang.php b/WEB-INF/resources/he.lang.php index 52892dbc..fddf8d50 100644 --- a/WEB-INF/resources/he.lang.php +++ b/WEB-INF/resources/he.lang.php @@ -424,6 +424,8 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => 'שכחת סיסמה?', +// TODO: translate the following. +// 'form.login.2fa_code' => '2FA code', // TODO: re-translate form.login.about as it has changed. // 'form.login.about' => 'Anuko Time Tracker is an open source time tracking system.', 'form.login.about' => 'Anuko Time Tracker הינה מערכת פשוטה, קלה לשימוש וחינמית לניהול זמן.', diff --git a/WEB-INF/resources/hu.lang.php b/WEB-INF/resources/hu.lang.php index b880b22f..d3cbd7f9 100644 --- a/WEB-INF/resources/hu.lang.php +++ b/WEB-INF/resources/hu.lang.php @@ -419,6 +419,7 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => 'Elfelejtetted a jelszót?', // TODO: translate the following. +// 'form.login.2fa_code' => '2FA code', // 'form.login.about' => 'Anuko Time Tracker is an open source time tracking system.', // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. diff --git a/WEB-INF/resources/it.lang.php b/WEB-INF/resources/it.lang.php index 40bca63e..8d2cfaa0 100644 --- a/WEB-INF/resources/it.lang.php +++ b/WEB-INF/resources/it.lang.php @@ -408,6 +408,8 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => 'Password dimenticata?', +// TODO: translate the following. +// 'form.login.2fa_code' => '2FA code', 'form.login.about' => 'Anuko Time Tracker è un sistema open source per registrare i tempi di lavoro.', // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. diff --git a/WEB-INF/resources/ja.lang.php b/WEB-INF/resources/ja.lang.php index 518c4269..3dc0b877 100644 --- a/WEB-INF/resources/ja.lang.php +++ b/WEB-INF/resources/ja.lang.php @@ -420,6 +420,7 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => 'パスワードを忘れましたか?', // TODO: translate the following. +// 'form.login.2fa_code' => '2FA code', // 'form.login.about' => 'Anuko Time Tracker is an open source time tracking system.', // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. diff --git a/WEB-INF/resources/ko.lang.php b/WEB-INF/resources/ko.lang.php index dde93402..63999c23 100644 --- a/WEB-INF/resources/ko.lang.php +++ b/WEB-INF/resources/ko.lang.php @@ -424,6 +424,7 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => '암호를 잊으셨습니까?', // TODO: translate the following. +// 'form.login.2fa_code' => '2FA code', // 'form.login.about' => 'Anuko Time Tracker is an open source time tracking system.', // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. diff --git a/WEB-INF/resources/nl.lang.php b/WEB-INF/resources/nl.lang.php index bd1ad69c..131d7993 100644 --- a/WEB-INF/resources/nl.lang.php +++ b/WEB-INF/resources/nl.lang.php @@ -363,6 +363,8 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => 'Wachtwoord vergeten?', +// TODO: translate the following. +// 'form.login.2fa_code' => '2FA code', 'form.login.about' => 'Anuko Time Tracker is een open source tijdregistratiesysteem.', // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. diff --git a/WEB-INF/resources/no.lang.php b/WEB-INF/resources/no.lang.php index c38c74dd..d2a934ee 100644 --- a/WEB-INF/resources/no.lang.php +++ b/WEB-INF/resources/no.lang.php @@ -421,6 +421,8 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => 'Glemt passordet?', +// TODO: translate the following. +// 'form.login.2fa_code' => '2FA code', // TODO: re-translate form.login.about as it has changed. // '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.', diff --git a/WEB-INF/resources/pl.lang.php b/WEB-INF/resources/pl.lang.php index 86556229..4316435a 100644 --- a/WEB-INF/resources/pl.lang.php +++ b/WEB-INF/resources/pl.lang.php @@ -410,6 +410,8 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => 'Nie pamiętasz hasła?', +// TODO: translate the following. +// 'form.login.2fa_code' => '2FA code', 'form.login.about' => 'Anuko Time Tracker jest otwartoźródłowym systemem śledzenia czasu.', // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. diff --git a/WEB-INF/resources/pt-br.lang.php b/WEB-INF/resources/pt-br.lang.php index bbabafcb..c5a670a0 100644 --- a/WEB-INF/resources/pt-br.lang.php +++ b/WEB-INF/resources/pt-br.lang.php @@ -362,6 +362,8 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => 'Esqueceu a senha?', +// TODO: translate the following. +// 'form.login.2fa_code' => '2FA code', 'form.login.about' => 'Anuko Time Tracker é um sistema de código aberto de rastreamento do tempo.', // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. diff --git a/WEB-INF/resources/pt.lang.php b/WEB-INF/resources/pt.lang.php index 41e06c0f..a40d8ed8 100644 --- a/WEB-INF/resources/pt.lang.php +++ b/WEB-INF/resources/pt.lang.php @@ -403,6 +403,7 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => 'Esqueceu a senha?', // TODO: translate the following. +// 'form.login.2fa_code' => '2FA code', // 'form.login.about' => 'Anuko Time Tracker is an open source time tracking system.', // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. diff --git a/WEB-INF/resources/ro.lang.php b/WEB-INF/resources/ro.lang.php index 7f3168d4..97c9a854 100644 --- a/WEB-INF/resources/ro.lang.php +++ b/WEB-INF/resources/ro.lang.php @@ -424,6 +424,7 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => 'Parola pierduta?', // TODO: translate the following. +// 'form.login.2fa_code' => '2FA code', // 'form.login.about' => 'Anuko Time Tracker is an open source time tracking system.', // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. diff --git a/WEB-INF/resources/ru.lang.php b/WEB-INF/resources/ru.lang.php index 35966616..faa1c7ab 100644 --- a/WEB-INF/resources/ru.lang.php +++ b/WEB-INF/resources/ru.lang.php @@ -364,6 +364,7 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => 'Забыли пароль?', +'form.login.2fa_code' => '2FA код', 'form.login.about' => 'Anuko Time Tracker - это открытая (open source) система трекинга рабочего времени.', // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. diff --git a/WEB-INF/resources/sk.lang.php b/WEB-INF/resources/sk.lang.php index c52d5460..356e4e0a 100644 --- a/WEB-INF/resources/sk.lang.php +++ b/WEB-INF/resources/sk.lang.php @@ -413,6 +413,8 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => 'Zabudnuté heslo?', +// TODO: translate the following. +// 'form.login.2fa_code' => '2FA code', 'form.login.about' => 'Anuko Time Tracker je systém na sledovanie času s otvoreným zdrojovým kódom.', // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. diff --git a/WEB-INF/resources/sl.lang.php b/WEB-INF/resources/sl.lang.php index 1302ccd6..2bcba236 100644 --- a/WEB-INF/resources/sl.lang.php +++ b/WEB-INF/resources/sl.lang.php @@ -396,6 +396,7 @@ // Login form. See example at https://timetracker.anuko.com/login.php. // TODO: translate the following. // 'form.login.forgot_password' => 'Forgot password?', +// 'form.login.2fa_code' => '2FA code', // 'form.login.about' => 'Anuko Time Tracker is an open source time tracking system.', // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. diff --git a/WEB-INF/resources/sr.lang.php b/WEB-INF/resources/sr.lang.php index ba65b461..dff5f262 100644 --- a/WEB-INF/resources/sr.lang.php +++ b/WEB-INF/resources/sr.lang.php @@ -408,6 +408,8 @@ // Forma prijave. Pogledajte primer na https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => 'Zaboravili ste lozinku?', +// TODO: translate the following. +// 'form.login.2fa_code' => '2FA code', // TODO: re-translate form.login.about as it has changed. // '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.', diff --git a/WEB-INF/resources/sv.lang.php b/WEB-INF/resources/sv.lang.php index bd815b25..62a747e1 100644 --- a/WEB-INF/resources/sv.lang.php +++ b/WEB-INF/resources/sv.lang.php @@ -406,6 +406,8 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => 'Glömt lösenordet?', +// TODO: translate the following. +// 'form.login.2fa_code' => '2FA code', // TODO: re-translate form.login.about as it has changed. // '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.', diff --git a/WEB-INF/resources/tr.lang.php b/WEB-INF/resources/tr.lang.php index a2f50330..e0a291f0 100644 --- a/WEB-INF/resources/tr.lang.php +++ b/WEB-INF/resources/tr.lang.php @@ -432,6 +432,7 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => 'Parolanızı unuttunuz mu?', // TODO: translate the following. +// 'form.login.2fa_code' => '2FA code', // 'form.login.about' => 'Anuko Time Tracker is an open source time tracking system.', // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. diff --git a/WEB-INF/resources/zh-cn.lang.php b/WEB-INF/resources/zh-cn.lang.php index 7932da3b..7dc37961 100644 --- a/WEB-INF/resources/zh-cn.lang.php +++ b/WEB-INF/resources/zh-cn.lang.php @@ -413,6 +413,8 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => '忘记密码?', +// TODO: translate the following. +// 'form.login.2fa_code' => '2FA code', // TODO: re-translate form.login.about as it has changed. // 'form.login.about' => 'Anuko Time Tracker is an open source time tracking system.', 'form.login.about' => 'Anuko Time Tracker 是一种简单、易用、开放源代码的实时跟踪系统。', diff --git a/WEB-INF/resources/zh-tw.lang.php b/WEB-INF/resources/zh-tw.lang.php index 78e3b25b..2497f7ce 100644 --- a/WEB-INF/resources/zh-tw.lang.php +++ b/WEB-INF/resources/zh-tw.lang.php @@ -418,6 +418,7 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => '忘記密碼?', // TODO: translate the following. +// 'form.login.2fa_code' => '2FA code', // 'form.login.about' => 'Anuko Time Tracker is an open source time tracking system.', // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. diff --git a/initialize.php b/initialize.php index 1eb308b0..2f537eb6 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.1.5661"); +define("APP_VERSION", "1.21.1.5662"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); diff --git a/login.php b/login.php index 93b1cd08..5ea77d18 100644 --- a/login.php +++ b/login.php @@ -48,6 +48,19 @@ setcookie(LOGIN_COOKIE_NAME, $cl_login, time() + COOKIE_EXPIRE, '/'); $user = new ttUser(null, $auth->getUserId()); + + // Determine if we have to additionally use two-factor authentication. + $config = $user->getConfigHelper(); + $use2FA = $config->getDefinedValue('2fa'); + if ($use2FA) { + + // TODO: send 2fa code to user. + $auth->doLogout(); + + header('Location: 2fa.php'); + exit(); + } + // Redirect, depending on user role. if ($user->can('administer_site')) { header('Location: admin_groups.php'); From f2fbe6e5a6251cf86be04f5aa8baa8c1bee038c7 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Wed, 16 Mar 2022 15:25:33 +0000 Subject: [PATCH 006/168] Some refactoring in translation files related to 2fa. --- 2fa.php | 87 ++++++++++++++++++++++++++++++++ WEB-INF/resources/ca.lang.php | 5 +- WEB-INF/resources/cs.lang.php | 5 +- WEB-INF/resources/da.lang.php | 6 ++- WEB-INF/resources/de.lang.php | 6 ++- WEB-INF/resources/en.lang.php | 3 ++ WEB-INF/resources/es.lang.php | 5 +- WEB-INF/resources/et.lang.php | 6 ++- WEB-INF/resources/fa.lang.php | 5 +- WEB-INF/resources/fi.lang.php | 8 +-- WEB-INF/resources/fr.lang.php | 6 ++- WEB-INF/resources/gr.lang.php | 6 ++- WEB-INF/resources/he.lang.php | 6 ++- WEB-INF/resources/hu.lang.php | 5 +- WEB-INF/resources/it.lang.php | 6 ++- WEB-INF/resources/ja.lang.php | 5 +- WEB-INF/resources/ko.lang.php | 5 +- WEB-INF/resources/nl.lang.php | 6 ++- WEB-INF/resources/no.lang.php | 6 ++- WEB-INF/resources/pl.lang.php | 6 ++- WEB-INF/resources/pt-br.lang.php | 6 ++- WEB-INF/resources/pt.lang.php | 5 +- WEB-INF/resources/ro.lang.php | 5 +- WEB-INF/resources/ru.lang.php | 4 +- WEB-INF/resources/sk.lang.php | 6 ++- WEB-INF/resources/sl.lang.php | 5 +- WEB-INF/resources/sr.lang.php | 6 ++- WEB-INF/resources/sv.lang.php | 6 ++- WEB-INF/resources/tr.lang.php | 5 +- WEB-INF/resources/zh-cn.lang.php | 6 ++- WEB-INF/resources/zh-tw.lang.php | 5 +- WEB-INF/templates/2fa.tpl | 24 +++++++++ 32 files changed, 230 insertions(+), 46 deletions(-) create mode 100644 2fa.php create mode 100644 WEB-INF/templates/2fa.tpl diff --git a/2fa.php b/2fa.php new file mode 100644 index 00000000..7c1728d2 --- /dev/null +++ b/2fa.php @@ -0,0 +1,87 @@ +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_2fa_code = $request->getParameter('2fa_code'); +$cl_password = $request->getParameter('password'); + +$form = new Form('2faForm'); +$form->addInput(array('type'=>'text','maxlength'=>'100','name'=>'2fa_code','value'=>$cl_2fa_code)); +$form->addInput(array('type'=>'hidden','name'=>'browser_today','value'=>'')); // User current date, which gets filled in on btn_login click. +$form->addInput(array('type'=>'submit','name'=>'btn_login','onclick'=>'browser_today.value=get_date()','value'=>$i18n->get('button.login'))); + +if ($request->isPost()) { + // Validate user input. + if (!ttValidString($cl_2fa_code)) $err->add($i18n->get('error.field'), $i18n->get('fprm.2fa.2fa_code')); + + 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; + + // Remember user login in a cookie. + setcookie(LOGIN_COOKIE_NAME, $cl_login, time() + COOKIE_EXPIRE, '/'); + + $user = new ttUser(null, $auth->getUserId()); + + // Determine if we have to additionally use two-factor authentication. + $config = $user->getConfigHelper(); + $use2FA = $config->getDefinedValue('2fa'); + if ($use2FA) { + + // TODO: send 2fa code to user. + $auth->doLogout(); + + header('Location: 2fa.php'); + exit(); + } + + // 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(); + } else + $err->add($i18n->get('error.auth')); + } +} // isPost + +if(!isTrue('MULTIORG_MODE') && !ttOrgHelper::getOrgs()) + $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' == 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); +$smarty->assign('onload', 'onLoad="document.loginForm.'.(!$cl_login?'login':'password').'.focus()"'); +$smarty->assign('about_text', $i18n->get('form.login.about')); +$smarty->assign('title', $i18n->get('title.login')); +$smarty->assign('content_page_name', '2fa.tpl'); +$smarty->display('index.tpl'); diff --git a/WEB-INF/resources/ca.lang.php b/WEB-INF/resources/ca.lang.php index 6c577de0..3bd936dd 100644 --- a/WEB-INF/resources/ca.lang.php +++ b/WEB-INF/resources/ca.lang.php @@ -418,9 +418,12 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => '¿Ha oblidat la seva paraula de pas?', // TODO: translate the following. -// 'form.login.2fa_code' => '2FA code', // 'form.login.about' => 'Anuko Time Tracker is an open source time tracking system.', +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// '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', diff --git a/WEB-INF/resources/cs.lang.php b/WEB-INF/resources/cs.lang.php index feee2237..a4f11602 100644 --- a/WEB-INF/resources/cs.lang.php +++ b/WEB-INF/resources/cs.lang.php @@ -425,9 +425,12 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => 'Zapomenuté heslo?', // TODO: translate the following. -// 'form.login.2fa_code' => '2FA code', // 'form.login.about' => 'Anuko Time Tracker is an open source time tracking system.', +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// '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', diff --git a/WEB-INF/resources/da.lang.php b/WEB-INF/resources/da.lang.php index 6a598edb..d1b991f4 100644 --- a/WEB-INF/resources/da.lang.php +++ b/WEB-INF/resources/da.lang.php @@ -402,10 +402,12 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => 'Har du glemt din adgangskode?', -// TODO: translate the following. -// 'form.login.2fa_code' => '2FA code', 'form.login.about' => 'Anuko Time Tracker er et open source tidsregistrerings system.', +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// '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', diff --git a/WEB-INF/resources/de.lang.php b/WEB-INF/resources/de.lang.php index ad6727fb..75c2c0a7 100644 --- a/WEB-INF/resources/de.lang.php +++ b/WEB-INF/resources/de.lang.php @@ -389,10 +389,12 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => 'Passwort vergessen?', -// TODO: translate the following. -// 'form.login.2fa_code' => '2FA code', 'form.login.about' => 'Anuko Time Tracker ist ein Open-Source Zeiterfassungssystem.', +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// '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', diff --git a/WEB-INF/resources/en.lang.php b/WEB-INF/resources/en.lang.php index 8df7601d..7adacead 100644 --- a/WEB-INF/resources/en.lang.php +++ b/WEB-INF/resources/en.lang.php @@ -374,6 +374,9 @@ 'form.login.2fa_code' => '2FA code', 'form.login.about' => 'Anuko Time Tracker is an open source time tracking system.', +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +'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', diff --git a/WEB-INF/resources/es.lang.php b/WEB-INF/resources/es.lang.php index 441b4258..a78f1e6f 100644 --- a/WEB-INF/resources/es.lang.php +++ b/WEB-INF/resources/es.lang.php @@ -422,9 +422,12 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => '¿Olvido su contraseña?', // TODO: translate the following. -// 'form.login.2fa_code' => '2FA code', // 'form.login.about' => 'Anuko Time Tracker is an open source time tracking system.', +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// '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.', diff --git a/WEB-INF/resources/et.lang.php b/WEB-INF/resources/et.lang.php index 715b68bc..854a52e0 100644 --- a/WEB-INF/resources/et.lang.php +++ b/WEB-INF/resources/et.lang.php @@ -410,10 +410,12 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => 'Unustasid salasõna?', -// TODO: translate the following. -// 'form.login.2fa_code' => '2FA code', 'form.login.about' => 'Anuko Time Tracker on avatud lähtekoodiga ajaarvestussüsteem.', +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// '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', diff --git a/WEB-INF/resources/fa.lang.php b/WEB-INF/resources/fa.lang.php index f470be33..bda4a475 100644 --- a/WEB-INF/resources/fa.lang.php +++ b/WEB-INF/resources/fa.lang.php @@ -420,9 +420,12 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => 'بازیابی رمز عبور؟', // TODO: translate the following. -// 'form.login.2fa_code' => '2FA code', // 'form.login.about' => 'Anuko Time Tracker is an open source time tracking system.', +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// 'form.2fa.2fa_code' => '2FA code', + // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. 'form.reset_password.message' => 'درخواست بازیابی رمزعبور به ایمیل فرستاده شد.', // TODO: check translation of form.reset_password.email_subject. This is the subject for email message for password reset. Below is the English original. diff --git a/WEB-INF/resources/fi.lang.php b/WEB-INF/resources/fi.lang.php index db157940..1d90ef23 100644 --- a/WEB-INF/resources/fi.lang.php +++ b/WEB-INF/resources/fi.lang.php @@ -407,11 +407,13 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => 'Salasana unohtunut?', -// TODO: translate the following. -// 'form.login.2fa_code' => '2FA code', - // 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.', +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// '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ö', diff --git a/WEB-INF/resources/fr.lang.php b/WEB-INF/resources/fr.lang.php index d68e4cb4..ccb42935 100644 --- a/WEB-INF/resources/fr.lang.php +++ b/WEB-INF/resources/fr.lang.php @@ -399,10 +399,12 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => 'Mot de passe oublié?', -// TODO: translate the following. -// 'form.login.2fa_code' => '2FA code', 'form.login.about' => 'Anuko Time Tracker est un système de gestion du temps, open source.', +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// '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', diff --git a/WEB-INF/resources/gr.lang.php b/WEB-INF/resources/gr.lang.php index 7c8e3956..4cf1f64f 100644 --- a/WEB-INF/resources/gr.lang.php +++ b/WEB-INF/resources/gr.lang.php @@ -398,10 +398,12 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => 'Ξεχάσατε τον κωδικό πρόσβασης;', -// TODO: translate the following. -// 'form.login.2fa_code' => '2FA code', 'form.login.about' => 'Anuko Time Tracker είναι ένα ανοικτού κώδικα σύστημα παρακολούθησης χρόνου.', +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// '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', diff --git a/WEB-INF/resources/he.lang.php b/WEB-INF/resources/he.lang.php index fddf8d50..4342ea84 100644 --- a/WEB-INF/resources/he.lang.php +++ b/WEB-INF/resources/he.lang.php @@ -424,12 +424,14 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => 'שכחת סיסמה?', -// TODO: translate the following. -// 'form.login.2fa_code' => '2FA code', // TODO: re-translate form.login.about as it has changed. // 'form.login.about' => 'Anuko Time Tracker is an open source time tracking system.', 'form.login.about' => 'Anuko Time Tracker הינה מערכת פשוטה, קלה לשימוש וחינמית לניהול זמן.', +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// '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', diff --git a/WEB-INF/resources/hu.lang.php b/WEB-INF/resources/hu.lang.php index d3cbd7f9..b105d248 100644 --- a/WEB-INF/resources/hu.lang.php +++ b/WEB-INF/resources/hu.lang.php @@ -419,9 +419,12 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => 'Elfelejtetted a jelszót?', // TODO: translate the following. -// 'form.login.2fa_code' => '2FA code', // 'form.login.about' => 'Anuko Time Tracker is an open source time tracking system.', +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// '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.', diff --git a/WEB-INF/resources/it.lang.php b/WEB-INF/resources/it.lang.php index 8d2cfaa0..16727cfe 100644 --- a/WEB-INF/resources/it.lang.php +++ b/WEB-INF/resources/it.lang.php @@ -408,10 +408,12 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => 'Password dimenticata?', -// TODO: translate the following. -// 'form.login.2fa_code' => '2FA code', 'form.login.about' => 'Anuko Time Tracker è un sistema open source per registrare i tempi di lavoro.', +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// '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', diff --git a/WEB-INF/resources/ja.lang.php b/WEB-INF/resources/ja.lang.php index 3dc0b877..46c70553 100644 --- a/WEB-INF/resources/ja.lang.php +++ b/WEB-INF/resources/ja.lang.php @@ -420,9 +420,12 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => 'パスワードを忘れましたか?', // TODO: translate the following. -// 'form.login.2fa_code' => '2FA code', // 'form.login.about' => 'Anuko Time Tracker is an open source time tracking system.', +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// '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のパスワードの初期化の要求', diff --git a/WEB-INF/resources/ko.lang.php b/WEB-INF/resources/ko.lang.php index 63999c23..a8235693 100644 --- a/WEB-INF/resources/ko.lang.php +++ b/WEB-INF/resources/ko.lang.php @@ -424,9 +424,12 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => '암호를 잊으셨습니까?', // TODO: translate the following. -// 'form.login.2fa_code' => '2FA code', // 'form.login.about' => 'Anuko Time Tracker is an open source time tracking system.', +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// '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 암호 재설정 요청', diff --git a/WEB-INF/resources/nl.lang.php b/WEB-INF/resources/nl.lang.php index 131d7993..9a7c1707 100644 --- a/WEB-INF/resources/nl.lang.php +++ b/WEB-INF/resources/nl.lang.php @@ -363,10 +363,12 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => 'Wachtwoord vergeten?', -// TODO: translate the following. -// 'form.login.2fa_code' => '2FA code', 'form.login.about' => 'Anuko Time Tracker is een open source tijdregistratiesysteem.', +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// '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', diff --git a/WEB-INF/resources/no.lang.php b/WEB-INF/resources/no.lang.php index d2a934ee..b7500bba 100644 --- a/WEB-INF/resources/no.lang.php +++ b/WEB-INF/resources/no.lang.php @@ -421,12 +421,14 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => 'Glemt passordet?', -// TODO: translate the following. -// 'form.login.2fa_code' => '2FA code', // TODO: re-translate form.login.about as it has changed. // '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.', +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// '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.', diff --git a/WEB-INF/resources/pl.lang.php b/WEB-INF/resources/pl.lang.php index 4316435a..0e482eeb 100644 --- a/WEB-INF/resources/pl.lang.php +++ b/WEB-INF/resources/pl.lang.php @@ -410,10 +410,12 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => 'Nie pamiętasz hasła?', -// TODO: translate the following. -// 'form.login.2fa_code' => '2FA code', 'form.login.about' => 'Anuko Time Tracker jest otwartoźródłowym systemem śledzenia czasu.', +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// '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', diff --git a/WEB-INF/resources/pt-br.lang.php b/WEB-INF/resources/pt-br.lang.php index c5a670a0..f0b564de 100644 --- a/WEB-INF/resources/pt-br.lang.php +++ b/WEB-INF/resources/pt-br.lang.php @@ -362,10 +362,12 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => 'Esqueceu a senha?', -// TODO: translate the following. -// 'form.login.2fa_code' => '2FA code', 'form.login.about' => 'Anuko Time Tracker é um sistema de código aberto de rastreamento do tempo.', +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// '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', diff --git a/WEB-INF/resources/pt.lang.php b/WEB-INF/resources/pt.lang.php index a40d8ed8..dcb0d035 100644 --- a/WEB-INF/resources/pt.lang.php +++ b/WEB-INF/resources/pt.lang.php @@ -403,9 +403,12 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => 'Esqueceu a senha?', // TODO: translate the following. -// 'form.login.2fa_code' => '2FA code', // 'form.login.about' => 'Anuko Time Tracker is an open source time tracking system.', +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// '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.', diff --git a/WEB-INF/resources/ro.lang.php b/WEB-INF/resources/ro.lang.php index 97c9a854..7a5b41a3 100644 --- a/WEB-INF/resources/ro.lang.php +++ b/WEB-INF/resources/ro.lang.php @@ -424,9 +424,12 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => 'Parola pierduta?', // TODO: translate the following. -// 'form.login.2fa_code' => '2FA code', // 'form.login.about' => 'Anuko Time Tracker is an open source time tracking system.', +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// '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', diff --git a/WEB-INF/resources/ru.lang.php b/WEB-INF/resources/ru.lang.php index faa1c7ab..da8d8b0f 100644 --- a/WEB-INF/resources/ru.lang.php +++ b/WEB-INF/resources/ru.lang.php @@ -364,9 +364,11 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => 'Забыли пароль?', -'form.login.2fa_code' => '2FA код', 'form.login.about' => 'Anuko Time Tracker - это открытая (open source) система трекинга рабочего времени.', +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +'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', diff --git a/WEB-INF/resources/sk.lang.php b/WEB-INF/resources/sk.lang.php index 356e4e0a..3bdc0e4b 100644 --- a/WEB-INF/resources/sk.lang.php +++ b/WEB-INF/resources/sk.lang.php @@ -413,10 +413,12 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => 'Zabudnuté heslo?', -// TODO: translate the following. -// 'form.login.2fa_code' => '2FA code', 'form.login.about' => 'Anuko Time Tracker je systém na sledovanie času s otvoreným zdrojovým kódom.', +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// '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', diff --git a/WEB-INF/resources/sl.lang.php b/WEB-INF/resources/sl.lang.php index 2bcba236..d7406f37 100644 --- a/WEB-INF/resources/sl.lang.php +++ b/WEB-INF/resources/sl.lang.php @@ -396,9 +396,12 @@ // Login form. See example at https://timetracker.anuko.com/login.php. // TODO: translate the following. // 'form.login.forgot_password' => 'Forgot password?', -// 'form.login.2fa_code' => '2FA code', // 'form.login.about' => 'Anuko Time Tracker is an open source time tracking system.', +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// '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', diff --git a/WEB-INF/resources/sr.lang.php b/WEB-INF/resources/sr.lang.php index dff5f262..b26d4f04 100644 --- a/WEB-INF/resources/sr.lang.php +++ b/WEB-INF/resources/sr.lang.php @@ -408,12 +408,14 @@ // Forma prijave. Pogledajte primer na https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => 'Zaboravili ste lozinku?', -// TODO: translate the following. -// 'form.login.2fa_code' => '2FA code', // TODO: re-translate form.login.about as it has changed. // '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.', +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// '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', diff --git a/WEB-INF/resources/sv.lang.php b/WEB-INF/resources/sv.lang.php index 62a747e1..1c139a0e 100644 --- a/WEB-INF/resources/sv.lang.php +++ b/WEB-INF/resources/sv.lang.php @@ -406,12 +406,14 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => 'Glömt lösenordet?', -// TODO: translate the following. -// 'form.login.2fa_code' => '2FA code', // TODO: re-translate form.login.about as it has changed. // '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.', +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// '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', diff --git a/WEB-INF/resources/tr.lang.php b/WEB-INF/resources/tr.lang.php index e0a291f0..9f57fa3e 100644 --- a/WEB-INF/resources/tr.lang.php +++ b/WEB-INF/resources/tr.lang.php @@ -432,9 +432,12 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => 'Parolanızı unuttunuz mu?', // TODO: translate the following. -// 'form.login.2fa_code' => '2FA code', // 'form.login.about' => 'Anuko Time Tracker is an open source time tracking system.', +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// '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', diff --git a/WEB-INF/resources/zh-cn.lang.php b/WEB-INF/resources/zh-cn.lang.php index 7dc37961..400529f6 100644 --- a/WEB-INF/resources/zh-cn.lang.php +++ b/WEB-INF/resources/zh-cn.lang.php @@ -413,12 +413,14 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => '忘记密码?', -// TODO: translate the following. -// 'form.login.2fa_code' => '2FA code', // TODO: re-translate form.login.about as it has changed. // 'form.login.about' => 'Anuko Time Tracker is an open source time tracking system.', 'form.login.about' => 'Anuko Time Tracker 是一种简单、易用、开放源代码的实时跟踪系统。', +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// '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时间追踪器密码重设请求', diff --git a/WEB-INF/resources/zh-tw.lang.php b/WEB-INF/resources/zh-tw.lang.php index 2497f7ce..3ece1f0f 100644 --- a/WEB-INF/resources/zh-tw.lang.php +++ b/WEB-INF/resources/zh-tw.lang.php @@ -418,9 +418,12 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => '忘記密碼?', // TODO: translate the following. -// 'form.login.2fa_code' => '2FA code', // 'form.login.about' => 'Anuko Time Tracker is an open source time tracking system.', +// Two-factor authentication form. See example at https://timetracker.anuko.com/2fa.php. +// TODO: translate the following. +// '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時間追蹤器密碼重設請求', diff --git a/WEB-INF/templates/2fa.tpl b/WEB-INF/templates/2fa.tpl new file mode 100644 index 00000000..e634b624 --- /dev/null +++ b/WEB-INF/templates/2fa.tpl @@ -0,0 +1,24 @@ +{* Copyright (c) Anuko International Ltd. https://www.anuko.com +License: See license.txt *} + + + +{$forms.2faForm.open} + + + + + + + + + + +
{$forms.2faForm.2fa_code.control}
{$forms.2faForm.btn_login.control}
+{$forms.2faForm.close} + From 3c7cba88f69edd54f694f34f0907990977673b79 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Wed, 16 Mar 2022 15:40:39 +0000 Subject: [PATCH 007/168] Added title.2fa string to translation files. --- 2fa.php | 7 ++----- WEB-INF/resources/ca.lang.php | 2 ++ WEB-INF/resources/cs.lang.php | 2 ++ WEB-INF/resources/da.lang.php | 2 ++ WEB-INF/resources/de.lang.php | 2 ++ WEB-INF/resources/en.lang.php | 1 + WEB-INF/resources/es.lang.php | 2 ++ WEB-INF/resources/et.lang.php | 2 ++ WEB-INF/resources/fa.lang.php | 2 ++ WEB-INF/resources/fi.lang.php | 2 ++ WEB-INF/resources/fr.lang.php | 2 ++ WEB-INF/resources/gr.lang.php | 2 ++ WEB-INF/resources/he.lang.php | 2 ++ WEB-INF/resources/hu.lang.php | 2 ++ WEB-INF/resources/it.lang.php | 2 ++ WEB-INF/resources/ja.lang.php | 2 ++ WEB-INF/resources/ko.lang.php | 2 ++ WEB-INF/resources/nl.lang.php | 2 ++ WEB-INF/resources/no.lang.php | 3 ++- WEB-INF/resources/pl.lang.php | 2 ++ WEB-INF/resources/pt-br.lang.php | 2 ++ WEB-INF/resources/pt.lang.php | 3 ++- WEB-INF/resources/ro.lang.php | 2 ++ WEB-INF/resources/ru.lang.php | 1 + WEB-INF/resources/sk.lang.php | 2 ++ WEB-INF/resources/sl.lang.php | 2 ++ WEB-INF/resources/sr.lang.php | 2 ++ WEB-INF/resources/sv.lang.php | 2 ++ WEB-INF/resources/tr.lang.php | 4 +++- WEB-INF/resources/zh-cn.lang.php | 2 ++ WEB-INF/resources/zh-tw.lang.php | 2 ++ initialize.php | 2 +- 32 files changed, 62 insertions(+), 9 deletions(-) diff --git a/2fa.php b/2fa.php index 7c1728d2..15d1a262 100644 --- a/2fa.php +++ b/2fa.php @@ -28,12 +28,9 @@ if ($request->isPost()) { // Validate user input. - if (!ttValidString($cl_2fa_code)) $err->add($i18n->get('error.field'), $i18n->get('fprm.2fa.2fa_code')); + if (!ttValidString($cl_2fa_code)) $err->add($i18n->get('error.field'), $i18n->get('form.2fa.2fa_code')); 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. @@ -82,6 +79,6 @@ $smarty->assign('show_hint', $show_hint); $smarty->assign('onload', 'onLoad="document.loginForm.'.(!$cl_login?'login':'password').'.focus()"'); $smarty->assign('about_text', $i18n->get('form.login.about')); -$smarty->assign('title', $i18n->get('title.login')); +$smarty->assign('title', $i18n->get('title.2fa')); $smarty->assign('content_page_name', '2fa.tpl'); $smarty->display('index.tpl'); diff --git a/WEB-INF/resources/ca.lang.php b/WEB-INF/resources/ca.lang.php index 3bd936dd..8be97f9b 100644 --- a/WEB-INF/resources/ca.lang.php +++ b/WEB-INF/resources/ca.lang.php @@ -277,6 +277,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', diff --git a/WEB-INF/resources/cs.lang.php b/WEB-INF/resources/cs.lang.php index a4f11602..a9cc1182 100644 --- a/WEB-INF/resources/cs.lang.php +++ b/WEB-INF/resources/cs.lang.php @@ -286,6 +286,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', diff --git a/WEB-INF/resources/da.lang.php b/WEB-INF/resources/da.lang.php index d1b991f4..74a239ca 100644 --- a/WEB-INF/resources/da.lang.php +++ b/WEB-INF/resources/da.lang.php @@ -266,6 +266,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', diff --git a/WEB-INF/resources/de.lang.php b/WEB-INF/resources/de.lang.php index 75c2c0a7..e55d0564 100644 --- a/WEB-INF/resources/de.lang.php +++ b/WEB-INF/resources/de.lang.php @@ -257,6 +257,8 @@ 'title.error' => 'Fehler', 'title.success' => 'Erfol', 'title.login' => 'Anmelden', +// TODO: translate the follolwing. +// 'title.2fa' => 'Two Factor Authentication', 'title.groups' => 'Gruppen', 'title.add_group' => 'Gruppe anlegen', 'title.edit_group' => 'Gruppe bearbeiten', diff --git a/WEB-INF/resources/en.lang.php b/WEB-INF/resources/en.lang.php index 7adacead..f48c96eb 100644 --- a/WEB-INF/resources/en.lang.php +++ b/WEB-INF/resources/en.lang.php @@ -241,6 +241,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', diff --git a/WEB-INF/resources/es.lang.php b/WEB-INF/resources/es.lang.php index a78f1e6f..b7fbecd1 100644 --- a/WEB-INF/resources/es.lang.php +++ b/WEB-INF/resources/es.lang.php @@ -276,6 +276,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', diff --git a/WEB-INF/resources/et.lang.php b/WEB-INF/resources/et.lang.php index 854a52e0..a81b9021 100644 --- a/WEB-INF/resources/et.lang.php +++ b/WEB-INF/resources/et.lang.php @@ -277,6 +277,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', diff --git a/WEB-INF/resources/fa.lang.php b/WEB-INF/resources/fa.lang.php index bda4a475..af722d6e 100644 --- a/WEB-INF/resources/fa.lang.php +++ b/WEB-INF/resources/fa.lang.php @@ -277,6 +277,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', diff --git a/WEB-INF/resources/fi.lang.php b/WEB-INF/resources/fi.lang.php index 1d90ef23..86ee80d8 100644 --- a/WEB-INF/resources/fi.lang.php +++ b/WEB-INF/resources/fi.lang.php @@ -270,6 +270,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', diff --git a/WEB-INF/resources/fr.lang.php b/WEB-INF/resources/fr.lang.php index ccb42935..9debf467 100644 --- a/WEB-INF/resources/fr.lang.php +++ b/WEB-INF/resources/fr.lang.php @@ -264,6 +264,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', diff --git a/WEB-INF/resources/gr.lang.php b/WEB-INF/resources/gr.lang.php index 4cf1f64f..587e719a 100644 --- a/WEB-INF/resources/gr.lang.php +++ b/WEB-INF/resources/gr.lang.php @@ -259,6 +259,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', diff --git a/WEB-INF/resources/he.lang.php b/WEB-INF/resources/he.lang.php index 4342ea84..bde356c9 100644 --- a/WEB-INF/resources/he.lang.php +++ b/WEB-INF/resources/he.lang.php @@ -285,6 +285,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', diff --git a/WEB-INF/resources/hu.lang.php b/WEB-INF/resources/hu.lang.php index b105d248..a8fc41ef 100644 --- a/WEB-INF/resources/hu.lang.php +++ b/WEB-INF/resources/hu.lang.php @@ -278,6 +278,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', diff --git a/WEB-INF/resources/it.lang.php b/WEB-INF/resources/it.lang.php index 16727cfe..af53176e 100644 --- a/WEB-INF/resources/it.lang.php +++ b/WEB-INF/resources/it.lang.php @@ -267,6 +267,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', diff --git a/WEB-INF/resources/ja.lang.php b/WEB-INF/resources/ja.lang.php index 46c70553..f4cfc565 100644 --- a/WEB-INF/resources/ja.lang.php +++ b/WEB-INF/resources/ja.lang.php @@ -282,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', diff --git a/WEB-INF/resources/ko.lang.php b/WEB-INF/resources/ko.lang.php index a8235693..1c677aa9 100644 --- a/WEB-INF/resources/ko.lang.php +++ b/WEB-INF/resources/ko.lang.php @@ -282,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', diff --git a/WEB-INF/resources/nl.lang.php b/WEB-INF/resources/nl.lang.php index 9a7c1707..dd89e356 100644 --- a/WEB-INF/resources/nl.lang.php +++ b/WEB-INF/resources/nl.lang.php @@ -235,6 +235,8 @@ 'title.error' => 'Fout', 'title.success' => 'Succes', 'title.login' => 'Aanmelden', +// TODO: translate the follolwing. +// 'title.2fa' => 'Two Factor Authentication', 'title.groups' => 'Groepen', 'title.add_group' => 'Groep toevoegen', 'title.edit_group' => 'Groep bewerken', diff --git a/WEB-INF/resources/no.lang.php b/WEB-INF/resources/no.lang.php index b7500bba..55cd54d4 100644 --- a/WEB-INF/resources/no.lang.php +++ b/WEB-INF/resources/no.lang.php @@ -281,7 +281,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', diff --git a/WEB-INF/resources/pl.lang.php b/WEB-INF/resources/pl.lang.php index 0e482eeb..021e30cd 100644 --- a/WEB-INF/resources/pl.lang.php +++ b/WEB-INF/resources/pl.lang.php @@ -271,6 +271,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', diff --git a/WEB-INF/resources/pt-br.lang.php b/WEB-INF/resources/pt-br.lang.php index f0b564de..b2b71608 100644 --- a/WEB-INF/resources/pt-br.lang.php +++ b/WEB-INF/resources/pt-br.lang.php @@ -238,6 +238,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', diff --git a/WEB-INF/resources/pt.lang.php b/WEB-INF/resources/pt.lang.php index dcb0d035..ebbc50b3 100644 --- a/WEB-INF/resources/pt.lang.php +++ b/WEB-INF/resources/pt.lang.php @@ -267,7 +267,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', diff --git a/WEB-INF/resources/ro.lang.php b/WEB-INF/resources/ro.lang.php index 7a5b41a3..670d00dc 100644 --- a/WEB-INF/resources/ro.lang.php +++ b/WEB-INF/resources/ro.lang.php @@ -283,6 +283,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', diff --git a/WEB-INF/resources/ru.lang.php b/WEB-INF/resources/ru.lang.php index da8d8b0f..9ef4dc02 100644 --- a/WEB-INF/resources/ru.lang.php +++ b/WEB-INF/resources/ru.lang.php @@ -236,6 +236,7 @@ 'title.error' => 'Ошибка', 'title.success' => 'Успех', 'title.login' => 'Вход в систему', +'title.2fa' => 'Двухфакторная аутентификация', 'title.groups' => 'Группы', 'title.add_group' => 'Добавление группы', 'title.edit_group' => 'Редактирование группы', diff --git a/WEB-INF/resources/sk.lang.php b/WEB-INF/resources/sk.lang.php index 3bdc0e4b..e75e0f2e 100644 --- a/WEB-INF/resources/sk.lang.php +++ b/WEB-INF/resources/sk.lang.php @@ -274,6 +274,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', diff --git a/WEB-INF/resources/sl.lang.php b/WEB-INF/resources/sl.lang.php index d7406f37..15026929 100644 --- a/WEB-INF/resources/sl.lang.php +++ b/WEB-INF/resources/sl.lang.php @@ -263,6 +263,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', diff --git a/WEB-INF/resources/sr.lang.php b/WEB-INF/resources/sr.lang.php index b26d4f04..44ad24e3 100644 --- a/WEB-INF/resources/sr.lang.php +++ b/WEB-INF/resources/sr.lang.php @@ -269,6 +269,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', diff --git a/WEB-INF/resources/sv.lang.php b/WEB-INF/resources/sv.lang.php index 1c139a0e..2098b705 100644 --- a/WEB-INF/resources/sv.lang.php +++ b/WEB-INF/resources/sv.lang.php @@ -265,6 +265,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', diff --git a/WEB-INF/resources/tr.lang.php b/WEB-INF/resources/tr.lang.php index 9f57fa3e..4df3f5bd 100644 --- a/WEB-INF/resources/tr.lang.php +++ b/WEB-INF/resources/tr.lang.php @@ -292,7 +292,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', diff --git a/WEB-INF/resources/zh-cn.lang.php b/WEB-INF/resources/zh-cn.lang.php index 400529f6..7fc1e3a1 100644 --- a/WEB-INF/resources/zh-cn.lang.php +++ b/WEB-INF/resources/zh-cn.lang.php @@ -274,6 +274,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', diff --git a/WEB-INF/resources/zh-tw.lang.php b/WEB-INF/resources/zh-tw.lang.php index 3ece1f0f..0d3885b2 100644 --- a/WEB-INF/resources/zh-tw.lang.php +++ b/WEB-INF/resources/zh-tw.lang.php @@ -279,6 +279,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', diff --git a/initialize.php b/initialize.php index 2f537eb6..3fb0921f 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.1.5662"); +define("APP_VERSION", "1.21.1.5663"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From 3ce1ed036de850af767d163d322dad93907a9df6 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Wed, 16 Mar 2022 19:03:50 +0000 Subject: [PATCH 008/168] Smoe more work in progress on 2fa. --- WEB-INF/resources/ca.lang.php | 5 ++ WEB-INF/resources/cs.lang.php | 5 ++ WEB-INF/resources/da.lang.php | 5 ++ WEB-INF/resources/de.lang.php | 5 ++ WEB-INF/resources/en.lang.php | 5 +- WEB-INF/resources/es.lang.php | 5 ++ WEB-INF/resources/et.lang.php | 5 ++ WEB-INF/resources/fa.lang.php | 5 ++ WEB-INF/resources/fi.lang.php | 5 ++ WEB-INF/resources/fr.lang.php | 5 ++ WEB-INF/resources/gr.lang.php | 5 ++ WEB-INF/resources/he.lang.php | 5 ++ WEB-INF/resources/hu.lang.php | 5 ++ WEB-INF/resources/it.lang.php | 5 ++ WEB-INF/resources/ja.lang.php | 5 ++ WEB-INF/resources/ko.lang.php | 5 ++ WEB-INF/resources/nl.lang.php | 5 ++ WEB-INF/resources/no.lang.php | 5 ++ WEB-INF/resources/pl.lang.php | 5 ++ WEB-INF/resources/pt-br.lang.php | 5 ++ WEB-INF/resources/pt.lang.php | 5 ++ WEB-INF/resources/ro.lang.php | 5 ++ WEB-INF/resources/ru.lang.php | 4 + WEB-INF/resources/sk.lang.php | 5 ++ WEB-INF/resources/sl.lang.php | 5 ++ WEB-INF/resources/sr.lang.php | 5 ++ WEB-INF/resources/sv.lang.php | 5 ++ WEB-INF/resources/tr.lang.php | 5 ++ WEB-INF/resources/zh-cn.lang.php | 5 ++ WEB-INF/resources/zh-tw.lang.php | 5 ++ initialize.php | 2 +- login.php | 121 ++++++++++++++++++++++--------- 32 files changed, 236 insertions(+), 36 deletions(-) diff --git a/WEB-INF/resources/ca.lang.php b/WEB-INF/resources/ca.lang.php index 8be97f9b..cdf0c7ed 100644 --- a/WEB-INF/resources/ca.lang.php +++ b/WEB-INF/resources/ca.lang.php @@ -422,6 +422,11 @@ // 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.2fa_code' => '2FA code', diff --git a/WEB-INF/resources/cs.lang.php b/WEB-INF/resources/cs.lang.php index a9cc1182..2c64a859 100644 --- a/WEB-INF/resources/cs.lang.php +++ b/WEB-INF/resources/cs.lang.php @@ -429,6 +429,11 @@ // 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.2fa_code' => '2FA code', diff --git a/WEB-INF/resources/da.lang.php b/WEB-INF/resources/da.lang.php index 74a239ca..53644ed9 100644 --- a/WEB-INF/resources/da.lang.php +++ b/WEB-INF/resources/da.lang.php @@ -406,6 +406,11 @@ '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.2fa_code' => '2FA code', diff --git a/WEB-INF/resources/de.lang.php b/WEB-INF/resources/de.lang.php index e55d0564..006d7b89 100644 --- a/WEB-INF/resources/de.lang.php +++ b/WEB-INF/resources/de.lang.php @@ -393,6 +393,11 @@ '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. +// 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.2fa_code' => '2FA code', diff --git a/WEB-INF/resources/en.lang.php b/WEB-INF/resources/en.lang.php index f48c96eb..3d5e59a8 100644 --- a/WEB-INF/resources/en.lang.php +++ b/WEB-INF/resources/en.lang.php @@ -372,9 +372,12 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => 'Forgot password?', -'form.login.2fa_code' => '2FA code', '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.2fa_code' => '2FA code', diff --git a/WEB-INF/resources/es.lang.php b/WEB-INF/resources/es.lang.php index b7fbecd1..9ce3cb25 100644 --- a/WEB-INF/resources/es.lang.php +++ b/WEB-INF/resources/es.lang.php @@ -426,6 +426,11 @@ // 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.2fa_code' => '2FA code', diff --git a/WEB-INF/resources/et.lang.php b/WEB-INF/resources/et.lang.php index a81b9021..884d0e32 100644 --- a/WEB-INF/resources/et.lang.php +++ b/WEB-INF/resources/et.lang.php @@ -414,6 +414,11 @@ '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.2fa_code' => '2FA code', diff --git a/WEB-INF/resources/fa.lang.php b/WEB-INF/resources/fa.lang.php index af722d6e..2eb5e532 100644 --- a/WEB-INF/resources/fa.lang.php +++ b/WEB-INF/resources/fa.lang.php @@ -424,6 +424,11 @@ // 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.2fa_code' => '2FA code', diff --git a/WEB-INF/resources/fi.lang.php b/WEB-INF/resources/fi.lang.php index 86ee80d8..5b85ba63 100644 --- a/WEB-INF/resources/fi.lang.php +++ b/WEB-INF/resources/fi.lang.php @@ -412,6 +412,11 @@ // 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.2fa_code' => '2FA code', diff --git a/WEB-INF/resources/fr.lang.php b/WEB-INF/resources/fr.lang.php index 9debf467..19c3fbaa 100644 --- a/WEB-INF/resources/fr.lang.php +++ b/WEB-INF/resources/fr.lang.php @@ -403,6 +403,11 @@ '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.2fa_code' => '2FA code', diff --git a/WEB-INF/resources/gr.lang.php b/WEB-INF/resources/gr.lang.php index 587e719a..9c07dd52 100644 --- a/WEB-INF/resources/gr.lang.php +++ b/WEB-INF/resources/gr.lang.php @@ -402,6 +402,11 @@ '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.2fa_code' => '2FA code', diff --git a/WEB-INF/resources/he.lang.php b/WEB-INF/resources/he.lang.php index bde356c9..3cb53daf 100644 --- a/WEB-INF/resources/he.lang.php +++ b/WEB-INF/resources/he.lang.php @@ -430,6 +430,11 @@ // '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.2fa_code' => '2FA code', diff --git a/WEB-INF/resources/hu.lang.php b/WEB-INF/resources/hu.lang.php index a8fc41ef..61438d08 100644 --- a/WEB-INF/resources/hu.lang.php +++ b/WEB-INF/resources/hu.lang.php @@ -423,6 +423,11 @@ // 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.2fa_code' => '2FA code', diff --git a/WEB-INF/resources/it.lang.php b/WEB-INF/resources/it.lang.php index af53176e..373587f5 100644 --- a/WEB-INF/resources/it.lang.php +++ b/WEB-INF/resources/it.lang.php @@ -412,6 +412,11 @@ '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.2fa_code' => '2FA code', diff --git a/WEB-INF/resources/ja.lang.php b/WEB-INF/resources/ja.lang.php index f4cfc565..b86ed26e 100644 --- a/WEB-INF/resources/ja.lang.php +++ b/WEB-INF/resources/ja.lang.php @@ -424,6 +424,11 @@ // 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.2fa_code' => '2FA code', diff --git a/WEB-INF/resources/ko.lang.php b/WEB-INF/resources/ko.lang.php index 1c677aa9..1a810638 100644 --- a/WEB-INF/resources/ko.lang.php +++ b/WEB-INF/resources/ko.lang.php @@ -428,6 +428,11 @@ // 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.2fa_code' => '2FA code', diff --git a/WEB-INF/resources/nl.lang.php b/WEB-INF/resources/nl.lang.php index dd89e356..b2a7fd84 100644 --- a/WEB-INF/resources/nl.lang.php +++ b/WEB-INF/resources/nl.lang.php @@ -367,6 +367,11 @@ '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. +// 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.2fa_code' => '2FA code', diff --git a/WEB-INF/resources/no.lang.php b/WEB-INF/resources/no.lang.php index 55cd54d4..6cabab82 100644 --- a/WEB-INF/resources/no.lang.php +++ b/WEB-INF/resources/no.lang.php @@ -426,6 +426,11 @@ // '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.2fa_code' => '2FA code', diff --git a/WEB-INF/resources/pl.lang.php b/WEB-INF/resources/pl.lang.php index 021e30cd..0aac60b4 100644 --- a/WEB-INF/resources/pl.lang.php +++ b/WEB-INF/resources/pl.lang.php @@ -414,6 +414,11 @@ '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.2fa_code' => '2FA code', diff --git a/WEB-INF/resources/pt-br.lang.php b/WEB-INF/resources/pt-br.lang.php index b2b71608..d311c7a8 100644 --- a/WEB-INF/resources/pt-br.lang.php +++ b/WEB-INF/resources/pt-br.lang.php @@ -366,6 +366,11 @@ '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.2fa_code' => '2FA code', diff --git a/WEB-INF/resources/pt.lang.php b/WEB-INF/resources/pt.lang.php index ebbc50b3..942ce209 100644 --- a/WEB-INF/resources/pt.lang.php +++ b/WEB-INF/resources/pt.lang.php @@ -406,6 +406,11 @@ // 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.2fa_code' => '2FA code', diff --git a/WEB-INF/resources/ro.lang.php b/WEB-INF/resources/ro.lang.php index 670d00dc..39e507f7 100644 --- a/WEB-INF/resources/ro.lang.php +++ b/WEB-INF/resources/ro.lang.php @@ -428,6 +428,11 @@ // 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.2fa_code' => '2FA code', diff --git a/WEB-INF/resources/ru.lang.php b/WEB-INF/resources/ru.lang.php index 9ef4dc02..98b6694a 100644 --- a/WEB-INF/resources/ru.lang.php +++ b/WEB-INF/resources/ru.lang.php @@ -367,6 +367,10 @@ '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.2fa_code' => '2FA код', diff --git a/WEB-INF/resources/sk.lang.php b/WEB-INF/resources/sk.lang.php index e75e0f2e..08bda858 100644 --- a/WEB-INF/resources/sk.lang.php +++ b/WEB-INF/resources/sk.lang.php @@ -417,6 +417,11 @@ '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.2fa_code' => '2FA code', diff --git a/WEB-INF/resources/sl.lang.php b/WEB-INF/resources/sl.lang.php index 15026929..804b7fc8 100644 --- a/WEB-INF/resources/sl.lang.php +++ b/WEB-INF/resources/sl.lang.php @@ -400,6 +400,11 @@ // '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.2fa_code' => '2FA code', diff --git a/WEB-INF/resources/sr.lang.php b/WEB-INF/resources/sr.lang.php index 44ad24e3..6f349550 100644 --- a/WEB-INF/resources/sr.lang.php +++ b/WEB-INF/resources/sr.lang.php @@ -414,6 +414,11 @@ // '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.2fa_code' => '2FA code', diff --git a/WEB-INF/resources/sv.lang.php b/WEB-INF/resources/sv.lang.php index 2098b705..4cb93fa0 100644 --- a/WEB-INF/resources/sv.lang.php +++ b/WEB-INF/resources/sv.lang.php @@ -412,6 +412,11 @@ // '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.2fa_code' => '2FA code', diff --git a/WEB-INF/resources/tr.lang.php b/WEB-INF/resources/tr.lang.php index 4df3f5bd..12bdebf3 100644 --- a/WEB-INF/resources/tr.lang.php +++ b/WEB-INF/resources/tr.lang.php @@ -436,6 +436,11 @@ // 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.2fa_code' => '2FA code', diff --git a/WEB-INF/resources/zh-cn.lang.php b/WEB-INF/resources/zh-cn.lang.php index 7fc1e3a1..c37b5661 100644 --- a/WEB-INF/resources/zh-cn.lang.php +++ b/WEB-INF/resources/zh-cn.lang.php @@ -419,6 +419,11 @@ // '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.2fa_code' => '2FA code', diff --git a/WEB-INF/resources/zh-tw.lang.php b/WEB-INF/resources/zh-tw.lang.php index 0d3885b2..fd78f3e3 100644 --- a/WEB-INF/resources/zh-tw.lang.php +++ b/WEB-INF/resources/zh-tw.lang.php @@ -422,6 +422,11 @@ // 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.2fa_code' => '2FA code', diff --git a/initialize.php b/initialize.php index 3fb0921f..c8b41c6d 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.1.5663"); +define("APP_VERSION", "1.21.1.5664"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); diff --git a/login.php b/login.php index 5ea77d18..da59757b 100644 --- a/login.php +++ b/login.php @@ -4,8 +4,9 @@ require_once('initialize.php'); import('form.Form'); -import('ttOrgHelper'); +//import('ttOrgHelper'); import('ttUser'); +import('ttUserHelper'); // Access checks. if ($request->isPost()) { @@ -33,46 +34,98 @@ 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; - - // Remember user login in a cookie. - setcookie(LOGIN_COOKIE_NAME, $cl_login, time() + COOKIE_EXPIRE, '/'); - - $user = new ttUser(null, $auth->getUserId()); - - // Determine if we have to additionally use two-factor authentication. - $config = $user->getConfigHelper(); - $use2FA = $config->getDefinedValue('2fa'); - if ($use2FA) { - - // TODO: send 2fa code to user. - $auth->doLogout(); - - header('Location: 2fa.php'); - exit(); - } - - // 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(); + // Check user login. + $loginSucceeded = $auth->doLogin($cl_login, $cl_password); + } + + // Do we have to use 2FA? + if ($err->no() && $loginSucceeded) { + $user = new ttUser(null, $auth->getUserId()); + + // Determine if we have to additionally use two-factor authentication. + $config = $user->getConfigHelper(); + $use2FA = $config->getDefinedValue('2fa'); + } + + // If we have to use 2FA, create and email auith code to user. + if ($use2FA) { + // 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; + + // Remember user login in a cookie. + setcookie(LOGIN_COOKIE_NAME, $cl_login, time() + COOKIE_EXPIRE, '/'); + + // 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(); + } else + $err->add($i18n->get('error.auth')); } // isPost if(!isTrue('MULTIORG_MODE') && !ttOrgHelper::getOrgs()) From 3d74117cda7abf4b2ae2634eb1a623d0a04794e0 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Wed, 16 Mar 2022 22:19:44 +0000 Subject: [PATCH 009/168] A bit more progress on 2fa. --- 2fa.php | 74 ++++++++------------------------ WEB-INF/resources/ca.lang.php | 1 + WEB-INF/resources/cs.lang.php | 1 + WEB-INF/resources/da.lang.php | 1 + WEB-INF/resources/de.lang.php | 1 + WEB-INF/resources/en.lang.php | 1 + WEB-INF/resources/es.lang.php | 1 + WEB-INF/resources/et.lang.php | 1 + WEB-INF/resources/fa.lang.php | 1 + WEB-INF/resources/fi.lang.php | 1 + WEB-INF/resources/fr.lang.php | 1 + WEB-INF/resources/gr.lang.php | 1 + WEB-INF/resources/he.lang.php | 1 + WEB-INF/resources/hu.lang.php | 1 + WEB-INF/resources/it.lang.php | 1 + WEB-INF/resources/ja.lang.php | 1 + WEB-INF/resources/ko.lang.php | 1 + WEB-INF/resources/nl.lang.php | 2 + WEB-INF/resources/no.lang.php | 1 + WEB-INF/resources/pl.lang.php | 1 + WEB-INF/resources/pt-br.lang.php | 1 + WEB-INF/resources/pt.lang.php | 1 + WEB-INF/resources/ro.lang.php | 1 + WEB-INF/resources/ru.lang.php | 1 + WEB-INF/resources/sk.lang.php | 1 + WEB-INF/resources/sl.lang.php | 1 + WEB-INF/resources/sr.lang.php | 1 + WEB-INF/resources/sv.lang.php | 1 + WEB-INF/resources/tr.lang.php | 1 + WEB-INF/resources/zh-cn.lang.php | 1 + WEB-INF/resources/zh-tw.lang.php | 1 + WEB-INF/templates/2fa.tpl | 11 +---- login.php | 9 ++-- 33 files changed, 56 insertions(+), 69 deletions(-) diff --git a/2fa.php b/2fa.php index 15d1a262..005b4ea4 100644 --- a/2fa.php +++ b/2fa.php @@ -4,81 +4,43 @@ require_once('initialize.php'); 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(); - } -} +// ... anything to check? // End of access checks. $cl_2fa_code = $request->getParameter('2fa_code'); -$cl_password = $request->getParameter('password'); $form = new Form('2faForm'); $form->addInput(array('type'=>'text','maxlength'=>'100','name'=>'2fa_code','value'=>$cl_2fa_code)); -$form->addInput(array('type'=>'hidden','name'=>'browser_today','value'=>'')); // User current date, which gets filled in on btn_login click. -$form->addInput(array('type'=>'submit','name'=>'btn_login','onclick'=>'browser_today.value=get_date()','value'=>$i18n->get('button.login'))); +$form->addInput(array('type'=>'submit','name'=>'btn_login','value'=>$i18n->get('button.login'))); if ($request->isPost()) { // Validate user input. if (!ttValidString($cl_2fa_code)) $err->add($i18n->get('error.field'), $i18n->get('form.2fa.2fa_code')); - if ($err->no()) { - - 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; - - // Remember user login in a cookie. - setcookie(LOGIN_COOKIE_NAME, $cl_login, time() + COOKIE_EXPIRE, '/'); - - $user = new ttUser(null, $auth->getUserId()); + // Get user id. + $user_id = ttUserHelper::getUserIdByTmpRef($cl_2fa_code); + if (!$user_id) $err->add($i18n->get('error.2fa_code')); - // Determine if we have to additionally use two-factor authentication. - $config = $user->getConfigHelper(); - $use2FA = $config->getDefinedValue('2fa'); - if ($use2FA) { - - // TODO: send 2fa code to user. - $auth->doLogout(); - - header('Location: 2fa.php'); - exit(); - } - - // 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(); - } else - $err->add($i18n->get('error.auth')); + if ($err->no()) { + $user = new ttUser(null, $user_id); // Note: reusing $user from initialize.php. + $auth->setAuth($user_id, $user->login); + + // Redirect, depending on user role. + if ($user->isClient()) { + header('Location: reports.php'); + } else { + header('Location: time.php'); + } + exit(); } } // isPost -if(!isTrue('MULTIORG_MODE') && !ttOrgHelper::getOrgs()) - $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' == 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); -$smarty->assign('onload', 'onLoad="document.loginForm.'.(!$cl_login?'login':'password').'.focus()"'); -$smarty->assign('about_text', $i18n->get('form.login.about')); +$smarty->assign('onload', 'onLoad="document.2faForm.2fa_code.focus()"'); $smarty->assign('title', $i18n->get('title.2fa')); $smarty->assign('content_page_name', '2fa.tpl'); $smarty->display('index.tpl'); diff --git a/WEB-INF/resources/ca.lang.php b/WEB-INF/resources/ca.lang.php index cdf0c7ed..ef2d2ea6 100644 --- a/WEB-INF/resources/ca.lang.php +++ b/WEB-INF/resources/ca.lang.php @@ -74,6 +74,7 @@ // '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.', diff --git a/WEB-INF/resources/cs.lang.php b/WEB-INF/resources/cs.lang.php index 2c64a859..0f44c1cc 100644 --- a/WEB-INF/resources/cs.lang.php +++ b/WEB-INF/resources/cs.lang.php @@ -76,6 +76,7 @@ // '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.', diff --git a/WEB-INF/resources/da.lang.php b/WEB-INF/resources/da.lang.php index 53644ed9..a159edfa 100644 --- a/WEB-INF/resources/da.lang.php +++ b/WEB-INF/resources/da.lang.php @@ -67,6 +67,7 @@ // '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. diff --git a/WEB-INF/resources/de.lang.php b/WEB-INF/resources/de.lang.php index 006d7b89..5e2c19d5 100644 --- a/WEB-INF/resources/de.lang.php +++ b/WEB-INF/resources/de.lang.php @@ -65,6 +65,7 @@ 'error.record' => 'Eintrag auswählen.', 'error.auth' => 'Benutzername oder Passwort ungültig.', // TODO: translate the following. +// 'error.2fa_code' => 'Invalid 2FA code.', // 'error.weak_password' => 'Weak password.', 'error.user_exists' => 'Benutzer mit diesem Konto ist bereits vorhanden.', 'error.object_exists' => 'Objekt mit diesem Namen ist bereits vorhanden.', diff --git a/WEB-INF/resources/en.lang.php b/WEB-INF/resources/en.lang.php index 3d5e59a8..89cfcd26 100644 --- a/WEB-INF/resources/en.lang.php +++ b/WEB-INF/resources/en.lang.php @@ -60,6 +60,7 @@ '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.', diff --git a/WEB-INF/resources/es.lang.php b/WEB-INF/resources/es.lang.php index 9ce3cb25..96988bdc 100644 --- a/WEB-INF/resources/es.lang.php +++ b/WEB-INF/resources/es.lang.php @@ -72,6 +72,7 @@ // '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.', diff --git a/WEB-INF/resources/et.lang.php b/WEB-INF/resources/et.lang.php index 884d0e32..035fb644 100644 --- a/WEB-INF/resources/et.lang.php +++ b/WEB-INF/resources/et.lang.php @@ -66,6 +66,7 @@ '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.', diff --git a/WEB-INF/resources/fa.lang.php b/WEB-INF/resources/fa.lang.php index 2eb5e532..e040921c 100644 --- a/WEB-INF/resources/fa.lang.php +++ b/WEB-INF/resources/fa.lang.php @@ -72,6 +72,7 @@ // '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. diff --git a/WEB-INF/resources/fi.lang.php b/WEB-INF/resources/fi.lang.php index 5b85ba63..3dfa9443 100644 --- a/WEB-INF/resources/fi.lang.php +++ b/WEB-INF/resources/fi.lang.php @@ -69,6 +69,7 @@ // '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. diff --git a/WEB-INF/resources/fr.lang.php b/WEB-INF/resources/fr.lang.php index 19c3fbaa..a7ef4f23 100644 --- a/WEB-INF/resources/fr.lang.php +++ b/WEB-INF/resources/fr.lang.php @@ -67,6 +67,7 @@ // '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. diff --git a/WEB-INF/resources/gr.lang.php b/WEB-INF/resources/gr.lang.php index 9c07dd52..72037b0e 100644 --- a/WEB-INF/resources/gr.lang.php +++ b/WEB-INF/resources/gr.lang.php @@ -64,6 +64,7 @@ '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' => 'Το αντικείμενο με αυτό το όνομα υπάρχει ήδη.', diff --git a/WEB-INF/resources/he.lang.php b/WEB-INF/resources/he.lang.php index 3cb53daf..1e03b4f7 100644 --- a/WEB-INF/resources/he.lang.php +++ b/WEB-INF/resources/he.lang.php @@ -82,6 +82,7 @@ // '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. diff --git a/WEB-INF/resources/hu.lang.php b/WEB-INF/resources/hu.lang.php index 61438d08..1b4cf6d2 100644 --- a/WEB-INF/resources/hu.lang.php +++ b/WEB-INF/resources/hu.lang.php @@ -74,6 +74,7 @@ // '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.', diff --git a/WEB-INF/resources/it.lang.php b/WEB-INF/resources/it.lang.php index 373587f5..8c12f14c 100644 --- a/WEB-INF/resources/it.lang.php +++ b/WEB-INF/resources/it.lang.php @@ -66,6 +66,7 @@ '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. diff --git a/WEB-INF/resources/ja.lang.php b/WEB-INF/resources/ja.lang.php index b86ed26e..30156d52 100644 --- a/WEB-INF/resources/ja.lang.php +++ b/WEB-INF/resources/ja.lang.php @@ -74,6 +74,7 @@ // '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. diff --git a/WEB-INF/resources/ko.lang.php b/WEB-INF/resources/ko.lang.php index 1a810638..49e904b0 100644 --- a/WEB-INF/resources/ko.lang.php +++ b/WEB-INF/resources/ko.lang.php @@ -74,6 +74,7 @@ // '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. diff --git a/WEB-INF/resources/nl.lang.php b/WEB-INF/resources/nl.lang.php index b2a7fd84..85899c8a 100644 --- a/WEB-INF/resources/nl.lang.php +++ b/WEB-INF/resources/nl.lang.php @@ -59,6 +59,8 @@ 'error.report' => 'Kies rapport.', 'error.record' => 'Kies record.', 'error.auth' => 'Onjuiste inlognaam of wachtwoord.', +// TODO: translate the following. +// 'error.2fa_code' => 'Invalid 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.', diff --git a/WEB-INF/resources/no.lang.php b/WEB-INF/resources/no.lang.php index 6cabab82..75910ddc 100644 --- a/WEB-INF/resources/no.lang.php +++ b/WEB-INF/resources/no.lang.php @@ -75,6 +75,7 @@ // '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. diff --git a/WEB-INF/resources/pl.lang.php b/WEB-INF/resources/pl.lang.php index 0aac60b4..4a4f9e72 100644 --- a/WEB-INF/resources/pl.lang.php +++ b/WEB-INF/resources/pl.lang.php @@ -70,6 +70,7 @@ // '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. diff --git a/WEB-INF/resources/pt-br.lang.php b/WEB-INF/resources/pt-br.lang.php index d311c7a8..ef122e23 100644 --- a/WEB-INF/resources/pt-br.lang.php +++ b/WEB-INF/resources/pt-br.lang.php @@ -60,6 +60,7 @@ '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.', diff --git a/WEB-INF/resources/pt.lang.php b/WEB-INF/resources/pt.lang.php index 942ce209..d6ded651 100644 --- a/WEB-INF/resources/pt.lang.php +++ b/WEB-INF/resources/pt.lang.php @@ -72,6 +72,7 @@ // '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.', diff --git a/WEB-INF/resources/ro.lang.php b/WEB-INF/resources/ro.lang.php index 39e507f7..39e2f379 100644 --- a/WEB-INF/resources/ro.lang.php +++ b/WEB-INF/resources/ro.lang.php @@ -78,6 +78,7 @@ // '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.', diff --git a/WEB-INF/resources/ru.lang.php b/WEB-INF/resources/ru.lang.php index 98b6694a..e4474b2f 100644 --- a/WEB-INF/resources/ru.lang.php +++ b/WEB-INF/resources/ru.lang.php @@ -59,6 +59,7 @@ 'error.report' => 'Выберите отчёт.', 'error.record' => 'Выберите запись.', 'error.auth' => 'Неправильно введен логин или пароль.', +'error.2fa_code' => 'Неверный код 2FA.', 'error.weak_password' => 'Слабый пароль.', 'error.user_exists' => 'Пользователь с таким логином уже существует.', 'error.object_exists' => 'Объект с таким именем уже есть.', diff --git a/WEB-INF/resources/sk.lang.php b/WEB-INF/resources/sk.lang.php index 08bda858..76337a61 100644 --- a/WEB-INF/resources/sk.lang.php +++ b/WEB-INF/resources/sk.lang.php @@ -71,6 +71,7 @@ // '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. diff --git a/WEB-INF/resources/sl.lang.php b/WEB-INF/resources/sl.lang.php index 804b7fc8..9730c474 100644 --- a/WEB-INF/resources/sl.lang.php +++ b/WEB-INF/resources/sl.lang.php @@ -69,6 +69,7 @@ // '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.', diff --git a/WEB-INF/resources/sr.lang.php b/WEB-INF/resources/sr.lang.php index 6f349550..3c490d20 100644 --- a/WEB-INF/resources/sr.lang.php +++ b/WEB-INF/resources/sr.lang.php @@ -67,6 +67,7 @@ // '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. diff --git a/WEB-INF/resources/sv.lang.php b/WEB-INF/resources/sv.lang.php index 4cb93fa0..30ed6da0 100644 --- a/WEB-INF/resources/sv.lang.php +++ b/WEB-INF/resources/sv.lang.php @@ -66,6 +66,7 @@ // '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. diff --git a/WEB-INF/resources/tr.lang.php b/WEB-INF/resources/tr.lang.php index 12bdebf3..5251ee8f 100644 --- a/WEB-INF/resources/tr.lang.php +++ b/WEB-INF/resources/tr.lang.php @@ -81,6 +81,7 @@ // '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.', diff --git a/WEB-INF/resources/zh-cn.lang.php b/WEB-INF/resources/zh-cn.lang.php index c37b5661..fa7f78a3 100644 --- a/WEB-INF/resources/zh-cn.lang.php +++ b/WEB-INF/resources/zh-cn.lang.php @@ -69,6 +69,7 @@ // '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. diff --git a/WEB-INF/resources/zh-tw.lang.php b/WEB-INF/resources/zh-tw.lang.php index fd78f3e3..71587399 100644 --- a/WEB-INF/resources/zh-tw.lang.php +++ b/WEB-INF/resources/zh-tw.lang.php @@ -72,6 +72,7 @@ // '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. diff --git a/WEB-INF/templates/2fa.tpl b/WEB-INF/templates/2fa.tpl index e634b624..1754f035 100644 --- a/WEB-INF/templates/2fa.tpl +++ b/WEB-INF/templates/2fa.tpl @@ -1,18 +1,11 @@ {* Copyright (c) Anuko International Ltd. https://www.anuko.com License: See license.txt *} - - {$forms.2faForm.open} - + - + diff --git a/login.php b/login.php index da59757b..dbf5d0c3 100644 --- a/login.php +++ b/login.php @@ -35,6 +35,7 @@ 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. @@ -42,6 +43,8 @@ // Check user login. $loginSucceeded = $auth->doLogin($cl_login, $cl_password); + if (!$loginSucceeded) + $err->add($i18n->get('error.auth')); } // Do we have to use 2FA? @@ -53,7 +56,7 @@ $use2FA = $config->getDefinedValue('2fa'); } - // If we have to use 2FA, create and email auith code to user. + // If we have to use 2FA, create and email auth code to user. if ($use2FA) { // To keep things simple, we use the same code as for password resets. $cryptographically_strong = true; @@ -106,7 +109,6 @@ } 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) @@ -124,8 +126,7 @@ header('Location: time.php'); } exit(); - } else - $err->add($i18n->get('error.auth')); + } } // isPost if(!isTrue('MULTIORG_MODE') && !ttOrgHelper::getOrgs()) From 274906af2c15cda962c21c8d8d7addc42482b7fd Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Thu, 17 Mar 2022 00:32:38 +0000 Subject: [PATCH 010/168] Added a hint for 2fa.php page to translation files. --- WEB-INF/resources/ca.lang.php | 1 + WEB-INF/resources/cs.lang.php | 1 + WEB-INF/resources/da.lang.php | 1 + WEB-INF/resources/de.lang.php | 1 + WEB-INF/resources/en.lang.php | 1 + WEB-INF/resources/es.lang.php | 1 + WEB-INF/resources/et.lang.php | 1 + WEB-INF/resources/fa.lang.php | 1 + WEB-INF/resources/fi.lang.php | 1 + WEB-INF/resources/fr.lang.php | 1 + WEB-INF/resources/gr.lang.php | 1 + WEB-INF/resources/he.lang.php | 1 + WEB-INF/resources/hu.lang.php | 1 + WEB-INF/resources/it.lang.php | 1 + WEB-INF/resources/ja.lang.php | 1 + WEB-INF/resources/ko.lang.php | 1 + WEB-INF/resources/nl.lang.php | 1 + WEB-INF/resources/no.lang.php | 1 + WEB-INF/resources/pl.lang.php | 1 + WEB-INF/resources/pt-br.lang.php | 1 + WEB-INF/resources/pt.lang.php | 1 + WEB-INF/resources/ro.lang.php | 1 + WEB-INF/resources/ru.lang.php | 1 + WEB-INF/resources/sk.lang.php | 1 + WEB-INF/resources/sl.lang.php | 1 + WEB-INF/resources/sr.lang.php | 1 + WEB-INF/resources/sv.lang.php | 1 + WEB-INF/resources/tr.lang.php | 1 + WEB-INF/resources/zh-cn.lang.php | 1 + WEB-INF/resources/zh-tw.lang.php | 1 + WEB-INF/templates/2fa.tpl | 1 + initialize.php | 2 +- 32 files changed, 32 insertions(+), 1 deletion(-) diff --git a/WEB-INF/resources/ca.lang.php b/WEB-INF/resources/ca.lang.php index ef2d2ea6..5db49232 100644 --- a/WEB-INF/resources/ca.lang.php +++ b/WEB-INF/resources/ca.lang.php @@ -430,6 +430,7 @@ // 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. diff --git a/WEB-INF/resources/cs.lang.php b/WEB-INF/resources/cs.lang.php index 0f44c1cc..c689bafb 100644 --- a/WEB-INF/resources/cs.lang.php +++ b/WEB-INF/resources/cs.lang.php @@ -437,6 +437,7 @@ // 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. diff --git a/WEB-INF/resources/da.lang.php b/WEB-INF/resources/da.lang.php index a159edfa..e2a0da44 100644 --- a/WEB-INF/resources/da.lang.php +++ b/WEB-INF/resources/da.lang.php @@ -414,6 +414,7 @@ // 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. diff --git a/WEB-INF/resources/de.lang.php b/WEB-INF/resources/de.lang.php index 5e2c19d5..10c6cba4 100644 --- a/WEB-INF/resources/de.lang.php +++ b/WEB-INF/resources/de.lang.php @@ -401,6 +401,7 @@ // 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. diff --git a/WEB-INF/resources/en.lang.php b/WEB-INF/resources/en.lang.php index 89cfcd26..fb31f0f8 100644 --- a/WEB-INF/resources/en.lang.php +++ b/WEB-INF/resources/en.lang.php @@ -380,6 +380,7 @@ '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. diff --git a/WEB-INF/resources/es.lang.php b/WEB-INF/resources/es.lang.php index 96988bdc..bbb923fd 100644 --- a/WEB-INF/resources/es.lang.php +++ b/WEB-INF/resources/es.lang.php @@ -434,6 +434,7 @@ // 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. diff --git a/WEB-INF/resources/et.lang.php b/WEB-INF/resources/et.lang.php index 035fb644..e698f939 100644 --- a/WEB-INF/resources/et.lang.php +++ b/WEB-INF/resources/et.lang.php @@ -422,6 +422,7 @@ // 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. diff --git a/WEB-INF/resources/fa.lang.php b/WEB-INF/resources/fa.lang.php index e040921c..7a2e8fbc 100644 --- a/WEB-INF/resources/fa.lang.php +++ b/WEB-INF/resources/fa.lang.php @@ -432,6 +432,7 @@ // 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. diff --git a/WEB-INF/resources/fi.lang.php b/WEB-INF/resources/fi.lang.php index 3dfa9443..4b6a1832 100644 --- a/WEB-INF/resources/fi.lang.php +++ b/WEB-INF/resources/fi.lang.php @@ -420,6 +420,7 @@ // 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. diff --git a/WEB-INF/resources/fr.lang.php b/WEB-INF/resources/fr.lang.php index a7ef4f23..b6d17027 100644 --- a/WEB-INF/resources/fr.lang.php +++ b/WEB-INF/resources/fr.lang.php @@ -411,6 +411,7 @@ // 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. diff --git a/WEB-INF/resources/gr.lang.php b/WEB-INF/resources/gr.lang.php index 72037b0e..a7eec732 100644 --- a/WEB-INF/resources/gr.lang.php +++ b/WEB-INF/resources/gr.lang.php @@ -410,6 +410,7 @@ // 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. diff --git a/WEB-INF/resources/he.lang.php b/WEB-INF/resources/he.lang.php index 1e03b4f7..e4ebe519 100644 --- a/WEB-INF/resources/he.lang.php +++ b/WEB-INF/resources/he.lang.php @@ -438,6 +438,7 @@ // 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. diff --git a/WEB-INF/resources/hu.lang.php b/WEB-INF/resources/hu.lang.php index 1b4cf6d2..7c761eec 100644 --- a/WEB-INF/resources/hu.lang.php +++ b/WEB-INF/resources/hu.lang.php @@ -431,6 +431,7 @@ // 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. diff --git a/WEB-INF/resources/it.lang.php b/WEB-INF/resources/it.lang.php index 8c12f14c..a873bb47 100644 --- a/WEB-INF/resources/it.lang.php +++ b/WEB-INF/resources/it.lang.php @@ -420,6 +420,7 @@ // 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. diff --git a/WEB-INF/resources/ja.lang.php b/WEB-INF/resources/ja.lang.php index 30156d52..9d580707 100644 --- a/WEB-INF/resources/ja.lang.php +++ b/WEB-INF/resources/ja.lang.php @@ -432,6 +432,7 @@ // 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. diff --git a/WEB-INF/resources/ko.lang.php b/WEB-INF/resources/ko.lang.php index 49e904b0..59222f08 100644 --- a/WEB-INF/resources/ko.lang.php +++ b/WEB-INF/resources/ko.lang.php @@ -436,6 +436,7 @@ // 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. diff --git a/WEB-INF/resources/nl.lang.php b/WEB-INF/resources/nl.lang.php index 85899c8a..6503da02 100644 --- a/WEB-INF/resources/nl.lang.php +++ b/WEB-INF/resources/nl.lang.php @@ -376,6 +376,7 @@ // 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. diff --git a/WEB-INF/resources/no.lang.php b/WEB-INF/resources/no.lang.php index 75910ddc..25549560 100644 --- a/WEB-INF/resources/no.lang.php +++ b/WEB-INF/resources/no.lang.php @@ -434,6 +434,7 @@ // 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. diff --git a/WEB-INF/resources/pl.lang.php b/WEB-INF/resources/pl.lang.php index 4a4f9e72..3a91e417 100644 --- a/WEB-INF/resources/pl.lang.php +++ b/WEB-INF/resources/pl.lang.php @@ -422,6 +422,7 @@ // 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. diff --git a/WEB-INF/resources/pt-br.lang.php b/WEB-INF/resources/pt-br.lang.php index ef122e23..af1c6e94 100644 --- a/WEB-INF/resources/pt-br.lang.php +++ b/WEB-INF/resources/pt-br.lang.php @@ -374,6 +374,7 @@ // 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. diff --git a/WEB-INF/resources/pt.lang.php b/WEB-INF/resources/pt.lang.php index d6ded651..8b915d4d 100644 --- a/WEB-INF/resources/pt.lang.php +++ b/WEB-INF/resources/pt.lang.php @@ -414,6 +414,7 @@ // 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. diff --git a/WEB-INF/resources/ro.lang.php b/WEB-INF/resources/ro.lang.php index 39e2f379..5f267efa 100644 --- a/WEB-INF/resources/ro.lang.php +++ b/WEB-INF/resources/ro.lang.php @@ -436,6 +436,7 @@ // 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. diff --git a/WEB-INF/resources/ru.lang.php b/WEB-INF/resources/ru.lang.php index e4474b2f..c64c8e9c 100644 --- a/WEB-INF/resources/ru.lang.php +++ b/WEB-INF/resources/ru.lang.php @@ -373,6 +373,7 @@ '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. diff --git a/WEB-INF/resources/sk.lang.php b/WEB-INF/resources/sk.lang.php index 76337a61..98d42961 100644 --- a/WEB-INF/resources/sk.lang.php +++ b/WEB-INF/resources/sk.lang.php @@ -425,6 +425,7 @@ // 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. diff --git a/WEB-INF/resources/sl.lang.php b/WEB-INF/resources/sl.lang.php index 9730c474..0f757c7d 100644 --- a/WEB-INF/resources/sl.lang.php +++ b/WEB-INF/resources/sl.lang.php @@ -408,6 +408,7 @@ // 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. diff --git a/WEB-INF/resources/sr.lang.php b/WEB-INF/resources/sr.lang.php index 3c490d20..60c504ca 100644 --- a/WEB-INF/resources/sr.lang.php +++ b/WEB-INF/resources/sr.lang.php @@ -422,6 +422,7 @@ // 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. diff --git a/WEB-INF/resources/sv.lang.php b/WEB-INF/resources/sv.lang.php index 30ed6da0..06e7f46a 100644 --- a/WEB-INF/resources/sv.lang.php +++ b/WEB-INF/resources/sv.lang.php @@ -420,6 +420,7 @@ // 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. diff --git a/WEB-INF/resources/tr.lang.php b/WEB-INF/resources/tr.lang.php index 5251ee8f..9d8ef411 100644 --- a/WEB-INF/resources/tr.lang.php +++ b/WEB-INF/resources/tr.lang.php @@ -444,6 +444,7 @@ // 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. diff --git a/WEB-INF/resources/zh-cn.lang.php b/WEB-INF/resources/zh-cn.lang.php index fa7f78a3..1a91e596 100644 --- a/WEB-INF/resources/zh-cn.lang.php +++ b/WEB-INF/resources/zh-cn.lang.php @@ -427,6 +427,7 @@ // 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. diff --git a/WEB-INF/resources/zh-tw.lang.php b/WEB-INF/resources/zh-tw.lang.php index 71587399..0a2f8510 100644 --- a/WEB-INF/resources/zh-tw.lang.php +++ b/WEB-INF/resources/zh-tw.lang.php @@ -430,6 +430,7 @@ // 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. diff --git a/WEB-INF/templates/2fa.tpl b/WEB-INF/templates/2fa.tpl index 1754f035..bd7e2f01 100644 --- a/WEB-INF/templates/2fa.tpl +++ b/WEB-INF/templates/2fa.tpl @@ -1,6 +1,7 @@ {* Copyright (c) Anuko International Ltd. https://www.anuko.com License: See license.txt *} +
{$i18n.form.2fa.hint}
{$forms.2faForm.open}
{$forms.2faForm.2fa_code.control}
diff --git a/initialize.php b/initialize.php index c8b41c6d..31f4096b 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.1.5664"); +define("APP_VERSION", "1.21.1.5665"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From 09c609f8f08d9d1f9a5d5daf9d53462c495a4d08 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Thu, 17 Mar 2022 13:39:56 +0000 Subject: [PATCH 011/168] Introduced override_2fa access right for top managers. --- dbinstall.php | 12 ++++++++---- initialize.php | 2 +- mysql.sql | 4 ++-- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/dbinstall.php b/dbinstall.php index 182910ad..f8e1b936 100644 --- a/dbinstall.php +++ b/dbinstall.php @@ -1151,7 +1151,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["convert11900to12100"]) { + if ($_POST["convert11900to12102"]) { 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')"); @@ -1193,6 +1193,10 @@ function ttGenerateKeys() { 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'"); } if ($_POST["cleanup"]) { @@ -1243,7 +1247,7 @@ function ttGenerateKeys() {

DB Install

-
Create database structure (v1.21.0) + Create database structure (v1.21.2)
(applies only to new installations, do not execute when updating)
@@ -1292,8 +1296,8 @@ function ttGenerateKeys() { - Update database structure (v1.19 to v1.21) - + Update database structure (v1.19 to v1.21.2) + diff --git a/initialize.php b/initialize.php index 31f4096b..8ed35c5c 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.1.5665"); +define("APP_VERSION", "1.21.2.5666"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); diff --git a/mysql.sql b/mysql.sql index 05743ee5..0856f94a 100644 --- a/mysql.sql +++ b/mysql.sql @@ -78,7 +78,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,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,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'); # @@ -621,4 +621,4 @@ CREATE TABLE `tt_site_config` ( PRIMARY KEY (`param_name`) ); -INSERT INTO `tt_site_config` (`param_name`, `param_value`, `created`) VALUES ('version_db', '1.21.0', now()); # TODO: change when structure changes. +INSERT INTO `tt_site_config` (`param_name`, `param_value`, `created`) VALUES ('version_db', '1.21.2', now()); # TODO: change when structure changes. From a201018610d9d9f82c886104a7af879a28e1325a Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Thu, 17 Mar 2022 13:56:41 +0000 Subject: [PATCH 012/168] Started to use override_2fa access right. --- initialize.php | 2 +- login.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/initialize.php b/initialize.php index 8ed35c5c..d56c43b3 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.2.5666"); +define("APP_VERSION", "1.21.2.5667"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); diff --git a/login.php b/login.php index dbf5d0c3..a097c888 100644 --- a/login.php +++ b/login.php @@ -56,8 +56,8 @@ $use2FA = $config->getDefinedValue('2fa'); } - // If we have to use 2FA, create and email auth code to user. - if ($use2FA) { + // If we have to use 2FA, email auth code to user and redirect to 2fa.php. + if ($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); From f2358bfc5e20cd3b741166379779505e8b149be8 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Thu, 17 Mar 2022 15:26:17 +0000 Subject: [PATCH 013/168] Initial implementation of two factor authentication. --- 2fa.php | 54 +++++++++++++++-------- WEB-INF/templates/2fa.tpl | 24 +++++++--- WEB-INF/templates/group_advanced_edit.tpl | 2 - initialize.php | 2 +- login.php | 9 ++-- 5 files changed, 60 insertions(+), 31 deletions(-) diff --git a/2fa.php b/2fa.php index 005b4ea4..a7e3dbb2 100644 --- a/2fa.php +++ b/2fa.php @@ -11,36 +11,54 @@ // ... anything to check? // End of access checks. -$cl_2fa_code = $request->getParameter('2fa_code'); -$form = new Form('2faForm'); -$form->addInput(array('type'=>'text','maxlength'=>'100','name'=>'2fa_code','value'=>$cl_2fa_code)); +$cl_login = $request->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_2fa_code)) $err->add($i18n->get('error.field'), $i18n->get('form.2fa.2fa_code')); - - // Get user id. - $user_id = ttUserHelper::getUserIdByTmpRef($cl_2fa_code); - if (!$user_id) $err->add($i18n->get('error.2fa_code')); + 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()) { - $user = new ttUser(null, $user_id); // Note: reusing $user from initialize.php. - $auth->setAuth($user_id, $user->login); - - // Redirect, depending on user role. - if ($user->isClient()) { - header('Location: reports.php'); - } else { - header('Location: time.php'); + // 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(); } - exit(); } } // isPost $smarty->assign('forms', array($form->getName()=>$form->toArray())); -$smarty->assign('onload', 'onLoad="document.2faForm.2fa_code.focus()"'); +$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/WEB-INF/templates/2fa.tpl b/WEB-INF/templates/2fa.tpl index bd7e2f01..d425cbc1 100644 --- a/WEB-INF/templates/2fa.tpl +++ b/WEB-INF/templates/2fa.tpl @@ -2,17 +2,29 @@ License: See license.txt *}
{$i18n.form.2fa.hint}
-{$forms.2faForm.open} +{$forms.twoFactorAuthForm.open} - + - - + + + - + + + + + + + + + + + +
{$forms.2faForm.2fa_code.control}{$forms.twoFactorAuthForm.login.control}
{$forms.2faForm.btn_login.control}{$forms.twoFactorAuthForm.password.control}
{$forms.twoFactorAuthForm.auth_code.control}
{$forms.twoFactorAuthForm.btn_login.control}
-{$forms.2faForm.close} +{$forms.twoFactorAuthForm.close} diff --git a/WEB-INF/templates/group_advanced_edit.tpl b/WEB-INF/templates/group_advanced_edit.tpl index d4bd55a1..82eac702 100644 --- a/WEB-INF/templates/group_advanced_edit.tpl +++ b/WEB-INF/templates/group_advanced_edit.tpl @@ -41,7 +41,6 @@ License: See license.txt *} {$i18n.label.what_is_it} -{if constant('2FA_DEBUG')}
@@ -51,7 +50,6 @@ License: See license.txt *} {$i18n.label.what_is_it} -{/if}
{$i18n.label.required_fields}
diff --git a/initialize.php b/initialize.php index d56c43b3..f3138065 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.2.5667"); +define("APP_VERSION", "1.21.2.5668"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); diff --git a/login.php b/login.php index a097c888..69377fed 100644 --- a/login.php +++ b/login.php @@ -43,8 +43,12 @@ // Check user login. $loginSucceeded = $auth->doLogin($cl_login, $cl_password); - if (!$loginSucceeded) + if ($loginSucceeded) { + // Remember user login in a cookie. + setcookie(LOGIN_COOKIE_NAME, $cl_login, time() + COOKIE_EXPIRE, '/'); + } else { $err->add($i18n->get('error.auth')); + } } // Do we have to use 2FA? @@ -114,9 +118,6 @@ if ($current_user_date) $_SESSION['date'] = $current_user_date; - // Remember user login in a cookie. - setcookie(LOGIN_COOKIE_NAME, $cl_login, time() + COOKIE_EXPIRE, '/'); - // Redirect, depending on user role. if ($user->can('administer_site')) { header('Location: admin_groups.php'); From e4f35b788164da94bbbdbb9f376d9e3ee970ed1a Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sat, 19 Mar 2022 14:16:55 +0000 Subject: [PATCH 014/168] Starting work on allowing multiple uncompleted entries. --- WEB-INF/resources/ca.lang.php | 1 + WEB-INF/resources/cs.lang.php | 1 + WEB-INF/resources/da.lang.php | 1 + WEB-INF/resources/de.lang.php | 2 ++ WEB-INF/resources/en.lang.php | 1 + WEB-INF/resources/es.lang.php | 1 + WEB-INF/resources/et.lang.php | 2 ++ WEB-INF/resources/fa.lang.php | 1 + WEB-INF/resources/fi.lang.php | 1 + WEB-INF/resources/fr.lang.php | 1 + WEB-INF/resources/gr.lang.php | 2 ++ WEB-INF/resources/he.lang.php | 1 + WEB-INF/resources/hu.lang.php | 1 + WEB-INF/resources/it.lang.php | 1 + WEB-INF/resources/ja.lang.php | 1 + WEB-INF/resources/ko.lang.php | 1 + WEB-INF/resources/nl.lang.php | 2 ++ WEB-INF/resources/no.lang.php | 1 + WEB-INF/resources/pl.lang.php | 1 + WEB-INF/resources/pt-br.lang.php | 2 ++ WEB-INF/resources/pt.lang.php | 1 + WEB-INF/resources/ro.lang.php | 1 + WEB-INF/resources/ru.lang.php | 1 + WEB-INF/resources/sk.lang.php | 1 + WEB-INF/resources/sl.lang.php | 1 + WEB-INF/resources/sr.lang.php | 1 + WEB-INF/resources/sv.lang.php | 1 + WEB-INF/resources/tr.lang.php | 1 + WEB-INF/resources/zh-cn.lang.php | 1 + WEB-INF/resources/zh-tw.lang.php | 1 + WEB-INF/templates/group_edit.tpl | 11 +++++++++++ group_edit.php | 6 ++++++ initialize.php | 2 +- 33 files changed, 53 insertions(+), 1 deletion(-) diff --git a/WEB-INF/resources/ca.lang.php b/WEB-INF/resources/ca.lang.php index 5db49232..8b318204 100644 --- a/WEB-INF/resources/ca.lang.php +++ b/WEB-INF/resources/ca.lang.php @@ -593,6 +593,7 @@ // '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', diff --git a/WEB-INF/resources/cs.lang.php b/WEB-INF/resources/cs.lang.php index c689bafb..fd4ba142 100644 --- a/WEB-INF/resources/cs.lang.php +++ b/WEB-INF/resources/cs.lang.php @@ -605,6 +605,7 @@ // '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', diff --git a/WEB-INF/resources/da.lang.php b/WEB-INF/resources/da.lang.php index e2a0da44..4e41f9dd 100644 --- a/WEB-INF/resources/da.lang.php +++ b/WEB-INF/resources/da.lang.php @@ -571,6 +571,7 @@ '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', diff --git a/WEB-INF/resources/de.lang.php b/WEB-INF/resources/de.lang.php index 10c6cba4..989afc57 100644 --- a/WEB-INF/resources/de.lang.php +++ b/WEB-INF/resources/de.lang.php @@ -546,6 +546,8 @@ 'form.group_edit.type_start_finish' => 'Start und Ende', 'form.group_edit.type_duration' => 'Dauer', 'form.group_edit.punch_mode' => 'Stechuhr-Modus', +// TODO: translate the following. +// 'form.group_edit.one_uncompleted' => 'One uncompleted', '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', diff --git a/WEB-INF/resources/en.lang.php b/WEB-INF/resources/en.lang.php index fb31f0f8..576be5b9 100644 --- a/WEB-INF/resources/en.lang.php +++ b/WEB-INF/resources/en.lang.php @@ -526,6 +526,7 @@ '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', diff --git a/WEB-INF/resources/es.lang.php b/WEB-INF/resources/es.lang.php index bbb923fd..3f815af0 100644 --- a/WEB-INF/resources/es.lang.php +++ b/WEB-INF/resources/es.lang.php @@ -607,6 +607,7 @@ // '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', diff --git a/WEB-INF/resources/et.lang.php b/WEB-INF/resources/et.lang.php index e698f939..76417e94 100644 --- a/WEB-INF/resources/et.lang.php +++ b/WEB-INF/resources/et.lang.php @@ -580,6 +580,8 @@ '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. diff --git a/WEB-INF/resources/fa.lang.php b/WEB-INF/resources/fa.lang.php index 7a2e8fbc..d4a52de8 100644 --- a/WEB-INF/resources/fa.lang.php +++ b/WEB-INF/resources/fa.lang.php @@ -594,6 +594,7 @@ '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', diff --git a/WEB-INF/resources/fi.lang.php b/WEB-INF/resources/fi.lang.php index 4b6a1832..d4850d85 100644 --- a/WEB-INF/resources/fi.lang.php +++ b/WEB-INF/resources/fi.lang.php @@ -578,6 +578,7 @@ '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', diff --git a/WEB-INF/resources/fr.lang.php b/WEB-INF/resources/fr.lang.php index b6d17027..7d2bb08c 100644 --- a/WEB-INF/resources/fr.lang.php +++ b/WEB-INF/resources/fr.lang.php @@ -568,6 +568,7 @@ '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', diff --git a/WEB-INF/resources/gr.lang.php b/WEB-INF/resources/gr.lang.php index a7eec732..222c98b0 100644 --- a/WEB-INF/resources/gr.lang.php +++ b/WEB-INF/resources/gr.lang.php @@ -558,6 +558,8 @@ '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' => 'Μη ολοκληρωμένες ενδείξεις', diff --git a/WEB-INF/resources/he.lang.php b/WEB-INF/resources/he.lang.php index e4ebe519..450fa585 100644 --- a/WEB-INF/resources/he.lang.php +++ b/WEB-INF/resources/he.lang.php @@ -598,6 +598,7 @@ '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', diff --git a/WEB-INF/resources/hu.lang.php b/WEB-INF/resources/hu.lang.php index 7c761eec..3b4069a3 100644 --- a/WEB-INF/resources/hu.lang.php +++ b/WEB-INF/resources/hu.lang.php @@ -597,6 +597,7 @@ // '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', diff --git a/WEB-INF/resources/it.lang.php b/WEB-INF/resources/it.lang.php index a873bb47..d1925e63 100644 --- a/WEB-INF/resources/it.lang.php +++ b/WEB-INF/resources/it.lang.php @@ -573,6 +573,7 @@ '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', diff --git a/WEB-INF/resources/ja.lang.php b/WEB-INF/resources/ja.lang.php index 9d580707..602427b1 100644 --- a/WEB-INF/resources/ja.lang.php +++ b/WEB-INF/resources/ja.lang.php @@ -600,6 +600,7 @@ // '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', diff --git a/WEB-INF/resources/ko.lang.php b/WEB-INF/resources/ko.lang.php index 59222f08..2200b708 100644 --- a/WEB-INF/resources/ko.lang.php +++ b/WEB-INF/resources/ko.lang.php @@ -598,6 +598,7 @@ // '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', diff --git a/WEB-INF/resources/nl.lang.php b/WEB-INF/resources/nl.lang.php index 6503da02..497d941e 100644 --- a/WEB-INF/resources/nl.lang.php +++ b/WEB-INF/resources/nl.lang.php @@ -516,6 +516,8 @@ 'form.group_edit.type_start_finish' => 'begin en einde', 'form.group_edit.type_duration' => 'duur', 'form.group_edit.punch_mode' => 'Start/stop modus', +// TODO: translate the following. +// 'form.group_edit.one_uncompleted' => 'One uncompleted', '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', diff --git a/WEB-INF/resources/no.lang.php b/WEB-INF/resources/no.lang.php index 25549560..f45cbf12 100644 --- a/WEB-INF/resources/no.lang.php +++ b/WEB-INF/resources/no.lang.php @@ -600,6 +600,7 @@ // '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', diff --git a/WEB-INF/resources/pl.lang.php b/WEB-INF/resources/pl.lang.php index 3a91e417..e6bf9b24 100644 --- a/WEB-INF/resources/pl.lang.php +++ b/WEB-INF/resources/pl.lang.php @@ -580,6 +580,7 @@ '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', diff --git a/WEB-INF/resources/pt-br.lang.php b/WEB-INF/resources/pt-br.lang.php index af1c6e94..36a20c20 100644 --- a/WEB-INF/resources/pt-br.lang.php +++ b/WEB-INF/resources/pt-br.lang.php @@ -514,6 +514,8 @@ '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', diff --git a/WEB-INF/resources/pt.lang.php b/WEB-INF/resources/pt.lang.php index 8b915d4d..5def6759 100644 --- a/WEB-INF/resources/pt.lang.php +++ b/WEB-INF/resources/pt.lang.php @@ -578,6 +578,7 @@ // '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', diff --git a/WEB-INF/resources/ro.lang.php b/WEB-INF/resources/ro.lang.php index 5f267efa..05e6fe5a 100644 --- a/WEB-INF/resources/ro.lang.php +++ b/WEB-INF/resources/ro.lang.php @@ -601,6 +601,7 @@ // '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', diff --git a/WEB-INF/resources/ru.lang.php b/WEB-INF/resources/ru.lang.php index c64c8e9c..2fbe0dfb 100644 --- a/WEB-INF/resources/ru.lang.php +++ b/WEB-INF/resources/ru.lang.php @@ -513,6 +513,7 @@ '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' => 'Индикаторы незавершения', diff --git a/WEB-INF/resources/sk.lang.php b/WEB-INF/resources/sk.lang.php index 98d42961..440944c7 100644 --- a/WEB-INF/resources/sk.lang.php +++ b/WEB-INF/resources/sk.lang.php @@ -584,6 +584,7 @@ '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', diff --git a/WEB-INF/resources/sl.lang.php b/WEB-INF/resources/sl.lang.php index 0f757c7d..7ad63d0e 100644 --- a/WEB-INF/resources/sl.lang.php +++ b/WEB-INF/resources/sl.lang.php @@ -574,6 +574,7 @@ // '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', diff --git a/WEB-INF/resources/sr.lang.php b/WEB-INF/resources/sr.lang.php index 60c504ca..1fc9691f 100644 --- a/WEB-INF/resources/sr.lang.php +++ b/WEB-INF/resources/sr.lang.php @@ -580,6 +580,7 @@ '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', diff --git a/WEB-INF/resources/sv.lang.php b/WEB-INF/resources/sv.lang.php index 06e7f46a..f3a94315 100644 --- a/WEB-INF/resources/sv.lang.php +++ b/WEB-INF/resources/sv.lang.php @@ -577,6 +577,7 @@ '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', diff --git a/WEB-INF/resources/tr.lang.php b/WEB-INF/resources/tr.lang.php index 9d8ef411..b8763bf2 100644 --- a/WEB-INF/resources/tr.lang.php +++ b/WEB-INF/resources/tr.lang.php @@ -614,6 +614,7 @@ // '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', diff --git a/WEB-INF/resources/zh-cn.lang.php b/WEB-INF/resources/zh-cn.lang.php index 1a91e596..4c797a3c 100644 --- a/WEB-INF/resources/zh-cn.lang.php +++ b/WEB-INF/resources/zh-cn.lang.php @@ -589,6 +589,7 @@ // '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', diff --git a/WEB-INF/resources/zh-tw.lang.php b/WEB-INF/resources/zh-tw.lang.php index 0a2f8510..1cf542d9 100644 --- a/WEB-INF/resources/zh-tw.lang.php +++ b/WEB-INF/resources/zh-tw.lang.php @@ -592,6 +592,7 @@ // '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', diff --git a/WEB-INF/templates/group_edit.tpl b/WEB-INF/templates/group_edit.tpl index 05a134dd..673af5e2 100644 --- a/WEB-INF/templates/group_edit.tpl +++ b/WEB-INF/templates/group_edit.tpl @@ -110,6 +110,17 @@ function chLocation(newLocation) { document.location = newLocation; } {$i18n.label.what_is_it} +{if constant('ONE_UNCOMPLETED_DEBUG')} +
+ + + + {$forms.groupForm.one_uncompleted.control} + {$i18n.label.what_is_it} + {$i18n.label.what_is_it} + + +{/if}
diff --git a/group_edit.php b/group_edit.php index ab3065d5..dbfca56b 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/initialize.php b/initialize.php index f3138065..0295c2bc 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.2.5668"); +define("APP_VERSION", "1.21.2.5669"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From 630f9ded901a89ccdf665405bf708961540eb6d3 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sun, 20 Mar 2022 15:25:17 +0000 Subject: [PATCH 015/168] Implemented multiple uncompleted entries with config option. --- WEB-INF/lib/ttTimeHelper.class.php | 44 ++++++++++++++++++++++++++---- WEB-INF/templates/group_edit.tpl | 2 -- WEB-INF/templates/puncher.tpl | 6 ++-- WEB-INF/templates/time.tpl | 6 ---- WEB-INF/templates/time_script.tpl | 16 +++++++++++ initialize.php | 2 +- puncher.php | 20 ++++++++------ time.php | 4 ++- time_edit.php | 4 ++- 9 files changed, 77 insertions(+), 27 deletions(-) diff --git a/WEB-INF/lib/ttTimeHelper.class.php b/WEB-INF/lib/ttTimeHelper.class.php index b45c6d17..fa110fa4 100644 --- a/WEB-INF/lib/ttTimeHelper.class.php +++ b/WEB-INF/lib/ttTimeHelper.class.php @@ -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)"; @@ -512,9 +511,9 @@ 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; + //$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"; @@ -673,10 +672,43 @@ static function getTimeForMonth2($date) { // getUncompleted - retrieves an uncompleted record for user, if one exists. static function getUncompleted($user_id) { + + 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"; + $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"; + " 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 eraliest uncompleted for date. $res = $mdb2->query($sql); if (!is_a($res, 'PEAR_Error')) { if (!$res->numRows()) { @@ -740,6 +772,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/templates/group_edit.tpl b/WEB-INF/templates/group_edit.tpl index 673af5e2..909b485e 100644 --- a/WEB-INF/templates/group_edit.tpl +++ b/WEB-INF/templates/group_edit.tpl @@ -110,7 +110,6 @@ function chLocation(newLocation) { document.location = newLocation; } {$i18n.label.what_is_it} -{if constant('ONE_UNCOMPLETED_DEBUG')}
@@ -120,7 +119,6 @@ function chLocation(newLocation) { document.location = newLocation; } {$i18n.label.what_is_it} -{/if}
diff --git a/WEB-INF/templates/puncher.tpl b/WEB-INF/templates/puncher.tpl index 8060c2ff..9190af39 100644 --- a/WEB-INF/templates/puncher.tpl +++ b/WEB-INF/templates/puncher.tpl @@ -48,11 +48,11 @@ function stopTimer() { } -{if $uncompleted} +{if $uncompleted_today} + {if $show_navigation}
{$i18n.label.day_view} @@ -113,6 +121,9 @@ License: See license.txt *} {if $time_records} + + +
@@ -177,10 +188,13 @@ License: See license.txt *} {/if} {/if} - diff --git a/initialize.php b/initialize.php index 84470d54..2f7224b8 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.4.5691"); +define("APP_VERSION", "1.21.4.5692"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); diff --git a/puncher.php b/puncher.php index 92f500d8..2b25462d 100644 --- a/puncher.php +++ b/puncher.php @@ -32,7 +32,7 @@ // 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_today parameter is in correct format. + // 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'); diff --git a/time.php b/time.php index 4f08cfe2..faf064a4 100644 --- a/time.php +++ b/time.php @@ -46,6 +46,30 @@ 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. From c88dfcd6d32eea8818f266e39144732f9c263184 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sat, 9 Apr 2022 15:18:33 +0000 Subject: [PATCH 038/168] Resuming refactoring. --- WEB-INF/lib/common.lib.php | 2 +- initialize.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/WEB-INF/lib/common.lib.php b/WEB-INF/lib/common.lib.php index fc27d294..2d8e287d 100644 --- a/WEB-INF/lib/common.lib.php +++ b/WEB-INF/lib/common.lib.php @@ -577,7 +577,7 @@ function ttEndsWith($string, $endString) function ttDateToUserFormat($date) { global $user; - $o_date = new DateAndTime(DB_DATEFORMAT, $date); + $o_date = new ttDate($date); return $o_date->toString($user->date_format); } diff --git a/initialize.php b/initialize.php index 2f7224b8..e787e27b 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.4.5692"); +define("APP_VERSION", "1.21.4.5693"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From ba5e71ff40894946290f51979dc07f4ab00e21d0 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sat, 9 Apr 2022 16:52:18 +0000 Subject: [PATCH 039/168] Incremental refactoring. --- WEB-INF/lib/form/ActionForm.class.php | 43 +++++++-------------------- initialize.php | 2 +- time_edit.php | 1 - week.php | 1 - 4 files changed, 12 insertions(+), 35 deletions(-) diff --git a/WEB-INF/lib/form/ActionForm.class.php b/WEB-INF/lib/form/ActionForm.class.php index abc5e006..5b88292c 100644 --- a/WEB-INF/lib/form/ActionForm.class.php +++ b/WEB-INF/lib/form/ActionForm.class.php @@ -1,32 +1,9 @@ mValues[$name] = $value; if ($this->mForm) { if (isset($this->mForm->elements[$name])) { if ($this->mForm->elements[$name]->class=="DateField") { - $dt = new DateAndTime($user->date_format, $value); - $value = $dt->toString(DB_DATEFORMAT); + // TODO: It appears that we never get here. Refactor this entire class. + $dt = new ttDate($user->date_format, $value); + $value = $dt->toString(); } $this->mForm->elements[$name]->setValueSafe($value); } @@ -140,8 +118,9 @@ function setAttributes($value) { if ($this->mForm) { if (isset($this->mForm->elements[$name])) { if ($this->mForm->elements[$name]->class=="DateField") { - $dt = new DateAndTime($user->date_format, $value); - $value = $dt->toString(DB_DATEFORMAT); + // TODO: It appears that we never get here. Refactor this entire class. + $dt = new ttDate($user->date_format, $value); + $value = $dt->toString(); } $this->mForm->elements[$name]->setValueSafe($value); } diff --git a/initialize.php b/initialize.php index e787e27b..a8b14bca 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.4.5693"); +define("APP_VERSION", "1.21.4.5694"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); diff --git a/time_edit.php b/time_edit.php index a00f6605..03c72119 100644 --- a/time_edit.php +++ b/time_edit.php @@ -9,7 +9,6 @@ import('ttClientHelper'); import('ttTimeHelper'); import('ttConfigHelper'); -import('DateAndTime'); import('ttDate'); // Access checks. diff --git a/week.php b/week.php index 1e0ec1b4..92eb95ee 100644 --- a/week.php +++ b/week.php @@ -13,7 +13,6 @@ import('ttClientHelper'); import('ttTimeHelper'); import('ttDate'); -import('DateAndTime'); // Access checks. if (!(ttAccessAllowed('track_own_time') || ttAccessAllowed('track_time'))) { From 43b0cadb33efc2bb97b3a62a95d6a7da5d93874b Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sat, 9 Apr 2022 18:40:23 +0000 Subject: [PATCH 040/168] More refactoring. --- expense_edit.php | 16 ++++++++-------- initialize.php | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/expense_edit.php b/expense_edit.php index 258437b3..e57db88d 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'))) { @@ -37,7 +37,7 @@ } // 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; @@ -150,11 +150,11 @@ 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)); + $browser_today = new ttDate($request->getParameter('browser_today', null)); if ($new_date->after($browser_today)) $err->add($i18n->get('error.future_date')); } @@ -178,9 +178,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(); } } @@ -194,9 +194,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/initialize.php b/initialize.php index a8b14bca..45fa6778 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.4.5694"); +define("APP_VERSION", "1.21.4.5695"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From 49ef4b11da9b456e26d5e1592fa79a3194a49b7e Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sun, 10 Apr 2022 16:04:20 +0000 Subject: [PATCH 041/168] Resuming refactoring. --- WEB-INF/lib/ttDate.class.php | 9 +++++++++ initialize.php | 2 +- reports.php | 33 +++++++++++++++++---------------- 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/WEB-INF/lib/ttDate.class.php b/WEB-INF/lib/ttDate.class.php index 1f1dfeaa..3b798214 100644 --- a/WEB-INF/lib/ttDate.class.php +++ b/WEB-INF/lib/ttDate.class.php @@ -153,6 +153,15 @@ function after(/*ttDate*/ $obj) { } + 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; } diff --git a/initialize.php b/initialize.php index 45fa6778..201e96f4 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.4.5695"); +define("APP_VERSION", "1.21.4.5696"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); diff --git a/reports.php b/reports.php index c1df0438..858ca61c 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'))) { @@ -357,9 +357,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'); @@ -438,17 +438,18 @@ } 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')); - - if ($start_date->compare($end_date) > 0) - $err->add($i18n->get('error.interval'), $i18n->get('label.end_date'), $i18n->get('label.start_date')); + // 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'); From 66ee957d754819df5f672746af4d62aad44771a7 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sun, 10 Apr 2022 16:15:10 +0000 Subject: [PATCH 042/168] A bit more refactoring. --- initialize.php | 2 +- invoice_view.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/initialize.php b/initialize.php index 201e96f4..5703b55b 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.4.5696"); +define("APP_VERSION", "1.21.4.5697"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); diff --git a/invoice_view.php b/invoice_view.php index b03b719e..b1fcf91f 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']); From a44ba11b58ef6f76c7570a3092bb94c0088b2008 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sun, 10 Apr 2022 16:39:55 +0000 Subject: [PATCH 043/168] More refactoring. --- expenses.php | 11 +++++------ initialize.php | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/expenses.php b/expenses.php index 897d64d9..34169b7d 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'); @@ -65,13 +65,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'); @@ -203,7 +202,7 @@ // Prohibit creating entries in future. if (!$user->isOptionEnabled('future_entries')) { - $browser_today = new DateAndTime(DB_DATEFORMAT, $request->getParameter('browser_today', null)); + $browser_today = new ttDate($request->getParameter('browser_today', null)); if ($selected_date->after($browser_today)) $err->add($i18n->get('error.future_date')); } diff --git a/initialize.php b/initialize.php index 5703b55b..e1a5ffd0 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.4.5697"); +define("APP_VERSION", "1.21.4.5698"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From 8b66de3dd6a21fc65fa26a3a6fcbf940d8d2b05f Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sun, 10 Apr 2022 16:53:05 +0000 Subject: [PATCH 044/168] A bit more refactoring. --- charts.php | 6 +++--- expense_delete.php | 4 ++-- initialize.php | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/charts.php b/charts.php index cfd01c5f..1ad1c88d 100644 --- a/charts.php +++ b/charts.php @@ -6,7 +6,7 @@ require_once('initialize.php'); import('form.Form'); -import('DateAndTime'); +import('ttDate'); import('ttChartHelper'); import('ttUserConfig'); import('PieChartEx'); @@ -72,8 +72,8 @@ // 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; diff --git a/expense_delete.php b/expense_delete.php index 6c5cb791..6dbf421a 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/initialize.php b/initialize.php index e1a5ffd0..6be6126d 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.4.5698"); +define("APP_VERSION", "1.21.4.5699"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From 0a61c1b2469f7c519419b35712223838a37f40b9 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sun, 10 Apr 2022 17:28:27 +0000 Subject: [PATCH 045/168] Cleanup. --- WEB-INF/lib/ttTeamHelper.class.php | 30 ++---------------------------- WEB-INF/lib/ttTimeHelper.class.php | 2 -- initialize.php | 2 +- 3 files changed, 3 insertions(+), 31 deletions(-) diff --git a/WEB-INF/lib/ttTeamHelper.class.php b/WEB-INF/lib/ttTeamHelper.class.php index 981f01b5..e12c2575 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 dd097747..2e9eddbb 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 { diff --git a/initialize.php b/initialize.php index 6be6126d..9ae6dcdb 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.4.5699"); +define("APP_VERSION", "1.21.4.5700"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From 2cf98d9ab1e736794ff0131a41cb642374198fe5 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sun, 10 Apr 2022 17:45:17 +0000 Subject: [PATCH 046/168] A bit more cleanup and refactoring. --- WEB-INF/lib/ttGroupHelper.class.php | 4 +--- initialize.php | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/WEB-INF/lib/ttGroupHelper.class.php b/WEB-INF/lib/ttGroupHelper.class.php index 81b0c7f3..81d1af69 100644 --- a/WEB-INF/lib/ttGroupHelper.class.php +++ b/WEB-INF/lib/ttGroupHelper.class.php @@ -431,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']); @@ -679,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/initialize.php b/initialize.php index 9ae6dcdb..6b4471a2 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.4.5700"); +define("APP_VERSION", "1.21.4.5701"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From 912ffe1bfcbb21a8e87e6a162b57148321109a22 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sun, 10 Apr 2022 20:01:41 +0000 Subject: [PATCH 047/168] Refactoring in progress. --- WEB-INF/lib/ttChartHelper.class.php | 40 ++++++----------------------- WEB-INF/lib/ttPeriod.class.php | 12 +++++++++ initialize.php | 2 +- 3 files changed, 21 insertions(+), 33 deletions(-) diff --git a/WEB-INF/lib/ttChartHelper.class.php b/WEB-INF/lib/ttChartHelper.class.php index 4e4f9c83..f0523036 100644 --- a/WEB-INF/lib/ttChartHelper.class.php +++ b/WEB-INF/lib/ttChartHelper.class.php @@ -1,32 +1,8 @@ endDate = new ttDate(); switch ($period_type) { + case INTERVAL_THIS_DAY: + $this->startDate->setFromUnixTimestamp($ttDateInstance->getTimestamp()); + $this->endDate->setFromUnixTimestamp($ttDateInstance->getTimestamp()); + 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])); @@ -32,6 +39,11 @@ function __construct($ttDateInstance, $period_type = 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_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; } } diff --git a/initialize.php b/initialize.php index 6b4471a2..a078f888 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.4.5701"); +define("APP_VERSION", "1.21.4.5702"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From 50e2643ca231372b72c42d4094cd195903efe31d Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sun, 10 Apr 2022 20:36:34 +0000 Subject: [PATCH 048/168] Refactoring in progress. --- WEB-INF/lib/ttPeriod.class.php | 24 ++++++++++++++++++++++++ WEB-INF/lib/ttReportHelper.class.php | 10 ++++++---- initialize.php | 2 +- 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/WEB-INF/lib/ttPeriod.class.php b/WEB-INF/lib/ttPeriod.class.php index 85022a85..1d63ad9d 100644 --- a/WEB-INF/lib/ttPeriod.class.php +++ b/WEB-INF/lib/ttPeriod.class.php @@ -6,6 +6,10 @@ define('INTERVAL_THIS_WEEK', 2); define('INTERVAL_THIS_MONTH', 3); define('INTERVAL_THIS_YEAR', 4); +define('INTERVAL_ALL_TIME', 5); +define('INTERVAL_LAST_WEEK', 6); +define('INTERVAL_LAST_MONTH', 7); +define('INTERVAL_LAST_DAY', 8); class ttPeriod { var $startDate; // ttDate object. @@ -30,16 +34,31 @@ function __construct($ttDateInstance, $period_type = INTERVAL_THIS_MONTH) { $this->endDate->setFromUnixTimestamp($ttDateInstance->getTimestamp()); break; + case INTERVAL_LAST_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_LAST_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_LAST_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])); @@ -47,6 +66,11 @@ function __construct($ttDateInstance, $period_type = INTERVAL_THIS_MONTH) { } } + // 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) { diff --git a/WEB-INF/lib/ttReportHelper.class.php b/WEB-INF/lib/ttReportHelper.class.php index 20e6d7a4..bafa4022 100644 --- a/WEB-INF/lib/ttReportHelper.class.php +++ b/WEB-INF/lib/ttReportHelper.class.php @@ -4,7 +4,9 @@ import('ttClientHelper'); import('DateAndTime'); +import('ttDate'); import('Period'); +import('ttPeriod'); import('ttTimeHelper'); import('ttConfigHelper'); @@ -119,12 +121,12 @@ static function getWhere($options) { // Prepare sql query part for where. $dateFormat = $user->getDateFormat(); if ($options['period']) - $period = new Period($options['period'], new DateAndTime($dateFormat)); + $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"; diff --git a/initialize.php b/initialize.php index a078f888..7fbdc50f 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.4.5702"); +define("APP_VERSION", "1.21.4.5703"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From 094e353b2f55375aedd70f14f495c23f179e1f24 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Mon, 11 Apr 2022 15:43:46 +0000 Subject: [PATCH 049/168] Resuming refactoring. --- WEB-INF/lib/ttReportHelper.class.php | 29 +++++++++++++--------------- initialize.php | 2 +- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/WEB-INF/lib/ttReportHelper.class.php b/WEB-INF/lib/ttReportHelper.class.php index bafa4022..995cfa2b 100644 --- a/WEB-INF/lib/ttReportHelper.class.php +++ b/WEB-INF/lib/ttReportHelper.class.php @@ -3,9 +3,7 @@ License: See license.txt */ import('ttClientHelper'); -import('DateAndTime'); import('ttDate'); -import('Period'); import('ttPeriod'); import('ttTimeHelper'); import('ttConfigHelper'); @@ -128,7 +126,7 @@ static function getWhere($options) { 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)."'". + $where = " where l.status = 1 and l.date >= '".$period->getStartDate()."' and l.date <= '".$period->getEndDate()."'". " $user_list_part $dropdown_parts $cf_text_parts"; return $where; } @@ -203,14 +201,14 @@ static function getExpenseWhere($options) { // Prepare sql query part for where. $dateFormat = $user->getDateFormat(); if ($options['period']) - $period = new Period($options['period'], new DateAndTime($dateFormat)); + $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)."'". + $where = " where ei.status = 1 and ei.date >= '".$period->getStartDate()."' and ei.date <= '".$period->getEndDate()."'". " $user_list_part $dropdown_parts $cf_text_parts"; return $where; } @@ -883,17 +881,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; diff --git a/initialize.php b/initialize.php index 7fbdc50f..88849a73 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.4.5703"); +define("APP_VERSION", "1.21.4.5705"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From 83b155b827c5ec9923b6aadd74ad462f7d8e3b61 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Mon, 11 Apr 2022 16:31:38 +0000 Subject: [PATCH 050/168] Refactoring. Fully replaced older Period class usage with newer ttPeriod. --- WEB-INF/lib/Period.class.php | 136 ------------------------ WEB-INF/lib/ttTimesheetHelper.class.php | 14 +-- initialize.php | 2 +- 3 files changed, 9 insertions(+), 143 deletions(-) delete mode 100644 WEB-INF/lib/Period.class.php diff --git a/WEB-INF/lib/Period.class.php b/WEB-INF/lib/Period.class.php deleted file mode 100644 index 57ea0e44..00000000 --- a/WEB-INF/lib/Period.class.php +++ /dev/null @@ -1,136 +0,0 @@ -getWeekStart(); - - $this->startDate = new DateAndTime(); - $this->startDate->setFormat($date_point->getFormat()); - $this->endDate = new DateAndTime(); - $this->endDate->setFormat($date_point->getFormat()); - $t_arr = localtime($date_point->getTimestamp()); - $t_arr[5] = $t_arr[5] + 1900; - - if ($t_arr[6] < $weekStartDay) { - $startWeekBias = $weekStartDay - 7; - } else { - $startWeekBias = $weekStartDay; - } - - switch ($period_type) { - case INTERVAL_THIS_DAY: - $this->startDate->setTimestamp($date_point->getTimestamp()); - $this->endDate->setTimestamp($date_point->getTimestamp()); - break; - - case INTERVAL_LAST_DAY: - $this->startDate->setTimestamp(mktime(0,0,0,$t_arr[4]+1,$t_arr[3]-1,$t_arr[5])); - $this->endDate->setTimestamp(mktime(0,0,0,$t_arr[4]+1,$t_arr[3]-1,$t_arr[5])); - break; - - case INTERVAL_THIS_WEEK: - $this->startDate->setTimestamp(mktime(0,0,0,$t_arr[4]+1,$t_arr[3]-$t_arr[6]+$startWeekBias,$t_arr[5])); - $this->endDate->setTimestamp(mktime(0,0,0,$t_arr[4]+1,$t_arr[3]-$t_arr[6]+6+$startWeekBias,$t_arr[5])); - break; - case INTERVAL_LAST_WEEK: - $this->startDate->setTimestamp(mktime(0,0,0,$t_arr[4]+1,$t_arr[3]-$t_arr[6]-7+$startWeekBias,$t_arr[5])); - $this->endDate->setTimestamp(mktime(0,0,0,$t_arr[4]+1,$t_arr[3]-$t_arr[6]-1+$startWeekBias,$t_arr[5])); - break; - case INTERVAL_THIS_MONTH: - $this->startDate->setTimestamp(mktime(0,0,0,$t_arr[4]+1,1,$t_arr[5])); - $this->endDate->setTimestamp(mktime(0,0,0,$t_arr[4]+2,0,$t_arr[5])); - break; - case INTERVAL_LAST_MONTH: - $this->startDate->setTimestamp(mktime(0,0,0,$t_arr[4],1,$t_arr[5])); - $this->endDate->setTimestamp(mktime(0,0,0,$t_arr[4]+1,0,$t_arr[5])); - break; - - case INTERVAL_THIS_YEAR: - $this->startDate->setTimestamp(mktime(0, 0, 0, 1, 1, $t_arr[5])); - $this->endDate->setTimestamp(mktime(0, 0, 0, 12, 31, $t_arr[5])); - break; - } - } - - function setPeriod($b_date, $e_date) { - $this->startDate = $b_date; - $this->endDate = $e_date; - } - - // return date string - function getStartDate($format="") { - return $this->startDate->toString($format); - } - - // return date string - function getEndDate($format="") { - return $this->endDate->toString($format); - } -} diff --git a/WEB-INF/lib/ttTimesheetHelper.class.php b/WEB-INF/lib/ttTimesheetHelper.class.php index 5ca3abe6..ec2032ef 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 { @@ -545,15 +547,15 @@ static function getMatchingTimesheets($options) { // Determine start and end dates. $dateFormat = $user->getDateFormat(); if ($options['period']) - $period = new Period($options['period'], new DateAndTime($dateFormat)); + $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/initialize.php b/initialize.php index 88849a73..242fa0ab 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.4.5705"); +define("APP_VERSION", "1.21.4.5706"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From 87386884d31673d31421cfb331f0957dc28ae971 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Mon, 11 Apr 2022 16:45:51 +0000 Subject: [PATCH 051/168] Incremental refactoring. --- WEB-INF/lib/ttInvoiceHelper.class.php | 8 ++++---- WEB-INF/lib/ttReportHelper.class.php | 4 ++-- WEB-INF/lib/ttTimesheetHelper.class.php | 2 +- initialize.php | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/WEB-INF/lib/ttInvoiceHelper.class.php b/WEB-INF/lib/ttInvoiceHelper.class.php index 9a20730a..3fa4f2ce 100644 --- a/WEB-INF/lib/ttInvoiceHelper.class.php +++ b/WEB-INF/lib/ttInvoiceHelper.class.php @@ -226,11 +226,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 = ''; diff --git a/WEB-INF/lib/ttReportHelper.class.php b/WEB-INF/lib/ttReportHelper.class.php index 995cfa2b..693fe652 100644 --- a/WEB-INF/lib/ttReportHelper.class.php +++ b/WEB-INF/lib/ttReportHelper.class.php @@ -118,7 +118,7 @@ static function getWhere($options) { // Prepare sql query part for where. $dateFormat = $user->getDateFormat(); - if ($options['period']) + if (isset($options['period']) && $options['period']) $period = new ttPeriod(new ttDate(), $options['period']); else { $period = new ttPeriod(new ttDate()); @@ -200,7 +200,7 @@ static function getExpenseWhere($options) { // Prepare sql query part for where. $dateFormat = $user->getDateFormat(); - if ($options['period']) + if (isset($options['period']) && $options['period']) $period = new ttPeriod(new ttDate(), $options['period']); else { $period = new ttPeriod(new ttDate()); diff --git a/WEB-INF/lib/ttTimesheetHelper.class.php b/WEB-INF/lib/ttTimesheetHelper.class.php index ec2032ef..070d31d7 100644 --- a/WEB-INF/lib/ttTimesheetHelper.class.php +++ b/WEB-INF/lib/ttTimesheetHelper.class.php @@ -546,7 +546,7 @@ static function getMatchingTimesheets($options) { // Determine start and end dates. $dateFormat = $user->getDateFormat(); - if ($options['period']) + if (isset($options['period']) && $options['period']) $period = new ttPeriod(new ttDate(), $options['period']); else { $period = new ttPeriod(new ttDate()); diff --git a/initialize.php b/initialize.php index 242fa0ab..ee6c4692 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.4.5706"); +define("APP_VERSION", "1.21.4.5707"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From a299407343dd43cb46a90ac3063fa9bcc5b7d700 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Mon, 11 Apr 2022 17:01:00 +0000 Subject: [PATCH 052/168] A bit more refactoring. --- WEB-INF/lib/ttInvoiceHelper.class.php | 17 ++++++++--------- initialize.php | 2 +- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/WEB-INF/lib/ttInvoiceHelper.class.php b/WEB-INF/lib/ttInvoiceHelper.class.php index 3fa4f2ce..2ac63db4 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; } @@ -304,14 +303,14 @@ static function createInvoice($fields) { $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/initialize.php b/initialize.php index ee6c4692..97e7b770 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.4.5707"); +define("APP_VERSION", "1.21.4.5708"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From d17164a8ecd5880d4b16a5eff98bd130566bc5ce Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Mon, 11 Apr 2022 17:54:04 +0000 Subject: [PATCH 053/168] More progress with refactoring. --- WEB-INF/lib/ttTimesheetHelper.class.php | 24 ++++++++++++------------ initialize.php | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/WEB-INF/lib/ttTimesheetHelper.class.php b/WEB-INF/lib/ttTimesheetHelper.class.php index 070d31d7..16d74029 100644 --- a/WEB-INF/lib/ttTimesheetHelper.class.php +++ b/WEB-INF/lib/ttTimesheetHelper.class.php @@ -47,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; @@ -448,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 = ''; @@ -488,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. diff --git a/initialize.php b/initialize.php index 97e7b770..b567181c 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.4.5708"); +define("APP_VERSION", "1.21.4.5709"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From 24d68b74cec8556cb594366e59a75d2107ff1e3c Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Mon, 11 Apr 2022 19:29:44 +0000 Subject: [PATCH 054/168] Fixed loading fav reports. --- WEB-INF/lib/form/ActionForm.class.php | 8 ++++---- WEB-INF/lib/ttFavReportHelper.class.php | 9 +++++---- initialize.php | 2 +- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/WEB-INF/lib/form/ActionForm.class.php b/WEB-INF/lib/form/ActionForm.class.php index 5b88292c..253091b4 100644 --- a/WEB-INF/lib/form/ActionForm.class.php +++ b/WEB-INF/lib/form/ActionForm.class.php @@ -88,8 +88,8 @@ function setAttribute($name,$value) { if ($this->mForm) { if (isset($this->mForm->elements[$name])) { if ($this->mForm->elements[$name]->class=="DateField") { - // TODO: It appears that we never get here. Refactor this entire class. - $dt = new ttDate($user->date_format, $value); + // We get here when loading a fav report. Refactor this entire class. + $dt = new ttDate($value, $user->getDateFormat()); $value = $dt->toString(); } $this->mForm->elements[$name]->setValueSafe($value); @@ -118,8 +118,8 @@ function setAttributes($value) { if ($this->mForm) { if (isset($this->mForm->elements[$name])) { if ($this->mForm->elements[$name]->class=="DateField") { - // TODO: It appears that we never get here. Refactor this entire class. - $dt = new ttDate($user->date_format, $value); + // We get here when changing to --- no --- fav report. Refactor this entire class. + $dt = new ttDate($value, $user->getDateFormat()); $value = $dt->toString(); } $this->mForm->elements[$name]->setValueSafe($value); diff --git a/WEB-INF/lib/ttFavReportHelper.class.php b/WEB-INF/lib/ttFavReportHelper.class.php index 303750d6..9e1827e0 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 { @@ -212,12 +213,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( diff --git a/initialize.php b/initialize.php index b567181c..17750f01 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.4.5709"); +define("APP_VERSION", "1.21.5.5710"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From 7478a60d4496328cf020a94c6e8b5b02aece11ab Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Mon, 11 Apr 2022 20:41:55 +0000 Subject: [PATCH 055/168] Refactoring. Finished replacing older DateAndTime class with newer ttDate class. --- WEB-INF/lib/DateAndTime.class.php | 348 ------------------------ WEB-INF/lib/form/DateField.class.php | 14 +- WEB-INF/lib/ttDate.class.php | 1 - WEB-INF/lib/ttFavReportHelper.class.php | 4 +- 4 files changed, 7 insertions(+), 360 deletions(-) delete mode 100644 WEB-INF/lib/DateAndTime.class.php diff --git a/WEB-INF/lib/DateAndTime.class.php b/WEB-INF/lib/DateAndTime.class.php deleted file mode 100644 index e7224598..00000000 --- a/WEB-INF/lib/DateAndTime.class.php +++ /dev/null @@ -1,348 +0,0 @@ -$sDate parsed, or false on error. - */ - -function my_strptime($sDate, $sFormat) -{ - $aResult = array - ( - 'tm_sec' => 0, - 'tm_min' => 0, - 'tm_hour' => 0, - 'tm_mday' => 1, - 'tm_mon' => 0, - 'tm_year' => 0, - 'tm_wday' => 0, - 'tm_yday' => 0, - 'unparsed' => $sDate, - ); - - while($sFormat != "") - { - // ===== Search a %x element, Check the static string before the %x ===== - $nIdxFound = strpos($sFormat, '%'); - if($nIdxFound === false) - { - - // There is no more format. Check the last static string. - $aResult['unparsed'] = ($sFormat == $sDate) ? "" : $sDate; - break; - } - - $sFormatBefore = mb_substr($sFormat, 0, $nIdxFound); - $sDateBefore = mb_substr($sDate, 0, $nIdxFound); - - if($sFormatBefore != $sDateBefore) break; - - // ===== Read the value of the %x found ===== - $sFormat = mb_substr($sFormat, $nIdxFound); - $sDate = mb_substr($sDate, $nIdxFound); - - $aResult['unparsed'] = $sDate; - - $sFormatCurrent = mb_substr($sFormat, 0, 2); - $sFormatAfter = mb_substr($sFormat, 2); - - $nValue = -1; - $sDateAfter = ""; - switch($sFormatCurrent) - { - case '%S': // Seconds after the minute (0-59) - - sscanf($sDate, "%2d%[^\\n]", $nValue, $sDateAfter); - - if(($nValue < 0) || ($nValue > 59)) return false; - - $aResult['tm_sec'] = $nValue; - break; - - // ---------- - case '%M': // Minutes after the hour (0-59) - sscanf($sDate, "%2d%[^\\n]", $nValue, $sDateAfter); - - if(($nValue < 0) || ($nValue > 59)) return false; - - $aResult['tm_min'] = $nValue; - break; - - // ---------- - case '%H': // Hour since midnight (0-23) - sscanf($sDate, "%2d%[^\\n]", $nValue, $sDateAfter); - - if(($nValue < 0) || ($nValue > 23)) return false; - - $aResult['tm_hour'] = $nValue; - break; - - // ---------- - case '%d': // Day of the month (1-31) - sscanf($sDate, "%2d%[^\\n]", $nValue, $sDateAfter); - - if(($nValue < 1) || ($nValue > 31)) return false; - - $aResult['tm_mday'] = $nValue; - break; - - // ---------- - case '%m': // Months since January (0-11) - sscanf($sDate, "%2d%[^\\n]", $nValue, $sDateAfter); - - if(($nValue < 1) || ($nValue > 12)) return false; - - $aResult['tm_mon'] = ($nValue - 1); - break; - - // ---------- - case '%Y': // Years since 1900 - sscanf($sDate, "%4d%[^\\n]", $nValue, $sDateAfter); - - if($nValue < 1900) return false; - - $aResult['tm_year'] = ($nValue - 1900); - break; - - // ---------- - default: - //sscanf($sDate, "%s%[^\\n]", $skip, $sDateAfter); - preg_match('/^(.+)(\s|$)/uU', $sDate, $matches); - if (isset($matches[1])) { - $sDateAfter = mb_substr($sDate, mb_strlen($matches[1])); - } else { - $sDateAfter = ''; - } - //break 2; // Break Switch and while - break; - } - - // ===== Next please ===== - $sFormat = $sFormatAfter; - $sDate = $sDateAfter; - - $aResult['unparsed'] = $sDate; - - } // END while($sFormat != "") - - - // ===== Create the other value of the result array ===== - $nParsedDateTimestamp = mktime($aResult['tm_hour'], $aResult['tm_min'], $aResult['tm_sec'], - $aResult['tm_mon'] + 1, $aResult['tm_mday'], $aResult['tm_year'] + 1900); - - // Before PHP 5.1 return -1 when error - if(($nParsedDateTimestamp === false) - ||($nParsedDateTimestamp === -1)) return false; - - $aResult['tm_wday'] = (int) strftime("%w", $nParsedDateTimestamp); // Days since Sunday (0-6) - $aResult['tm_yday'] = (strftime("%j", $nParsedDateTimestamp) - 1); // Days since January 1 (0-365) - - return $aResult; -} // END of function - -class DateAndTime { - var $mHour = 0; - var $mMinute = 0; - var $mSecond = 0; - var $mMonth; - var $mDay; // day of week - var $mDate; // day of month - var $mYear; - var $mIntrFormat = "%d.%m.%Y %H:%M:%S"; //29.02.2004 16:21:42 internal format date - var $mLocalFormat; - var $mParseResult = 0; - var $mAutoComplete = true; - - /** - * Constructor - * - * @param String $format - * @param String $strfDateTime - * @return DateAndTime - */ - function __construct($format="",$strfDateTime="") { - $this->mLocalFormat = ($format ? $format : $this->mIntrFormat); - $d = ($strfDateTime ? $strfDateTime : $this->do_strftime($this->mLocalFormat)); - $this->parseVal($d); - } - - function setFormat($format) { - $this->mLocalFormat = $format; - } - - function getFormat() { - return $this->mLocalFormat; - } - - //01 to 31 - function getDate() { return $this->mDate; } - - //0 (for Sunday) through 6 (for Saturday) - function getDay() { return $this->mDay; } - - //01 through 12 - function getMonth() { return $this->mMonth; } - - //1999 or 2003 - function getYear() { return $this->mYear; } - - function setDate($value) { $this->mDate = $value; } - function setMonth($value) { $this->mMonth = $value; } - function setYear($value) { $this->mYear = $value; } - - function setTimestamp($ts) { - $this->mDate = date("d",$ts); - $this->mDay = date("w",$ts); - $this->mMonth = date("m",$ts); - $this->mYear = date("Y",$ts); - $this->mHour = date("H",$ts); - $this->mMinute = date("i",$ts); - $this->mSecond = date("s",$ts); - } - - /** - * Return UNIX timestamp - */ - function getTimestamp() { - return @mktime($this->mHour, $this->mMinute, $this->mSecond, $this->mMonth, $this->mDate, $this->mYear); - } - - function compare($datetime) { - $ts1 = $this->getTimestamp(); - $ts2 = $datetime->getTimestamp(); - if ($ts1<$ts2) return -1; - if ($ts1==$ts2) return 0; - if ($ts1>$ts2) return 1; - } - - function toString($format="") { - if ($this->mParseResult==0) { - if ($format) { - return $this->do_strftime($format, $this->getTimestamp()); - } else { - return $this->do_strftime($this->mLocalFormat, $this->getTimestamp()); - } - } else { - if ($format) { - return $this->do_strftime($format); - } else { - return $this->do_strftime($this->mLocalFormat); - } - } - } - - function parseVal($szDate, $format="") { - $useformat = ($format ? $format : $this->mLocalFormat); - $res = my_strptime($szDate, $useformat); - if ($res !== false) { - $this->mDate = $res['tm_mday']; - $this->mDay = $res['tm_wday']; - $this->mMonth = $res['tm_mon'] + 1; // tm_mon - Months since January (0-11) - $this->mYear = 1900 + $res['tm_year']; // tm_year - Years since 1900 - $this->mHour = $res['tm_hour']; - $this->mMinute = $res['tm_min']; - $this->mSecond = $res['tm_sec']; - $this->mParseResult = 0; - } elseif ($this->mAutoComplete) { - $this->setTimestamp(time()); - $this->mParseResult = 1; - } - } - - function isError() { - if ($this->mParseResult != 0) return true; - return false; - } - - function before(/*DateAndTime*/ $obj) { - if ($this->getTimestamp() < $obj->getTimestamp()) return true; - return false; - } - - function after(/*DateAndTime*/ $obj) { - if ($this->getTimestamp() > $obj->getTimestamp()) return true; - return false; - } - - function equals(/*DateAndTime*/ $obj) { - if ($this->getTimestamp() == $obj->getTimestamp()) return true; - return false; - } - - function decDay(/*int*/$days=1) { - $this->setTimestamp(@mktime($this->mHour, $this->mMinute, $this->mSecond, $this->mMonth, $this->mDate - $days, $this->mYear)); - } - - function incDay(/*int*/$days=1) { - $this->setTimestamp(@mktime($this->mHour, $this->mMinute, $this->mSecond, $this->mMonth, $this->mDate + $days, $this->mYear)); - } - - /** - * @param $format string Datetime format string - * @return string Preprocessed string with all locale-depended format - * characters replaced by localized i18n strings. - */ - function preprocessFormatString($format) { - global $i18n; - - // replace locale-dependent strings - $format = str_replace('%a', mb_substr($i18n->getWeekDayName($this->mDay), 0, 3, 'utf-8'), $format); - $format = str_replace('%A', $i18n->getWeekDayName($this->mDay), $format); - /* This block is commented out because we currently do not use these formatters. - $abbrev_month = mb_substr($i18n->monthNames[$this->mMonth], 0, 3, 'utf-8'); - $format = str_replace('%b', $abbrev_month, $format); - $format = str_replace('%h', $abbrev_month, $format); - $format = str_replace('%z', date('O'), $format); - $format = str_replace('%Z', date('O'), $format); // format as 'O' for consistency with JS strftime - if (strpos($format, '%c') !== false) { - $format = str_replace('%c', $this->preprocessFormatString('%a %d %b %Y %T %Z'), $format); - }*/ - return $format; - } - - function do_strftime($format, $timestamp = null) - { - if (!is_null($timestamp)) { - return strftime($this->preprocessFormatString($format), $timestamp); - } else { - return strftime($this->preprocessFormatString($format)); - } - } -} diff --git a/WEB-INF/lib/form/DateField.class.php b/WEB-INF/lib/form/DateField.class.php index 922e8bf5..d349d568 100644 --- a/WEB-INF/lib/form/DateField.class.php +++ b/WEB-INF/lib/form/DateField.class.php @@ -3,20 +3,18 @@ License: See license.txt */ import('form.TextField'); -import('DateAndTime'); +import('ttDate'); class DateField extends TextField { var $mWeekStartDay = 0; var $mDateFormat = "d/m/Y"; var $lToday = "Today"; - var $mDateObj; var $lCalendarButtons = array('today'=>'Today', 'close'=>'Close'); function __construct($name) { $this->class = 'DateField'; $this->name = $name; - $this->mDateObj = new DateAndTime(); $this->localize(); } @@ -24,8 +22,6 @@ function localize() { global $user; global $i18n; - $this->mDateObj->setFormat($user->getDateFormat()); - $this->mMonthNames = $i18n->monthNames; $this->mWeekDayShortNames = $i18n->weekdayShortNames; $this->lToday = $i18n->get('label.today'); @@ -39,15 +35,15 @@ function localize() { // set current value taken from session or database function setValueSafe($value) { if (isset($value) && (strlen($value) > 0)) { - $this->mDateObj->parseVal($value, DB_DATEFORMAT); - $this->value = $this->mDateObj->toString($this->mDateFormat); //? + $ttDate = new ttDate($value); + $this->value = $ttDate->toString($this->mDateFormat); } } // get value for storing in session or database function getValueSafe() { if (strlen($this->value)>0) { - $this->mDateObj->parseVal($this->value, $this->mDateFormat); //? - return $this->mDateObj->toString(DB_DATEFORMAT); + $ttDate = new ttDate($this->value, $this->mDateFormat); + return $ttDate->toString(); } else { return null; } diff --git a/WEB-INF/lib/ttDate.class.php b/WEB-INF/lib/ttDate.class.php index 3b798214..25dc4874 100644 --- a/WEB-INF/lib/ttDate.class.php +++ b/WEB-INF/lib/ttDate.class.php @@ -5,7 +5,6 @@ import('I18n'); // ttDate class is used to represent a date in Time Tracker. -// It is a planned replacement for DateAndTime class, which is problematic with php8.1. class ttDate { var $year; // year diff --git a/WEB-INF/lib/ttFavReportHelper.class.php b/WEB-INF/lib/ttFavReportHelper.class.php index 9e1827e0..3de0c94c 100644 --- a/WEB-INF/lib/ttFavReportHelper.class.php +++ b/WEB-INF/lib/ttFavReportHelper.class.php @@ -340,11 +340,11 @@ static function loadReport(&$bean) { $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']); From 886f8979d84e84a4b67207ec512d5e794f34e455 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Fri, 15 Apr 2022 14:19:03 +0000 Subject: [PATCH 056/168] Fixed ttUser::getUserDetails to work on top managers in subgroups. --- WEB-INF/lib/ttUser.class.php | 2 +- initialize.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/WEB-INF/lib/ttUser.class.php b/WEB-INF/lib/ttUser.class.php index 7114bc7c..e0565b3d 100644 --- a/WEB-INF/lib/ttUser.class.php +++ b/WEB-INF/lib/ttUser.class.php @@ -603,7 +603,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)". diff --git a/initialize.php b/initialize.php index 17750f01..8709beed 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.5.5710"); +define("APP_VERSION", "1.21.5.5712"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From b292d803a1bd81bb82cff089e7d19e2786e7319a Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Fri, 15 Apr 2022 16:02:52 +0000 Subject: [PATCH 057/168] Fixed browser_today population on time.php. --- WEB-INF/templates/time.tpl | 2 +- initialize.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/WEB-INF/templates/time.tpl b/WEB-INF/templates/time.tpl index b04959cf..6f6f7394 100644 --- a/WEB-INF/templates/time.tpl +++ b/WEB-INF/templates/time.tpl @@ -122,7 +122,7 @@ function handleStop(buttonElement) { {if $time_records} - +
{$i18n.label.files} + {if $record.approved || $record.timesheet_id || $record.invoice_id}   {else} + {if ($record.duration == '0:00' && $record.start <> '')} + + {/if} {$i18n.label.edit} {/if}
diff --git a/initialize.php b/initialize.php index 8709beed..b5707136 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.5.5712"); +define("APP_VERSION", "1.21.5.5713"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From e5a3f1ce50205b3fa4b6bc1ca3570ab29e75d101 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Fri, 15 Apr 2022 18:06:47 +0000 Subject: [PATCH 058/168] Improved a check for browser today. --- WEB-INF/lib/common.lib.php | 16 ++++++++++++++++ expense_edit.php | 2 +- expenses.php | 2 +- initialize.php | 2 +- puncher.php | 2 +- time.php | 4 ++-- time_edit.php | 2 +- week.php | 2 +- 8 files changed, 24 insertions(+), 8 deletions(-) diff --git a/WEB-INF/lib/common.lib.php b/WEB-INF/lib/common.lib.php index 2d8e287d..52d82ed3 100644 --- a/WEB-INF/lib/common.lib.php +++ b/WEB-INF/lib/common.lib.php @@ -308,6 +308,22 @@ function ttValidDbDateFormatDate($val) return checkdate($date_parts[1], $date_parts[2], $date_parts[0]); } +// ttValidBrowserToday is used to check browser today parameter for sanity. +function ttValidBrowserToday($val) +{ + if (!ttValidDbDateFormatDate($val)) + return false; + + // Check if browser today is after server tomorrow. + $browser_today = new ttDate($val); + $server_tomorrow = new ttDate(); + $server_tomorrow->incrementDay(); + if ($browser_today->after($server_tomorrow)) + return false; + + return true; +} + // ttValidTime is used to check user input for time post. function ttValidTime($val) { diff --git a/expense_edit.php b/expense_edit.php index e57db88d..0281c2cf 100644 --- a/expense_edit.php +++ b/expense_edit.php @@ -30,7 +30,7 @@ if ($request->isPost()) { // Validate that browser_today parameter is in correct format. $browser_today = $request->getParameter('browser_today'); - if ($browser_today && !ttValidDbDateFormatDate($browser_today)) { + if ($browser_today && !ttValidBrowserToday($browser_today)) { header('Location: access_denied.php'); exit(); } diff --git a/expenses.php b/expenses.php index 34169b7d..b86c34d5 100644 --- a/expenses.php +++ b/expenses.php @@ -47,7 +47,7 @@ if ($request->isPost()) { // Validate that browser_today parameter is in correct format. $browser_today = $request->getParameter('browser_today'); - if ($browser_today && !ttValidDbDateFormatDate($browser_today)) { + if ($browser_today && !ttValidBrowserToday($browser_today)) { header('Location: access_denied.php'); exit(); } diff --git a/initialize.php b/initialize.php index b5707136..f91645a7 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.5.5713"); +define("APP_VERSION", "1.21.5.5714"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); diff --git a/puncher.php b/puncher.php index 2b25462d..b959dcfb 100644 --- a/puncher.php +++ b/puncher.php @@ -24,7 +24,7 @@ if ($request->isPost()) { // Validate that browser_today parameter is in correct format. $browser_today = $request->getParameter('browser_today'); - if ($browser_today && !ttValidDbDateFormatDate($browser_today)) { + if ($browser_today && !ttValidBrowserToday($browser_today)) { header('Location: access_denied.php'); exit(); } diff --git a/time.php b/time.php index faf064a4..32920344 100644 --- a/time.php +++ b/time.php @@ -41,7 +41,7 @@ if ($request->isPost()) { // Validate that browser_today parameter is in correct format. $browser_today = $request->getParameter('browser_today'); - if ($browser_today && !ttValidDbDateFormatDate($browser_today)) { + if ($browser_today && !ttValidBrowserToday($browser_today)) { header('Location: access_denied.php'); exit(); } @@ -59,7 +59,7 @@ // Validate that browser_date parameter is in correct format. $browser_date = $request->getParameter('browser_date'); - if ($browser_date && !ttValidDbDateFormatDate($browser_date)) { + if ($browser_date && !ttValidBrowserToday($browser_date)) { header('Location: access_denied.php'); exit(); } diff --git a/time_edit.php b/time_edit.php index 03c72119..ad673ffe 100644 --- a/time_edit.php +++ b/time_edit.php @@ -27,7 +27,7 @@ if ($request->isPost()) { // Validate that browser_today parameter is in correct format. $browser_today = $request->getParameter('browser_today'); - if ($browser_today && !ttValidDbDateFormatDate($browser_today)) { + if ($browser_today && !ttValidBrowserToday($browser_today)) { header('Location: access_denied.php'); exit(); } diff --git a/week.php b/week.php index 92eb95ee..cb3a1d9f 100644 --- a/week.php +++ b/week.php @@ -47,7 +47,7 @@ if ($request->isPost()) { // Validate that browser_today parameter is in correct format. $browser_today = $request->getParameter('browser_today'); - if ($browser_today && !ttValidDbDateFormatDate($browser_today)) { + if ($browser_today && !ttValidBrowserToday($browser_today)) { header('Location: access_denied.php'); exit(); } From aa5adfb54c3823b3172280be5914534e98279587 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sat, 16 Apr 2022 13:24:42 +0000 Subject: [PATCH 059/168] Improved handling of future entries by tolerating out of time synced browsers. --- WEB-INF/lib/common.lib.php | 16 ---------------- expense_edit.php | 6 ++++-- expenses.php | 6 ++++-- initialize.php | 2 +- puncher.php | 2 +- time.php | 8 +++++--- time_edit.php | 6 ++++-- week.php | 2 +- 8 files changed, 20 insertions(+), 28 deletions(-) diff --git a/WEB-INF/lib/common.lib.php b/WEB-INF/lib/common.lib.php index 52d82ed3..2d8e287d 100644 --- a/WEB-INF/lib/common.lib.php +++ b/WEB-INF/lib/common.lib.php @@ -308,22 +308,6 @@ function ttValidDbDateFormatDate($val) return checkdate($date_parts[1], $date_parts[2], $date_parts[0]); } -// ttValidBrowserToday is used to check browser today parameter for sanity. -function ttValidBrowserToday($val) -{ - if (!ttValidDbDateFormatDate($val)) - return false; - - // Check if browser today is after server tomorrow. - $browser_today = new ttDate($val); - $server_tomorrow = new ttDate(); - $server_tomorrow->incrementDay(); - if ($browser_today->after($server_tomorrow)) - return false; - - return true; -} - // ttValidTime is used to check user input for time post. function ttValidTime($val) { diff --git a/expense_edit.php b/expense_edit.php index 0281c2cf..98098a19 100644 --- a/expense_edit.php +++ b/expense_edit.php @@ -30,7 +30,7 @@ if ($request->isPost()) { // Validate that browser_today parameter is in correct format. $browser_today = $request->getParameter('browser_today'); - if ($browser_today && !ttValidBrowserToday($browser_today)) { + if ($browser_today && !ttValidDbDateFormatDate($browser_today)) { header('Location: access_denied.php'); exit(); } @@ -155,7 +155,9 @@ // Prohibit creating entries in future. if (!$user->isOptionEnabled('future_entries')) { $browser_today = new ttDate($request->getParameter('browser_today', null)); - if ($new_date->after($browser_today)) + $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')); diff --git a/expenses.php b/expenses.php index b86c34d5..ba091a63 100644 --- a/expenses.php +++ b/expenses.php @@ -47,7 +47,7 @@ if ($request->isPost()) { // Validate that browser_today parameter is in correct format. $browser_today = $request->getParameter('browser_today'); - if ($browser_today && !ttValidBrowserToday($browser_today)) { + if ($browser_today && !ttValidDbDateFormatDate($browser_today)) { header('Location: access_denied.php'); exit(); } @@ -203,7 +203,9 @@ // Prohibit creating entries in future. if (!$user->isOptionEnabled('future_entries')) { $browser_today = new ttDate($request->getParameter('browser_today', null)); - if ($selected_date->after($browser_today)) + $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')); } // Finished validating input data. diff --git a/initialize.php b/initialize.php index f91645a7..a67bd344 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.5.5714"); +define("APP_VERSION", "1.21.5.5715"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); diff --git a/puncher.php b/puncher.php index b959dcfb..2b25462d 100644 --- a/puncher.php +++ b/puncher.php @@ -24,7 +24,7 @@ if ($request->isPost()) { // Validate that browser_today parameter is in correct format. $browser_today = $request->getParameter('browser_today'); - if ($browser_today && !ttValidBrowserToday($browser_today)) { + if ($browser_today && !ttValidDbDateFormatDate($browser_today)) { header('Location: access_denied.php'); exit(); } diff --git a/time.php b/time.php index 32920344..766ee5fd 100644 --- a/time.php +++ b/time.php @@ -41,7 +41,7 @@ if ($request->isPost()) { // Validate that browser_today parameter is in correct format. $browser_today = $request->getParameter('browser_today'); - if ($browser_today && !ttValidBrowserToday($browser_today)) { + if ($browser_today && !ttValidDbDateFormatDate($browser_today)) { header('Location: access_denied.php'); exit(); } @@ -59,7 +59,7 @@ // Validate that browser_date parameter is in correct format. $browser_date = $request->getParameter('browser_date'); - if ($browser_date && !ttValidBrowserToday($browser_date)) { + if ($browser_date && !ttValidDbDateFormatDate($browser_date)) { header('Location: access_denied.php'); exit(); } @@ -417,7 +417,9 @@ // Prohibit creating entries in future. if (!$user->isOptionEnabled('future_entries')) { $browser_today = new ttDate($request->getParameter('browser_today', null)); - if ($selected_date->after($browser_today)) + $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')); } diff --git a/time_edit.php b/time_edit.php index ad673ffe..7c3491ea 100644 --- a/time_edit.php +++ b/time_edit.php @@ -27,7 +27,7 @@ if ($request->isPost()) { // Validate that browser_today parameter is in correct format. $browser_today = $request->getParameter('browser_today'); - if ($browser_today && !ttValidBrowserToday($browser_today)) { + if ($browser_today && !ttValidDbDateFormatDate($browser_today)) { header('Location: access_denied.php'); exit(); } @@ -332,7 +332,9 @@ // Prohibit creating entries in future. if ($err->no() && !$user->isOptionEnabled('future_entries')) { $browser_today = new ttDate($request->getParameter('browser_today', null)); - if ($new_date->after($browser_today)) + $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')); } diff --git a/week.php b/week.php index cb3a1d9f..92eb95ee 100644 --- a/week.php +++ b/week.php @@ -47,7 +47,7 @@ if ($request->isPost()) { // Validate that browser_today parameter is in correct format. $browser_today = $request->getParameter('browser_today'); - if ($browser_today && !ttValidBrowserToday($browser_today)) { + if ($browser_today && !ttValidDbDateFormatDate($browser_today)) { header('Location: access_denied.php'); exit(); } From 855e71a0b04fdc3bc449b961c56dcaa495daec3a Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sat, 16 Apr 2022 14:31:43 +0000 Subject: [PATCH 060/168] Cosmetic improvement to handling of future entries. --- WEB-INF/lib/ttWeekViewHelper.class.php | 4 +++- initialize.php | 2 +- puncher.php | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/WEB-INF/lib/ttWeekViewHelper.class.php b/WEB-INF/lib/ttWeekViewHelper.class.php index deefd8a1..9908d12b 100644 --- a/WEB-INF/lib/ttWeekViewHelper.class.php +++ b/WEB-INF/lib/ttWeekViewHelper.class.php @@ -504,7 +504,9 @@ static function insertDurationFromWeekView($fields, $custom_fields, $err) { // Prohibit creating entries in future. if (!$user->isOptionEnabled('future_entries') && $fields['browser_today']) { $objBrowserToday = new ttDate($fields['browser_today']); - if ($objEntryDate->after($objBrowserToday)) { + $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/initialize.php b/initialize.php index a67bd344..3a99f518 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.5.5715"); +define("APP_VERSION", "1.21.5.5716"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); diff --git a/puncher.php b/puncher.php index 2b25462d..9714ac24 100644 --- a/puncher.php +++ b/puncher.php @@ -235,9 +235,9 @@ } // Finished validating user input. - // Prohibit creating entries in future. Tricky with a bogus browser_today data in post. + // Prohibit creating entries in future. if (!$user->isOptionEnabled('future_entries')) { - // Just check if the date we are using is after server tomorrow. + // $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)) From 38797d661ba091cc7fbb8f92e2a9dc4ef94f8750 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Tue, 19 Apr 2022 15:55:49 +0000 Subject: [PATCH 061/168] Introduced an option defining whether or not to show weekends on week view. --- WEB-INF/resources/ca.lang.php | 1 + WEB-INF/resources/cs.lang.php | 1 + WEB-INF/resources/da.lang.php | 1 + WEB-INF/resources/de.lang.php | 2 ++ WEB-INF/resources/en.lang.php | 1 + WEB-INF/resources/es.lang.php | 1 + WEB-INF/resources/et.lang.php | 2 ++ WEB-INF/resources/fa.lang.php | 1 + WEB-INF/resources/fi.lang.php | 1 + WEB-INF/resources/fr.lang.php | 1 + WEB-INF/resources/gr.lang.php | 1 + WEB-INF/resources/he.lang.php | 1 + WEB-INF/resources/hu.lang.php | 1 + WEB-INF/resources/it.lang.php | 1 + WEB-INF/resources/ja.lang.php | 1 + WEB-INF/resources/ko.lang.php | 1 + WEB-INF/resources/nl.lang.php | 2 ++ WEB-INF/resources/no.lang.php | 1 + WEB-INF/resources/pl.lang.php | 1 + WEB-INF/resources/pt-br.lang.php | 2 ++ WEB-INF/resources/pt.lang.php | 1 + WEB-INF/resources/ro.lang.php | 1 + WEB-INF/resources/ru.lang.php | 1 + WEB-INF/resources/sk.lang.php | 1 + WEB-INF/resources/sl.lang.php | 1 + WEB-INF/resources/sr.lang.php | 1 + WEB-INF/resources/sv.lang.php | 1 + WEB-INF/resources/tr.lang.php | 1 + WEB-INF/resources/zh-cn.lang.php | 1 + WEB-INF/resources/zh-tw.lang.php | 1 + WEB-INF/templates/week_view.tpl | 9 +++++++++ initialize.php | 2 +- week.php | 27 ++++++++++++++++++++++++++- week_view.php | 4 ++++ 34 files changed, 74 insertions(+), 2 deletions(-) diff --git a/WEB-INF/resources/ca.lang.php b/WEB-INF/resources/ca.lang.php index 7964ede5..1326d15a 100644 --- a/WEB-INF/resources/ca.lang.php +++ b/WEB-INF/resources/ca.lang.php @@ -243,6 +243,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', diff --git a/WEB-INF/resources/cs.lang.php b/WEB-INF/resources/cs.lang.php index 5b930374..d65c6dab 100644 --- a/WEB-INF/resources/cs.lang.php +++ b/WEB-INF/resources/cs.lang.php @@ -252,6 +252,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', diff --git a/WEB-INF/resources/da.lang.php b/WEB-INF/resources/da.lang.php index 9c4d6c21..3fba7380 100644 --- a/WEB-INF/resources/da.lang.php +++ b/WEB-INF/resources/da.lang.php @@ -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' => 'Kun Total', diff --git a/WEB-INF/resources/de.lang.php b/WEB-INF/resources/de.lang.php index 2cf17c45..fe487464 100644 --- a/WEB-INF/resources/de.lang.php +++ b/WEB-INF/resources/de.lang.php @@ -228,6 +228,8 @@ 'label.mark_paid' => 'Als bezahlt setzen', 'label.week_note' => 'Wochennotiz', 'label.week_list' => 'Wochenliste', +// TODO: translate the following: +// 'label.weekends' => 'Weekends', 'label.work_units' => 'Arbeitseinheiten', 'label.work_units_short' => 'Einheiten', 'label.totals_only' => 'Nur Gesamtstunden', diff --git a/WEB-INF/resources/en.lang.php b/WEB-INF/resources/en.lang.php index bffac8f7..5b433b0a 100644 --- a/WEB-INF/resources/en.lang.php +++ b/WEB-INF/resources/en.lang.php @@ -214,6 +214,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', diff --git a/WEB-INF/resources/es.lang.php b/WEB-INF/resources/es.lang.php index 3d16d8e2..f57f5795 100644 --- a/WEB-INF/resources/es.lang.php +++ b/WEB-INF/resources/es.lang.php @@ -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' => 'Solo totales', diff --git a/WEB-INF/resources/et.lang.php b/WEB-INF/resources/et.lang.php index 849eff78..57b92ff3 100644 --- a/WEB-INF/resources/et.lang.php +++ b/WEB-INF/resources/et.lang.php @@ -244,6 +244,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', diff --git a/WEB-INF/resources/fa.lang.php b/WEB-INF/resources/fa.lang.php index bdddffa4..62b70671 100644 --- a/WEB-INF/resources/fa.lang.php +++ b/WEB-INF/resources/fa.lang.php @@ -248,6 +248,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', diff --git a/WEB-INF/resources/fi.lang.php b/WEB-INF/resources/fi.lang.php index 074ee794..fa1dd973 100644 --- a/WEB-INF/resources/fi.lang.php +++ b/WEB-INF/resources/fi.lang.php @@ -240,6 +240,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', diff --git a/WEB-INF/resources/fr.lang.php b/WEB-INF/resources/fr.lang.php index 548d6e0c..7472ddf6 100644 --- a/WEB-INF/resources/fr.lang.php +++ b/WEB-INF/resources/fr.lang.php @@ -234,6 +234,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', diff --git a/WEB-INF/resources/gr.lang.php b/WEB-INF/resources/gr.lang.php index 4de8f91d..93fb5fba 100644 --- a/WEB-INF/resources/gr.lang.php +++ b/WEB-INF/resources/gr.lang.php @@ -229,6 +229,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' => 'Σύνολα μόνο', diff --git a/WEB-INF/resources/he.lang.php b/WEB-INF/resources/he.lang.php index 6abbd474..388622c2 100644 --- a/WEB-INF/resources/he.lang.php +++ b/WEB-INF/resources/he.lang.php @@ -255,6 +255,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' => 'סיכומים בלבד', diff --git a/WEB-INF/resources/hu.lang.php b/WEB-INF/resources/hu.lang.php index b90ffdc5..635147d8 100644 --- a/WEB-INF/resources/hu.lang.php +++ b/WEB-INF/resources/hu.lang.php @@ -249,6 +249,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', diff --git a/WEB-INF/resources/it.lang.php b/WEB-INF/resources/it.lang.php index 34ca3745..55ea5deb 100644 --- a/WEB-INF/resources/it.lang.php +++ b/WEB-INF/resources/it.lang.php @@ -233,6 +233,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', diff --git a/WEB-INF/resources/ja.lang.php b/WEB-INF/resources/ja.lang.php index 8794e2ed..0cf54775 100644 --- a/WEB-INF/resources/ja.lang.php +++ b/WEB-INF/resources/ja.lang.php @@ -252,6 +252,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' => '全体だけ', diff --git a/WEB-INF/resources/ko.lang.php b/WEB-INF/resources/ko.lang.php index 69f6fd6c..42648461 100644 --- a/WEB-INF/resources/ko.lang.php +++ b/WEB-INF/resources/ko.lang.php @@ -252,6 +252,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' => '오직 전체만', diff --git a/WEB-INF/resources/nl.lang.php b/WEB-INF/resources/nl.lang.php index 012f9d14..eacfd5a1 100644 --- a/WEB-INF/resources/nl.lang.php +++ b/WEB-INF/resources/nl.lang.php @@ -209,6 +209,8 @@ 'label.mark_paid' => 'Markeer als betaald', 'label.week_note' => 'Week aantekening', 'label.week_list' => 'Week overzicht', +// TODO: translate the following. +// 'label.weekends' => 'Weekends', 'label.work_units' => 'Werk eenheid', 'label.work_units_short' => 'Eenheid', 'label.totals_only' => 'Alleen totalen', diff --git a/WEB-INF/resources/no.lang.php b/WEB-INF/resources/no.lang.php index 9ffef320..dd92cc78 100644 --- a/WEB-INF/resources/no.lang.php +++ b/WEB-INF/resources/no.lang.php @@ -252,6 +252,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', diff --git a/WEB-INF/resources/pl.lang.php b/WEB-INF/resources/pl.lang.php index 430416f5..50ac9c62 100644 --- a/WEB-INF/resources/pl.lang.php +++ b/WEB-INF/resources/pl.lang.php @@ -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' => 'Tylko sumy', diff --git a/WEB-INF/resources/pt-br.lang.php b/WEB-INF/resources/pt-br.lang.php index d156ccda..0b250d69 100644 --- a/WEB-INF/resources/pt-br.lang.php +++ b/WEB-INF/resources/pt-br.lang.php @@ -211,6 +211,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', diff --git a/WEB-INF/resources/pt.lang.php b/WEB-INF/resources/pt.lang.php index 4277ff18..e7b16d92 100644 --- a/WEB-INF/resources/pt.lang.php +++ b/WEB-INF/resources/pt.lang.php @@ -238,6 +238,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', diff --git a/WEB-INF/resources/ro.lang.php b/WEB-INF/resources/ro.lang.php index 755e2c47..b160169d 100644 --- a/WEB-INF/resources/ro.lang.php +++ b/WEB-INF/resources/ro.lang.php @@ -250,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' => 'Numai totaluri', diff --git a/WEB-INF/resources/ru.lang.php b/WEB-INF/resources/ru.lang.php index 61777ff4..f24aa6f7 100644 --- a/WEB-INF/resources/ru.lang.php +++ b/WEB-INF/resources/ru.lang.php @@ -209,6 +209,7 @@ 'label.mark_paid' => 'Отметить оплату', 'label.week_note' => 'Комментарий недели', 'label.week_list' => 'Список недели', +'label.weekends' => 'Выходные', 'label.work_units' => 'Единицы работы', 'label.work_units_short' => 'Единицы', 'label.totals_only' => 'Только итоги', diff --git a/WEB-INF/resources/sk.lang.php b/WEB-INF/resources/sk.lang.php index fb8fae3e..ca4f0535 100644 --- a/WEB-INF/resources/sk.lang.php +++ b/WEB-INF/resources/sk.lang.php @@ -244,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' => 'Iba celkové', diff --git a/WEB-INF/resources/sl.lang.php b/WEB-INF/resources/sl.lang.php index 02e79b40..574c32f0 100644 --- a/WEB-INF/resources/sl.lang.php +++ b/WEB-INF/resources/sl.lang.php @@ -234,6 +234,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', diff --git a/WEB-INF/resources/sr.lang.php b/WEB-INF/resources/sr.lang.php index 3917fc0a..06867886 100644 --- a/WEB-INF/resources/sr.lang.php +++ b/WEB-INF/resources/sr.lang.php @@ -240,6 +240,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. diff --git a/WEB-INF/resources/sv.lang.php b/WEB-INF/resources/sv.lang.php index c638d213..9f5725b8 100644 --- a/WEB-INF/resources/sv.lang.php +++ b/WEB-INF/resources/sv.lang.php @@ -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' => 'Visa endast summeringar', diff --git a/WEB-INF/resources/tr.lang.php b/WEB-INF/resources/tr.lang.php index c55b901e..f8fa7dd2 100644 --- a/WEB-INF/resources/tr.lang.php +++ b/WEB-INF/resources/tr.lang.php @@ -257,6 +257,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', diff --git a/WEB-INF/resources/zh-cn.lang.php b/WEB-INF/resources/zh-cn.lang.php index 2550f758..ca1709d3 100644 --- a/WEB-INF/resources/zh-cn.lang.php +++ b/WEB-INF/resources/zh-cn.lang.php @@ -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' => 'Totals only', diff --git a/WEB-INF/resources/zh-tw.lang.php b/WEB-INF/resources/zh-tw.lang.php index 08928dec..c59a87ef 100644 --- a/WEB-INF/resources/zh-tw.lang.php +++ b/WEB-INF/resources/zh-tw.lang.php @@ -250,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', diff --git a/WEB-INF/templates/week_view.tpl b/WEB-INF/templates/week_view.tpl index 3226e0c4..00e6cd1d 100644 --- a/WEB-INF/templates/week_view.tpl +++ b/WEB-INF/templates/week_view.tpl @@ -39,6 +39,15 @@ License: See license.txt *} + + + + + +
{$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/initialize.php b/initialize.php index 3a99f518..7b2064ad 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.5.5716"); +define("APP_VERSION", "1.21.6.5717"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); diff --git a/week.php b/week.php index 92eb95ee..302e4dfb 100644 --- a/week.php +++ b/week.php @@ -73,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'); @@ -158,6 +159,22 @@ // Get column headers, which are day numbers in month. $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; + } + unset($dayHeaders[$weekend_start_idx]); + unset($dayHeaders[$weekend_end_idx]); + unset($lockedDays[$weekend_start_idx]); + unset($lockedDays[$weekend_end_idx]); +} + // Get already existing records. $records = ttWeekViewHelper::getRecordsForInterval($startDate->toString(), $endDate->toString(), $showFiles); // Build data array for the table. Format is described in ttWeekViewHelper::getDataForWeekView function. @@ -273,8 +290,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) + $table->addColumn(new TableColumn($dayHeaders[$i], $dayHeaders[$i], new WeekViewCellRenderer(), $dayTotals[$dayHeaders[$i]])); + else { + if ($i <> $weekend_start_idx && $i <> $weekend_end_idx) + $table->addColumn(new TableColumn($dayHeaders[$i], $dayHeaders[$i], new WeekViewCellRenderer(), $dayTotals[$dayHeaders[$i]])); + } } $table->setInteractive(false); $form->addInputElement($table); diff --git a/week_view.php b/week_view.php index 1718afd4..7db10226 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')); } From f3ecd48c47df8af907cb46dec59cdd62793535e6 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Tue, 19 Apr 2022 16:00:27 +0000 Subject: [PATCH 062/168] Fixed label in html. --- WEB-INF/templates/week_view.tpl | 4 ++-- initialize.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/WEB-INF/templates/week_view.tpl b/WEB-INF/templates/week_view.tpl index 00e6cd1d..9cd7b90a 100644 --- a/WEB-INF/templates/week_view.tpl +++ b/WEB-INF/templates/week_view.tpl @@ -39,9 +39,9 @@ License: See license.txt *}
- + - + {$forms.weekViewForm.weekends.control} {$i18n.label.what_is_it} {$i18n.label.what_is_it} diff --git a/initialize.php b/initialize.php index 7b2064ad..89c734ad 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.6.5717"); +define("APP_VERSION", "1.21.6.5718"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From dcfeafbc71bb9e1329f997bfc7e7b42050174453 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Tue, 26 Apr 2022 15:52:14 +0000 Subject: [PATCH 063/168] Dutch translation improved. --- WEB-INF/resources/nl.lang.php | 3 +-- initialize.php | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/WEB-INF/resources/nl.lang.php b/WEB-INF/resources/nl.lang.php index eacfd5a1..949a3ac8 100644 --- a/WEB-INF/resources/nl.lang.php +++ b/WEB-INF/resources/nl.lang.php @@ -209,8 +209,7 @@ 'label.mark_paid' => 'Markeer als betaald', 'label.week_note' => 'Week aantekening', 'label.week_list' => 'Week overzicht', -// TODO: translate the following. -// 'label.weekends' => 'Weekends', +'label.weekends' => 'Weekeinden', 'label.work_units' => 'Werk eenheid', 'label.work_units_short' => 'Eenheid', 'label.totals_only' => 'Alleen totalen', diff --git a/initialize.php b/initialize.php index 89c734ad..53810201 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.6.5718"); +define("APP_VERSION", "1.21.6.5719"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From 761d451e6382b59fd96f133b5c9dc9cca3ccb753 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Thu, 30 Jun 2022 14:00:35 +0000 Subject: [PATCH 064/168] Starting to work on filtering by notes in reports. --- WEB-INF/resources/ca.lang.php | 2 ++ WEB-INF/resources/cs.lang.php | 2 ++ WEB-INF/resources/da.lang.php | 2 ++ WEB-INF/resources/de.lang.php | 2 ++ WEB-INF/resources/en.lang.php | 1 + WEB-INF/resources/es.lang.php | 2 ++ WEB-INF/resources/et.lang.php | 2 ++ WEB-INF/resources/fa.lang.php | 2 ++ WEB-INF/resources/fi.lang.php | 2 ++ WEB-INF/resources/fr.lang.php | 2 ++ WEB-INF/resources/gr.lang.php | 2 ++ WEB-INF/resources/he.lang.php | 2 ++ WEB-INF/resources/hu.lang.php | 1 + WEB-INF/resources/it.lang.php | 2 ++ WEB-INF/resources/ja.lang.php | 2 ++ WEB-INF/resources/ko.lang.php | 2 ++ WEB-INF/resources/nl.lang.php | 2 ++ WEB-INF/resources/no.lang.php | 2 ++ WEB-INF/resources/pl.lang.php | 2 ++ WEB-INF/resources/pt-br.lang.php | 2 ++ WEB-INF/resources/pt.lang.php | 2 ++ WEB-INF/resources/ro.lang.php | 2 ++ WEB-INF/resources/ru.lang.php | 1 + WEB-INF/resources/sk.lang.php | 2 ++ WEB-INF/resources/sl.lang.php | 1 + WEB-INF/resources/sr.lang.php | 2 ++ WEB-INF/resources/sv.lang.php | 2 ++ WEB-INF/resources/tr.lang.php | 2 ++ WEB-INF/resources/zh-cn.lang.php | 2 ++ WEB-INF/resources/zh-tw.lang.php | 2 ++ WEB-INF/templates/reports.tpl | 8 ++++++++ reports.php | 5 +++++ 32 files changed, 69 insertions(+) diff --git a/WEB-INF/resources/ca.lang.php b/WEB-INF/resources/ca.lang.php index 1326d15a..f2eec1b5 100644 --- a/WEB-INF/resources/ca.lang.php +++ b/WEB-INF/resources/ca.lang.php @@ -474,6 +474,8 @@ // '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', diff --git a/WEB-INF/resources/cs.lang.php b/WEB-INF/resources/cs.lang.php index d65c6dab..8573d71c 100644 --- a/WEB-INF/resources/cs.lang.php +++ b/WEB-INF/resources/cs.lang.php @@ -482,6 +482,8 @@ // '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', diff --git a/WEB-INF/resources/da.lang.php b/WEB-INF/resources/da.lang.php index 3fba7380..dd1abd39 100644 --- a/WEB-INF/resources/da.lang.php +++ b/WEB-INF/resources/da.lang.php @@ -459,6 +459,8 @@ // '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', diff --git a/WEB-INF/resources/de.lang.php b/WEB-INF/resources/de.lang.php index fe487464..e295e728 100644 --- a/WEB-INF/resources/de.lang.php +++ b/WEB-INF/resources/de.lang.php @@ -442,6 +442,8 @@ // 'form.reports.include_pending' => 'pending', 'form.reports.select_period' => 'Zeitraum auswählen', 'form.reports.set_period' => 'oder Datum eingeben', +// TODO: translate the following. +// 'form.reports.note_containing' => 'Note containing', 'form.reports.show_fields' => 'Felder anzeigen', // TODO: translate the following. // 'form.reports.time_fields' => 'Time fields', diff --git a/WEB-INF/resources/en.lang.php b/WEB-INF/resources/en.lang.php index 5b433b0a..5df99c40 100644 --- a/WEB-INF/resources/en.lang.php +++ b/WEB-INF/resources/en.lang.php @@ -425,6 +425,7 @@ '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', diff --git a/WEB-INF/resources/es.lang.php b/WEB-INF/resources/es.lang.php index f57f5795..7c59d3ae 100644 --- a/WEB-INF/resources/es.lang.php +++ b/WEB-INF/resources/es.lang.php @@ -487,6 +487,8 @@ // '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', diff --git a/WEB-INF/resources/et.lang.php b/WEB-INF/resources/et.lang.php index 57b92ff3..7a53024b 100644 --- a/WEB-INF/resources/et.lang.php +++ b/WEB-INF/resources/et.lang.php @@ -472,6 +472,8 @@ // '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', diff --git a/WEB-INF/resources/fa.lang.php b/WEB-INF/resources/fa.lang.php index 62b70671..2963414e 100644 --- a/WEB-INF/resources/fa.lang.php +++ b/WEB-INF/resources/fa.lang.php @@ -480,6 +480,8 @@ // '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', diff --git a/WEB-INF/resources/fi.lang.php b/WEB-INF/resources/fi.lang.php index fa1dd973..112dc6b6 100644 --- a/WEB-INF/resources/fi.lang.php +++ b/WEB-INF/resources/fi.lang.php @@ -465,6 +465,8 @@ // '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', diff --git a/WEB-INF/resources/fr.lang.php b/WEB-INF/resources/fr.lang.php index 7472ddf6..a1268d1a 100644 --- a/WEB-INF/resources/fr.lang.php +++ b/WEB-INF/resources/fr.lang.php @@ -456,6 +456,8 @@ // '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', diff --git a/WEB-INF/resources/gr.lang.php b/WEB-INF/resources/gr.lang.php index 93fb5fba..9f1bf5d9 100644 --- a/WEB-INF/resources/gr.lang.php +++ b/WEB-INF/resources/gr.lang.php @@ -451,6 +451,8 @@ // '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', diff --git a/WEB-INF/resources/he.lang.php b/WEB-INF/resources/he.lang.php index 388622c2..3167fd8d 100644 --- a/WEB-INF/resources/he.lang.php +++ b/WEB-INF/resources/he.lang.php @@ -483,6 +483,8 @@ // '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', diff --git a/WEB-INF/resources/hu.lang.php b/WEB-INF/resources/hu.lang.php index 635147d8..636c29d0 100644 --- a/WEB-INF/resources/hu.lang.php +++ b/WEB-INF/resources/hu.lang.php @@ -478,6 +478,7 @@ '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', diff --git a/WEB-INF/resources/it.lang.php b/WEB-INF/resources/it.lang.php index 55ea5deb..d517fded 100644 --- a/WEB-INF/resources/it.lang.php +++ b/WEB-INF/resources/it.lang.php @@ -464,6 +464,8 @@ // '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', diff --git a/WEB-INF/resources/ja.lang.php b/WEB-INF/resources/ja.lang.php index 0cf54775..59500e01 100644 --- a/WEB-INF/resources/ja.lang.php +++ b/WEB-INF/resources/ja.lang.php @@ -482,6 +482,8 @@ // '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', diff --git a/WEB-INF/resources/ko.lang.php b/WEB-INF/resources/ko.lang.php index 42648461..e2f16cce 100644 --- a/WEB-INF/resources/ko.lang.php +++ b/WEB-INF/resources/ko.lang.php @@ -480,6 +480,8 @@ // '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', diff --git a/WEB-INF/resources/nl.lang.php b/WEB-INF/resources/nl.lang.php index 949a3ac8..9f846bc5 100644 --- a/WEB-INF/resources/nl.lang.php +++ b/WEB-INF/resources/nl.lang.php @@ -411,6 +411,8 @@ 'form.reports.include_pending' => 'in afwachting', 'form.reports.select_period' => 'Kies periode', 'form.reports.set_period' => 'of stel datums in', +// TODO: translate the following. +// 'form.reports.note_containing' => 'Note containing', 'form.reports.show_fields' => 'Toon velden', 'form.reports.time_fields' => 'Tijd velden', 'form.reports.user_fields' => 'Medewerker velden', diff --git a/WEB-INF/resources/no.lang.php b/WEB-INF/resources/no.lang.php index dd92cc78..cba06a82 100644 --- a/WEB-INF/resources/no.lang.php +++ b/WEB-INF/resources/no.lang.php @@ -482,6 +482,8 @@ // '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', diff --git a/WEB-INF/resources/pl.lang.php b/WEB-INF/resources/pl.lang.php index 50ac9c62..3470add1 100644 --- a/WEB-INF/resources/pl.lang.php +++ b/WEB-INF/resources/pl.lang.php @@ -467,6 +467,8 @@ // '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', diff --git a/WEB-INF/resources/pt-br.lang.php b/WEB-INF/resources/pt-br.lang.php index 0b250d69..60d50576 100644 --- a/WEB-INF/resources/pt-br.lang.php +++ b/WEB-INF/resources/pt-br.lang.php @@ -414,6 +414,8 @@ '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', diff --git a/WEB-INF/resources/pt.lang.php b/WEB-INF/resources/pt.lang.php index e7b16d92..d5a4413a 100644 --- a/WEB-INF/resources/pt.lang.php +++ b/WEB-INF/resources/pt.lang.php @@ -459,6 +459,8 @@ // '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', diff --git a/WEB-INF/resources/ro.lang.php b/WEB-INF/resources/ro.lang.php index b160169d..f450bfd4 100644 --- a/WEB-INF/resources/ro.lang.php +++ b/WEB-INF/resources/ro.lang.php @@ -482,6 +482,8 @@ // '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', diff --git a/WEB-INF/resources/ru.lang.php b/WEB-INF/resources/ru.lang.php index f24aa6f7..47f87d49 100644 --- a/WEB-INF/resources/ru.lang.php +++ b/WEB-INF/resources/ru.lang.php @@ -412,6 +412,7 @@ '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' => 'Поля пользователя', diff --git a/WEB-INF/resources/sk.lang.php b/WEB-INF/resources/sk.lang.php index ca4f0535..1909ec9d 100644 --- a/WEB-INF/resources/sk.lang.php +++ b/WEB-INF/resources/sk.lang.php @@ -470,6 +470,8 @@ // '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', diff --git a/WEB-INF/resources/sl.lang.php b/WEB-INF/resources/sl.lang.php index 574c32f0..304da23a 100644 --- a/WEB-INF/resources/sl.lang.php +++ b/WEB-INF/resources/sl.lang.php @@ -456,6 +456,7 @@ // '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', diff --git a/WEB-INF/resources/sr.lang.php b/WEB-INF/resources/sr.lang.php index 06867886..d1fb802e 100644 --- a/WEB-INF/resources/sr.lang.php +++ b/WEB-INF/resources/sr.lang.php @@ -467,6 +467,8 @@ // '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', diff --git a/WEB-INF/resources/sv.lang.php b/WEB-INF/resources/sv.lang.php index 9f5725b8..1df983ec 100644 --- a/WEB-INF/resources/sv.lang.php +++ b/WEB-INF/resources/sv.lang.php @@ -465,6 +465,8 @@ // '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', diff --git a/WEB-INF/resources/tr.lang.php b/WEB-INF/resources/tr.lang.php index f8fa7dd2..c6fe800a 100644 --- a/WEB-INF/resources/tr.lang.php +++ b/WEB-INF/resources/tr.lang.php @@ -493,6 +493,8 @@ // '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', diff --git a/WEB-INF/resources/zh-cn.lang.php b/WEB-INF/resources/zh-cn.lang.php index ca1709d3..5b701c85 100644 --- a/WEB-INF/resources/zh-cn.lang.php +++ b/WEB-INF/resources/zh-cn.lang.php @@ -471,6 +471,8 @@ // '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', diff --git a/WEB-INF/resources/zh-tw.lang.php b/WEB-INF/resources/zh-tw.lang.php index c59a87ef..14ef3c26 100644 --- a/WEB-INF/resources/zh-tw.lang.php +++ b/WEB-INF/resources/zh-tw.lang.php @@ -474,6 +474,8 @@ // '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', diff --git a/WEB-INF/templates/reports.tpl b/WEB-INF/templates/reports.tpl index aa8e9d63..d2dfcb47 100644 --- a/WEB-INF/templates/reports.tpl +++ b/WEB-INF/templates/reports.tpl @@ -358,6 +358,14 @@ License: See license.txt *}
{/if} +{if isTrue('FILTER_BY_NOTES_DEBUG')} + + + + {$forms.reportForm.notes_containing.control} + +
+{/if}
diff --git a/reports.php b/reports.php index 858ca61c..8cf3736b 100644 --- a/reports.php +++ b/reports.php @@ -239,6 +239,11 @@ $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. +if (isTrue('FILTER_BY_NOTES_DEBUG')) { + $form->addInput(array('type'=>'text','maxlength'=>'256','name'=>'notes_containing')); +} + // Add checkboxes for "Show fields" block. if ($showClient) $form->addInput(array('type'=>'checkbox','name'=>'chclient')); From a18e57436cb9170c22c6de7128053113913e289e Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Wed, 6 Jul 2022 19:19:56 +0000 Subject: [PATCH 065/168] Initial implementation of report filter by notes. --- WEB-INF/lib/ttFavReportHelper.class.php | 7 +++++-- WEB-INF/lib/ttReportHelper.class.php | 17 +++++++++++++++-- WEB-INF/templates/reports.tpl | 6 +++--- dbinstall.php | 9 +++++---- initialize.php | 2 +- mysql.sql | 3 ++- reports.php | 2 +- 7 files changed, 32 insertions(+), 14 deletions(-) diff --git a/WEB-INF/lib/ttFavReportHelper.class.php b/WEB-INF/lib/ttFavReportHelper.class.php index 3de0c94c..df756649 100644 --- a/WEB-INF/lib/ttFavReportHelper.class.php +++ b/WEB-INF/lib/ttFavReportHelper.class.php @@ -98,7 +98,7 @@ static function insertReport($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,". + " 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,". @@ -110,7 +110,7 @@ static function insertReport($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'].", ". @@ -146,6 +146,7 @@ static function updateReport($fields) { "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']).", ". @@ -233,6 +234,7 @@ static function saveReport($bean) { '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, @@ -336,6 +338,7 @@ static function loadReport(&$bean) { $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']); diff --git a/WEB-INF/lib/ttReportHelper.class.php b/WEB-INF/lib/ttReportHelper.class.php index 693fe652..45b6ec1b 100644 --- a/WEB-INF/lib/ttReportHelper.class.php +++ b/WEB-INF/lib/ttReportHelper.class.php @@ -116,6 +116,12 @@ static function getWhere($options) { $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 (isset($options['period']) && $options['period']) @@ -127,7 +133,7 @@ static function getWhere($options) { new ttDate($options['period_end'], $dateFormat)); } $where = " where l.status = 1 and l.date >= '".$period->getStartDate()."' and l.date <= '".$period->getEndDate()."'". - " $user_list_part $dropdown_parts $cf_text_parts"; + $user_list_part.$dropdown_parts.$cf_text_parts.$note_containing_part; return $where; } @@ -198,6 +204,12 @@ static function getExpenseWhere($options) { $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 (isset($options['period']) && $options['period']) @@ -209,7 +221,7 @@ static function getExpenseWhere($options) { new ttDate($options['period_end'], $dateFormat)); } $where = " where ei.status = 1 and ei.date >= '".$period->getStartDate()."' and ei.date <= '".$period->getEndDate()."'". - " $user_list_part $dropdown_parts $cf_text_parts"; + $user_list_part.$dropdown_parts.$cf_text_parts.$note_containing_part; return $where; } @@ -1541,6 +1553,7 @@ static function getReportOptions($bean) { $options['period'] = $bean->getAttribute('period'); $options['period_start'] = $bean->getAttribute('start_date'); $options['period_end'] = $bean->getAttribute('end_date'); + $options['note_containing'] = $bean->getAttribute('note_containing'); $options['show_client'] = $bean->getAttribute('chclient'); $options['show_invoice'] = $bean->getAttribute('chinvoice'); $options['show_approved'] = $bean->getAttribute('chapproved'); diff --git a/WEB-INF/templates/reports.tpl b/WEB-INF/templates/reports.tpl index d2dfcb47..3edec9d8 100644 --- a/WEB-INF/templates/reports.tpl +++ b/WEB-INF/templates/reports.tpl @@ -359,10 +359,10 @@ License: See license.txt *} {/if} {if isTrue('FILTER_BY_NOTES_DEBUG')} - + - - + + {/if} diff --git a/dbinstall.php b/dbinstall.php index f8e1b936..bc0db6a0 100644 --- a/dbinstall.php +++ b/dbinstall.php @@ -1151,7 +1151,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["convert11900to12102"]) { + if ($_POST["convert11900to12107"]) { 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')"); @@ -1197,6 +1197,7 @@ function ttGenerateKeys() { 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`"); } if ($_POST["cleanup"]) { @@ -1247,7 +1248,7 @@ function ttGenerateKeys() {

DB Install

{$forms.reportForm.notes_containing.control}{$forms.reportForm.note_containing.control}
-
Create database structure (v1.21.2) + Create database structure (v1.21.7)
(applies only to new installations, do not execute when updating)
@@ -1296,8 +1297,8 @@ function ttGenerateKeys() { - Update database structure (v1.19 to v1.21.2) - + Update database structure (v1.19 to v1.21.7) + diff --git a/initialize.php b/initialize.php index 53810201..7ab83978 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.6.5719"); +define("APP_VERSION", "1.21.7.5720"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); diff --git a/mysql.sql b/mysql.sql index 0856f94a..4fd33fa9 100644 --- a/mysql.sql +++ b/mysql.sql @@ -273,6 +273,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 @@ -621,4 +622,4 @@ CREATE TABLE `tt_site_config` ( PRIMARY KEY (`param_name`) ); -INSERT INTO `tt_site_config` (`param_name`, `param_value`, `created`) VALUES ('version_db', '1.21.2', now()); # TODO: change when structure changes. +INSERT INTO `tt_site_config` (`param_name`, `param_value`, `created`) VALUES ('version_db', '1.21.7', now()); # TODO: change when structure changes. diff --git a/reports.php b/reports.php index 8cf3736b..0dbc79a8 100644 --- a/reports.php +++ b/reports.php @@ -241,7 +241,7 @@ // Add control for notes containing. if (isTrue('FILTER_BY_NOTES_DEBUG')) { - $form->addInput(array('type'=>'text','maxlength'=>'256','name'=>'notes_containing')); + $form->addInput(array('type'=>'text','maxlength'=>'80','name'=>'note_containing')); } // Add checkboxes for "Show fields" block. From d50ff284a5dcb55e3fd276f5499ef05a700a41a7 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Fri, 8 Jul 2022 15:56:44 +0000 Subject: [PATCH 066/168] Removed FILTER_BY_NOTES_DEBUG condition. --- WEB-INF/templates/reports.tpl | 2 -- dbinstall.php | 1 + initialize.php | 2 +- reports.php | 4 +--- 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/WEB-INF/templates/reports.tpl b/WEB-INF/templates/reports.tpl index 3edec9d8..51c98212 100644 --- a/WEB-INF/templates/reports.tpl +++ b/WEB-INF/templates/reports.tpl @@ -358,14 +358,12 @@ License: See license.txt *}
{/if} -{if isTrue('FILTER_BY_NOTES_DEBUG')} {$forms.reportForm.note_containing.control}
-{/if}
diff --git a/dbinstall.php b/dbinstall.php index bc0db6a0..85d3f021 100644 --- a/dbinstall.php +++ b/dbinstall.php @@ -1198,6 +1198,7 @@ function ttGenerateKeys() { 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'"); } if ($_POST["cleanup"]) { diff --git a/initialize.php b/initialize.php index 7ab83978..0eb176f0 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.7.5720"); +define("APP_VERSION", "1.21.7.5721"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); diff --git a/reports.php b/reports.php index 0dbc79a8..65aba65d 100644 --- a/reports.php +++ b/reports.php @@ -240,9 +240,7 @@ $form->addInput(array('type'=>'datefield','maxlength'=>'20','name'=>'end_date')); // Add control for notes containing. -if (isTrue('FILTER_BY_NOTES_DEBUG')) { - $form->addInput(array('type'=>'text','maxlength'=>'80','name'=>'note_containing')); -} +$form->addInput(array('type'=>'text','maxlength'=>'80','name'=>'note_containing')); // Add checkboxes for "Show fields" block. if ($showClient) From 01bb80be971ad6c7d685c88c9da0150060eb4ff0 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Fri, 8 Jul 2022 16:11:18 +0000 Subject: [PATCH 067/168] Fixed export / import for note_containing in fav reports. --- WEB-INF/lib/ttGroupExportHelper.class.php | 1 + WEB-INF/lib/ttOrgExportHelper.class.php | 28 ++--------------------- WEB-INF/lib/ttOrgImportHelper.class.php | 5 ++-- initialize.php | 2 +- 4 files changed, 7 insertions(+), 29 deletions(-) diff --git a/WEB-INF/lib/ttGroupExportHelper.class.php b/WEB-INF/lib/ttGroupExportHelper.class.php index a464f2e2..b7b6491e 100644 --- a/WEB-INF/lib/ttGroupExportHelper.class.php +++ b/WEB-INF/lib/ttGroupExportHelper.class.php @@ -566,6 +566,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/ttOrgExportHelper.class.php b/WEB-INF/lib/ttOrgExportHelper.class.php index 277eac3a..9380231e 100644 --- a/WEB-INF/lib/ttOrgExportHelper.class.php +++ b/WEB-INF/lib/ttOrgExportHelper.class.php @@ -1,30 +1,6 @@ $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'], @@ -1049,7 +1050,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 +1061,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'].", ". diff --git a/initialize.php b/initialize.php index 0eb176f0..183c62f9 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.7.5721"); +define("APP_VERSION", "1.21.7.5722"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From 821535bfa70fae447bede1c617e375458dd31751 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Fri, 19 Aug 2022 13:45:10 +0000 Subject: [PATCH 068/168] Resolved a few compatibility issues with php8. --- WEB-INF/lib/ttOrgImportHelper.class.php | 6 ++++-- initialize.php | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/WEB-INF/lib/ttOrgImportHelper.class.php b/WEB-INF/lib/ttOrgImportHelper.class.php index 61ed9ec7..92ba6276 100644 --- a/WEB-INF/lib/ttOrgImportHelper.class.php +++ b/WEB-INF/lib/ttOrgImportHelper.class.php @@ -167,6 +167,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 +194,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 +220,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( @@ -899,7 +900,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']; @@ -1014,6 +1015,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']; diff --git a/initialize.php b/initialize.php index 183c62f9..f16a12b2 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.7.5722"); +define("APP_VERSION", "1.21.8.5723"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From 8392a26f00e6303095a2ff06680f054ea4fb431f Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sat, 20 Aug 2022 14:22:15 +0000 Subject: [PATCH 069/168] Fixed a bug in export-import. --- WEB-INF/lib/ttGroupExportHelper.class.php | 1 + WEB-INF/lib/ttOrgImportHelper.class.php | 8 +++++--- initialize.php | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/WEB-INF/lib/ttGroupExportHelper.class.php b/WEB-INF/lib/ttGroupExportHelper.class.php index b7b6491e..fb0f6002 100644 --- a/WEB-INF/lib/ttGroupExportHelper.class.php +++ b/WEB-INF/lib/ttGroupExportHelper.class.php @@ -424,6 +424,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); } diff --git a/WEB-INF/lib/ttOrgImportHelper.class.php b/WEB-INF/lib/ttOrgImportHelper.class.php index 92ba6276..aae3267c 100644 --- a/WEB-INF/lib/ttOrgImportHelper.class.php +++ b/WEB-INF/lib/ttOrgImportHelper.class.php @@ -349,7 +349,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; @@ -1153,9 +1154,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/initialize.php b/initialize.php index f16a12b2..07317888 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.8.5723"); +define("APP_VERSION", "1.21.9.5724"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From a3ec5e331f92641bb774b6f0310303d3043023be Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Fri, 16 Sep 2022 13:02:36 +0000 Subject: [PATCH 070/168] Fixed a JavaScript error condition (issue #128). --- WEB-INF/templates/user_edit.tpl | 6 +++++- initialize.php | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/WEB-INF/templates/user_edit.tpl b/WEB-INF/templates/user_edit.tpl index 744e8c40..e4d916a5 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/initialize.php b/initialize.php index 07317888..c5cb7fe3 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.9.5724"); +define("APP_VERSION", "1.21.9.5725"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From 20ec9b24e53bc00b14241c6041dbd6c0f1fb0666 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Wed, 21 Sep 2022 20:51:28 +0000 Subject: [PATCH 071/168] Dutch translation improved. --- WEB-INF/resources/nl.lang.php | 3 +-- initialize.php | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/WEB-INF/resources/nl.lang.php b/WEB-INF/resources/nl.lang.php index 9f846bc5..2e87007c 100644 --- a/WEB-INF/resources/nl.lang.php +++ b/WEB-INF/resources/nl.lang.php @@ -411,8 +411,7 @@ 'form.reports.include_pending' => 'in afwachting', 'form.reports.select_period' => 'Kies periode', 'form.reports.set_period' => 'of stel datums in', -// TODO: translate the following. -// 'form.reports.note_containing' => 'Note containing', +'form.reports.note_containing' => 'Notitie bevat', 'form.reports.show_fields' => 'Toon velden', 'form.reports.time_fields' => 'Tijd velden', 'form.reports.user_fields' => 'Medewerker velden', diff --git a/initialize.php b/initialize.php index c5cb7fe3..92a3f313 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.9.5725"); +define("APP_VERSION", "1.21.9.5726"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From 491f6c4a35c723ab7e6ddd58c0065166dcfff25d Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Mon, 17 Oct 2022 15:17:19 +0000 Subject: [PATCH 072/168] Starting to implement cost per hour column on reports. --- WEB-INF/resources/ca.lang.php | 1 + WEB-INF/resources/cs.lang.php | 1 + WEB-INF/resources/da.lang.php | 1 + WEB-INF/resources/de.lang.php | 1 + WEB-INF/resources/en.lang.php | 1 + WEB-INF/resources/es.lang.php | 1 + WEB-INF/resources/et.lang.php | 1 + WEB-INF/resources/fa.lang.php | 1 + WEB-INF/resources/fi.lang.php | 1 + WEB-INF/resources/fr.lang.php | 1 + WEB-INF/resources/gr.lang.php | 1 + WEB-INF/resources/he.lang.php | 1 + WEB-INF/resources/hu.lang.php | 1 + WEB-INF/resources/it.lang.php | 1 + WEB-INF/resources/ja.lang.php | 1 + WEB-INF/resources/ko.lang.php | 1 + WEB-INF/resources/nl.lang.php | 2 ++ WEB-INF/resources/no.lang.php | 1 + WEB-INF/resources/pl.lang.php | 1 + WEB-INF/resources/pt-br.lang.php | 1 + WEB-INF/resources/pt.lang.php | 1 + WEB-INF/resources/ro.lang.php | 1 + WEB-INF/resources/ru.lang.php | 1 + WEB-INF/resources/sk.lang.php | 1 + WEB-INF/resources/sl.lang.php | 1 + WEB-INF/resources/sr.lang.php | 1 + WEB-INF/resources/sv.lang.php | 1 + WEB-INF/resources/tr.lang.php | 1 + WEB-INF/resources/zh-cn.lang.php | 1 + WEB-INF/resources/zh-tw.lang.php | 1 + WEB-INF/templates/display_options.tpl | 10 ++++++++++ display_options.php | 4 ++++ initialize.php | 2 +- 33 files changed, 46 insertions(+), 1 deletion(-) diff --git a/WEB-INF/resources/ca.lang.php b/WEB-INF/resources/ca.lang.php index f2eec1b5..ae5a1df7 100644 --- a/WEB-INF/resources/ca.lang.php +++ b/WEB-INF/resources/ca.lang.php @@ -677,5 +677,6 @@ // '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', ); diff --git a/WEB-INF/resources/cs.lang.php b/WEB-INF/resources/cs.lang.php index 8573d71c..18c04aa6 100644 --- a/WEB-INF/resources/cs.lang.php +++ b/WEB-INF/resources/cs.lang.php @@ -689,5 +689,6 @@ // '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', ); diff --git a/WEB-INF/resources/da.lang.php b/WEB-INF/resources/da.lang.php index dd1abd39..9e4a61c9 100644 --- a/WEB-INF/resources/da.lang.php +++ b/WEB-INF/resources/da.lang.php @@ -655,5 +655,6 @@ // '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', ); diff --git a/WEB-INF/resources/de.lang.php b/WEB-INF/resources/de.lang.php index e295e728..a2e730dd 100644 --- a/WEB-INF/resources/de.lang.php +++ b/WEB-INF/resources/de.lang.php @@ -630,5 +630,6 @@ // TODO: translate the following. // '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', ); diff --git a/WEB-INF/resources/en.lang.php b/WEB-INF/resources/en.lang.php index 5df99c40..f55cef6f 100644 --- a/WEB-INF/resources/en.lang.php +++ b/WEB-INF/resources/en.lang.php @@ -601,5 +601,6 @@ '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', ); diff --git a/WEB-INF/resources/es.lang.php b/WEB-INF/resources/es.lang.php index 7c59d3ae..b7ec4116 100644 --- a/WEB-INF/resources/es.lang.php +++ b/WEB-INF/resources/es.lang.php @@ -692,5 +692,6 @@ // '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', ); diff --git a/WEB-INF/resources/et.lang.php b/WEB-INF/resources/et.lang.php index 7a53024b..0b2fd1d8 100644 --- a/WEB-INF/resources/et.lang.php +++ b/WEB-INF/resources/et.lang.php @@ -665,5 +665,6 @@ // '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', ); diff --git a/WEB-INF/resources/fa.lang.php b/WEB-INF/resources/fa.lang.php index 2963414e..ec3dfe6d 100644 --- a/WEB-INF/resources/fa.lang.php +++ b/WEB-INF/resources/fa.lang.php @@ -678,5 +678,6 @@ // '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', ); diff --git a/WEB-INF/resources/fi.lang.php b/WEB-INF/resources/fi.lang.php index 112dc6b6..28033da2 100644 --- a/WEB-INF/resources/fi.lang.php +++ b/WEB-INF/resources/fi.lang.php @@ -662,5 +662,6 @@ // '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', ); diff --git a/WEB-INF/resources/fr.lang.php b/WEB-INF/resources/fr.lang.php index a1268d1a..20bc298f 100644 --- a/WEB-INF/resources/fr.lang.php +++ b/WEB-INF/resources/fr.lang.php @@ -651,5 +651,6 @@ // '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', ); diff --git a/WEB-INF/resources/gr.lang.php b/WEB-INF/resources/gr.lang.php index 9f1bf5d9..db2aa859 100644 --- a/WEB-INF/resources/gr.lang.php +++ b/WEB-INF/resources/gr.lang.php @@ -644,5 +644,6 @@ // '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', ); diff --git a/WEB-INF/resources/he.lang.php b/WEB-INF/resources/he.lang.php index 3167fd8d..ef6e20f3 100644 --- a/WEB-INF/resources/he.lang.php +++ b/WEB-INF/resources/he.lang.php @@ -683,5 +683,6 @@ // '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', ); diff --git a/WEB-INF/resources/hu.lang.php b/WEB-INF/resources/hu.lang.php index 636c29d0..5987b885 100644 --- a/WEB-INF/resources/hu.lang.php +++ b/WEB-INF/resources/hu.lang.php @@ -680,5 +680,6 @@ // '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', ); diff --git a/WEB-INF/resources/it.lang.php b/WEB-INF/resources/it.lang.php index d517fded..63c2ada5 100644 --- a/WEB-INF/resources/it.lang.php +++ b/WEB-INF/resources/it.lang.php @@ -657,5 +657,6 @@ // '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', ); diff --git a/WEB-INF/resources/ja.lang.php b/WEB-INF/resources/ja.lang.php index 59500e01..5e7070b1 100644 --- a/WEB-INF/resources/ja.lang.php +++ b/WEB-INF/resources/ja.lang.php @@ -684,5 +684,6 @@ // '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', ); diff --git a/WEB-INF/resources/ko.lang.php b/WEB-INF/resources/ko.lang.php index e2f16cce..a4ea97c2 100644 --- a/WEB-INF/resources/ko.lang.php +++ b/WEB-INF/resources/ko.lang.php @@ -682,5 +682,6 @@ // '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', ); diff --git a/WEB-INF/resources/nl.lang.php b/WEB-INF/resources/nl.lang.php index 2e87007c..73265f98 100644 --- a/WEB-INF/resources/nl.lang.php +++ b/WEB-INF/resources/nl.lang.php @@ -586,5 +586,7 @@ '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', 'form.display_options.inactive_projects' => 'Niet actieve projecten', +// TODO: translate the following. +// 'form.display_options.cost_per_hour' => 'Cost per hour', 'form.display_options.custom_css' => 'Aangepaste CSS', ); diff --git a/WEB-INF/resources/no.lang.php b/WEB-INF/resources/no.lang.php index cba06a82..8e3c4c88 100644 --- a/WEB-INF/resources/no.lang.php +++ b/WEB-INF/resources/no.lang.php @@ -684,5 +684,6 @@ // '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', ); diff --git a/WEB-INF/resources/pl.lang.php b/WEB-INF/resources/pl.lang.php index 3470add1..bf04ee74 100644 --- a/WEB-INF/resources/pl.lang.php +++ b/WEB-INF/resources/pl.lang.php @@ -663,5 +663,6 @@ // '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', ); diff --git a/WEB-INF/resources/pt-br.lang.php b/WEB-INF/resources/pt-br.lang.php index 60d50576..36dca948 100644 --- a/WEB-INF/resources/pt-br.lang.php +++ b/WEB-INF/resources/pt-br.lang.php @@ -594,6 +594,7 @@ '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', ); diff --git a/WEB-INF/resources/pt.lang.php b/WEB-INF/resources/pt.lang.php index d5a4413a..cce36ad0 100644 --- a/WEB-INF/resources/pt.lang.php +++ b/WEB-INF/resources/pt.lang.php @@ -662,5 +662,6 @@ // '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', ); diff --git a/WEB-INF/resources/ro.lang.php b/WEB-INF/resources/ro.lang.php index f450bfd4..03e7a992 100644 --- a/WEB-INF/resources/ro.lang.php +++ b/WEB-INF/resources/ro.lang.php @@ -685,5 +685,6 @@ // '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', ); diff --git a/WEB-INF/resources/ru.lang.php b/WEB-INF/resources/ru.lang.php index 47f87d49..5a037b84 100644 --- a/WEB-INF/resources/ru.lang.php +++ b/WEB-INF/resources/ru.lang.php @@ -587,5 +587,6 @@ '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', ); diff --git a/WEB-INF/resources/sk.lang.php b/WEB-INF/resources/sk.lang.php index 1909ec9d..c3ad4da5 100644 --- a/WEB-INF/resources/sk.lang.php +++ b/WEB-INF/resources/sk.lang.php @@ -667,5 +667,6 @@ // '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', ); diff --git a/WEB-INF/resources/sl.lang.php b/WEB-INF/resources/sl.lang.php index 304da23a..04e70974 100644 --- a/WEB-INF/resources/sl.lang.php +++ b/WEB-INF/resources/sl.lang.php @@ -657,5 +657,6 @@ // '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', ); diff --git a/WEB-INF/resources/sr.lang.php b/WEB-INF/resources/sr.lang.php index d1fb802e..9823f810 100644 --- a/WEB-INF/resources/sr.lang.php +++ b/WEB-INF/resources/sr.lang.php @@ -665,5 +665,6 @@ // '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', ); diff --git a/WEB-INF/resources/sv.lang.php b/WEB-INF/resources/sv.lang.php index 1df983ec..4250318c 100644 --- a/WEB-INF/resources/sv.lang.php +++ b/WEB-INF/resources/sv.lang.php @@ -663,5 +663,6 @@ // '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', ); diff --git a/WEB-INF/resources/tr.lang.php b/WEB-INF/resources/tr.lang.php index c6fe800a..32838159 100644 --- a/WEB-INF/resources/tr.lang.php +++ b/WEB-INF/resources/tr.lang.php @@ -698,5 +698,6 @@ // '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', ); diff --git a/WEB-INF/resources/zh-cn.lang.php b/WEB-INF/resources/zh-cn.lang.php index 5b701c85..967a172e 100644 --- a/WEB-INF/resources/zh-cn.lang.php +++ b/WEB-INF/resources/zh-cn.lang.php @@ -673,5 +673,6 @@ // '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', ); diff --git a/WEB-INF/resources/zh-tw.lang.php b/WEB-INF/resources/zh-tw.lang.php index 14ef3c26..2fd31a76 100644 --- a/WEB-INF/resources/zh-tw.lang.php +++ b/WEB-INF/resources/zh-tw.lang.php @@ -676,5 +676,6 @@ // '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', ); diff --git a/WEB-INF/templates/display_options.tpl b/WEB-INF/templates/display_options.tpl index 6486c0b2..c09a18e9 100644 --- a/WEB-INF/templates/display_options.tpl +++ b/WEB-INF/templates/display_options.tpl @@ -49,6 +49,16 @@ License: See license.txt *} {$i18n.label.what_is_it} +{if defined('REPORT_COST_PER_HOUR')} + + + + + +{/if} diff --git a/display_options.php b/display_options.php index 6e0ccc54..e449c3c9 100644 --- a/display_options.php +++ b/display_options.php @@ -21,6 +21,7 @@ $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')); } else { $cl_time_note_on_separate_row = $config->getDefinedValue('time_note_on_separate_row'); @@ -28,6 +29,7 @@ $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(); } @@ -49,6 +51,7 @@ // 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)); @@ -66,6 +69,7 @@ $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))) { diff --git a/initialize.php b/initialize.php index 92a3f313..86aad804 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.9.5726"); +define("APP_VERSION", "1.21.9.5727"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From 69de200456a6ad941f2a268f2efb406512b3973e Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Mon, 17 Oct 2022 18:50:39 +0000 Subject: [PATCH 073/168] Work in progress on including cost per hour in reports. --- WEB-INF/lib/ttReportHelper.class.php | 13 ++++++-- WEB-INF/resources/ca.lang.php | 1 + WEB-INF/resources/cs.lang.php | 1 + WEB-INF/resources/da.lang.php | 1 + WEB-INF/resources/de.lang.php | 2 ++ WEB-INF/resources/en.lang.php | 1 + WEB-INF/resources/es.lang.php | 1 + WEB-INF/resources/et.lang.php | 2 ++ WEB-INF/resources/fa.lang.php | 1 + WEB-INF/resources/fi.lang.php | 1 + WEB-INF/resources/fr.lang.php | 1 + WEB-INF/resources/gr.lang.php | 2 ++ WEB-INF/resources/he.lang.php | 1 + WEB-INF/resources/hu.lang.php | 1 + WEB-INF/resources/it.lang.php | 2 ++ WEB-INF/resources/ja.lang.php | 1 + WEB-INF/resources/ko.lang.php | 1 + WEB-INF/resources/nl.lang.php | 2 ++ WEB-INF/resources/no.lang.php | 1 + WEB-INF/resources/pl.lang.php | 1 + WEB-INF/resources/pt-br.lang.php | 2 ++ WEB-INF/resources/pt.lang.php | 1 + WEB-INF/resources/ro.lang.php | 1 + WEB-INF/resources/ru.lang.php | 1 + WEB-INF/resources/sk.lang.php | 1 + WEB-INF/resources/sl.lang.php | 1 + WEB-INF/resources/sr.lang.php | 1 + WEB-INF/resources/sv.lang.php | 1 + WEB-INF/resources/tr.lang.php | 1 + WEB-INF/resources/zh-cn.lang.php | 1 + WEB-INF/resources/zh-tw.lang.php | 1 + WEB-INF/templates/puncher.tpl | 2 +- WEB-INF/templates/report.tpl | 45 ++++++++++++++++++---------- WEB-INF/templates/time.tpl | 2 +- WEB-INF/templates/week.tpl | 2 +- report.php | 8 ++++- 36 files changed, 87 insertions(+), 21 deletions(-) diff --git a/WEB-INF/lib/ttReportHelper.class.php b/WEB-INF/lib/ttReportHelper.class.php index 45b6ec1b..d15d1e9b 100644 --- a/WEB-INF/lib/ttReportHelper.class.php +++ b/WEB-INF/lib/ttReportHelper.class.php @@ -333,12 +333,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'); @@ -501,6 +508,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. diff --git a/WEB-INF/resources/ca.lang.php b/WEB-INF/resources/ca.lang.php index ae5a1df7..a06c8a69 100644 --- a/WEB-INF/resources/ca.lang.php +++ b/WEB-INF/resources/ca.lang.php @@ -494,6 +494,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', diff --git a/WEB-INF/resources/cs.lang.php b/WEB-INF/resources/cs.lang.php index 18c04aa6..e8d297ed 100644 --- a/WEB-INF/resources/cs.lang.php +++ b/WEB-INF/resources/cs.lang.php @@ -502,6 +502,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', diff --git a/WEB-INF/resources/da.lang.php b/WEB-INF/resources/da.lang.php index 9e4a61c9..6e27e537 100644 --- a/WEB-INF/resources/da.lang.php +++ b/WEB-INF/resources/da.lang.php @@ -477,6 +477,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', diff --git a/WEB-INF/resources/de.lang.php b/WEB-INF/resources/de.lang.php index a2e730dd..8d5e030c 100644 --- a/WEB-INF/resources/de.lang.php +++ b/WEB-INF/resources/de.lang.php @@ -459,6 +459,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' => 'Exportiere', +// TODO: translate the following. +// 'form.report.per_hour' => 'Per hour', 'form.report.assign_to_invoice' => 'Zu Rechnung hinzufügen', // TODO: translate the following. // 'form.report.assign_to_timesheet' => 'Assign to timesheet', diff --git a/WEB-INF/resources/en.lang.php b/WEB-INF/resources/en.lang.php index f55cef6f..2f42d273 100644 --- a/WEB-INF/resources/en.lang.php +++ b/WEB-INF/resources/en.lang.php @@ -440,6 +440,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', diff --git a/WEB-INF/resources/es.lang.php b/WEB-INF/resources/es.lang.php index b7ec4116..6dbfd29a 100644 --- a/WEB-INF/resources/es.lang.php +++ b/WEB-INF/resources/es.lang.php @@ -507,6 +507,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', diff --git a/WEB-INF/resources/et.lang.php b/WEB-INF/resources/et.lang.php index 0b2fd1d8..dc08bd01 100644 --- a/WEB-INF/resources/et.lang.php +++ b/WEB-INF/resources/et.lang.php @@ -489,6 +489,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', diff --git a/WEB-INF/resources/fa.lang.php b/WEB-INF/resources/fa.lang.php index ec3dfe6d..f8d8f684 100644 --- a/WEB-INF/resources/fa.lang.php +++ b/WEB-INF/resources/fa.lang.php @@ -498,6 +498,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', diff --git a/WEB-INF/resources/fi.lang.php b/WEB-INF/resources/fi.lang.php index 28033da2..8d5ec21e 100644 --- a/WEB-INF/resources/fi.lang.php +++ b/WEB-INF/resources/fi.lang.php @@ -483,6 +483,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', diff --git a/WEB-INF/resources/fr.lang.php b/WEB-INF/resources/fr.lang.php index 20bc298f..8e728971 100644 --- a/WEB-INF/resources/fr.lang.php +++ b/WEB-INF/resources/fr.lang.php @@ -474,6 +474,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', diff --git a/WEB-INF/resources/gr.lang.php b/WEB-INF/resources/gr.lang.php index db2aa859..dc03db95 100644 --- a/WEB-INF/resources/gr.lang.php +++ b/WEB-INF/resources/gr.lang.php @@ -468,6 +468,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', diff --git a/WEB-INF/resources/he.lang.php b/WEB-INF/resources/he.lang.php index ef6e20f3..5249ee8e 100644 --- a/WEB-INF/resources/he.lang.php +++ b/WEB-INF/resources/he.lang.php @@ -502,6 +502,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', diff --git a/WEB-INF/resources/hu.lang.php b/WEB-INF/resources/hu.lang.php index 5987b885..f19593ba 100644 --- a/WEB-INF/resources/hu.lang.php +++ b/WEB-INF/resources/hu.lang.php @@ -496,6 +496,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', diff --git a/WEB-INF/resources/it.lang.php b/WEB-INF/resources/it.lang.php index 63c2ada5..f936ef12 100644 --- a/WEB-INF/resources/it.lang.php +++ b/WEB-INF/resources/it.lang.php @@ -481,6 +481,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', diff --git a/WEB-INF/resources/ja.lang.php b/WEB-INF/resources/ja.lang.php index 5e7070b1..02d4ad22 100644 --- a/WEB-INF/resources/ja.lang.php +++ b/WEB-INF/resources/ja.lang.php @@ -502,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', diff --git a/WEB-INF/resources/ko.lang.php b/WEB-INF/resources/ko.lang.php index a4ea97c2..491631d0 100644 --- a/WEB-INF/resources/ko.lang.php +++ b/WEB-INF/resources/ko.lang.php @@ -500,6 +500,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', diff --git a/WEB-INF/resources/nl.lang.php b/WEB-INF/resources/nl.lang.php index 73265f98..90ee0769 100644 --- a/WEB-INF/resources/nl.lang.php +++ b/WEB-INF/resources/nl.lang.php @@ -426,6 +426,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' => 'Exporteer', +// TODO: translate the following. +// 'form.report.per_hour' => 'Per hour', 'form.report.assign_to_invoice' => 'Voeg toe aan factuur', 'form.report.assign_to_timesheet' => 'Wijs toe aan tijdenoverzicht', diff --git a/WEB-INF/resources/no.lang.php b/WEB-INF/resources/no.lang.php index 8e3c4c88..9fd52043 100644 --- a/WEB-INF/resources/no.lang.php +++ b/WEB-INF/resources/no.lang.php @@ -501,6 +501,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', diff --git a/WEB-INF/resources/pl.lang.php b/WEB-INF/resources/pl.lang.php index bf04ee74..325948cd 100644 --- a/WEB-INF/resources/pl.lang.php +++ b/WEB-INF/resources/pl.lang.php @@ -485,6 +485,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', diff --git a/WEB-INF/resources/pt-br.lang.php b/WEB-INF/resources/pt-br.lang.php index 36dca948..279d9eb6 100644 --- a/WEB-INF/resources/pt-br.lang.php +++ b/WEB-INF/resources/pt-br.lang.php @@ -430,6 +430,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', diff --git a/WEB-INF/resources/pt.lang.php b/WEB-INF/resources/pt.lang.php index cce36ad0..d5c248b8 100644 --- a/WEB-INF/resources/pt.lang.php +++ b/WEB-INF/resources/pt.lang.php @@ -477,6 +477,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', diff --git a/WEB-INF/resources/ro.lang.php b/WEB-INF/resources/ro.lang.php index 03e7a992..0984a779 100644 --- a/WEB-INF/resources/ro.lang.php +++ b/WEB-INF/resources/ro.lang.php @@ -501,6 +501,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', diff --git a/WEB-INF/resources/ru.lang.php b/WEB-INF/resources/ru.lang.php index 5a037b84..faab0e60 100644 --- a/WEB-INF/resources/ru.lang.php +++ b/WEB-INF/resources/ru.lang.php @@ -427,6 +427,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' => 'Включить в табель', diff --git a/WEB-INF/resources/sk.lang.php b/WEB-INF/resources/sk.lang.php index c3ad4da5..8ba54629 100644 --- a/WEB-INF/resources/sk.lang.php +++ b/WEB-INF/resources/sk.lang.php @@ -488,6 +488,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', diff --git a/WEB-INF/resources/sl.lang.php b/WEB-INF/resources/sl.lang.php index 04e70974..d041eaee 100644 --- a/WEB-INF/resources/sl.lang.php +++ b/WEB-INF/resources/sl.lang.php @@ -473,6 +473,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', diff --git a/WEB-INF/resources/sr.lang.php b/WEB-INF/resources/sr.lang.php index 9823f810..ac51518d 100644 --- a/WEB-INF/resources/sr.lang.php +++ b/WEB-INF/resources/sr.lang.php @@ -485,6 +485,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', diff --git a/WEB-INF/resources/sv.lang.php b/WEB-INF/resources/sv.lang.php index 4250318c..3c43c91c 100644 --- a/WEB-INF/resources/sv.lang.php +++ b/WEB-INF/resources/sv.lang.php @@ -483,6 +483,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', diff --git a/WEB-INF/resources/tr.lang.php b/WEB-INF/resources/tr.lang.php index 32838159..d37a1c54 100644 --- a/WEB-INF/resources/tr.lang.php +++ b/WEB-INF/resources/tr.lang.php @@ -513,6 +513,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', diff --git a/WEB-INF/resources/zh-cn.lang.php b/WEB-INF/resources/zh-cn.lang.php index 967a172e..93c85118 100644 --- a/WEB-INF/resources/zh-cn.lang.php +++ b/WEB-INF/resources/zh-cn.lang.php @@ -491,6 +491,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', diff --git a/WEB-INF/resources/zh-tw.lang.php b/WEB-INF/resources/zh-tw.lang.php index 2fd31a76..75ac102b 100644 --- a/WEB-INF/resources/zh-tw.lang.php +++ b/WEB-INF/resources/zh-tw.lang.php @@ -494,6 +494,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', diff --git a/WEB-INF/templates/puncher.tpl b/WEB-INF/templates/puncher.tpl index 9190af39..98bd5ca2 100644 --- a/WEB-INF/templates/puncher.tpl +++ b/WEB-INF/templates/puncher.tpl @@ -145,7 +145,7 @@ startTimer(); {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']} diff --git a/WEB-INF/templates/report.tpl b/WEB-INF/templates/report.tpl index 8ae981a3..3bd9f448 100644 --- a/WEB-INF/templates/report.tpl +++ b/WEB-INF/templates/report.tpl @@ -38,7 +38,7 @@ License: See license.txt *} {if $user->can('view_reports') || $user->can('view_all_reports') || $user->isClient()}{/if} - {* user custom fileds *} + {* user custom fields *} {if isset($custom_fields) && $custom_fields->userFields} {foreach $custom_fields->userFields as $userField} {assign var="checkbox_control_name" value='show_user_field_'|cat:$userField['id']} @@ -48,7 +48,7 @@ License: See license.txt *} {if $bean->getAttribute('chclient')}{/if} {if $bean->getAttribute('chproject')}{/if} {if $bean->getAttribute('chtask')}{/if} - {* time custom fileds *} + {* time custom fields *} {if isset($custom_fields) && $custom_fields->timeFields} {foreach $custom_fields->timeFields as $timeField} {assign var="checkbox_control_name" value='show_time_field_'|cat:$timeField['id']} @@ -60,7 +60,10 @@ License: See license.txt *} {if $bean->getAttribute('chduration')}{/if} {if $bean->getAttribute('chunits')}{/if} {if $bean->getAttribute('chnote') && !$note_on_separate_row}{/if} - {if $bean->getAttribute('chcost')}{/if} + {if $bean->getAttribute('chcost')} + {if $show_cost_per_hour}{/if} + + {/if} {if $bean->getAttribute('chapproved')}{/if} {if $bean->getAttribute('chpaid')}{/if} {if $bean->getAttribute('chip')}{/if} @@ -79,7 +82,7 @@ License: See license.txt *} {if $user->can('view_reports') || $user->can('view_all_reports') || $user->isClient()}{/if} - {* user custom fileds *} + {* user custom fields *} {if isset($custom_fields) && $custom_fields->userFields} {foreach $custom_fields->userFields as $userField} {assign var="checkbox_control_name" value='show_user_field_'|cat:$userField['id']} @@ -90,7 +93,7 @@ License: See license.txt *} {if $bean->getAttribute('chproject')}{/if} {if $bean->getAttribute('chtask')}{/if} - {* time custom fileds *} + {* time custom fields *} {if isset($custom_fields) && $custom_fields->timeFields} {foreach $custom_fields->timeFields as $timeField} {assign var="checkbox_control_name" value='show_time_field_'|cat:$timeField['id']} @@ -102,7 +105,10 @@ License: See license.txt *} {if $bean->getAttribute('chduration')}{/if} {if $bean->getAttribute('chunits')}{/if} {if $bean->getAttribute('chnote') && !$note_on_separate_row}{/if} - {if $bean->getAttribute('chcost')}{/if} + {if $bean->getAttribute('chcost')} + {if $show_cost_per_hour}{/if} + + {/if} {if $bean->getAttribute('chapproved')}{/if} {if $bean->getAttribute('chpaid')}{/if} {if $bean->getAttribute('chip')}{/if} @@ -120,7 +126,7 @@ License: See license.txt *} {if $user->can('view_reports') || $user->can('view_all_reports') || $user->isClient()}{/if} - {* user custom fileds *} + {* user custom fields *} {if isset($custom_fields) && $custom_fields->userFields} {foreach $custom_fields->userFields as $userField} {assign var="control_name" value='user_field_'|cat:$userField['id']} @@ -131,7 +137,7 @@ License: See license.txt *} {if $bean->getAttribute('chclient')}{/if} {if $bean->getAttribute('chproject')}{/if} {if $bean->getAttribute('chtask')}{/if} - {* time custom fileds *} + {* time custom fields *} {if isset($custom_fields) && $custom_fields->timeFields} {foreach $custom_fields->timeFields as $timeField} {assign var="control_name" value='time_field_'|cat:$timeField['id']} @@ -144,7 +150,10 @@ License: See license.txt *} {if $bean->getAttribute('chduration')}{/if} {if $bean->getAttribute('chunits')}{/if} {if $bean->getAttribute('chnote') && !$note_on_separate_row}{/if} - {if $bean->getAttribute('chcost')}{/if} + {if $bean->getAttribute('chcost')} + {if $show_cost_per_hour}{/if} + + {/if} {if $bean->getAttribute('chapproved')}{/if} {if $bean->getAttribute('chpaid')}{/if} {if $bean->getAttribute('chip')}{/if} @@ -180,7 +189,7 @@ License: See license.txt *} {if $user->can('view_reports') || $user->can('view_all_reports') || $user->isClient()}{/if} - {* user custom fileds *} + {* user custom fields *} {if isset($custom_fields) && $custom_fields->userFields} {foreach $custom_fields->userFields as $userField} {assign var="checkbox_control_name" value='show_user_field_'|cat:$userField['id']} @@ -190,7 +199,7 @@ License: See license.txt *} {if $bean->getAttribute('chclient')}{/if} {if $bean->getAttribute('chproject')}{/if} {if $bean->getAttribute('chtask')}{/if} - {* time custom fileds *} + {* time custom fields *} {if isset($custom_fields) && $custom_fields->timeFields} {foreach $custom_fields->timeFields as $timeField} {assign var="checkbox_control_name" value='show_time_field_'|cat:$timeField['id']} @@ -202,7 +211,10 @@ License: See license.txt *} {if $bean->getAttribute('chduration')}{/if} {if $bean->getAttribute('chunits')}{/if} {if $bean->getAttribute('chnote') && !$note_on_separate_row}{/if} - {if $bean->getAttribute('chcost')}{/if} + {if $bean->getAttribute('chcost')} + {if $show_cost_per_hour}{/if} + + {/if} {if $bean->getAttribute('chapproved')}{/if} {if $bean->getAttribute('chpaid')}{/if} {if $bean->getAttribute('chip')}{/if} @@ -218,7 +230,7 @@ License: See license.txt *} {if $user->can('view_reports') || $user->can('view_all_reports') || $user->isClient()}{/if} - {* user custom fileds *} + {* user custom fields *} {if isset($custom_fields) && $custom_fields->userFields} {foreach $custom_fields->userFields as $userField} {assign var="checkbox_control_name" value='show_user_field_'|cat:$userField['id']} @@ -228,7 +240,7 @@ License: See license.txt *} {if $bean->getAttribute('chclient')}{/if} {if $bean->getAttribute('chproject')}{/if} {if $bean->getAttribute('chtask')}{/if} - {* time custom fileds *} + {* time custom fields *} {if isset($custom_fields) && $custom_fields->timeFields} {foreach $custom_fields->timeFields as $timeField} {assign var="checkbox_control_name" value='show_time_field_'|cat:$timeField['id']} @@ -240,7 +252,10 @@ License: See license.txt *} {if $bean->getAttribute('chduration')}{/if} {if $bean->getAttribute('chunits')}{/if} {if $bean->getAttribute('chnote') && !$note_on_separate_row}{/if} - {if $bean->getAttribute('chcost')}{/if} + {if $bean->getAttribute('chcost')} + {if $show_cost_per_hour}{/if} + + {/if} {if $bean->getAttribute('chapproved')}{/if} {if $bean->getAttribute('chpaid')}{/if} {if $bean->getAttribute('chip')}{/if} diff --git a/WEB-INF/templates/time.tpl b/WEB-INF/templates/time.tpl index 6f6f7394..b601531b 100644 --- a/WEB-INF/templates/time.tpl +++ b/WEB-INF/templates/time.tpl @@ -160,7 +160,7 @@ function handleStop(buttonElement) { {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']} diff --git a/WEB-INF/templates/week.tpl b/WEB-INF/templates/week.tpl index 41568f60..74e94053 100644 --- a/WEB-INF/templates/week.tpl +++ b/WEB-INF/templates/week.tpl @@ -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/report.php b/report.php index bd2576f1..38e57fe2 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'); if ($user->isPluginEnabled('ap')) { $cl_mark_approved_select_option = $request->getParameter('mark_approved_select_options', ($request->isPost() ? null : @$_SESSION['mark_approved_select_option'])); @@ -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); From 350f206391875be2a1b1e3847f80030aca02210f Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Tue, 18 Oct 2022 18:02:06 +0000 Subject: [PATCH 074/168] Fixed report exports for cost_per_hour column. --- WEB-INF/lib/ttReportHelper.class.php | 4 ++++ initialize.php | 2 +- report.php | 2 +- tofile.php | 20 +++++++++++++++++--- topdf.php | 7 +++++++ 5 files changed, 30 insertions(+), 5 deletions(-) diff --git a/WEB-INF/lib/ttReportHelper.class.php b/WEB-INF/lib/ttReportHelper.class.php index d15d1e9b..cfcc6435 100644 --- a/WEB-INF/lib/ttReportHelper.class.php +++ b/WEB-INF/lib/ttReportHelper.class.php @@ -613,6 +613,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']); diff --git a/initialize.php b/initialize.php index 86aad804..ed2fe659 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.9.5727"); +define("APP_VERSION", "1.21.9.5728"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); diff --git a/report.php b/report.php index 38e57fe2..4945ac17 100644 --- a/report.php +++ b/report.php @@ -18,7 +18,7 @@ // End of access checks. $config = new ttConfigHelper($user->getConfig()); -$show_cost_per_hour = $config->getDefinedValue('report_cost_per_hour'); +$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'])); diff --git a/tofile.php b/tofile.php index 530d2f7d..6ea39ad8 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); @@ -123,6 +125,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()) @@ -213,7 +220,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').'"'; @@ -255,10 +266,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 85ff8d84..931fa814 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. @@ -152,6 +153,7 @@ if ($bean->getAttribute('chduration')) { $colspan++; $html .= "'; } if ($bean->getAttribute('chunits')) { $colspan++; $html .= "'; } if ($show_note_column) { $colspan++; $html .= ''; } + if ($bean->getAttribute('chcost') && $show_cost_per_hour) { $colspan++; $html .= "'; } if ($bean->getAttribute('chcost')) { $colspan++; $html .= "'; } if ($bean->getAttribute('chapproved')) { $colspan++; $html .= "'; } if ($bean->getAttribute('chpaid')) { $colspan++; $html .= "'; } @@ -266,6 +268,11 @@ if ($bean->getAttribute('chduration')) $html .= "'; if ($bean->getAttribute('chunits')) $html .= "'; if ($show_note_column) $html .= ''; + if ($bean->getAttribute('chcost') && $show_cost_per_hour) { + $html .= "'; + } if ($bean->getAttribute('chcost')) { $html .= "'; if ($show_note_column) $body .= ''; + if ($show_cost_per_hour) + $body .= ''; if ($options['show_cost']) $body .= ''; if ($options['show_approved']) @@ -1263,6 +1267,7 @@ static function prepareReportBody($options, $comment = null) if ($options['show_duration']) $body .= ''; if ($options['show_work_units']) $body .= ''; if ($show_note_column) $body .= ''; + if ($show_cost_per_hour) $body .= ''; if ($options['show_cost']) { $body .= ''; if ($show_note_column) $body .= ''; + if ($show_cost_per_hour) + $body .= ''; if ($options['show_cost']) $body .= ''; if ($options['show_approved']) { @@ -1382,6 +1389,7 @@ static function prepareReportBody($options, $comment = null) if ($options['show_duration']) $body .= ''; if ($options['show_work_units']) $body .= ''; if ($show_note_column) $body .= ''; + if ($show_cost_per_hour) $body .= ''; if ($options['show_cost']) { $body .= ''; if ($options['show_work_units']) $body .= ''; if ($show_note_column) $body .= ''; + if ($show_cost_per_hour) $body .= ''; if ($options['show_cost']) { $body .= ' -{if defined('REPORT_COST_PER_HOUR')} @@ -58,7 +57,6 @@ License: See license.txt *} {$i18n.label.what_is_it} -{/if} diff --git a/initialize.php b/initialize.php index 25386c62..e58e54a1 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.9.5729"); +define("APP_VERSION", "1.21.10.5730"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From dc03995e430b92b0e3f814d32f586a994996dc81 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Thu, 20 Oct 2022 22:01:13 +0000 Subject: [PATCH 077/168] German translation improved. --- WEB-INF/resources/de.lang.php | 3 +-- initialize.php | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/WEB-INF/resources/de.lang.php b/WEB-INF/resources/de.lang.php index 8d5e030c..95d9af2c 100644 --- a/WEB-INF/resources/de.lang.php +++ b/WEB-INF/resources/de.lang.php @@ -459,8 +459,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' => 'Exportiere', -// TODO: translate the following. -// 'form.report.per_hour' => 'Per hour', +'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', diff --git a/initialize.php b/initialize.php index e58e54a1..90872e60 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.10.5730"); +define("APP_VERSION", "1.21.10.5731"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From e8b8d42fc07ebce3d8a08303c21c488a3165019e Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Fri, 21 Oct 2022 14:53:20 +0000 Subject: [PATCH 078/168] Starting to work on project custom fields. --- WEB-INF/templates/cf_custom_fields.tpl | 2 ++ cf_custom_field_add.php | 16 ++++++++++++---- initialize.php | 2 +- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/WEB-INF/templates/cf_custom_fields.tpl b/WEB-INF/templates/cf_custom_fields.tpl index 70253cb3..812943db 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 *} {elseif CustomFields::ENTITY_USER == $field['entity_type']} + {elseif CustomFields::ENTITY_PROJECT == $field['entity_type']} + {else} {/if} diff --git a/cf_custom_field_add.php b/cf_custom_field_add.php index 6cfd8725..38d67282 100644 --- a/cf_custom_field_add.php +++ b/cf_custom_field_add.php @@ -27,10 +27,18 @@ $form = new Form('fieldForm'); $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')) -)); +if (defined('DEBUG_PROJECT_CUSTOM_FIELDS')) { + $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_PROJECT=>$i18n->get('entity.project')) + )); +} else { + $form->addInput(array('type'=>'combobox','name'=>'entity', + 'data'=>array(CustomFields::ENTITY_TIME=>$i18n->get('entity.time'), + CustomFields::ENTITY_USER=>$i18n->get('entity.user')) + )); +} $form->addInput(array('type'=>'combobox','name'=>'type', 'data'=>array(CustomFields::TYPE_TEXT=>$i18n->get('label.type_text'), CustomFields::TYPE_DROPDOWN=>$i18n->get('label.type_dropdown')) diff --git a/initialize.php b/initialize.php index 90872e60..6033d935 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.10.5731"); +define("APP_VERSION", "1.21.10.5732"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From b0381c6478f062d37bdbb38714d23ee6c2151805 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Fri, 21 Oct 2022 16:16:14 +0000 Subject: [PATCH 079/168] Work in progress on project custom fields. --- WEB-INF/resources/ca.lang.php | 1 + WEB-INF/resources/cs.lang.php | 1 + WEB-INF/resources/da.lang.php | 1 + WEB-INF/resources/de.lang.php | 1 + WEB-INF/resources/en.lang.php | 1 + WEB-INF/resources/es.lang.php | 1 + WEB-INF/resources/et.lang.php | 1 + WEB-INF/resources/fa.lang.php | 1 + WEB-INF/resources/fi.lang.php | 1 + WEB-INF/resources/fr.lang.php | 1 + WEB-INF/resources/gr.lang.php | 1 + WEB-INF/resources/he.lang.php | 1 + WEB-INF/resources/hu.lang.php | 1 + WEB-INF/resources/it.lang.php | 1 + WEB-INF/resources/ja.lang.php | 1 + WEB-INF/resources/ko.lang.php | 1 + WEB-INF/resources/nl.lang.php | 2 ++ WEB-INF/resources/no.lang.php | 1 + WEB-INF/resources/pl.lang.php | 1 + WEB-INF/resources/pt-br.lang.php | 2 ++ WEB-INF/resources/pt.lang.php | 1 + WEB-INF/resources/ro.lang.php | 1 + WEB-INF/resources/ru.lang.php | 1 + WEB-INF/resources/sk.lang.php | 1 + WEB-INF/resources/sl.lang.php | 1 + WEB-INF/resources/sr.lang.php | 1 + WEB-INF/resources/sv.lang.php | 1 + WEB-INF/resources/tr.lang.php | 1 + WEB-INF/resources/zh-cn.lang.php | 1 + WEB-INF/resources/zh-tw.lang.php | 1 + WEB-INF/templates/reports.tpl | 16 +++++++++++++--- initialize.php | 2 +- plugins/CustomFields.class.php | 29 ++--------------------------- reports.php | 17 +++++++++++++++++ 34 files changed, 65 insertions(+), 31 deletions(-) diff --git a/WEB-INF/resources/ca.lang.php b/WEB-INF/resources/ca.lang.php index a06c8a69..13d8237c 100644 --- a/WEB-INF/resources/ca.lang.php +++ b/WEB-INF/resources/ca.lang.php @@ -480,6 +480,7 @@ // 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', diff --git a/WEB-INF/resources/cs.lang.php b/WEB-INF/resources/cs.lang.php index e8d297ed..4a93086d 100644 --- a/WEB-INF/resources/cs.lang.php +++ b/WEB-INF/resources/cs.lang.php @@ -488,6 +488,7 @@ // 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 ---', diff --git a/WEB-INF/resources/da.lang.php b/WEB-INF/resources/da.lang.php index 6e27e537..595590f0 100644 --- a/WEB-INF/resources/da.lang.php +++ b/WEB-INF/resources/da.lang.php @@ -465,6 +465,7 @@ // 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', diff --git a/WEB-INF/resources/de.lang.php b/WEB-INF/resources/de.lang.php index 95d9af2c..209df6b7 100644 --- a/WEB-INF/resources/de.lang.php +++ b/WEB-INF/resources/de.lang.php @@ -448,6 +448,7 @@ // 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' => 'Gruppieren nach', 'form.reports.group_by_no' => '--- keine Gruppierung ---', 'form.reports.group_by_date' => 'Datum', diff --git a/WEB-INF/resources/en.lang.php b/WEB-INF/resources/en.lang.php index 2f42d273..83808aa4 100644 --- a/WEB-INF/resources/en.lang.php +++ b/WEB-INF/resources/en.lang.php @@ -429,6 +429,7 @@ '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', diff --git a/WEB-INF/resources/es.lang.php b/WEB-INF/resources/es.lang.php index 6dbfd29a..507bbcaa 100644 --- a/WEB-INF/resources/es.lang.php +++ b/WEB-INF/resources/es.lang.php @@ -493,6 +493,7 @@ // 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', diff --git a/WEB-INF/resources/et.lang.php b/WEB-INF/resources/et.lang.php index dc08bd01..0295dc70 100644 --- a/WEB-INF/resources/et.lang.php +++ b/WEB-INF/resources/et.lang.php @@ -478,6 +478,7 @@ // 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', diff --git a/WEB-INF/resources/fa.lang.php b/WEB-INF/resources/fa.lang.php index f8d8f684..07887202 100644 --- a/WEB-INF/resources/fa.lang.php +++ b/WEB-INF/resources/fa.lang.php @@ -486,6 +486,7 @@ // 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' => 'تاریخ', diff --git a/WEB-INF/resources/fi.lang.php b/WEB-INF/resources/fi.lang.php index 8d5ec21e..e9e92e01 100644 --- a/WEB-INF/resources/fi.lang.php +++ b/WEB-INF/resources/fi.lang.php @@ -471,6 +471,7 @@ // 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ä', diff --git a/WEB-INF/resources/fr.lang.php b/WEB-INF/resources/fr.lang.php index 8e728971..145fcb28 100644 --- a/WEB-INF/resources/fr.lang.php +++ b/WEB-INF/resources/fr.lang.php @@ -462,6 +462,7 @@ // 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', diff --git a/WEB-INF/resources/gr.lang.php b/WEB-INF/resources/gr.lang.php index dc03db95..6265976d 100644 --- a/WEB-INF/resources/gr.lang.php +++ b/WEB-INF/resources/gr.lang.php @@ -457,6 +457,7 @@ // 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' => 'ημερομηνία', diff --git a/WEB-INF/resources/he.lang.php b/WEB-INF/resources/he.lang.php index 5249ee8e..727b0b76 100644 --- a/WEB-INF/resources/he.lang.php +++ b/WEB-INF/resources/he.lang.php @@ -489,6 +489,7 @@ // 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' => 'תאריך', diff --git a/WEB-INF/resources/hu.lang.php b/WEB-INF/resources/hu.lang.php index f19593ba..8b0be952 100644 --- a/WEB-INF/resources/hu.lang.php +++ b/WEB-INF/resources/hu.lang.php @@ -482,6 +482,7 @@ // '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', diff --git a/WEB-INF/resources/it.lang.php b/WEB-INF/resources/it.lang.php index f936ef12..d283830a 100644 --- a/WEB-INF/resources/it.lang.php +++ b/WEB-INF/resources/it.lang.php @@ -470,6 +470,7 @@ // 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', diff --git a/WEB-INF/resources/ja.lang.php b/WEB-INF/resources/ja.lang.php index 02d4ad22..112e454c 100644 --- a/WEB-INF/resources/ja.lang.php +++ b/WEB-INF/resources/ja.lang.php @@ -488,6 +488,7 @@ // 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' => '日付', diff --git a/WEB-INF/resources/ko.lang.php b/WEB-INF/resources/ko.lang.php index 491631d0..6bb78d77 100644 --- a/WEB-INF/resources/ko.lang.php +++ b/WEB-INF/resources/ko.lang.php @@ -486,6 +486,7 @@ // 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' => '날짜', diff --git a/WEB-INF/resources/nl.lang.php b/WEB-INF/resources/nl.lang.php index 90ee0769..38d93efd 100644 --- a/WEB-INF/resources/nl.lang.php +++ b/WEB-INF/resources/nl.lang.php @@ -415,6 +415,8 @@ 'form.reports.show_fields' => 'Toon velden', 'form.reports.time_fields' => 'Tijd velden', 'form.reports.user_fields' => 'Medewerker velden', +// TODO: translate the following. +// 'form.reports.project_fields' => 'Project fields', 'form.reports.group_by' => 'Groeperen op', 'form.reports.group_by_no' => '--- niet groeperen ---', 'form.reports.group_by_date' => 'datum', diff --git a/WEB-INF/resources/no.lang.php b/WEB-INF/resources/no.lang.php index 9fd52043..18e1ee69 100644 --- a/WEB-INF/resources/no.lang.php +++ b/WEB-INF/resources/no.lang.php @@ -488,6 +488,7 @@ // 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', diff --git a/WEB-INF/resources/pl.lang.php b/WEB-INF/resources/pl.lang.php index 325948cd..b7dd5024 100644 --- a/WEB-INF/resources/pl.lang.php +++ b/WEB-INF/resources/pl.lang.php @@ -473,6 +473,7 @@ // 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', diff --git a/WEB-INF/resources/pt-br.lang.php b/WEB-INF/resources/pt-br.lang.php index 279d9eb6..fff94fa3 100644 --- a/WEB-INF/resources/pt-br.lang.php +++ b/WEB-INF/resources/pt-br.lang.php @@ -419,6 +419,8 @@ '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', diff --git a/WEB-INF/resources/pt.lang.php b/WEB-INF/resources/pt.lang.php index d5c248b8..075e7b41 100644 --- a/WEB-INF/resources/pt.lang.php +++ b/WEB-INF/resources/pt.lang.php @@ -465,6 +465,7 @@ // 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', diff --git a/WEB-INF/resources/ro.lang.php b/WEB-INF/resources/ro.lang.php index 0984a779..0d8fe6d1 100644 --- a/WEB-INF/resources/ro.lang.php +++ b/WEB-INF/resources/ro.lang.php @@ -488,6 +488,7 @@ // 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', diff --git a/WEB-INF/resources/ru.lang.php b/WEB-INF/resources/ru.lang.php index faab0e60..805be45b 100644 --- a/WEB-INF/resources/ru.lang.php +++ b/WEB-INF/resources/ru.lang.php @@ -416,6 +416,7 @@ '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' => 'дате', diff --git a/WEB-INF/resources/sk.lang.php b/WEB-INF/resources/sk.lang.php index 8ba54629..e57e63d7 100644 --- a/WEB-INF/resources/sk.lang.php +++ b/WEB-INF/resources/sk.lang.php @@ -476,6 +476,7 @@ // 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', diff --git a/WEB-INF/resources/sl.lang.php b/WEB-INF/resources/sl.lang.php index d041eaee..8ea8c445 100644 --- a/WEB-INF/resources/sl.lang.php +++ b/WEB-INF/resources/sl.lang.php @@ -460,6 +460,7 @@ // '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', diff --git a/WEB-INF/resources/sr.lang.php b/WEB-INF/resources/sr.lang.php index ac51518d..055545b3 100644 --- a/WEB-INF/resources/sr.lang.php +++ b/WEB-INF/resources/sr.lang.php @@ -473,6 +473,7 @@ // 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', diff --git a/WEB-INF/resources/sv.lang.php b/WEB-INF/resources/sv.lang.php index 3c43c91c..e0d212b9 100644 --- a/WEB-INF/resources/sv.lang.php +++ b/WEB-INF/resources/sv.lang.php @@ -471,6 +471,7 @@ // 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', diff --git a/WEB-INF/resources/tr.lang.php b/WEB-INF/resources/tr.lang.php index d37a1c54..09887171 100644 --- a/WEB-INF/resources/tr.lang.php +++ b/WEB-INF/resources/tr.lang.php @@ -499,6 +499,7 @@ // 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', diff --git a/WEB-INF/resources/zh-cn.lang.php b/WEB-INF/resources/zh-cn.lang.php index 93c85118..87f1cc1c 100644 --- a/WEB-INF/resources/zh-cn.lang.php +++ b/WEB-INF/resources/zh-cn.lang.php @@ -477,6 +477,7 @@ // 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' => '日期', diff --git a/WEB-INF/resources/zh-tw.lang.php b/WEB-INF/resources/zh-tw.lang.php index 75ac102b..181756e7 100644 --- a/WEB-INF/resources/zh-tw.lang.php +++ b/WEB-INF/resources/zh-tw.lang.php @@ -480,6 +480,7 @@ // 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' => '日期', diff --git a/WEB-INF/templates/reports.tpl b/WEB-INF/templates/reports.tpl index 51c98212..370a6d46 100644 --- a/WEB-INF/templates/reports.tpl +++ b/WEB-INF/templates/reports.tpl @@ -245,9 +245,6 @@ function handleCheckboxes() { } -{* Copyright (c) Anuko International Ltd. https://www.anuko.com -License: See license.txt *} - {$forms.reportForm.open}
{$forms.displayOptionsForm.report_cost_per_hour.control} + {$i18n.label.what_is_it} + {$i18n.label.what_is_it} +
{$i18n.form.display_options.custom_css}
{$record.client|escape}
{$i18n.label.date}{$i18n.label.user}{$i18n.label.client}{$i18n.label.project}{$i18n.label.task}{$i18n.label.duration}{$i18n.label.work_units_short}{$i18n.label.note}{$i18n.label.cost}{$i18n.form.report.per_hour}{$i18n.label.cost}{$i18n.label.approved}{$i18n.label.paid}{$i18n.label.ip}
{$i18n.label.subtotal}{$subtotals[$prev_grouped_by]['user']|escape}{$subtotals[$prev_grouped_by]['project']|escape}{$subtotals[$prev_grouped_by]['task']|escape}{$subtotals[$prev_grouped_by]['time']}{$subtotals[$prev_grouped_by]['units']}{if $user->can('manage_invoices') || $user->isClient()}{$subtotals[$prev_grouped_by]['cost']}{else}{$subtotals[$prev_grouped_by]['expenses']}{/if}{if $user->can('manage_invoices') || $user->isClient()}{$subtotals[$prev_grouped_by]['cost']}{else}{$subtotals[$prev_grouped_by]['expenses']}{/if}
{$item.date}{$item.user|escape}{$item.client|escape}{$item.project|escape}{$item.task|escape}{$item.duration}{$item.units}{$item.note|escape}{if $user->can('manage_invoices') || $user->isClient()}{$item.cost}{else}{$item.expense}{/if}{if $user->can('manage_invoices') || $user->isClient()}{$item.cost_per_hour}{/if}{if $user->can('manage_invoices') || $user->isClient()}{$item.cost}{else}{$item.expense}{/if}{if $item.approved == 1}{$i18n.label.yes}{else}{$i18n.label.no}{/if}{if $item.paid == 1}{$i18n.label.yes}{else}{$i18n.label.no}{/if}{if $item.modified}{$item.modified_ip} {$item.modified}{else}{$item.created_ip} {$item.created}{/if}{$i18n.label.subtotal}{$subtotals[$cur_grouped_by]['user']|escape}{$subtotals[$cur_grouped_by]['client']|escape}{$subtotals[$cur_grouped_by]['project']|escape}{$subtotals[$cur_grouped_by]['task']|escape}{$subtotals[$cur_grouped_by]['time']}{$subtotals[$cur_grouped_by]['units']}{if $user->can('manage_invoices') || $user->isClient()}{$subtotals[$cur_grouped_by]['cost']}{else}{$subtotals[$cur_grouped_by]['expenses']}{/if}{if $user->can('manage_invoices') || $user->isClient()}{$subtotals[$cur_grouped_by]['cost']}{else}{$subtotals[$cur_grouped_by]['expenses']}{/if}
{$i18n.label.total}{$totals['time']}{$totals['units']}{$user->currency|escape} {if $user->can('manage_invoices') || $user->isClient()}{$totals['cost']}{else}{$totals['expenses']}{/if}{$user->currency|escape} {if $user->can('manage_invoices') || $user->isClient()}{$totals['cost']}{else}{$totals['expenses']}{/if}{$record.client|escape}
{$record.date}".$i18n->get('label.duration').'".$i18n->get('label.work_units_short').''.$i18n->get('label.note').'".$i18n->get('form.report.per_hour').'".$i18n->get('label.cost').'".$i18n->get('label.approved').'".$i18n->get('label.paid').'".$item['duration'].'".$item['units'].''.htmlspecialchars($item['note']).'"; + $html .= $item['cost_per_hour']; + $html .= '"; if ($user->can('manage_invoices') || $user->isClient()) From 85c1f6d14ceb9df215bae6c7478bc0ec363e2b77 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Tue, 18 Oct 2022 18:16:56 +0000 Subject: [PATCH 075/168] Fixed emailed reports for cost_per_hour column. --- WEB-INF/lib/ttReportHelper.class.php | 9 +++++++++ initialize.php | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/WEB-INF/lib/ttReportHelper.class.php b/WEB-INF/lib/ttReportHelper.class.php index cfcc6435..056f4d4f 100644 --- a/WEB-INF/lib/ttReportHelper.class.php +++ b/WEB-INF/lib/ttReportHelper.class.php @@ -1037,6 +1037,7 @@ 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); @@ -1085,6 +1086,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++; @@ -1201,6 +1203,8 @@ static function prepareReportBody($options, $comment = null) $body .= ''.$i18n->get('label.work_units_short').''.$i18n->get('label.note').''.$i18n->get('form.report.per_hour').''.$i18n->get('label.cost').''.$subtotals[$prev_grouped_by]['time'].''.$subtotals[$prev_grouped_by]['units'].''; $body .= ($canViewReports || $isClient) ? $subtotals[$prev_grouped_by]['cost'] : $subtotals[$prev_grouped_by]['expenses']; @@ -1318,6 +1323,8 @@ static function prepareReportBody($options, $comment = null) $body .= ''.$record['units'].''.htmlspecialchars($record['note']).''.$record['cost_per_hour'].''.$record['cost'].''.$subtotals[$cur_grouped_by]['time'].''.$subtotals[$cur_grouped_by]['units'].''; $body .= ($canViewReports || $isClient) ? $subtotals[$cur_grouped_by]['cost'] : $subtotals[$cur_grouped_by]['expenses']; @@ -1424,6 +1432,7 @@ static function prepareReportBody($options, $comment = null) if ($options['show_duration']) $body .= ''.$totals['time'].''.$totals['units'].''.htmlspecialchars($user->currency).' '; $body .= ($canViewReports || $isClient) ? $totals['cost'] : $totals['expenses']; diff --git a/initialize.php b/initialize.php index ed2fe659..25386c62 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.9.5728"); +define("APP_VERSION", "1.21.9.5729"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From bd30ae8c78ced5dea93d10b0153f5b677e87891c Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Tue, 18 Oct 2022 19:32:54 +0000 Subject: [PATCH 076/168] Finished adding cost per hour column in reports. --- WEB-INF/templates/display_options.tpl | 2 -- initialize.php | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/WEB-INF/templates/display_options.tpl b/WEB-INF/templates/display_options.tpl index c09a18e9..5b487ec8 100644 --- a/WEB-INF/templates/display_options.tpl +++ b/WEB-INF/templates/display_options.tpl @@ -49,7 +49,6 @@ License: See license.txt *} {$i18n.label.what_is_it}
{$i18n.form.display_options.custom_css}
{$i18n.entity.time}{$i18n.entity.user}{$i18n.entity.project}
@@ -513,6 +510,19 @@ License: See license.txt *} {/foreach} {/if} +{if 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.label.fav_report}:
{$i18n.form.reports.project_fields}
{$forms.reportForm.$control_name.control} {$forms.reportForm.$checkbox_control_name.control}
diff --git a/initialize.php b/initialize.php index 6033d935..e6e45730 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.10.5732"); +define("APP_VERSION", "1.21.10.5733"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); diff --git a/plugins/CustomFields.class.php b/plugins/CustomFields.class.php index 4e2f1319..0e52871e 100644 --- a/plugins/CustomFields.class.php +++ b/plugins/CustomFields.class.php @@ -1,31 +1,6 @@ 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'); From ac2e96281d8be1c80d50cc08290609b60fc9f997 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Fri, 21 Oct 2022 19:28:55 +0000 Subject: [PATCH 080/168] Added handling of custom fields on project_add, project_edit, and project_delete pages. --- WEB-INF/lib/ttProjectHelper.class.php | 11 ++++ WEB-INF/templates/project_add.tpl | 11 ++++ WEB-INF/templates/project_edit.tpl | 12 +++++ project_add.php | 54 +++++++++++++++++-- project_edit.php | 75 ++++++++++++++++++++++++--- user_edit.php | 2 +- 6 files changed, 153 insertions(+), 12 deletions(-) diff --git a/WEB-INF/lib/ttProjectHelper.class.php b/WEB-INF/lib/ttProjectHelper.class.php index e1d5754c..e6459bd0 100644 --- a/WEB-INF/lib/ttProjectHelper.class.php +++ b/WEB-INF/lib/ttProjectHelper.class.php @@ -220,6 +220,17 @@ static function delete($id) { if (!ttClientHelper::deleteProject($id)) return false; + // Mark project custom fields as deleted, + require_once('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; diff --git a/WEB-INF/templates/project_add.tpl b/WEB-INF/templates/project_add.tpl index 8cc43fca..7b1fd835 100644 --- a/WEB-INF/templates/project_add.tpl +++ b/WEB-INF/templates/project_add.tpl @@ -15,6 +15,17 @@ License: See license.txt *} +{if isset($custom_fields) && $custom_fields->projectFields} + {foreach $custom_fields->projectFields as $projectField} + {assign var="control_name" value='project_field_'|cat:$projectField['id']} + + + + + + + {/foreach} +{/if} {if $show_files} diff --git a/WEB-INF/templates/project_edit.tpl b/WEB-INF/templates/project_edit.tpl index 3ecc85a8..4e3b02e7 100644 --- a/WEB-INF/templates/project_edit.tpl +++ b/WEB-INF/templates/project_edit.tpl @@ -15,6 +15,18 @@ License: See license.txt *} +{if isset($custom_fields) && $custom_fields->projectFields} + {foreach $custom_fields->projectFields as $projectField} + {assign var="control_name" value='project_field_'|cat:$projectField['id']} + + + + + + + {/foreach} +{/if} + diff --git a/project_add.php b/project_add.php index 35b45c97..859fdc25 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 7d8e113d..a0190ff8 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,13 @@ // 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')); @@ -72,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/user_edit.php b/user_edit.php index 6bdbef69..0633e8a1 100644 --- a/user_edit.php +++ b/user_edit.php @@ -199,7 +199,7 @@ function render(&$table, $value, $row, $column, $selected = false) { 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. From 2700f1d275ac01a18beeae8f9d3cd124fc214193 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sat, 22 Oct 2022 13:51:57 +0000 Subject: [PATCH 081/168] Added a condition on including project custom fields on reports. --- WEB-INF/templates/reports.tpl | 2 +- initialize.php | 2 +- reports.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/WEB-INF/templates/reports.tpl b/WEB-INF/templates/reports.tpl index 370a6d46..6a5ecbbe 100644 --- a/WEB-INF/templates/reports.tpl +++ b/WEB-INF/templates/reports.tpl @@ -510,7 +510,7 @@ function handleCheckboxes() { {/foreach} {/if} -{if isset($custom_fields) && $custom_fields->projectFields} +{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']} diff --git a/initialize.php b/initialize.php index e6e45730..f31382c7 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.10.5733"); +define("APP_VERSION", "1.21.10.5734"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); diff --git a/reports.php b/reports.php index 2fb61ccd..6d5b441a 100644 --- a/reports.php +++ b/reports.php @@ -316,7 +316,7 @@ } // If we have projects custom fields - add controls for them. -if (isset($custom_fields) && $custom_fields->projectFields) { +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; From 318f74cc352cbbbd049077bc61375d7e2d4d6ff1 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sat, 22 Oct 2022 19:11:06 +0000 Subject: [PATCH 082/168] Made progress on including project custom fields on reports. --- WEB-INF/lib/ttProjectHelper.class.php | 2 +- WEB-INF/lib/ttReportHelper.class.php | 276 +++++++++++++++++++++++++- WEB-INF/templates/report.tpl | 36 ++++ initialize.php | 2 +- reports.php | 7 + 5 files changed, 318 insertions(+), 5 deletions(-) diff --git a/WEB-INF/lib/ttProjectHelper.class.php b/WEB-INF/lib/ttProjectHelper.class.php index e6459bd0..bd166e86 100644 --- a/WEB-INF/lib/ttProjectHelper.class.php +++ b/WEB-INF/lib/ttProjectHelper.class.php @@ -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(); diff --git a/WEB-INF/lib/ttReportHelper.class.php b/WEB-INF/lib/ttReportHelper.class.php index 056f4d4f..85c4d80b 100644 --- a/WEB-INF/lib/ttReportHelper.class.php +++ b/WEB-INF/lib/ttReportHelper.class.php @@ -108,6 +108,30 @@ 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 = isset($options['users']) ? $options['users'] : '-1'; if ($user->can('view_reports') || $user->can('view_all_reports') || $user->isClient()) @@ -196,6 +220,30 @@ 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 prparing 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 = isset($options['users']) ? $options['users'] : '-1'; if ($user->can('view_reports') || $user->can('view_all_reports') || $user->isClient()) @@ -306,6 +354,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"); @@ -372,8 +436,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']; @@ -393,6 +456,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']) @@ -495,6 +578,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. @@ -557,6 +656,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) @@ -826,6 +945,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)"; @@ -876,6 +1015,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"; @@ -1617,7 +1776,15 @@ static function getReportOptions($bean) { } } - // 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; + $options[$control_name] = $bean->getAttribute($control_name); + $options[$checkbox_control_name] = $bean->getAttribute($checkbox_control_name); + } + } } $options['group_by1'] = $bean->getAttribute('group_by1'); @@ -1795,6 +1962,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) { @@ -1829,6 +2009,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) { @@ -1882,6 +2075,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) { @@ -1916,6 +2122,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) { @@ -2151,6 +2370,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; @@ -2240,6 +2480,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; } @@ -2299,6 +2560,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; diff --git a/WEB-INF/templates/report.tpl b/WEB-INF/templates/report.tpl index 3bd9f448..b63e505f 100644 --- a/WEB-INF/templates/report.tpl +++ b/WEB-INF/templates/report.tpl @@ -47,6 +47,13 @@ License: See license.txt *} {/if} {if $bean->getAttribute('chclient')}{/if} {if $bean->getAttribute('chproject')}{/if} + {* project custom fields *} + {if isset($custom_fields) && $custom_fields->projectFields} + {foreach $custom_fields->projectFields as $projectField} + {assign var="checkbox_control_name" value='show_project_field_'|cat:$projectField['id']} + {if $bean->getAttribute($checkbox_control_name)}{/if} + {/foreach} + {/if} {if $bean->getAttribute('chtask')}{/if} {* time custom fields *} {if isset($custom_fields) && $custom_fields->timeFields} @@ -91,6 +98,13 @@ License: See license.txt *} {/if} {if $bean->getAttribute('chclient')}{/if} {if $bean->getAttribute('chproject')}{/if} + {* project custom fields *} + {if isset($custom_fields) && $custom_fields->projectFields} + {foreach $custom_fields->projectFields as $projectField} + {assign var="checkbox_control_name" value='show_project_field_'|cat:$projectField['id']} + {if $bean->getAttribute($checkbox_control_name)}{/if} + {/foreach} + {/if} {if $bean->getAttribute('chtask')}{/if} {* time custom fields *} @@ -136,6 +150,14 @@ License: See license.txt *} {/if} {if $bean->getAttribute('chclient')}{/if} {if $bean->getAttribute('chproject')}{/if} + {* project custom fields *} + {if 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']} + {if $bean->getAttribute($checkbox_control_name)}{/if} + {/foreach} + {/if} {if $bean->getAttribute('chtask')}{/if} {* time custom fields *} {if isset($custom_fields) && $custom_fields->timeFields} @@ -198,6 +220,13 @@ License: See license.txt *} {/if} {if $bean->getAttribute('chclient')}{/if} {if $bean->getAttribute('chproject')}{/if} + {* project custom fields *} + {if isset($custom_fields) && $custom_fields->projectFields} + {foreach $custom_fields->projectFields as $projectField} + {assign var="checkbox_control_name" value='show_project_field_'|cat:$projectField['id']} + {if $bean->getAttribute($checkbox_control_name)}{/if} + {/foreach} + {/if} {if $bean->getAttribute('chtask')}{/if} {* time custom fields *} {if isset($custom_fields) && $custom_fields->timeFields} @@ -239,6 +268,13 @@ License: See license.txt *} {/if} {if $bean->getAttribute('chclient')}{/if} {if $bean->getAttribute('chproject')}{/if} + {* project custom fields *} + {if isset($custom_fields) && $custom_fields->projectFields} + {foreach $custom_fields->projectFields as $projectField} + {assign var="checkbox_control_name" value='show_project_field_'|cat:$projectField['id']} + {if $bean->getAttribute($checkbox_control_name)}{/if} + {/foreach} + {/if} {if $bean->getAttribute('chtask')}{/if} {* time custom fields *} {if isset($custom_fields) && $custom_fields->timeFields} diff --git a/initialize.php b/initialize.php index f31382c7..90cd7347 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.10.5734"); +define("APP_VERSION", "1.21.10.5735"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); diff --git a/reports.php b/reports.php index 6d5b441a..057787e7 100644 --- a/reports.php +++ b/reports.php @@ -357,6 +357,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)); From 3837b1a97765e4533b3aa4baf13cc9c78cc6a3fe Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sun, 23 Oct 2022 15:29:04 +0000 Subject: [PATCH 083/168] Fixed pdf reports for per hour cost column, fixed all report exports for project custom fields. --- WEB-INF/lib/common.lib.php | 14 +++++++-- WEB-INF/lib/ttReportHelper.class.php | 11 +++++-- initialize.php | 2 +- tofile.php | 24 ++++++++++++++++ topdf.php | 43 ++++++++++++++++++++++++++++ 5 files changed, 89 insertions(+), 5 deletions(-) diff --git a/WEB-INF/lib/common.lib.php b/WEB-INF/lib/common.lib.php index 2d8e287d..370816cb 100644 --- a/WEB-INF/lib/common.lib.php +++ b/WEB-INF/lib/common.lib.php @@ -555,7 +555,7 @@ function ttMitigateCSRF() { return true; } -// ttStartsWith functions checks if a string starts with a given substring. +// ttStartsWith function checks if a string starts with a given substring. function ttStartsWith($string, $startString) { if (is_null($string)) @@ -565,7 +565,7 @@ function ttStartsWith($string, $startString) return (substr($string, 0, $len) === $startString); } -// ttEndsWith functions checks if a string ends with a given substring. +// ttEndsWith function checks if a string ends with a given substring. function ttEndsWith($string, $endString) { $len = strlen($endString); @@ -573,6 +573,16 @@ function ttEndsWith($string, $endString) return (substr($string, -$len) === $endString); } +// ttContains functions checks if a string contains a given substring. +function ttContains($string, $part) +{ + // Note: in php8 we can use str_contanins. + if (strpos($string, $part) !== false) + return true; + + return false; +} + // ttDateToUserFormat converts a date from database format to user format. function ttDateToUserFormat($date) { diff --git a/WEB-INF/lib/ttReportHelper.class.php b/WEB-INF/lib/ttReportHelper.class.php index 85c4d80b..6cfc1464 100644 --- a/WEB-INF/lib/ttReportHelper.class.php +++ b/WEB-INF/lib/ttReportHelper.class.php @@ -232,7 +232,7 @@ static function getExpenseWhere($options) { } } - // Continue prparing parts for text custom fields using LIKE operator. + // 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']; @@ -2533,7 +2533,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; @@ -2605,6 +2605,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/initialize.php b/initialize.php index 90cd7347..d24ca7fa 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.10.5735"); +define("APP_VERSION", "1.21.11.5736"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); diff --git a/tofile.php b/tofile.php index 6ea39ad8..0134885f 100644 --- a/tofile.php +++ b/tofile.php @@ -106,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) { @@ -206,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) { @@ -246,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) { diff --git a/topdf.php b/topdf.php index 931fa814..4e1e677d 100644 --- a/topdf.php +++ b/topdf.php @@ -139,6 +139,14 @@ } if ($bean->getAttribute('chclient')) { $colspan++; $html .= ''; } if ($bean->getAttribute('chproject')) { $colspan++; $html .= ''; } + // 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 .= ''; } + } + } if ($bean->getAttribute('chtask')) { $colspan++; $html .= ''; } // Time custom field labels. if (isset($custom_fields) && $custom_fields->timeFields) { @@ -194,6 +202,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 .= ''; if ($bean->getAttribute('chunits')) $html .= "'; if ($show_note_column) $html .= ''; + if ($bean->getAttribute('chcost') && $show_cost_per_hour) $html .= ''; if ($bean->getAttribute('chcost')) { $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) { @@ -339,6 +364,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 .= ''; if ($bean->getAttribute('chunits')) $html .= "'; if ($show_note_column) $html .= ''; + if ($bean->getAttribute('chcost') && $show_cost_per_hour) $html .= ''; if ($bean->getAttribute('chcost')) { $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) { @@ -402,6 +444,7 @@ if ($bean->getAttribute('chduration')) $html .= "'; if ($bean->getAttribute('chunits')) $html .= "'; if ($show_note_column) $html .= ''; + if ($bean->getAttribute('chcost') && $show_cost_per_hour) $html .= ''; if ($bean->getAttribute('chcost')) { $html .= "'; 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. @@ -1412,6 +1428,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) { @@ -1462,6 +1486,14 @@ static function prepareReportBody($options, $comment = null) $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. @@ -1534,6 +1566,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) { @@ -1577,6 +1617,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) { diff --git a/initialize.php b/initialize.php index d24ca7fa..d310471e 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.11.5736"); +define("APP_VERSION", "1.21.11.5737"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From 95481ddf41d2914555d8b48b6dcfd7acb7aa25f1 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sun, 23 Oct 2022 16:15:16 +0000 Subject: [PATCH 085/168] Fixed fav reports to keep project custom fields settings. --- WEB-INF/lib/ttFavReportHelper.class.php | 23 +++++++++++++++++++++++ initialize.php | 2 +- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/WEB-INF/lib/ttFavReportHelper.class.php b/WEB-INF/lib/ttFavReportHelper.class.php index df756649..0d8cc071 100644 --- a/WEB-INF/lib/ttFavReportHelper.class.php +++ b/WEB-INF/lib/ttFavReportHelper.class.php @@ -328,6 +328,17 @@ 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); + } + } } $bean->setAttribute('client', $val['client_id']); @@ -579,6 +590,18 @@ 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'; + } + } + $reportSpec = null; if (isset($reportSpecArray)) $reportSpec = implode(',', $reportSpecArray); diff --git a/initialize.php b/initialize.php index d310471e..5ff39645 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.11.5737"); +define("APP_VERSION", "1.21.11.5738"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From 10713c13048462dbd2c33c7cc38492f4c7a140af Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sun, 23 Oct 2022 16:24:37 +0000 Subject: [PATCH 086/168] Fixed adjusting fav report options for cron. --- WEB-INF/lib/ttFavReportHelper.class.php | 12 +++++++++++- initialize.php | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/WEB-INF/lib/ttFavReportHelper.class.php b/WEB-INF/lib/ttFavReportHelper.class.php index 0d8cc071..00a1bd5b 100644 --- a/WEB-INF/lib/ttFavReportHelper.class.php +++ b/WEB-INF/lib/ttFavReportHelper.class.php @@ -541,7 +541,17 @@ static function adjustOptions($options) { $options[$checkbox_field_name] = $checkbox_value; } } - // TODO: add project fields here. + // 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); + $options[$field_name] = $field_value; + $checkbox_value = ttFavReportHelper::getFieldSettingFromReportSpec($checkbox_field_name, $report_spec); + $options[$checkbox_field_name] = $checkbox_value; + } + } } // Adjust period_start and period_end to user date format. diff --git a/initialize.php b/initialize.php index 5ff39645..5fd8012f 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.11.5738"); +define("APP_VERSION", "1.21.11.5739"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From 00a75a7ed04fa22de4211891580deee454f04ec2 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sun, 23 Oct 2022 16:32:14 +0000 Subject: [PATCH 087/168] Initial implementation of project custom fields. --- cf_custom_field_add.php | 17 +++++------------ initialize.php | 2 +- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/cf_custom_field_add.php b/cf_custom_field_add.php index 38d67282..c1080375 100644 --- a/cf_custom_field_add.php +++ b/cf_custom_field_add.php @@ -27,18 +27,11 @@ $form = new Form('fieldForm'); $form->addInput(array('type'=>'text','maxlength'=>'100','name'=>'name','value'=>'')); -if (defined('DEBUG_PROJECT_CUSTOM_FIELDS')) { - $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_PROJECT=>$i18n->get('entity.project')) - )); -} else { - $form->addInput(array('type'=>'combobox','name'=>'entity', - 'data'=>array(CustomFields::ENTITY_TIME=>$i18n->get('entity.time'), - CustomFields::ENTITY_USER=>$i18n->get('entity.user')) - )); -} +$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_PROJECT=>$i18n->get('entity.project')) +)); $form->addInput(array('type'=>'combobox','name'=>'type', 'data'=>array(CustomFields::TYPE_TEXT=>$i18n->get('label.type_text'), CustomFields::TYPE_DROPDOWN=>$i18n->get('label.type_dropdown')) diff --git a/initialize.php b/initialize.php index 5fd8012f..552e5e2c 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.11.5739"); +define("APP_VERSION", "1.21.12.5740"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From c0ae1e6d07f2a64e4d54e47133b5bfda29ffcf87 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Tue, 25 Oct 2022 16:26:46 +0000 Subject: [PATCH 088/168] Added a what is it link explaining note cotaining option in reports. --- WEB-INF/templates/reports.tpl | 5 ++++- initialize.php | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/WEB-INF/templates/reports.tpl b/WEB-INF/templates/reports.tpl index 6a5ecbbe..42cdd661 100644 --- a/WEB-INF/templates/reports.tpl +++ b/WEB-INF/templates/reports.tpl @@ -358,7 +358,10 @@ function handleCheckboxes() { - +
{$forms.projectForm.description.control}
{$forms.projectForm.$control_name.control}
{$forms.projectForm.description.control}
{$forms.projectForm.$control_name.control}
{$i18n.form.reports.project_fields}
{$i18n.label.client}{$i18n.label.project}{{$projectField['label']|escape}}{$i18n.label.task}{$subtotals[$prev_grouped_by]['client']|escape}{$subtotals[$prev_grouped_by]['project']|escape}{$subtotals[$prev_grouped_by]['task']|escape}{$item.client|escape}{$item.project|escape}{$item.$control_name|escape}{$item.task|escape}{$subtotals[$cur_grouped_by]['client']|escape}{$subtotals[$cur_grouped_by]['project']|escape}{$subtotals[$cur_grouped_by]['task']|escape}'.$i18n->get('label.client').''.$i18n->get('label.project').''.htmlspecialchars($projectField['label']).''.$i18n->get('label.task').''; $html .= htmlspecialchars($subtotals[$prev_grouped_by]['task']); @@ -212,6 +228,7 @@ if ($bean->getAttribute('chduration')) $html .= "".$subtotals[$prev_grouped_by]['time'].'".$subtotals[$prev_grouped_by]['units'].'"; if ($user->can('manage_invoices') || $user->isClient()) @@ -254,6 +271,14 @@ } if ($bean->getAttribute('chclient')) $html .= ''.htmlspecialchars($item['client']).''.htmlspecialchars($item['project']).''.htmlspecialchars($item[$field_name]).''.htmlspecialchars($item['task']).''; $html .= htmlspecialchars($subtotals[$prev_grouped_by]['task']); @@ -357,6 +390,7 @@ if ($bean->getAttribute('chduration')) $html .= "".$subtotals[$prev_grouped_by]['time'].'".$subtotals[$prev_grouped_by]['units'].'"; if ($user->can('manage_invoices') || $user->isClient()) @@ -388,6 +422,14 @@ } if ($bean->getAttribute('chclient')) $html .= '".$totals['time'].'".$totals['units'].'".htmlspecialchars($user->currency).' '; if ($user->can('manage_invoices') || $user->isClient()) From 0ec60fc296df0ca7447db3e2b725c8bbcac24e39 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sun, 23 Oct 2022 15:46:07 +0000 Subject: [PATCH 084/168] Fixed emailed reports to handle project custom fields. --- WEB-INF/lib/common.lib.php | 2 +- WEB-INF/lib/ttReportHelper.class.php | 48 ++++++++++++++++++++++++++++ initialize.php | 2 +- 3 files changed, 50 insertions(+), 2 deletions(-) diff --git a/WEB-INF/lib/common.lib.php b/WEB-INF/lib/common.lib.php index 370816cb..0f38f80f 100644 --- a/WEB-INF/lib/common.lib.php +++ b/WEB-INF/lib/common.lib.php @@ -573,7 +573,7 @@ function ttEndsWith($string, $endString) return (substr($string, -$len) === $endString); } -// ttContains functions checks if a string contains a given substring. +// ttContains function checks if a string contains a given substring. function ttContains($string, $part) { // Note: in php8 we can use str_contanins. diff --git a/WEB-INF/lib/ttReportHelper.class.php b/WEB-INF/lib/ttReportHelper.class.php index 6cfc1464..a3f89449 100644 --- a/WEB-INF/lib/ttReportHelper.class.php +++ b/WEB-INF/lib/ttReportHelper.class.php @@ -1232,6 +1232,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) { @@ -1342,6 +1350,14 @@ static function prepareReportBody($options, $comment = null) $body .= ''.$i18n->get('label.client').''.$i18n->get('label.project').''.htmlspecialchars($projectField['label']).''.$i18n->get('label.task').''.$subtotals[$prev_grouped_by]['client'].''.$subtotals[$prev_grouped_by]['project'].''.$subtotals[$prev_grouped_by]['task'].''.htmlspecialchars($record['client']).''.htmlspecialchars($record['project']).''.htmlspecialchars($record[$field_name]).''.htmlspecialchars($record['task']).''.$subtotals[$prev_grouped_by]['client'].''.$subtotals[$prev_grouped_by]['project'].''.$subtotals[$prev_grouped_by]['task'].'
{$forms.reportForm.note_containing.control}{$forms.reportForm.note_containing.control} + {$i18n.label.what_is_it} + {$i18n.label.what_is_it} +
diff --git a/initialize.php b/initialize.php index 552e5e2c..0e90ef89 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.12.5740"); +define("APP_VERSION", "1.21.12.5741"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From 3102ef6e950150eccccee5f63922bf25fae8a83c Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Thu, 27 Oct 2022 15:18:50 +0000 Subject: [PATCH 089/168] Version change for 1.22 release. --- initialize.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/initialize.php b/initialize.php index 0e90ef89..7fcb17aa 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.21.12.5741"); +define("APP_VERSION", "1.22.0.5742"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From c4d2026603df8fa31a31f2ea440df436d67df7a7 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Thu, 27 Oct 2022 19:55:16 +0000 Subject: [PATCH 090/168] Addressed some php8 warnings. --- WEB-INF/lib/ttUser.class.php | 2 +- WEB-INF/lib/ttWeekViewHelper.class.php | 108 +++++++++++++++++++------ initialize.php | 2 +- 3 files changed, 86 insertions(+), 26 deletions(-) diff --git a/WEB-INF/lib/ttUser.class.php b/WEB-INF/lib/ttUser.class.php index e0565b3d..2e7ff279 100644 --- a/WEB-INF/lib/ttUser.class.php +++ b/WEB-INF/lib/ttUser.class.php @@ -682,7 +682,7 @@ 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 = $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']); diff --git a/WEB-INF/lib/ttWeekViewHelper.class.php b/WEB-INF/lib/ttWeekViewHelper.class.php index 9908d12b..4ee5346a 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 = 1; $i < 6; $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 = 1; $i < 6; $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 = 1; $i < 6; $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 = 1; $i < 6; $i++) { + $control_id = $pos.'_'. $dayHeaders[$i]; + $dataArray[$pos][$dayHeaders[$i]] = array('control_id' => $control_id, 'tt_log_id' => null,'note' => null); + } } $pos--; } @@ -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 = 1; $i < 6; $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 = 1; $i < 6; $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 = 1; $i < 6; $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 = 1; $i < 6; $i++) { + $control_id = $pos.'_'. $dayHeaders[$i]; + $dataArray[$pos][$dayHeaders[$i]] = array('control_id' => $control_id, 'tt_log_id' => null,'note' => null); + } } $pos--; } diff --git a/initialize.php b/initialize.php index 7fcb17aa..04f1245c 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.22.0.5742"); +define("APP_VERSION", "1.22.0.5743"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From c2c48e291c4b12bb2e8fb5c64fc25ea5d1ae17b5 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Fri, 28 Oct 2022 13:57:06 +0000 Subject: [PATCH 091/168] Fixed a bug on week view related to locking. Addressed a few php8 warnings. --- WEB-INF/templates/charts.tpl | 2 +- WEB-INF/templates/expenses.tpl | 2 +- WEB-INF/templates/time.tpl | 2 +- WEB-INF/templates/timesheets.tpl | 2 +- WEB-INF/templates/week.tpl | 2 +- initialize.php | 2 +- week.php | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/WEB-INF/templates/charts.tpl b/WEB-INF/templates/charts.tpl index ff15ea8f..b38988e8 100644 --- a/WEB-INF/templates/charts.tpl +++ b/WEB-INF/templates/charts.tpl @@ -25,7 +25,7 @@ function adjustTodayLinks() {
{$forms.chartForm.date.control}
-{if $user_dropdown} +{if isset($user_dropdown)} diff --git a/WEB-INF/templates/expenses.tpl b/WEB-INF/templates/expenses.tpl index 5b1da05f..859ae350 100644 --- a/WEB-INF/templates/expenses.tpl +++ b/WEB-INF/templates/expenses.tpl @@ -119,7 +119,7 @@ function recalculateCost() {
{$forms.expensesForm.date.control}
{$forms.chartForm.date.control}
-{if $user_dropdown} +{if isset($user_dropdown)} diff --git a/WEB-INF/templates/time.tpl b/WEB-INF/templates/time.tpl index b601531b..fa24e806 100644 --- a/WEB-INF/templates/time.tpl +++ b/WEB-INF/templates/time.tpl @@ -23,7 +23,7 @@ function handleStop(buttonElement) {
{$forms.timeRecordForm.date.control}
{$forms.expensesForm.date.control}
-{if $user_dropdown} +{if isset($user_dropdown)} diff --git a/WEB-INF/templates/timesheets.tpl b/WEB-INF/templates/timesheets.tpl index e5f9abdb..2bd92736 100644 --- a/WEB-INF/templates/timesheets.tpl +++ b/WEB-INF/templates/timesheets.tpl @@ -7,7 +7,7 @@ License: See license.txt *} {$forms.timesheetsForm.open}
{$forms.timeRecordForm.date.control}
-{if $user_dropdown} +{if isset($user_dropdown)} diff --git a/WEB-INF/templates/week.tpl b/WEB-INF/templates/week.tpl index 74e94053..6032d4eb 100644 --- a/WEB-INF/templates/week.tpl +++ b/WEB-INF/templates/week.tpl @@ -28,7 +28,7 @@ function fillDropdowns() {
{$forms.weekTimeForm.date.control}
-{if $user_dropdown} +{if isset($user_dropdown)} diff --git a/initialize.php b/initialize.php index 04f1245c..1ebbab82 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.22.0.5743"); +define("APP_VERSION", "1.22.1.5744"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); diff --git a/week.php b/week.php index 302e4dfb..9d96abfb 100644 --- a/week.php +++ b/week.php @@ -228,7 +228,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. From 50796b8d606dae53f1c39fa08e44be4f86e863f4 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Fri, 28 Oct 2022 15:40:46 +0000 Subject: [PATCH 092/168] Addressed a few more php8 warnings. --- WEB-INF/lib/ttUser.class.php | 2 +- WEB-INF/lib/ttUserHelper.class.php | 3 ++- initialize.php | 2 +- topdf.php | 1 + 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/WEB-INF/lib/ttUser.class.php b/WEB-INF/lib/ttUser.class.php index 2e7ff279..b32c2779 100644 --- a/WEB-INF/lib/ttUser.class.php +++ b/WEB-INF/lib/ttUser.class.php @@ -151,7 +151,7 @@ function __construct($login, $id = null) { // 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. diff --git a/WEB-INF/lib/ttUserHelper.class.php b/WEB-INF/lib/ttUserHelper.class.php index 122c7889..2aeada43 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/initialize.php b/initialize.php index 1ebbab82..432cbb89 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.22.1.5744"); +define("APP_VERSION", "1.22.1.5745"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); diff --git a/topdf.php b/topdf.php index 4e1e677d..6393d5f5 100644 --- a/topdf.php +++ b/topdf.php @@ -55,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; From 89cae455e41364dec480637ec387e3e849e84c25 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sat, 5 Nov 2022 14:59:31 +0000 Subject: [PATCH 093/168] Started work on adding a fav report selector on charts. --- WEB-INF/templates/charts.tpl | 67 ++++++++++++++++++++++++++++++------ charts.php | 16 ++++++++- initialize.php | 2 +- 3 files changed, 73 insertions(+), 12 deletions(-) diff --git a/WEB-INF/templates/charts.tpl b/WEB-INF/templates/charts.tpl index b38988e8..ee0c4834 100644 --- a/WEB-INF/templates/charts.tpl +++ b/WEB-INF/templates/charts.tpl @@ -19,26 +19,59 @@ 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"); + + 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 = ""; + } + } 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"; + } + } +} {$forms.chartForm.open}
{$forms.chartForm.date.control}
{$forms.weekTimeForm.date.control}
-{if isset($user_dropdown)} - +{if defined('FAV_REPORTS_ON_CHARTS_DEBUG')} + - - + + {/if} - - - - - - {if $chart_selector} @@ -47,6 +80,20 @@ function adjustTodayLinks() { {/if} +{if isset($user_dropdown)} + + + + + + +{/if} + + + + + +
{$forms.chartForm.date.control}
{$forms.chartForm.user.control}{$forms.chartForm.favorite_report.control}
{$forms.chartForm.interval.control}
{$forms.chartForm.user.control}
{$forms.chartForm.interval.control}
diff --git a/charts.php b/charts.php index 1ad1c88d..10335594 100644 --- a/charts.php +++ b/charts.php @@ -12,6 +12,7 @@ import('PieChartEx'); import('ttUserHelper'); import('ttTeamHelper'); +import('ttFavReportHelper'); define('ALL_USERS_OPTION_ID', -1); // An identifier for "all users" seclection in User dropdown. @@ -106,7 +107,7 @@ // Set user selection to all users, if necessary. $allUsersSetInSession = @$_SESSION['chart_all_users']; if ($allUsersSetInSession) - $userDropdownSelectionId = constant('ALL_USERS_OPTION_ID'); + $userDropdownSelectionId = constant('ALL_USERS_OPTION_ID'); } // Elements of chartForm. @@ -177,6 +178,19 @@ $largeScreenCalendarRowSpan += 2; } +// Fav report control. +if (defined('FAV_REPORTS_ON_CHARTS_DEBUG')) { + // Get saved favorite reports for user. + $report_list = ttFavReportHelper::getReports(); + $chart_form->addInput(array('type'=>'combobox', + 'name'=>'favorite_report', + 'onchange'=>'handleFavReportSelection();this.form.submit();', + 'data'=>$report_list, + 'datakeys'=>array('id','name'), + 'empty'=>array('-1'=>$i18n->get('dropdown.no')))); + $largeScreenCalendarRowSpan += 2; +} + // Calendar. $chart_form->addInput(array('type'=>'calendar','name'=>'date','value'=>$cl_date)); // calendar diff --git a/initialize.php b/initialize.php index 432cbb89..8bc3c5f4 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.22.1.5745"); +define("APP_VERSION", "1.22.1.5746"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From 6f0f261a87ab5c4bd59866a45c087f17af044cd3 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sat, 5 Nov 2022 16:17:41 +0000 Subject: [PATCH 094/168] Some progress on handling fav reports in charts. --- charts.php | 97 ++++++++++++++++++++++++++++---------------------- initialize.php | 2 +- 2 files changed, 55 insertions(+), 44 deletions(-) diff --git a/charts.php b/charts.php index 10335594..aa0b9bd5 100644 --- a/charts.php +++ b/charts.php @@ -79,10 +79,9 @@ $_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); @@ -91,12 +90,16 @@ // Remember all users selection in session. $_SESSION['chart_all_users'] = $userDropdownSelectionId == constant('ALL_USERS_OPTION_ID') ? true : false; -} else { - // Initialize chart interval. - $cl_interval = @$_SESSION['chart_interval']; - if (!$cl_interval) $cl_interval = $uc->getValue(SYSC_CHART_INTERVAL); + + $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']; @@ -108,12 +111,53 @@ $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. +if (defined('FAV_REPORTS_ON_CHARTS_DEBUG')) { + // Get saved favorite reports for user. + $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') || $user->can('view_all_charts')) { $rank = $user->getMaxRankForGroup($user->getGroup()); @@ -157,44 +201,11 @@ )); $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; -} - -// Fav report control. -if (defined('FAV_REPORTS_ON_CHARTS_DEBUG')) { - // Get saved favorite reports for user. - $report_list = ttFavReportHelper::getReports(); - $chart_form->addInput(array('type'=>'combobox', - 'name'=>'favorite_report', - 'onchange'=>'handleFavReportSelection();this.form.submit();', - 'data'=>$report_list, - 'datakeys'=>array('id','name'), - 'empty'=>array('-1'=>$i18n->get('dropdown.no')))); - $largeScreenCalendarRowSpan += 2; -} - // Calendar. $chart_form->addInput(array('type'=>'calendar','name'=>'date','value'=>$cl_date)); // calendar // Get data for our chart. +// TODO: adjust this call below for fav report selection. $totals = ttChartHelper::getTotals($userDropdownSelectionId, $cl_type, $cl_date, $cl_interval); $smarty->assign('totals', $totals); @@ -243,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/initialize.php b/initialize.php index 8bc3c5f4..c99e16dc 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.22.1.5746"); +define("APP_VERSION", "1.22.1.5747"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From 6b0489f569695247ba373b3a8c3e39f96fe7eacb Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sat, 5 Nov 2022 17:30:23 +0000 Subject: [PATCH 095/168] Initial implenentation of fav reports on charts. --- WEB-INF/lib/ttChartHelper.class.php | 100 +++++++++++++++++++++++++++- charts.php | 6 +- initialize.php | 2 +- 3 files changed, 104 insertions(+), 4 deletions(-) diff --git a/WEB-INF/lib/ttChartHelper.class.php b/WEB-INF/lib/ttChartHelper.class.php index f0523036..7ee71279 100644 --- a/WEB-INF/lib/ttChartHelper.class.php +++ b/WEB-INF/lib/ttChartHelper.class.php @@ -4,6 +4,8 @@ import('ttPeriod'); import('ttTimeHelper'); +import('ttFavReportHelper'); +import('ttReportHelper'); // Definitions for chart types. define('CHART_PROJECTS', 1); @@ -13,7 +15,7 @@ // Class ttChartHelper is a helper class for charts. class ttChartHelper { - // getTotals - returns total times by project or task for a given user in a specified period. + // getTotals - returns total times by project, task, or client for a given user in a specified period. static function getTotals($user_id, $chart_type, $selected_date, $interval_type) { global $user; @@ -123,6 +125,102 @@ static function getTotals($user_id, $chart_type, $selected_date, $interval_type) 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('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 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 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 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; + } + + // adjustType - adjust chart type to something that is available for a group. static function adjustType($requested_type) { global $user; diff --git a/charts.php b/charts.php index aa0b9bd5..9a67e86e 100644 --- a/charts.php +++ b/charts.php @@ -205,8 +205,10 @@ $chart_form->addInput(array('type'=>'calendar','name'=>'date','value'=>$cl_date)); // calendar // Get data for our chart. -// TODO: adjust this call below for fav report selection. -$totals = ttChartHelper::getTotals($userDropdownSelectionId, $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. diff --git a/initialize.php b/initialize.php index c99e16dc..c54bf1d7 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.22.1.5747"); +define("APP_VERSION", "1.22.2.5748"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From e9193034b33070e621e010ff13accee708fa7b3a Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sun, 6 Nov 2022 13:26:53 +0000 Subject: [PATCH 096/168] Removed debug condition for fav reports on charts feature. --- WEB-INF/templates/charts.tpl | 2 -- charts.php | 21 +++++++++------------ initialize.php | 2 +- 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/WEB-INF/templates/charts.tpl b/WEB-INF/templates/charts.tpl index ee0c4834..41a6fd1e 100644 --- a/WEB-INF/templates/charts.tpl +++ b/WEB-INF/templates/charts.tpl @@ -64,14 +64,12 @@ function handleFavReportSelection() {
{$forms.chartForm.date.control}
-{if defined('FAV_REPORTS_ON_CHARTS_DEBUG')} -{/if} {if $chart_selector} diff --git a/charts.php b/charts.php index 9a67e86e..1fd7792b 100644 --- a/charts.php +++ b/charts.php @@ -124,18 +124,15 @@ $largeScreenCalendarRowSpan = 1; // Number of rows calendar spans on large screens. // Fav report control. -if (defined('FAV_REPORTS_ON_CHARTS_DEBUG')) { - // Get saved favorite reports for user. - $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; -} +$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')); diff --git a/initialize.php b/initialize.php index c54bf1d7..43ef2f37 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.22.2.5748"); +define("APP_VERSION", "1.22.2.5749"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From 876d5387cce5819f2724e7dab3407ae44ccdb1a3 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sun, 6 Nov 2022 13:47:57 +0000 Subject: [PATCH 097/168] Improved charts by hiding calendar for fav reports. --- WEB-INF/templates/charts.tpl | 18 ++++++++++++++++-- initialize.php | 2 +- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/WEB-INF/templates/charts.tpl b/WEB-INF/templates/charts.tpl index 41a6fd1e..27d335ff 100644 --- a/WEB-INF/templates/charts.tpl +++ b/WEB-INF/templates/charts.tpl @@ -29,6 +29,8 @@ function handleFavReportSelection() { 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) { @@ -43,6 +45,12 @@ function handleFavReportSelection() { 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"; @@ -56,14 +64,20 @@ function handleFavReportSelection() { 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}
{$forms.chartForm.date.control}
{$forms.chartForm.favorite_report.control}
- + diff --git a/initialize.php b/initialize.php index 43ef2f37..60af5b19 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.22.2.5749"); +define("APP_VERSION", "1.22.2.5750"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From 66d2e4776f8e906b233783d91632f1aa0a80a287 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Fri, 18 Nov 2022 14:19:31 +0000 Subject: [PATCH 098/168] Removed a redundant call in puncher. --- initialize.php | 2 +- puncher.php | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/initialize.php b/initialize.php index 60af5b19..71a97bb7 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.22.2.5750"); +define("APP_VERSION", "1.22.2.5751"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); diff --git a/puncher.php b/puncher.php index 9714ac24..26b371fc 100644 --- a/puncher.php +++ b/puncher.php @@ -324,7 +324,6 @@ $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')); From 4692f7163525e25c5efff09de72bd3fb73690f08 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Tue, 6 Dec 2022 22:02:58 +0000 Subject: [PATCH 099/168] Dutch translation improved. --- WEB-INF/resources/nl.lang.php | 9 +++------ initialize.php | 2 +- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/WEB-INF/resources/nl.lang.php b/WEB-INF/resources/nl.lang.php index 38d93efd..c162f858 100644 --- a/WEB-INF/resources/nl.lang.php +++ b/WEB-INF/resources/nl.lang.php @@ -415,8 +415,7 @@ 'form.reports.show_fields' => 'Toon velden', 'form.reports.time_fields' => 'Tijd velden', 'form.reports.user_fields' => 'Medewerker velden', -// TODO: translate the following. -// 'form.reports.project_fields' => 'Project fields', +'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', @@ -428,8 +427,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', -// TODO: translate the following. -// 'form.report.per_hour' => 'Per hour', +'form.report.per_hour' => 'Per uur', 'form.report.assign_to_invoice' => 'Voeg toe aan factuur', 'form.report.assign_to_timesheet' => 'Wijs toe aan tijdenoverzicht', @@ -590,7 +588,6 @@ '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', 'form.display_options.inactive_projects' => 'Niet actieve projecten', -// TODO: translate the following. -// 'form.display_options.cost_per_hour' => 'Cost per hour', +'form.display_options.cost_per_hour' => 'Kosten per uur', 'form.display_options.custom_css' => 'Aangepaste CSS', ); diff --git a/initialize.php b/initialize.php index 71a97bb7..6c62a5c9 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.22.2.5751"); +define("APP_VERSION", "1.22.2.5752"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From 136681fa3019c4e80ef9e93a59d5334925486121 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Thu, 8 Dec 2022 16:36:09 +0000 Subject: [PATCH 100/168] Added previous month option on charts. --- WEB-INF/lib/ttChartHelper.class.php | 4 ++++ WEB-INF/lib/ttPeriod.class.php | 12 ++++++------ charts.php | 1 + initialize.php | 2 +- reports.php | 6 +++--- 5 files changed, 15 insertions(+), 10 deletions(-) diff --git a/WEB-INF/lib/ttChartHelper.class.php b/WEB-INF/lib/ttChartHelper.class.php index 7ee71279..eaa5a4c0 100644 --- a/WEB-INF/lib/ttChartHelper.class.php +++ b/WEB-INF/lib/ttChartHelper.class.php @@ -37,6 +37,10 @@ static function getTotals($user_id, $chart_type, $selected_date, $interval_type) $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 ttPeriod(new ttDate($selected_date), INTERVAL_THIS_YEAR); break; diff --git a/WEB-INF/lib/ttPeriod.class.php b/WEB-INF/lib/ttPeriod.class.php index 1d63ad9d..0dcbf71c 100644 --- a/WEB-INF/lib/ttPeriod.class.php +++ b/WEB-INF/lib/ttPeriod.class.php @@ -7,9 +7,9 @@ define('INTERVAL_THIS_MONTH', 3); define('INTERVAL_THIS_YEAR', 4); define('INTERVAL_ALL_TIME', 5); -define('INTERVAL_LAST_WEEK', 6); -define('INTERVAL_LAST_MONTH', 7); -define('INTERVAL_LAST_DAY', 8); +define('INTERVAL_PREVIOUS_WEEK', 6); +define('INTERVAL_PREVIOUS_MONTH', 7); +define('INTERVAL_PREVIOUS_DAY', 8); class ttPeriod { var $startDate; // ttDate object. @@ -34,7 +34,7 @@ function __construct($ttDateInstance, $period_type = INTERVAL_THIS_MONTH) { $this->endDate->setFromUnixTimestamp($ttDateInstance->getTimestamp()); break; - case INTERVAL_LAST_DAY: + 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; @@ -44,7 +44,7 @@ function __construct($ttDateInstance, $period_type = INTERVAL_THIS_MONTH) { $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_LAST_WEEK: + 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; @@ -54,7 +54,7 @@ function __construct($ttDateInstance, $period_type = INTERVAL_THIS_MONTH) { $this->endDate->setFromUnixTimestamp(mktime(0,0,0,$t_arr[4]+2,0,$t_arr[5])); break; - case INTERVAL_LAST_MONTH: + 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; diff --git a/charts.php b/charts.php index 1fd7792b..a4c24f0a 100644 --- a/charts.php +++ b/charts.php @@ -186,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'); diff --git a/initialize.php b/initialize.php index 6c62a5c9..d499de94 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.22.2.5752"); +define("APP_VERSION", "1.22.2.5753"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); diff --git a/reports.php b/reports.php index 057787e7..fd4ca156 100644 --- a/reports.php +++ b/reports.php @@ -229,11 +229,11 @@ $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')); From 700d2e19cd276432dd03e3817a5e03aa056ea3cc Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sun, 18 Dec 2022 13:53:20 +0000 Subject: [PATCH 101/168] Started to work on custom translation feature. --- dbinstall.php | 10 ++++++---- initialize.php | 2 +- mysql.sql | 3 ++- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/dbinstall.php b/dbinstall.php index 85d3f021..c79cd558 100644 --- a/dbinstall.php +++ b/dbinstall.php @@ -1151,7 +1151,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["convert11900to12107"]) { + if ($_POST["convert11900to12203"]) { 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')"); @@ -1199,6 +1199,8 @@ function ttGenerateKeys() { 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"]) { @@ -1249,7 +1251,7 @@ function ttGenerateKeys() {

DB Install

{$forms.chartForm.date.control}
{$forms.chartForm.date.control}
-
Create database structure (v1.21.7) + Create database structure (v1.22.3)
(applies only to new installations, do not execute when updating)
@@ -1298,8 +1300,8 @@ function ttGenerateKeys() { - Update database structure (v1.19 to v1.21.7) - + Update database structure (v1.19 to v1.22.3) + diff --git a/initialize.php b/initialize.php index d499de94..7c1e90f6 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.22.2.5753"); +define("APP_VERSION", "1.22.3.5754"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); diff --git a/mysql.sql b/mysql.sql index 4fd33fa9..d50b6c46 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 @@ -622,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.21.7', 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. From 6908f224aa200ae785a9b819ca73f81bb9af6f7f Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sun, 18 Dec 2022 15:03:08 +0000 Subject: [PATCH 102/168] Added localization string for custom translation. --- WEB-INF/resources/ca.lang.php | 1 + WEB-INF/resources/cs.lang.php | 1 + WEB-INF/resources/da.lang.php | 1 + WEB-INF/resources/de.lang.php | 1 + WEB-INF/resources/en.lang.php | 1 + WEB-INF/resources/es.lang.php | 1 + WEB-INF/resources/et.lang.php | 1 + WEB-INF/resources/fa.lang.php | 1 + WEB-INF/resources/fi.lang.php | 1 + WEB-INF/resources/fr.lang.php | 1 + WEB-INF/resources/gr.lang.php | 1 + WEB-INF/resources/he.lang.php | 1 + WEB-INF/resources/hu.lang.php | 1 + WEB-INF/resources/it.lang.php | 1 + WEB-INF/resources/ja.lang.php | 1 + WEB-INF/resources/ko.lang.php | 1 + WEB-INF/resources/nl.lang.php | 2 ++ WEB-INF/resources/no.lang.php | 1 + WEB-INF/resources/pl.lang.php | 1 + WEB-INF/resources/pt-br.lang.php | 2 ++ WEB-INF/resources/pt.lang.php | 1 + WEB-INF/resources/ro.lang.php | 1 + WEB-INF/resources/ru.lang.php | 1 + WEB-INF/resources/sk.lang.php | 1 + WEB-INF/resources/sl.lang.php | 1 + WEB-INF/resources/sr.lang.php | 1 + WEB-INF/resources/sv.lang.php | 1 + WEB-INF/resources/tr.lang.php | 1 + WEB-INF/resources/zh-cn.lang.php | 1 + WEB-INF/resources/zh-tw.lang.php | 1 + initialize.php | 2 +- 31 files changed, 33 insertions(+), 1 deletion(-) diff --git a/WEB-INF/resources/ca.lang.php b/WEB-INF/resources/ca.lang.php index 13d8237c..3cb54751 100644 --- a/WEB-INF/resources/ca.lang.php +++ b/WEB-INF/resources/ca.lang.php @@ -681,4 +681,5 @@ // 'form.display_options.inactive_projects' => 'Inactive projects', // 'form.display_options.cost_per_hour' => 'Cost per hour', // 'form.display_options.custom_css' => 'Custom CSS', +// 'form.display_options.custom_translation' => 'Custom translation', ); diff --git a/WEB-INF/resources/cs.lang.php b/WEB-INF/resources/cs.lang.php index 4a93086d..2c32c964 100644 --- a/WEB-INF/resources/cs.lang.php +++ b/WEB-INF/resources/cs.lang.php @@ -693,4 +693,5 @@ // 'form.display_options.inactive_projects' => 'Inactive projects', // 'form.display_options.cost_per_hour' => 'Cost per hour', // 'form.display_options.custom_css' => 'Custom CSS', +// 'form.display_options.custom_translation' => 'Custom translation', ); diff --git a/WEB-INF/resources/da.lang.php b/WEB-INF/resources/da.lang.php index 595590f0..8c86b742 100644 --- a/WEB-INF/resources/da.lang.php +++ b/WEB-INF/resources/da.lang.php @@ -659,4 +659,5 @@ // 'form.display_options.inactive_projects' => 'Inactive projects', // 'form.display_options.cost_per_hour' => 'Cost per hour', // 'form.display_options.custom_css' => 'Custom CSS', +// 'form.display_options.custom_translation' => 'Custom translation', ); diff --git a/WEB-INF/resources/de.lang.php b/WEB-INF/resources/de.lang.php index 209df6b7..eadc49e7 100644 --- a/WEB-INF/resources/de.lang.php +++ b/WEB-INF/resources/de.lang.php @@ -634,4 +634,5 @@ // 'form.display_options.inactive_projects' => 'Inactive projects', // 'form.display_options.cost_per_hour' => 'Cost per hour', // 'form.display_options.custom_css' => 'Custom CSS', +// 'form.display_options.custom_translation' => 'Custom translation', ); diff --git a/WEB-INF/resources/en.lang.php b/WEB-INF/resources/en.lang.php index 83808aa4..c1defcb5 100644 --- a/WEB-INF/resources/en.lang.php +++ b/WEB-INF/resources/en.lang.php @@ -605,4 +605,5 @@ 'form.display_options.inactive_projects' => 'Inactive projects', 'form.display_options.cost_per_hour' => 'Cost per hour', 'form.display_options.custom_css' => 'Custom CSS', +'form.display_options.custom_translation' => 'Custom translation', ); diff --git a/WEB-INF/resources/es.lang.php b/WEB-INF/resources/es.lang.php index 507bbcaa..63e6fbac 100644 --- a/WEB-INF/resources/es.lang.php +++ b/WEB-INF/resources/es.lang.php @@ -696,4 +696,5 @@ // 'form.display_options.inactive_projects' => 'Inactive projects', // 'form.display_options.cost_per_hour' => 'Cost per hour', // 'form.display_options.custom_css' => 'Custom CSS', +// 'form.display_options.custom_translation' => 'Custom translation', ); diff --git a/WEB-INF/resources/et.lang.php b/WEB-INF/resources/et.lang.php index 0295dc70..59f6ecf5 100644 --- a/WEB-INF/resources/et.lang.php +++ b/WEB-INF/resources/et.lang.php @@ -670,4 +670,5 @@ // 'form.display_options.inactive_projects' => 'Inactive projects', // 'form.display_options.cost_per_hour' => 'Cost per hour', // 'form.display_options.custom_css' => 'Custom CSS', +// 'form.display_options.custom_translation' => 'Custom translation', ); diff --git a/WEB-INF/resources/fa.lang.php b/WEB-INF/resources/fa.lang.php index 07887202..bfe8044d 100644 --- a/WEB-INF/resources/fa.lang.php +++ b/WEB-INF/resources/fa.lang.php @@ -682,4 +682,5 @@ // 'form.display_options.inactive_projects' => 'Inactive projects', // 'form.display_options.cost_per_hour' => 'Cost per hour', // 'form.display_options.custom_css' => 'Custom CSS', +// 'form.display_options.custom_translation' => 'Custom translation', ); diff --git a/WEB-INF/resources/fi.lang.php b/WEB-INF/resources/fi.lang.php index e9e92e01..d5cbe928 100644 --- a/WEB-INF/resources/fi.lang.php +++ b/WEB-INF/resources/fi.lang.php @@ -666,4 +666,5 @@ // 'form.display_options.inactive_projects' => 'Inactive projects', // 'form.display_options.cost_per_hour' => 'Cost per hour', // 'form.display_options.custom_css' => 'Custom CSS', +// 'form.display_options.custom_translation' => 'Custom translation', ); diff --git a/WEB-INF/resources/fr.lang.php b/WEB-INF/resources/fr.lang.php index 145fcb28..a0df933e 100644 --- a/WEB-INF/resources/fr.lang.php +++ b/WEB-INF/resources/fr.lang.php @@ -655,4 +655,5 @@ // 'form.display_options.inactive_projects' => 'Inactive projects', // 'form.display_options.cost_per_hour' => 'Cost per hour', // 'form.display_options.custom_css' => 'Custom CSS', +// 'form.display_options.custom_translation' => 'Custom translation', ); diff --git a/WEB-INF/resources/gr.lang.php b/WEB-INF/resources/gr.lang.php index 6265976d..c5c0c2b7 100644 --- a/WEB-INF/resources/gr.lang.php +++ b/WEB-INF/resources/gr.lang.php @@ -649,4 +649,5 @@ // 'form.display_options.inactive_projects' => 'Inactive projects', // 'form.display_options.cost_per_hour' => 'Cost per hour', // 'form.display_options.custom_css' => 'Custom CSS', +// 'form.display_options.custom_translation' => 'Custom translation', ); diff --git a/WEB-INF/resources/he.lang.php b/WEB-INF/resources/he.lang.php index 727b0b76..1d0c24f4 100644 --- a/WEB-INF/resources/he.lang.php +++ b/WEB-INF/resources/he.lang.php @@ -687,4 +687,5 @@ // 'form.display_options.inactive_projects' => 'Inactive projects', // 'form.display_options.cost_per_hour' => 'Cost per hour', // 'form.display_options.custom_css' => 'Custom CSS', +// 'form.display_options.custom_translation' => 'Custom translation', ); diff --git a/WEB-INF/resources/hu.lang.php b/WEB-INF/resources/hu.lang.php index 8b0be952..4c133d9f 100644 --- a/WEB-INF/resources/hu.lang.php +++ b/WEB-INF/resources/hu.lang.php @@ -684,4 +684,5 @@ // 'form.display_options.inactive_projects' => 'Inactive projects', // 'form.display_options.cost_per_hour' => 'Cost per hour', // 'form.display_options.custom_css' => 'Custom CSS', +// 'form.display_options.custom_translation' => 'Custom translation', ); diff --git a/WEB-INF/resources/it.lang.php b/WEB-INF/resources/it.lang.php index d283830a..7cf157ba 100644 --- a/WEB-INF/resources/it.lang.php +++ b/WEB-INF/resources/it.lang.php @@ -662,4 +662,5 @@ // 'form.display_options.inactive_projects' => 'Inactive projects', // 'form.display_options.cost_per_hour' => 'Cost per hour', // 'form.display_options.custom_css' => 'Custom CSS', +// 'form.display_options.custom_translation' => 'Custom translation', ); diff --git a/WEB-INF/resources/ja.lang.php b/WEB-INF/resources/ja.lang.php index 112e454c..16a882f8 100644 --- a/WEB-INF/resources/ja.lang.php +++ b/WEB-INF/resources/ja.lang.php @@ -688,4 +688,5 @@ // 'form.display_options.inactive_projects' => 'Inactive projects', // 'form.display_options.cost_per_hour' => 'Cost per hour', // 'form.display_options.custom_css' => 'Custom CSS', +// 'form.display_options.custom_translation' => 'Custom translation', ); diff --git a/WEB-INF/resources/ko.lang.php b/WEB-INF/resources/ko.lang.php index 6bb78d77..f0c695e0 100644 --- a/WEB-INF/resources/ko.lang.php +++ b/WEB-INF/resources/ko.lang.php @@ -686,4 +686,5 @@ // 'form.display_options.inactive_projects' => 'Inactive projects', // 'form.display_options.cost_per_hour' => 'Cost per hour', // 'form.display_options.custom_css' => 'Custom CSS', +// 'form.display_options.custom_translation' => 'Custom translation', ); diff --git a/WEB-INF/resources/nl.lang.php b/WEB-INF/resources/nl.lang.php index c162f858..febb14a9 100644 --- a/WEB-INF/resources/nl.lang.php +++ b/WEB-INF/resources/nl.lang.php @@ -590,4 +590,6 @@ 'form.display_options.inactive_projects' => 'Niet actieve projecten', 'form.display_options.cost_per_hour' => 'Kosten per uur', 'form.display_options.custom_css' => 'Aangepaste CSS', +// TODO: translate the following. +// 'form.display_options.custom_translation' => 'Custom translation', ); diff --git a/WEB-INF/resources/no.lang.php b/WEB-INF/resources/no.lang.php index 18e1ee69..ec80e93e 100644 --- a/WEB-INF/resources/no.lang.php +++ b/WEB-INF/resources/no.lang.php @@ -688,4 +688,5 @@ // 'form.display_options.inactive_projects' => 'Inactive projects', // 'form.display_options.cost_per_hour' => 'Cost per hour', // 'form.display_options.custom_css' => 'Custom CSS', +// 'form.display_options.custom_translation' => 'Custom translation', ); diff --git a/WEB-INF/resources/pl.lang.php b/WEB-INF/resources/pl.lang.php index b7dd5024..e6eba052 100644 --- a/WEB-INF/resources/pl.lang.php +++ b/WEB-INF/resources/pl.lang.php @@ -667,4 +667,5 @@ // 'form.display_options.inactive_projects' => 'Inactive projects', // 'form.display_options.cost_per_hour' => 'Cost per hour', // 'form.display_options.custom_css' => 'Custom CSS', +// '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 fff94fa3..04f86045 100644 --- a/WEB-INF/resources/pt-br.lang.php +++ b/WEB-INF/resources/pt-br.lang.php @@ -600,5 +600,7 @@ // 'form.display_options.inactive_projects' => 'Inactive projects', // 'form.display_options.cost_per_hour' => 'Cost per hour', 'form.display_options.custom_css' => 'CSS customizado', +// 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 075e7b41..fdedcf43 100644 --- a/WEB-INF/resources/pt.lang.php +++ b/WEB-INF/resources/pt.lang.php @@ -666,4 +666,5 @@ // 'form.display_options.inactive_projects' => 'Inactive projects', // 'form.display_options.cost_per_hour' => 'Cost per hour', // 'form.display_options.custom_css' => 'Custom CSS', +// 'form.display_options.custom_translation' => 'Custom translation', ); diff --git a/WEB-INF/resources/ro.lang.php b/WEB-INF/resources/ro.lang.php index 0d8fe6d1..55d010f8 100644 --- a/WEB-INF/resources/ro.lang.php +++ b/WEB-INF/resources/ro.lang.php @@ -689,4 +689,5 @@ // 'form.display_options.inactive_projects' => 'Inactive projects', // 'form.display_options.cost_per_hour' => 'Cost per hour', // 'form.display_options.custom_css' => 'Custom CSS', +// 'form.display_options.custom_translation' => 'Custom translation', ); diff --git a/WEB-INF/resources/ru.lang.php b/WEB-INF/resources/ru.lang.php index 805be45b..3e804c7d 100644 --- a/WEB-INF/resources/ru.lang.php +++ b/WEB-INF/resources/ru.lang.php @@ -591,4 +591,5 @@ 'form.display_options.inactive_projects' => 'Неактивные проекты', 'form.display_options.cost_per_hour' => 'Стоимость за час', 'form.display_options.custom_css' => 'Пользовательская CSS', +'form.display_options.custom_translation' => 'Пользовательский перевод', ); diff --git a/WEB-INF/resources/sk.lang.php b/WEB-INF/resources/sk.lang.php index e57e63d7..1802f518 100644 --- a/WEB-INF/resources/sk.lang.php +++ b/WEB-INF/resources/sk.lang.php @@ -671,4 +671,5 @@ // 'form.display_options.inactive_projects' => 'Inactive projects', // 'form.display_options.cost_per_hour' => 'Cost per hour', // 'form.display_options.custom_css' => 'Custom CSS', +// 'form.display_options.custom_translation' => 'Custom translation', ); diff --git a/WEB-INF/resources/sl.lang.php b/WEB-INF/resources/sl.lang.php index 8ea8c445..cd614967 100644 --- a/WEB-INF/resources/sl.lang.php +++ b/WEB-INF/resources/sl.lang.php @@ -661,4 +661,5 @@ // 'form.display_options.inactive_projects' => 'Inactive projects', // 'form.display_options.cost_per_hour' => 'Cost per hour', // 'form.display_options.custom_css' => 'Custom CSS', +// 'form.display_options.custom_translation' => 'Custom translation', ); diff --git a/WEB-INF/resources/sr.lang.php b/WEB-INF/resources/sr.lang.php index 055545b3..18c64ec7 100644 --- a/WEB-INF/resources/sr.lang.php +++ b/WEB-INF/resources/sr.lang.php @@ -669,4 +669,5 @@ // 'form.display_options.inactive_projects' => 'Inactive projects', // 'form.display_options.cost_per_hour' => 'Cost per hour', // 'form.display_options.custom_css' => 'Custom CSS', +// 'form.display_options.custom_translation' => 'Custom translation', ); diff --git a/WEB-INF/resources/sv.lang.php b/WEB-INF/resources/sv.lang.php index e0d212b9..6b2a0d2b 100644 --- a/WEB-INF/resources/sv.lang.php +++ b/WEB-INF/resources/sv.lang.php @@ -667,4 +667,5 @@ // 'form.display_options.inactive_projects' => 'Inactive projects', // 'form.display_options.cost_per_hour' => 'Cost per hour', // 'form.display_options.custom_css' => 'Custom CSS', +// 'form.display_options.custom_translation' => 'Custom translation', ); diff --git a/WEB-INF/resources/tr.lang.php b/WEB-INF/resources/tr.lang.php index 09887171..0473d081 100644 --- a/WEB-INF/resources/tr.lang.php +++ b/WEB-INF/resources/tr.lang.php @@ -702,4 +702,5 @@ // 'form.display_options.inactive_projects' => 'Inactive projects', // 'form.display_options.cost_per_hour' => 'Cost per hour', // 'form.display_options.custom_css' => 'Custom CSS', +// '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 87f1cc1c..44dc443e 100644 --- a/WEB-INF/resources/zh-cn.lang.php +++ b/WEB-INF/resources/zh-cn.lang.php @@ -677,4 +677,5 @@ // 'form.display_options.inactive_projects' => 'Inactive projects', // 'form.display_options.cost_per_hour' => 'Cost per hour', // 'form.display_options.custom_css' => 'Custom CSS', +// '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 181756e7..e5faa1cb 100644 --- a/WEB-INF/resources/zh-tw.lang.php +++ b/WEB-INF/resources/zh-tw.lang.php @@ -680,4 +680,5 @@ // 'form.display_options.inactive_projects' => 'Inactive projects', // 'form.display_options.cost_per_hour' => 'Cost per hour', // 'form.display_options.custom_css' => 'Custom CSS', +// 'form.display_options.custom_translation' => 'Custom translation', ); diff --git a/initialize.php b/initialize.php index 7c1e90f6..54e0c36d 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.22.3.5754"); +define("APP_VERSION", "1.22.3.5755"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From 08fbd9a080c042685df6ccc40b9fd0eba0914795 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Mon, 19 Dec 2022 15:44:16 +0000 Subject: [PATCH 103/168] Work in progress on custom translation. --- WEB-INF/lib/common.lib.php | 14 ++++++++++++++ WEB-INF/lib/ttUser.class.php | 17 ++++++++++++++--- WEB-INF/templates/display_options.tpl | 7 +++++++ default.css | 4 ++-- display_options.php | 7 ++++++- initialize.php | 2 +- 6 files changed, 44 insertions(+), 7 deletions(-) diff --git a/WEB-INF/lib/common.lib.php b/WEB-INF/lib/common.lib.php index 0f38f80f..7a79e0a7 100644 --- a/WEB-INF/lib/common.lib.php +++ b/WEB-INF/lib/common.lib.php @@ -162,6 +162,20 @@ function ttValidCss($val) return true; } +// ttValidTranslation is used to check user input for custom translation. +function ttValidTranslation($val) +{ + $val = trim($val); + if (strlen($val) == 0) + return true; + + // String must not contain any tags. + if (stristr($val, '<')) + return false; + + return false; // Not implemented yet. +} + // ttValidTemplateText is used to check template-based user input. // When templates are used, required input parts must be filled by user. // We identify these parts by 3 "stop sign" emojis (aka "octagonal sign" U+1F6D1). diff --git a/WEB-INF/lib/ttUser.class.php b/WEB-INF/lib/ttUser.class.php index b32c2779..37591fa5 100644 --- a/WEB-INF/lib/ttUser.class.php +++ b/WEB-INF/lib/ttUser.class.php @@ -50,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. @@ -73,7 +74,8 @@ function __construct($login, $id = null) { $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". + " 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"; @@ -131,6 +133,7 @@ function __construct($login, $id = null) { $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'])) { @@ -290,6 +293,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); @@ -682,7 +690,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 = - $password_complexity_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']); @@ -702,6 +711,7 @@ function updateGroup($fields) { 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']); @@ -709,7 +719,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.$password_complexity_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); diff --git a/WEB-INF/templates/display_options.tpl b/WEB-INF/templates/display_options.tpl index 5b487ec8..a6abe34f 100644 --- a/WEB-INF/templates/display_options.tpl +++ b/WEB-INF/templates/display_options.tpl @@ -63,6 +63,13 @@ License: See license.txt *} {$forms.displayOptionsForm.custom_css.control} {$i18n.label.what_is_it}
+{if defined('CUSTOM_TRANSLATION_DEBUG')} +
+ {$i18n.form.display_options.custom_translation} + {$forms.displayOptionsForm.custom_translation.control} + {$i18n.label.what_is_it} +
+{/if}
{$forms.displayOptionsForm.btn_save.control}
{$forms.displayOptionsForm.close} diff --git a/default.css b/default.css index 98e40562..38de0f53 100644 --- a/default.css +++ b/default.css @@ -363,7 +363,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 +371,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; diff --git a/display_options.php b/display_options.php index e449c3c9..e0b7bc98 100644 --- a/display_options.php +++ b/display_options.php @@ -23,6 +23,7 @@ $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'); @@ -31,6 +32,7 @@ $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'); @@ -55,11 +57,13 @@ // 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()) { @@ -72,7 +76,8 @@ $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/initialize.php b/initialize.php index 54e0c36d..bb1aa4f9 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.22.3.5755"); +define("APP_VERSION", "1.22.3.5756"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From bd106a61bb80f88b7c4d6b7f7e3d2e28441f3851 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Mon, 19 Dec 2022 21:26:01 +0000 Subject: [PATCH 104/168] More progress on custom translation feature. --- WEB-INF/lib/I18n.class.php | 40 +++++++++++++++++++++++++++++++++-- WEB-INF/lib/common.lib.php | 43 +++++++++++++++++++++++++++++++++++--- initialize.php | 2 +- 3 files changed, 79 insertions(+), 6 deletions(-) diff --git a/WEB-INF/lib/I18n.class.php b/WEB-INF/lib/I18n.class.php index 1760da48..99015a93 100644 --- a/WEB-INF/lib/I18n.class.php +++ b/WEB-INF/lib/I18n.class.php @@ -51,9 +51,16 @@ function get($key) { return $value; } + // get - keyExists determines if a key exists. + function keyExists($key) { + $value = $this->get($key); + return ($value != null && strlen($value) > 0); + } + // 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]); + if (!$value) continue; + + $value = htmlspecialchars(trim($parts[1])); + + $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/common.lib.php b/WEB-INF/lib/common.lib.php index 7a79e0a7..85ece448 100644 --- a/WEB-INF/lib/common.lib.php +++ b/WEB-INF/lib/common.lib.php @@ -169,11 +169,48 @@ function ttValidTranslation($val) if (strlen($val) == 0) return true; - // String must not contain any tags. - if (stristr($val, '<')) + // Validate lines. + $lines = preg_split("/\r\n|\n|\r/", $val); + for ($i = 0; $i < count($lines); $i++) { + if (!ttValidTranslationLine($lines[$i])) + return false; + } + + return true; +} + +// ttValidTranslationLine is used to check an individual line in custom translation. +function ttValidTranslationLine($val) +{ + $val = trim($val); + if (strlen($val) == 0) + return false; // Empty line is not valid. + + $parts = explode('=', $val); + if (count($parts) != 2) { + // We need exactly 2 parts. return false; + } + + $key = trim($parts[0]); + global $i18n; + if (!$i18n->keyExists($key)) { + // Key does not exist. + return false; + } - return false; // Not implemented yet. + $value = trim($parts[1]); + if (!$value) { + // Do not allow empty values. + return false; + } + + // Prohibit apostrophes in values for now until I figure out how to handle them properly. + if (ttContains($value, "'")) { + return false; + } + + return true; } // ttValidTemplateText is used to check template-based user input. diff --git a/initialize.php b/initialize.php index bb1aa4f9..060c63ea 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.22.3.5756"); +define("APP_VERSION", "1.22.3.5757"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From cd24019d91a1582bbe50c1e65a047c97fc9f19b1 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Tue, 20 Dec 2022 15:40:01 +0000 Subject: [PATCH 105/168] Allowed empty custom translation strings and also containing single quotes. --- WEB-INF/lib/I18n.class.php | 8 ++++---- WEB-INF/lib/common.lib.php | 11 ----------- WEB-INF/resources/ca.lang.php | 3 ++- WEB-INF/resources/cs.lang.php | 3 ++- WEB-INF/resources/da.lang.php | 3 ++- WEB-INF/resources/de.lang.php | 3 ++- WEB-INF/resources/en.lang.php | 3 ++- WEB-INF/resources/es.lang.php | 3 ++- WEB-INF/resources/et.lang.php | 3 ++- WEB-INF/resources/fa.lang.php | 4 +++- WEB-INF/resources/fi.lang.php | 3 ++- WEB-INF/resources/fr.lang.php | 3 ++- WEB-INF/resources/gr.lang.php | 3 ++- WEB-INF/resources/he.lang.php | 3 ++- WEB-INF/resources/hu.lang.php | 3 ++- WEB-INF/resources/it.lang.php | 3 ++- WEB-INF/resources/ja.lang.php | 3 ++- WEB-INF/resources/ko.lang.php | 3 ++- WEB-INF/resources/nl.lang.php | 3 ++- WEB-INF/resources/no.lang.php | 3 ++- WEB-INF/resources/pl.lang.php | 3 ++- WEB-INF/resources/pt-br.lang.php | 3 ++- WEB-INF/resources/pt.lang.php | 3 ++- WEB-INF/resources/ro.lang.php | 3 ++- WEB-INF/resources/ru.lang.php | 3 ++- WEB-INF/resources/sk.lang.php | 3 ++- WEB-INF/resources/sl.lang.php | 3 ++- WEB-INF/resources/sr.lang.php | 3 ++- WEB-INF/resources/sv.lang.php | 3 ++- WEB-INF/resources/tr.lang.php | 3 ++- WEB-INF/resources/zh-cn.lang.php | 3 ++- WEB-INF/resources/zh-tw.lang.php | 3 ++- initialize.php | 2 +- 33 files changed, 66 insertions(+), 46 deletions(-) diff --git a/WEB-INF/lib/I18n.class.php b/WEB-INF/lib/I18n.class.php index 99015a93..bd4e9ffd 100644 --- a/WEB-INF/lib/I18n.class.php +++ b/WEB-INF/lib/I18n.class.php @@ -54,7 +54,7 @@ function get($key) { // get - keyExists determines if a key exists. function keyExists($key) { $value = $this->get($key); - return ($value != null && strlen($value) > 0); + return ($value !== null); } // load - loads localized strings into $keys array by first going through the default file (en.lang.php) @@ -124,9 +124,9 @@ function load($langName) { $key = trim($parts[0]); $value = trim($parts[1]); - if (!$value) continue; - - $value = htmlspecialchars(trim($parts[1])); + // Escape single quotes and backslashes. + $value = addcslashes($value, "'\\"); + $value = htmlspecialchars($value); $pos = strpos($key, "."); if (!($pos === false)) { diff --git a/WEB-INF/lib/common.lib.php b/WEB-INF/lib/common.lib.php index 85ece448..86570a5f 100644 --- a/WEB-INF/lib/common.lib.php +++ b/WEB-INF/lib/common.lib.php @@ -199,17 +199,6 @@ function ttValidTranslationLine($val) return false; } - $value = trim($parts[1]); - if (!$value) { - // Do not allow empty values. - return false; - } - - // Prohibit apostrophes in values for now until I figure out how to handle them properly. - if (ttContains($value, "'")) { - return false; - } - return true; } diff --git a/WEB-INF/resources/ca.lang.php b/WEB-INF/resources/ca.lang.php index 3cb54751..327132ed 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à)'; diff --git a/WEB-INF/resources/cs.lang.php b/WEB-INF/resources/cs.lang.php index 2c32c964..c89457e6 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. diff --git a/WEB-INF/resources/da.lang.php b/WEB-INF/resources/da.lang.php index 8c86b742..6fe93c26 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)'; diff --git a/WEB-INF/resources/de.lang.php b/WEB-INF/resources/de.lang.php index eadc49e7..9cb0340b 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)'; diff --git a/WEB-INF/resources/en.lang.php b/WEB-INF/resources/en.lang.php index c1defcb5..5858b63d 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'; diff --git a/WEB-INF/resources/es.lang.php b/WEB-INF/resources/es.lang.php index 63e6fbac..b302de1c 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)'; diff --git a/WEB-INF/resources/et.lang.php b/WEB-INF/resources/et.lang.php index 59f6ecf5..19a74b4c 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. diff --git a/WEB-INF/resources/fa.lang.php b/WEB-INF/resources/fa.lang.php index bfe8044d..32dacdf0 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'); diff --git a/WEB-INF/resources/fi.lang.php b/WEB-INF/resources/fi.lang.php index d5cbe928..cdfc4c5f 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)'; diff --git a/WEB-INF/resources/fr.lang.php b/WEB-INF/resources/fr.lang.php index a0df933e..b488be0b 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)'; diff --git a/WEB-INF/resources/gr.lang.php b/WEB-INF/resources/gr.lang.php index c5c0c2b7..34f1890e 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 (Ελληνικά)'; diff --git a/WEB-INF/resources/he.lang.php b/WEB-INF/resources/he.lang.php index 1d0c24f4..585a7ec2 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 (עברית)'; diff --git a/WEB-INF/resources/hu.lang.php b/WEB-INF/resources/hu.lang.php index 4c133d9f..e2d7500d 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. diff --git a/WEB-INF/resources/it.lang.php b/WEB-INF/resources/it.lang.php index 7cf157ba..73ca8a02 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. diff --git a/WEB-INF/resources/ja.lang.php b/WEB-INF/resources/ja.lang.php index 16a882f8..0de6e88e 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 (日本語)'; diff --git a/WEB-INF/resources/ko.lang.php b/WEB-INF/resources/ko.lang.php index f0c695e0..fcbe14d2 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 (한국어)'; diff --git a/WEB-INF/resources/nl.lang.php b/WEB-INF/resources/nl.lang.php index febb14a9..16fe1ecf 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)'; diff --git a/WEB-INF/resources/no.lang.php b/WEB-INF/resources/no.lang.php index ec80e93e..720f71f3 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)'; diff --git a/WEB-INF/resources/pl.lang.php b/WEB-INF/resources/pl.lang.php index e6eba052..69dddf31 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)'; diff --git a/WEB-INF/resources/pt-br.lang.php b/WEB-INF/resources/pt-br.lang.php index 04f86045..c9a4ad99 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)'; diff --git a/WEB-INF/resources/pt.lang.php b/WEB-INF/resources/pt.lang.php index fdedcf43..abcd5793 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. diff --git a/WEB-INF/resources/ro.lang.php b/WEB-INF/resources/ro.lang.php index 55d010f8..03db7da4 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. diff --git a/WEB-INF/resources/ru.lang.php b/WEB-INF/resources/ru.lang.php index 3e804c7d..6c924df2 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 (Русский)'; diff --git a/WEB-INF/resources/sk.lang.php b/WEB-INF/resources/sk.lang.php index 1802f518..0404fb8d 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)'; diff --git a/WEB-INF/resources/sl.lang.php b/WEB-INF/resources/sl.lang.php index cd614967..b3153ad7 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. diff --git a/WEB-INF/resources/sr.lang.php b/WEB-INF/resources/sr.lang.php index 18c64ec7..6902d922 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)'; diff --git a/WEB-INF/resources/sv.lang.php b/WEB-INF/resources/sv.lang.php index 6b2a0d2b..dba0fd6f 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)'; diff --git a/WEB-INF/resources/tr.lang.php b/WEB-INF/resources/tr.lang.php index 0473d081..f2531ee7 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. diff --git a/WEB-INF/resources/zh-cn.lang.php b/WEB-INF/resources/zh-cn.lang.php index 44dc443e..d3051586 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 (简体中文)'; diff --git a/WEB-INF/resources/zh-tw.lang.php b/WEB-INF/resources/zh-tw.lang.php index e5faa1cb..890244f6 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 (簡體中文)'; diff --git a/initialize.php b/initialize.php index 060c63ea..55613a09 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.22.3.5757"); +define("APP_VERSION", "1.22.3.5758"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From 3c7b6f53a0f34d7b9fa712b9a0a7d48f21081b3e Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Wed, 21 Dec 2022 13:50:06 +0000 Subject: [PATCH 106/168] Initial release of custom translation feature in display options. --- WEB-INF/templates/display_options.tpl | 2 -- initialize.php | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/WEB-INF/templates/display_options.tpl b/WEB-INF/templates/display_options.tpl index a6abe34f..0d0418af 100644 --- a/WEB-INF/templates/display_options.tpl +++ b/WEB-INF/templates/display_options.tpl @@ -63,13 +63,11 @@ License: See license.txt *} {$forms.displayOptionsForm.custom_css.control} {$i18n.label.what_is_it}
-{if defined('CUSTOM_TRANSLATION_DEBUG')}
{$i18n.form.display_options.custom_translation} {$forms.displayOptionsForm.custom_translation.control} {$i18n.label.what_is_it}
-{/if}
{$forms.displayOptionsForm.btn_save.control}
{$forms.displayOptionsForm.close} diff --git a/initialize.php b/initialize.php index 55613a09..afb52e81 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.22.3.5758"); +define("APP_VERSION", "1.22.4.5759"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From f7dbaf2086bb5793ef5e5ecacc74709ac1c7a947 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Fri, 23 Dec 2022 13:01:08 +0000 Subject: [PATCH 107/168] Dutch transation improved. --- WEB-INF/resources/nl.lang.php | 3 +-- initialize.php | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/WEB-INF/resources/nl.lang.php b/WEB-INF/resources/nl.lang.php index 16fe1ecf..2ca444c0 100644 --- a/WEB-INF/resources/nl.lang.php +++ b/WEB-INF/resources/nl.lang.php @@ -591,6 +591,5 @@ 'form.display_options.inactive_projects' => 'Niet actieve projecten', 'form.display_options.cost_per_hour' => 'Kosten per uur', 'form.display_options.custom_css' => 'Aangepaste CSS', -// TODO: translate the following. -// 'form.display_options.custom_translation' => 'Custom translation', +'form.display_options.custom_translation' => 'Maatwerk vertaling', ); diff --git a/initialize.php b/initialize.php index afb52e81..08bcf66e 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.22.4.5759"); +define("APP_VERSION", "1.22.4.5760"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From a38d8502bbcea6f0d877ca4ea0ba202a0a830790 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sun, 25 Dec 2022 16:04:57 +0000 Subject: [PATCH 108/168] Fixed export-import for custom_css and custom_translation fields. --- WEB-INF/lib/ttGroupExportHelper.class.php | 3 ++- WEB-INF/lib/ttOrgImportHelper.class.php | 6 ++++-- initialize.php | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/WEB-INF/lib/ttGroupExportHelper.class.php b/WEB-INF/lib/ttGroupExportHelper.class.php index fb0f6002..ecd4f80a 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. diff --git a/WEB-INF/lib/ttOrgImportHelper.class.php b/WEB-INF/lib/ttOrgImportHelper.class.php index aae3267c..cc0ecf1d 100644 --- a/WEB-INF/lib/ttOrgImportHelper.class.php +++ b/WEB-INF/lib/ttOrgImportHelper.class.php @@ -99,7 +99,8 @@ function startElement($parser, $name, $attrs) { 'workday_minutes' => $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) { @@ -685,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']); @@ -710,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 .= ')'; diff --git a/initialize.php b/initialize.php index 08bcf66e..fb76ba64 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.22.4.5760"); +define("APP_VERSION", "1.22.4.5761"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From afa80d3b183cadcc586b6ffb3d33f0f4e156e1f8 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Wed, 28 Dec 2022 16:53:14 +0000 Subject: [PATCH 109/168] Fixed a bug with deleting custom field dropdown options with 0 as name. --- cf_dropdown_option_delete.php | 2 +- initialize.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cf_dropdown_option_delete.php b/cf_dropdown_option_delete.php index e6eeeeb7..cb8c8a31 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 || $option === false) { header('Location: access_denied.php'); exit(); } diff --git a/initialize.php b/initialize.php index fb76ba64..5a0189f2 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.22.4.5761"); +define("APP_VERSION", "1.22.5.5762"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From 17705ca52e9e9fbc8cf6c209afecc9c4d5969d7d Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Thu, 29 Dec 2022 16:10:51 +0000 Subject: [PATCH 110/168] Work in progress improving German translation. --- WEB-INF/resources/de.lang.php | 99 ++++++++++++++--------------------- initialize.php | 2 +- 2 files changed, 40 insertions(+), 61 deletions(-) diff --git a/WEB-INF/resources/de.lang.php b/WEB-INF/resources/de.lang.php index 9cb0340b..8f0aae4c 100644 --- a/WEB-INF/resources/de.lang.php +++ b/WEB-INF/resources/de.lang.php @@ -20,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', @@ -52,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.', @@ -65,40 +61,34 @@ 'error.report' => 'Bericht auswählen.', 'error.record' => 'Eintrag auswählen.', 'error.auth' => 'Benutzername oder Passwort ungültig.', -// TODO: translate the following. -// 'error.2fa_code' => 'Invalid 2FA code.', -// 'error.weak_password' => 'Weak password.', +'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.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.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.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.', @@ -120,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', @@ -152,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', @@ -185,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', @@ -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,33 +211,30 @@ 'label.mark_paid' => 'Als bezahlt setzen', 'label.week_note' => 'Wochennotiz', 'label.week_list' => 'Wochenliste', -// TODO: translate the following: -// 'label.weekends' => 'Weekends', +'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', // 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', diff --git a/initialize.php b/initialize.php index 5a0189f2..e71ea88f 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.22.5.5762"); +define("APP_VERSION", "1.22.5.5763"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From 9acc6b2db4c3739bd89632bc1d36216099799931 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Thu, 29 Dec 2022 21:44:28 +0000 Subject: [PATCH 111/168] Continued to improve German translation. --- WEB-INF/resources/de.lang.php | 78 +++++++++++++++-------------------- initialize.php | 2 +- 2 files changed, 34 insertions(+), 46 deletions(-) diff --git a/WEB-INF/resources/de.lang.php b/WEB-INF/resources/de.lang.php index 8f0aae4c..d0bbf01b 100644 --- a/WEB-INF/resources/de.lang.php +++ b/WEB-INF/resources/de.lang.php @@ -240,8 +240,7 @@ 'title.error' => 'Fehler', 'title.success' => 'Erfol', 'title.login' => 'Anmelden', -// TODO: translate the follolwing. -// 'title.2fa' => 'Two Factor Authentication', +'title.2fa' => 'Zwei-Faktor-Authentifizierung', 'title.groups' => 'Gruppen', 'title.add_group' => 'Gruppe anlegen', 'title.edit_group' => 'Gruppe bearbeiten', @@ -251,9 +250,8 @@ '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', @@ -262,10 +260,9 @@ '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', @@ -298,10 +295,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', @@ -321,14 +317,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.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. @@ -352,20 +347,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.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 @@ -377,14 +369,12 @@ 'form.login.about' => 'Anuko Time Tracker ist ein Open-Source Zeiterfassungssystem.', // 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", +'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. -// TODO: translate the following. -// 'form.2fa.hint' => 'Check your email for 2FA code and enter it here.', -// 'form.2fa.2fa_code' => '2FA code', +'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.', @@ -416,19 +406,17 @@ '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', -// TODO: translate the following. -// 'form.reports.note_containing' => 'Note containing', +// TODO: review form.reports.note_containing translation for accuracy. +// This is basically a string filter in user notes, see https://www.anuko.com/time-tracker/what-is/note-containing.htm +'form.reports.note_containing' => 'Enthält Notitz', // The concern is that it may be translated as "Containing a note", which is different + // from "Note containing a substring in this field." 'form.reports.show_fields' => 'Felder anzeigen', -// TODO: translate the following. -// 'form.reports.time_fields' => 'Time fields', -// 'form.reports.user_fields' => 'User fields', -// 'form.reports.project_fields' => 'Project 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', diff --git a/initialize.php b/initialize.php index e71ea88f..fb4b2f49 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.22.5.5763"); +define("APP_VERSION", "1.22.5.5764"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From eb8a5bab0ad7cf9654c40fcf005d6021f3756044 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Fri, 30 Dec 2022 16:08:27 +0000 Subject: [PATCH 112/168] Work in progress on German translation. Almost done. --- WEB-INF/resources/de.lang.php | 58 ++++++++++++++++------------------- initialize.php | 2 +- 2 files changed, 28 insertions(+), 32 deletions(-) diff --git a/WEB-INF/resources/de.lang.php b/WEB-INF/resources/de.lang.php index d0bbf01b..0217fa23 100644 --- a/WEB-INF/resources/de.lang.php +++ b/WEB-INF/resources/de.lang.php @@ -430,18 +430,21 @@ 'form.report.export' => 'Exportiere', 'form.report.per_hour' => 'Stundensatz', 'form.report.assign_to_invoice' => 'Zu Rechnung hinzufügen', -// TODO: translate the following. +// TODO: REVIEW the following. // 'form.report.assign_to_timesheet' => 'Assign to timesheet', +// See how this feature works: https://www.anuko.com/time-tracker/user-guide/timesheets.htm +// Basically, we are selecting records in a report and assign them TO (include IN) one of timesheets. +// The concern is that it is now translated as "Assign timesheet", which is different from how +// form.report.assign_to_invoice above is translated. +'form.report.assign_to_timesheet' => '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). @@ -466,8 +469,7 @@ 'form.tasks.inactive_tasks' => 'Inaktive 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_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', @@ -521,21 +523,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', -// TODO: translate the following. -// 'form.group_edit.one_uncompleted' => 'One uncompleted', +'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', -// 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', -// TODO: Translate the following. -// 'form.group_advanced_edit.password_complexity' => 'Password complexity', -// 'form.group_advanced_edit.2fa' => 'Two factor authentication', +'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?', @@ -569,7 +568,6 @@ '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.supervisor.label' => 'Dienstvorgesetzter', 'role.supervisor.low_case_label' => 'Dienstvorgesetzter', 'role.supervisor.description' => 'Eine Person mit ein paar Administrationsrechten.', @@ -587,20 +585,18 @@ 'role.admin.description' => 'Aadminsitrator 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.cost_per_hour' => 'Cost per hour', -// 'form.display_options.custom_css' => 'Custom CSS', -// 'form.display_options.custom_translation' => 'Custom translation', +'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/initialize.php b/initialize.php index fb4b2f49..67bd4373 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.22.5.5764"); +define("APP_VERSION", "1.22.5.5765"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From ae138917e7a9e4bf5efcf36bd7969303a0831d19 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sat, 31 Dec 2022 14:48:48 +0000 Subject: [PATCH 113/168] Bug fixes related to using 0 as entity name. --- WEB-INF/lib/form/TextField.class.php | 2 +- WEB-INF/lib/ttAdmin.class.php | 7 +++++-- admin_group_delete.php | 2 +- admin_group_edit.php | 2 +- cf_dropdown_option_delete.php | 2 +- cf_dropdown_option_edit.php | 2 +- initialize.php | 2 +- plugins/CustomFields.class.php | 8 +++++--- 8 files changed, 16 insertions(+), 11 deletions(-) diff --git a/WEB-INF/lib/form/TextField.class.php b/WEB-INF/lib/form/TextField.class.php index 91f8b5c2..bfc0e551 100644 --- a/WEB-INF/lib/form/TextField.class.php +++ b/WEB-INF/lib/form/TextField.class.php @@ -56,7 +56,7 @@ function getHtml() { if (!empty($this->on_change)) $html .= " onchange=\"$this->on_change\""; } - $html .= " value=\"".htmlspecialchars($this->getValue() ? $this->getValue() : '')."\""; + $html .= " value=\"".htmlspecialchars($this->getValue())."\""; if(!$this->isEnabled()) $html .= " readonly"; $html .= ">\n"; diff --git a/WEB-INF/lib/ttAdmin.class.php b/WEB-INF/lib/ttAdmin.class.php index 35a26afb..00bd0ff5 100644 --- a/WEB-INF/lib/ttAdmin.class.php +++ b/WEB-INF/lib/ttAdmin.class.php @@ -150,6 +150,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 +160,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/admin_group_delete.php b/admin_group_delete.php index 0cea02b3..909b2347 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 81588ebe..496c7b8f 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/cf_dropdown_option_delete.php b/cf_dropdown_option_delete.php index cb8c8a31..50c68501 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 == null || $option === false) { +if ($option == null) { header('Location: access_denied.php'); exit(); } diff --git a/cf_dropdown_option_edit.php b/cf_dropdown_option_edit.php index eee02f63..2477b057 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/initialize.php b/initialize.php index 67bd4373..b6d48738 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.22.5.5765"); +define("APP_VERSION", "1.22.6.5766"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); diff --git a/plugins/CustomFields.class.php b/plugins/CustomFields.class.php index 0e52871e..b4839cf1 100644 --- a/plugins/CustomFields.class.php +++ b/plugins/CustomFields.class.php @@ -221,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; @@ -229,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. From aa8c6961dcce1ae929f1108033871f9574bddae5 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sat, 31 Dec 2022 15:17:40 +0000 Subject: [PATCH 114/168] Fixes related to using 0 in fields. --- WEB-INF/lib/ttAdmin.class.php | 3 ++- admin_options.php | 2 +- initialize.php | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/WEB-INF/lib/ttAdmin.class.php b/WEB-INF/lib/ttAdmin.class.php index 00bd0ff5..4a631d5e 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']); diff --git a/admin_options.php b/admin_options.php index 84a0ed3a..aed85232 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/initialize.php b/initialize.php index b6d48738..3e332eba 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.22.6.5766"); +define("APP_VERSION", "1.22.6.5767"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From d019c044c0e8d21ce463f680fd45f86fcf6c8014 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sun, 1 Jan 2023 16:21:43 +0000 Subject: [PATCH 115/168] Disallowed adding a duplicate custom field with the same name and entity type. --- cf_custom_field_add.php | 2 +- initialize.php | 2 +- plugins/CustomFields.class.php | 21 +++++++++++++++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/cf_custom_field_add.php b/cf_custom_field_add.php index c1080375..87d46bcd 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()) { @@ -42,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/initialize.php b/initialize.php index 3e332eba..e43e33d3 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.22.6.5767"); +define("APP_VERSION", "1.22.6.5768"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); diff --git a/plugins/CustomFields.class.php b/plugins/CustomFields.class.php index b4839cf1..b561af80 100644 --- a/plugins/CustomFields.class.php +++ b/plugins/CustomFields.class.php @@ -278,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; From e0b9d2112e4b8134839e9540acb6d133bf1dce6b Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Mon, 2 Jan 2023 17:15:59 +0000 Subject: [PATCH 116/168] Fixed a printing problem by eliminating a 1st page break for large reports. --- default.css | 13 +++++++++++++ initialize.php | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/default.css b/default.css index 38de0f53..7c88fc2d 100644 --- a/default.css +++ b/default.css @@ -145,6 +145,19 @@ 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 elements. +inline-block above in itself is needed for nice horizontal centering on screen. */ +@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; diff --git a/initialize.php b/initialize.php index e43e33d3..c43bafdd 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.22.6.5768"); +define("APP_VERSION", "1.22.7.5769"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From 791d64428e8242a5bc4fd8af311c36ba1379d602 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Mon, 2 Jan 2023 20:57:22 +0000 Subject: [PATCH 117/168] A fix for invoices to allow 0 as invoice name. --- WEB-INF/lib/ttInvoiceHelper.class.php | 3 +-- initialize.php | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/WEB-INF/lib/ttInvoiceHelper.class.php b/WEB-INF/lib/ttInvoiceHelper.class.php index 2ac63db4..44206fa0 100644 --- a/WEB-INF/lib/ttInvoiceHelper.class.php +++ b/WEB-INF/lib/ttInvoiceHelper.class.php @@ -298,8 +298,7 @@ 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']; diff --git a/initialize.php b/initialize.php index c43bafdd..9f8a7a45 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.22.7.5769"); +define("APP_VERSION", "1.22.7.5770"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From b3f906536d4f6e8e9eb64e6b7a8c4a7db2969b06 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Thu, 5 Jan 2023 15:48:15 +0000 Subject: [PATCH 118/168] Some fixes for incompatible database structure. --- WEB-INF/lib/ttUser.class.php | 162 +++++++++++++++++++---------------- default.css | 5 +- initialize.php | 2 +- login.php | 12 ++- 4 files changed, 98 insertions(+), 83 deletions(-) diff --git a/WEB-INF/lib/ttUser.class.php b/WEB-INF/lib/ttUser.class.php index 37591fa5..2e86eacb 100644 --- a/WEB-INF/lib/ttUser.class.php +++ b/WEB-INF/lib/ttUser.class.php @@ -62,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) { @@ -69,86 +72,93 @@ 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.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"; + 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']; - $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); + $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()); } } diff --git a/default.css b/default.css index 7c88fc2d..d1a77ebd 100644 --- a/default.css +++ b/default.css @@ -148,8 +148,9 @@ div.page-hint { /* 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 elements. -inline-block above in itself is needed for nice horizontal centering on screen. */ +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; } diff --git a/initialize.php b/initialize.php index 9f8a7a45..e2f8a2e2 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,7 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.22.7.5770"); +define("APP_VERSION", "1.22.8.5771"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); diff --git a/login.php b/login.php index f5dd8eec..2d9dde04 100644 --- a/login.php +++ b/login.php @@ -55,13 +55,17 @@ if ($err->no() && $loginSucceeded) { $user = new ttUser(null, $auth->getUserId()); - // Determine if we have to additionally use two-factor authentication. - $config = $user->getConfigHelper(); - $use2FA = $config->getDefinedValue('2fa'); + 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 ($use2FA && !$user->can('override_2fa')) { + 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); From 94765d3b81cbbe5feb8d0a2418e3f29d06c61740 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Fri, 6 Jan 2023 16:22:10 +0000 Subject: [PATCH 119/168] An attempt to better support php8.1 or greater. --- initialize.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/initialize.php b/initialize.php index e2f8a2e2..0ed4d1d7 100644 --- a/initialize.php +++ b/initialize.php @@ -12,7 +12,13 @@ // Disable displaying errors on screen. ini_set('display_errors', 'Off'); -define("APP_VERSION", "1.22.8.5771"); +// 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', '>=')) { + mysqli_report(MYSQLI_REPORT_OFF); +} + +define("APP_VERSION", "1.22.9.5772"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From 6bce8835b9b5a5c185ba44a652b9955993ff27a2 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sat, 7 Jan 2023 18:12:18 +0000 Subject: [PATCH 120/168] Addressed a few more php8.0 warnings. --- dbinstall.php | 44 ++++++++++++++++++++++---------------------- initialize.php | 2 +- time.php | 2 +- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/dbinstall.php b/dbinstall.php index c79cd558..2db0bebc 100644 --- a/dbinstall.php +++ b/dbinstall.php @@ -201,7 +201,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); @@ -215,7 +215,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"); @@ -229,7 +229,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`"); @@ -244,7 +244,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"; @@ -281,15 +281,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 @@ -316,7 +316,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"); @@ -421,7 +421,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. @@ -518,7 +518,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"); @@ -625,7 +625,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"); @@ -643,7 +643,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); @@ -682,7 +682,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); @@ -693,7 +693,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); @@ -703,7 +703,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"); @@ -751,7 +751,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`"); @@ -794,7 +794,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(); @@ -833,7 +833,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"); @@ -938,7 +938,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)"; @@ -984,7 +984,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`"); @@ -1151,7 +1151,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["convert11900to12203"]) { + 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')"); @@ -1203,7 +1203,7 @@ function ttGenerateKeys() { 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(); diff --git a/initialize.php b/initialize.php index 0ed4d1d7..705d2db1 100644 --- a/initialize.php +++ b/initialize.php @@ -18,7 +18,7 @@ mysqli_report(MYSQLI_REPORT_OFF); } -define("APP_VERSION", "1.22.9.5772"); +define("APP_VERSION", "1.22.9.5773"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); diff --git a/time.php b/time.php index 766ee5fd..931436fa 100644 --- a/time.php +++ b/time.php @@ -386,7 +386,7 @@ if ($showTask && $taskRequired) { if (!$cl_task) $err->add($i18n->get('error.task')); } - if (strlen($cl_duration) == 0) { + if ($cl_duration == null || strlen($cl_duration) == 0) { if ($cl_start || $cl_finish) { if (!ttTimeHelper::isValidTime($cl_start)) $err->add($i18n->get('error.field'), $i18n->get('label.start')); From 0ade9c1b7310e67ac38ee9426f27253a703a32a0 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sun, 8 Jan 2023 15:24:49 +0000 Subject: [PATCH 121/168] Improved handling of a situation when mysqli_report function is not available. --- initialize.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/initialize.php b/initialize.php index 705d2db1..3c819192 100644 --- a/initialize.php +++ b/initialize.php @@ -15,10 +15,13 @@ // 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', '>=')) { - mysqli_report(MYSQLI_REPORT_OFF); + 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.9.5773"); +define("APP_VERSION", "1.22.9.5774"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From 88c0ea1884e59e7f1275cff94ca888962750a1e8 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sun, 8 Jan 2023 20:23:12 +0000 Subject: [PATCH 122/168] Updated smarty for better compatibility with php8.1. --- WEB-INF/lib/smarty/Autoloader.php | 9 +- WEB-INF/lib/smarty/Smarty.class.php | 35 ++++---- WEB-INF/lib/smarty/bootstrap.php | 2 +- WEB-INF/lib/smarty/debug.tpl | 53 +++++++----- WEB-INF/lib/smarty/functions.php | 51 +++++++++++ .../plugins/function.html_select_date.php | 73 ++++++++-------- .../lib/smarty/plugins/function.mailto.php | 28 ++++--- WEB-INF/lib/smarty/plugins/function.math.php | 4 +- .../smarty/plugins/modifier.capitalize.php | 2 + WEB-INF/lib/smarty/plugins/modifier.count.php | 36 ++++++++ .../smarty/plugins/modifier.date_format.php | 3 +- .../lib/smarty/plugins/modifier.escape.php | 84 ++----------------- .../lib/smarty/plugins/modifier.explode.php | 25 ++++++ .../smarty/plugins/modifier.number_format.php | 26 ++++++ .../lib/smarty/plugins/modifier.truncate.php | 6 +- .../plugins/modifiercompiler.escape.php | 49 +++-------- .../smarty/plugins/modifiercompiler.nl2br.php | 23 +++++ .../smarty/plugins/modifiercompiler.round.php | 23 +++++ .../plugins/modifiercompiler.str_repeat.php | 23 +++++ .../plugins/modifiercompiler.strip_tags.php | 4 +- .../plugins/modifiercompiler.strlen.php | 23 +++++ .../plugins/modifiercompiler.unescape.php | 26 ++++-- .../smarty/plugins/modifiercompiler.upper.php | 4 +- .../smarty/plugins/shared.mb_str_replace.php | 36 +++++++- WEB-INF/lib/smarty/readme | 7 -- .../sysplugins/smarty_cacheresource.php | 4 +- .../smarty_cacheresource_keyvaluestore.php | 4 +- .../smarty_internal_cacheresource_file.php | 2 +- .../smarty_internal_compile_block.php | 4 +- .../smarty_internal_compile_function.php | 8 +- .../smarty_internal_compile_include.php | 4 +- .../smarty_internal_compile_insert.php | 2 +- ...nternal_compile_private_foreachsection.php | 6 +- ...arty_internal_compile_private_modifier.php | 3 + ...ernal_compile_private_print_expression.php | 2 +- ...ernal_compile_private_special_variable.php | 6 +- .../smarty_internal_config_file_compiler.php | 10 ++- .../sysplugins/smarty_internal_debug.php | 4 +- .../smarty_internal_extension_handler.php | 28 +++---- .../smarty_internal_method_loadplugin.php | 4 +- .../smarty_internal_method_mustcompile.php | 2 +- .../smarty_internal_runtime_codeframe.php | 9 +- .../smarty_internal_runtime_make_nocache.php | 2 +- .../smarty_internal_runtime_writefile.php | 15 +--- .../sysplugins/smarty_internal_template.php | 5 +- .../smarty_internal_templatebase.php | 2 +- .../smarty_internal_templatecompilerbase.php | 19 +++-- .../smarty_internal_testinstall.php | 2 +- .../lib/smarty/sysplugins/smarty_resource.php | 4 +- .../sysplugins/smarty_resource_custom.php | 15 +++- .../lib/smarty/sysplugins/smarty_security.php | 34 +------- .../lib/smarty/sysplugins/smarty_variable.php | 1 + .../sysplugins/smartycompilerexception.php | 28 +++++++ dbinstall.php | 21 +++-- initialize.php | 2 +- 55 files changed, 563 insertions(+), 344 deletions(-) create mode 100644 WEB-INF/lib/smarty/functions.php create mode 100644 WEB-INF/lib/smarty/plugins/modifier.count.php create mode 100644 WEB-INF/lib/smarty/plugins/modifier.explode.php create mode 100644 WEB-INF/lib/smarty/plugins/modifier.number_format.php create mode 100644 WEB-INF/lib/smarty/plugins/modifiercompiler.nl2br.php create mode 100644 WEB-INF/lib/smarty/plugins/modifiercompiler.round.php create mode 100644 WEB-INF/lib/smarty/plugins/modifiercompiler.str_repeat.php create mode 100644 WEB-INF/lib/smarty/plugins/modifiercompiler.strlen.php delete mode 100644 WEB-INF/lib/smarty/readme diff --git a/WEB-INF/lib/smarty/Autoloader.php b/WEB-INF/lib/smarty/Autoloader.php index 1673ce2f..da7e32ab 100644 --- a/WEB-INF/lib/smarty/Autoloader.php +++ b/WEB-INF/lib/smarty/Autoloader.php @@ -5,6 +5,11 @@ * @package Smarty */ + +if (!defined('SMARTY_HELPER_FUNCTIONS_LOADED')) { + include __DIR__ . '/functions.php'; +} + /** * Smarty Autoloader * @@ -73,7 +78,7 @@ public static function registerBC($prepend = false) */ public static function register($prepend = false) { - self::$SMARTY_DIR = defined('SMARTY_DIR') ? SMARTY_DIR : dirname(__FILE__) . DIRECTORY_SEPARATOR; + self::$SMARTY_DIR = defined('SMARTY_DIR') ? SMARTY_DIR : __DIR__ . DIRECTORY_SEPARATOR; self::$SMARTY_SYSPLUGINS_DIR = defined('SMARTY_SYSPLUGINS_DIR') ? SMARTY_SYSPLUGINS_DIR : self::$SMARTY_DIR . 'sysplugins' . DIRECTORY_SEPARATOR; spl_autoload_register(array(__CLASS__, 'autoload'), true, $prepend); @@ -89,7 +94,7 @@ public static function autoload($class) if ($class[ 0 ] !== 'S' || strpos($class, 'Smarty') !== 0) { return; } - $_class = strtolower($class); + $_class = smarty_strtolower_ascii($class); if (isset(self::$rootClasses[ $_class ])) { $file = self::$SMARTY_DIR . self::$rootClasses[ $_class ]; if (is_file($file)) { diff --git a/WEB-INF/lib/smarty/Smarty.class.php b/WEB-INF/lib/smarty/Smarty.class.php index 85a3d9a6..5d2e3a4b 100644 --- a/WEB-INF/lib/smarty/Smarty.class.php +++ b/WEB-INF/lib/smarty/Smarty.class.php @@ -36,7 +36,7 @@ /** * */ - define('SMARTY_DIR', dirname(__FILE__) . DIRECTORY_SEPARATOR); + define('SMARTY_DIR', __DIR__ . DIRECTORY_SEPARATOR); } /** * set SMARTY_SYSPLUGINS_DIR to absolute path to Smarty internal plugins. @@ -61,23 +61,20 @@ define('SMARTY_MBSTRING', function_exists('mb_get_info')); } -// Note: Nik put this back in from smarty3 for php5.4 that we still support. -// See also comment around Smarty::$_CHARSET. -if (!defined('SMARTY_RESOURCE_CHAR_SET')) { - // UTF-8 can only be done properly when mbstring is available! - /** - * @deprecated in favor of Smarty::$_CHARSET - */ - define('SMARTY_RESOURCE_CHAR_SET', SMARTY_MBSTRING ? 'UTF-8' : 'ISO-8859-1'); +/** + * Load helper functions + */ +if (!defined('SMARTY_HELPER_FUNCTIONS_LOADED')) { + include __DIR__ . '/functions.php'; } -// End of inserted fragment by Nik. /** * Load Smarty_Autoloader */ if (!class_exists('Smarty_Autoloader')) { - include dirname(__FILE__) . '/bootstrap.php'; + include __DIR__ . '/bootstrap.php'; } + /** * Load always needed external class files */ @@ -110,7 +107,7 @@ class Smarty extends Smarty_Internal_TemplateBase /** * smarty version */ - const SMARTY_VERSION = '4.1.0'; + const SMARTY_VERSION = '4.3.0'; /** * define variable scopes */ @@ -172,9 +169,7 @@ class Smarty extends Smarty_Internal_TemplateBase /** * The character set to adhere to (e.g. "UTF-8") */ - // public static $_CHARSET = SMARTY_MBSTRING ? 'UTF-8' : 'ISO-8859-1'; - // Note by Nik: the above assignment only works with php >= 5.6, rolling back to SMARTY_RESOURCE_CHAR_SET as in smarty3. - public static $_CHARSET = SMARTY_RESOURCE_CHAR_SET; + public static $_CHARSET = SMARTY_MBSTRING ? 'UTF-8' : 'ISO-8859-1'; /** * The date format to be used internally @@ -881,7 +876,7 @@ public function getPluginsDir() $this->plugins_dir = (array)$this->plugins_dir; } foreach ($this->plugins_dir as $k => $v) { - $this->plugins_dir[ $k ] = $this->_realpath(rtrim($v, '/\\') . DIRECTORY_SEPARATOR, true); + $this->plugins_dir[ $k ] = $this->_realpath(rtrim($v ?? '', '/\\') . DIRECTORY_SEPARATOR, true); } $this->_cache[ 'plugin_files' ] = array(); $this->_pluginsDirNormalized = true; @@ -1359,7 +1354,7 @@ public function __set($name, $value) */ private function _normalizeDir($dirName, $dir) { - $this->{$dirName} = $this->_realpath(rtrim($dir, "/\\") . DIRECTORY_SEPARATOR, true); + $this->{$dirName} = $this->_realpath(rtrim($dir ?? '', "/\\") . DIRECTORY_SEPARATOR, true); } /** @@ -1381,7 +1376,7 @@ private function _normalizeTemplateConfig($isConfig) } foreach ($dir as $k => $v) { if (!isset($processed[ $k ])) { - $dir[ $k ] = $v = $this->_realpath(rtrim($v, "/\\") . DIRECTORY_SEPARATOR, true); + $dir[ $k ] = $v = $this->_realpath(rtrim($v ?? '', "/\\") . DIRECTORY_SEPARATOR, true); $processed[ $k ] = true; } } @@ -1396,7 +1391,7 @@ private function _normalizeTemplateConfig($isConfig) * * @void */ - public function muteUndefinedOrNullWarnings() { + public function muteUndefinedOrNullWarnings(): void { $this->isMutingUndefinedOrNullWarnings = true; } @@ -1404,7 +1399,7 @@ public function muteUndefinedOrNullWarnings() { * Indicates if PHP7 compatibility mode is set. * @bool */ - public function isMutingUndefinedOrNullWarnings() { + public function isMutingUndefinedOrNullWarnings(): bool { return $this->isMutingUndefinedOrNullWarnings; } diff --git a/WEB-INF/lib/smarty/bootstrap.php b/WEB-INF/lib/smarty/bootstrap.php index 2c830468..a226ac04 100644 --- a/WEB-INF/lib/smarty/bootstrap.php +++ b/WEB-INF/lib/smarty/bootstrap.php @@ -11,6 +11,6 @@ * Load and register Smarty Autoloader */ if (!class_exists('Smarty_Autoloader')) { - include dirname(__FILE__) . '/Autoloader.php'; + include __DIR__ . '/Autoloader.php'; } Smarty_Autoloader::register(true); diff --git a/WEB-INF/lib/smarty/debug.tpl b/WEB-INF/lib/smarty/debug.tpl index edc7bef9..4f82a582 100644 --- a/WEB-INF/lib/smarty/debug.tpl +++ b/WEB-INF/lib/smarty/debug.tpl @@ -1,9 +1,9 @@ {capture name='_smarty_debug' assign=debug_output} - - + + Smarty Debug Console - @@ -112,11 +115,11 @@

included templates & config files (load time in seconds)

{foreach $template_data as $template} - {$template.name} -
   + {$template.name} +
   (compile {$template['compile_time']|string_format:"%.5f"}) (render {$template['render_time']|string_format:"%.5f"}) (cache {$template['cache_time']|string_format:"%.5f"}) -
+
{/foreach}
{/if} @@ -125,13 +128,22 @@ {foreach $assigned_vars as $vars} - - + + + - - {/foreach}

${$vars@key}

- {if isset($vars['nocache'])}Nocache
{/if} - {if isset($vars['scope'])}Origin: {$vars['scope']|debug_print_var nofilter}{/if} +
+

${$vars@key}

+ {if isset($vars['nocache'])}Nocache
{/if} + {if isset($vars['scope'])}Origin: {$vars['scope']|debug_print_var nofilter}{/if} +
+

Value

+ {$vars['value']|debug_print_var:10:80 nofilter} +
+ {if isset($vars['attributes'])} +

Attributes

+ {$vars['attributes']|debug_print_var nofilter} + {/if}

Value

{$vars['value']|debug_print_var:10:80 nofilter}
{if isset($vars['attributes'])}

Attributes

{$vars['attributes']|debug_print_var nofilter} {/if}
@@ -139,11 +151,14 @@ {foreach $config_vars as $vars} - - + + - {/foreach} diff --git a/WEB-INF/lib/smarty/functions.php b/WEB-INF/lib/smarty/functions.php new file mode 100644 index 00000000..bac00e52 --- /dev/null +++ b/WEB-INF/lib/smarty/functions.php @@ -0,0 +1,51 @@ + $_value) { switch ($_key) { case 'time': - if (!is_array($_value) && $_value !== null) { - $template->_checkPlugins( - array( - array( - 'function' => 'smarty_make_timestamp', - 'file' => SMARTY_PLUGINS_DIR . 'shared.make_timestamp.php' - ) - ) - ); - $time = smarty_make_timestamp($_value); - } + $$_key = $_value; // we'll handle conversion below break; case 'month_names': if (is_array($_value) && count($_value) === 12) { @@ -178,43 +169,59 @@ function smarty_function_html_select_date($params, Smarty_Internal_Template $tem } // Note: date() is faster than strftime() // Note: explode(date()) is faster than date() date() date() - if (isset($params[ 'time' ]) && is_array($params[ 'time' ])) { - if (isset($params[ 'time' ][ $prefix . 'Year' ])) { + + if (isset($time) && is_array($time)) { + if (isset($time[$prefix . 'Year'])) { // $_REQUEST[$field_array] given - foreach (array( - 'Y' => 'Year', - 'm' => 'Month', - 'd' => 'Day' - ) as $_elementKey => $_elementName) { + foreach ([ + 'Y' => 'Year', + 'm' => 'Month', + 'd' => 'Day' + ] as $_elementKey => $_elementName) { $_variableName = '_' . strtolower($_elementName); $$_variableName = - isset($params[ 'time' ][ $prefix . $_elementName ]) ? $params[ 'time' ][ $prefix . $_elementName ] : + isset($time[$prefix . $_elementName]) ? $time[$prefix . $_elementName] : date($_elementKey); } - } elseif (isset($params[ 'time' ][ $field_array ][ $prefix . 'Year' ])) { + } elseif (isset($time[$field_array][$prefix . 'Year'])) { // $_REQUEST given - foreach (array( - 'Y' => 'Year', - 'm' => 'Month', - 'd' => 'Day' - ) as $_elementKey => $_elementName) { + foreach ([ + 'Y' => 'Year', + 'm' => 'Month', + 'd' => 'Day' + ] as $_elementKey => $_elementName) { $_variableName = '_' . strtolower($_elementName); - $$_variableName = isset($params[ 'time' ][ $field_array ][ $prefix . $_elementName ]) ? - $params[ 'time' ][ $field_array ][ $prefix . $_elementName ] : date($_elementKey); + $$_variableName = isset($time[$field_array][$prefix . $_elementName]) ? + $time[$field_array][$prefix . $_elementName] : date($_elementKey); } } else { // no date found, use NOW - list($_year, $_month, $_day) = $time = explode('-', date('Y-m-d')); + [$_year, $_month, $_day] = explode('-', date('Y-m-d')); } + } elseif (isset($time) && preg_match("/(\d*)-(\d*)-(\d*)/", $time, $matches)) { + $_year = $_month = $_day = null; + if ($matches[1] > '') $_year = (int) $matches[1]; + if ($matches[2] > '') $_month = (int) $matches[2]; + if ($matches[3] > '') $_day = (int) $matches[3]; } elseif ($time === null) { if (array_key_exists('time', $params)) { - $_year = $_month = $_day = $time = null; + $_year = $_month = $_day = null; } else { - list($_year, $_month, $_day) = $time = explode('-', date('Y-m-d')); + [$_year, $_month, $_day] = explode('-', date('Y-m-d')); } } else { - list($_year, $_month, $_day) = $time = explode('-', date('Y-m-d', $time)); + $template->_checkPlugins( + array( + array( + 'function' => 'smarty_make_timestamp', + 'file' => SMARTY_PLUGINS_DIR . 'shared.make_timestamp.php' + ) + ) + ); + $time = smarty_make_timestamp($time); + [$_year, $_month, $_day] = explode('-', date('Y-m-d', $time)); } + // make syntax "+N" or "-N" work with $start_year and $end_year // Note preg_match('!^(\+|\-)\s*(\d+)$!', $end_year, $match) is slower than trim+substr foreach (array( @@ -309,8 +316,8 @@ function smarty_function_html_select_date($params, Smarty_Internal_Template $tem for ($i = 1; $i <= 12; $i++) { $_val = sprintf('%02d', $i); $_text = isset($month_names) ? smarty_function_escape_special_chars($month_names[ $i ]) : - ($month_format === '%m' ? $_val : strftime($month_format, $_month_timestamps[ $i ])); - $_value = $month_value_format === '%m' ? $_val : strftime($month_value_format, $_month_timestamps[ $i ]); + ($month_format === '%m' ? $_val : @strftime($month_format, $_month_timestamps[ $i ])); + $_value = $month_value_format === '%m' ? $_val : @strftime($month_value_format, $_month_timestamps[ $i ]); $_html_months .= '' . $option_separator; } diff --git a/WEB-INF/lib/smarty/plugins/function.mailto.php b/WEB-INF/lib/smarty/plugins/function.mailto.php index 834d0535..671ac069 100644 --- a/WEB-INF/lib/smarty/plugins/function.mailto.php +++ b/WEB-INF/lib/smarty/plugins/function.mailto.php @@ -48,8 +48,13 @@ */ function smarty_function_mailto($params) { - static $_allowed_encoding = - array('javascript' => true, 'javascript_charcode' => true, 'hex' => true, 'none' => true); + static $_allowed_encoding = [ + 'javascript' => true, + 'javascript_charcode' => true, + 'hex' => true, + 'none' => true + ]; + $extra = ''; if (empty($params[ 'address' ])) { trigger_error("mailto: missing 'address' parameter", E_USER_WARNING); @@ -57,19 +62,19 @@ function smarty_function_mailto($params) } else { $address = $params[ 'address' ]; } + $text = $address; + // netscape and mozilla do not decode %40 (@) in BCC field (bug?) // so, don't encode it. - $search = array('%40', '%2C'); - $replace = array('@', ','); - $mail_parms = array(); + $mail_parms = []; foreach ($params as $var => $value) { switch ($var) { case 'cc': case 'bcc': case 'followupto': if (!empty($value)) { - $mail_parms[] = $var . '=' . str_replace($search, $replace, rawurlencode($value)); + $mail_parms[] = $var . '=' . str_replace(['%40', '%2C'], ['@', ','], rawurlencode($value)); } break; case 'subject': @@ -83,6 +88,7 @@ function smarty_function_mailto($params) default: } } + if ($mail_parms) { $address .= '?' . join('&', $mail_parms); } @@ -94,19 +100,21 @@ function smarty_function_mailto($params) ); return; } + + $string = '' . htmlspecialchars($text, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401, Smarty::$_CHARSET) . ''; + if ($encode === 'javascript') { - $string = '' . $text . ''; $js_encode = ''; for ($x = 0, $_length = strlen($string); $x < $_length; $x++) { $js_encode .= '%' . bin2hex($string[ $x ]); } return ''; } elseif ($encode === 'javascript_charcode') { - $string = '' . $text . ''; for ($x = 0, $_length = strlen($string); $x < $_length; $x++) { $ord[] = ord($string[ $x ]); } - return ''; + return ''; } elseif ($encode === 'hex') { preg_match('!^(.*)(\?.*)$!', $address, $match); if (!empty($match[ 2 ])) { @@ -129,6 +137,6 @@ function smarty_function_mailto($params) return '' . $text_encode . ''; } else { // no encoding - return '' . $text . ''; + return $string; } } diff --git a/WEB-INF/lib/smarty/plugins/function.math.php b/WEB-INF/lib/smarty/plugins/function.math.php index fd5b3d16..f9cf67fe 100644 --- a/WEB-INF/lib/smarty/plugins/function.math.php +++ b/WEB-INF/lib/smarty/plugins/function.math.php @@ -69,8 +69,8 @@ function smarty_function_math($params, $template) // Adapted from https://www.php.net/manual/en/function.eval.php#107377 $number = '(?:\d+(?:[,.]\d+)?|pi|π)'; // What is a number $functionsOrVars = '((?:0x[a-fA-F0-9]+)|([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*))'; - $operators = '[+\/*\^%-]'; // Allowed math operators - $regexp = '/^(('.$number.'|'.$functionsOrVars.'|('.$functionsOrVars.'\s*\((?1)+\)|\((?1)+\)))(?:'.$operators.'(?1))?)+$/'; + $operators = '[,+\/*\^%-]'; // Allowed math operators + $regexp = '/^(('.$number.'|'.$functionsOrVars.'|('.$functionsOrVars.'\s*\((?1)*\)|\((?1)*\)))(?:'.$operators.'(?1))?)+$/'; if (!preg_match($regexp, $equation)) { trigger_error("math: illegal characters", E_USER_WARNING); diff --git a/WEB-INF/lib/smarty/plugins/modifier.capitalize.php b/WEB-INF/lib/smarty/plugins/modifier.capitalize.php index c5fc400a..2903d61d 100644 --- a/WEB-INF/lib/smarty/plugins/modifier.capitalize.php +++ b/WEB-INF/lib/smarty/plugins/modifier.capitalize.php @@ -22,6 +22,8 @@ */ function smarty_modifier_capitalize($string, $uc_digits = false, $lc_rest = false) { + $string = (string) $string; + if (Smarty::$_MBSTRING) { if ($lc_rest) { // uppercase (including hyphenated words) diff --git a/WEB-INF/lib/smarty/plugins/modifier.count.php b/WEB-INF/lib/smarty/plugins/modifier.count.php new file mode 100644 index 00000000..ca35fc11 --- /dev/null +++ b/WEB-INF/lib/smarty/plugins/modifier.count.php @@ -0,0 +1,36 @@ + Prior to PHP 8.0.0, if the parameter was neither an array nor an object that implements the Countable interface, + * > 1 would be returned, unless value was null, in which case 0 would be returned. + */ + + if ($arrayOrObject instanceof Countable || is_array($arrayOrObject)) { + return count($arrayOrObject, (int) $mode); + } elseif ($arrayOrObject === null) { + return 0; + } + return 1; +} diff --git a/WEB-INF/lib/smarty/plugins/modifier.date_format.php b/WEB-INF/lib/smarty/plugins/modifier.date_format.php index 8e7e0b6e..e3589fd0 100644 --- a/WEB-INF/lib/smarty/plugins/modifier.date_format.php +++ b/WEB-INF/lib/smarty/plugins/modifier.date_format.php @@ -78,7 +78,8 @@ function smarty_modifier_date_format($string, $format = null, $default_date = '' } $format = str_replace($_win_from, $_win_to, $format); } - return strftime($format, $timestamp); + // @ to suppress deprecation errors when running in PHP8.1 or higher. + return @strftime($format, $timestamp); } else { return date($format, $timestamp); } diff --git a/WEB-INF/lib/smarty/plugins/modifier.escape.php b/WEB-INF/lib/smarty/plugins/modifier.escape.php index 47489aa9..11e44682 100644 --- a/WEB-INF/lib/smarty/plugins/modifier.escape.php +++ b/WEB-INF/lib/smarty/plugins/modifier.escape.php @@ -23,95 +23,25 @@ */ function smarty_modifier_escape($string, $esc_type = 'html', $char_set = null, $double_encode = true) { - static $_double_encode = true; static $is_loaded_1 = false; static $is_loaded_2 = false; if (!$char_set) { $char_set = Smarty::$_CHARSET; } + + $string = (string)$string; + switch ($esc_type) { case 'html': - if ($_double_encode) { - // php >=5.3.2 - go native - return htmlspecialchars($string, ENT_QUOTES, $char_set, $double_encode); - } else { - if ($double_encode) { - // php <5.2.3 - only handle double encoding - return htmlspecialchars($string, ENT_QUOTES, $char_set); - } else { - // php <5.2.3 - prevent double encoding - $string = preg_replace('!&(#?\w+);!', '%%%SMARTY_START%%%\\1%%%SMARTY_END%%%', $string); - $string = htmlspecialchars($string, ENT_QUOTES, $char_set); - $string = str_replace( - array( - '%%%SMARTY_START%%%', - '%%%SMARTY_END%%%' - ), - array( - '&', - ';' - ), - $string - ); - return $string; - } - } + return htmlspecialchars($string, ENT_QUOTES, $char_set, $double_encode); // no break case 'htmlall': if (Smarty::$_MBSTRING) { - // mb_convert_encoding ignores htmlspecialchars() - if ($_double_encode) { - // php >=5.3.2 - go native - $string = htmlspecialchars($string, ENT_QUOTES, $char_set, $double_encode); - } else { - if ($double_encode) { - // php <5.2.3 - only handle double encoding - $string = htmlspecialchars($string, ENT_QUOTES, $char_set); - } else { - // php <5.2.3 - prevent double encoding - $string = preg_replace('!&(#?\w+);!', '%%%SMARTY_START%%%\\1%%%SMARTY_END%%%', $string); - $string = htmlspecialchars($string, ENT_QUOTES, $char_set); - $string = - str_replace( - array( - '%%%SMARTY_START%%%', - '%%%SMARTY_END%%%' - ), - array( - '&', - ';' - ), - $string - ); - return $string; - } - } - // htmlentities() won't convert everything, so use mb_convert_encoding - return mb_convert_encoding($string, 'HTML-ENTITIES', $char_set); + $string = mb_convert_encoding($string, 'UTF-8', $char_set); + return htmlentities($string, ENT_QUOTES, 'UTF-8', $double_encode); } // no MBString fallback - if ($_double_encode) { - return htmlentities($string, ENT_QUOTES, $char_set, $double_encode); - } else { - if ($double_encode) { - return htmlentities($string, ENT_QUOTES, $char_set); - } else { - $string = preg_replace('!&(#?\w+);!', '%%%SMARTY_START%%%\\1%%%SMARTY_END%%%', $string); - $string = htmlentities($string, ENT_QUOTES, $char_set); - $string = str_replace( - array( - '%%%SMARTY_START%%%', - '%%%SMARTY_END%%%' - ), - array( - '&', - ';' - ), - $string - ); - return $string; - } - } + return htmlentities($string, ENT_QUOTES, $char_set, $double_encode); // no break case 'url': return rawurlencode($string); diff --git a/WEB-INF/lib/smarty/plugins/modifier.explode.php b/WEB-INF/lib/smarty/plugins/modifier.explode.php new file mode 100644 index 00000000..5186fde3 --- /dev/null +++ b/WEB-INF/lib/smarty/plugins/modifier.explode.php @@ -0,0 +1,25 @@ +=8.1 + return explode($separator, $string ?? '', $limit ?? PHP_INT_MAX); +} diff --git a/WEB-INF/lib/smarty/plugins/modifier.number_format.php b/WEB-INF/lib/smarty/plugins/modifier.number_format.php new file mode 100644 index 00000000..8c612601 --- /dev/null +++ b/WEB-INF/lib/smarty/plugins/modifier.number_format.php @@ -0,0 +1,26 @@ +=8.1 + return number_format($num ?? 0.0, $decimals, $decimal_separator, $thousands_separator); +} diff --git a/WEB-INF/lib/smarty/plugins/modifier.truncate.php b/WEB-INF/lib/smarty/plugins/modifier.truncate.php index 33e7e53a..80dcdb53 100644 --- a/WEB-INF/lib/smarty/plugins/modifier.truncate.php +++ b/WEB-INF/lib/smarty/plugins/modifier.truncate.php @@ -42,8 +42,8 @@ function smarty_modifier_truncate($string, $length = 80, $etc = '...', $break_wo if (!$middle) { return mb_substr($string, 0, $length, Smarty::$_CHARSET) . $etc; } - return mb_substr($string, 0, $length / 2, Smarty::$_CHARSET) . $etc . - mb_substr($string, -$length / 2, $length, Smarty::$_CHARSET); + return mb_substr($string, 0, intval($length / 2), Smarty::$_CHARSET) . $etc . + mb_substr($string, -intval($length / 2), $length, Smarty::$_CHARSET); } return $string; } @@ -56,7 +56,7 @@ function smarty_modifier_truncate($string, $length = 80, $etc = '...', $break_wo if (!$middle) { return substr($string, 0, $length) . $etc; } - return substr($string, 0, $length / 2) . $etc . substr($string, -$length / 2); + return substr($string, 0, intval($length / 2)) . $etc . substr($string, -intval($length / 2)); } return $string; } diff --git a/WEB-INF/lib/smarty/plugins/modifiercompiler.escape.php b/WEB-INF/lib/smarty/plugins/modifiercompiler.escape.php index 70b95cc9..602c3dbf 100644 --- a/WEB-INF/lib/smarty/plugins/modifiercompiler.escape.php +++ b/WEB-INF/lib/smarty/plugins/modifiercompiler.escape.php @@ -18,12 +18,10 @@ * @param Smarty_Internal_TemplateCompilerBase $compiler * * @return string with compiled code - * @throws \SmartyException + * @throws SmartyException */ function smarty_modifiercompiler_escape($params, Smarty_Internal_TemplateCompilerBase $compiler) { - static $_double_encode = true; - static $is_loaded = false; $compiler->template->_checkPlugins( array( array( @@ -41,53 +39,30 @@ function smarty_modifiercompiler_escape($params, Smarty_Internal_TemplateCompile } switch ($esc_type) { case 'html': - if ($_double_encode) { - return 'htmlspecialchars(' . $params[ 0 ] . ', ENT_QUOTES, ' . var_export($char_set, true) . ', ' . - var_export($double_encode, true) . ')'; - } elseif ($double_encode) { - return 'htmlspecialchars(' . $params[ 0 ] . ', ENT_QUOTES, ' . var_export($char_set, true) . ')'; - } else { - // fall back to modifier.escape.php - } + return 'htmlspecialchars((string)' . $params[ 0 ] . ', ENT_QUOTES, ' . var_export($char_set, true) . ', ' . + var_export($double_encode, true) . ')'; // no break case 'htmlall': if (Smarty::$_MBSTRING) { - if ($_double_encode) { - // php >=5.2.3 - go native - return 'mb_convert_encoding(htmlspecialchars(' . $params[ 0 ] . ', ENT_QUOTES, ' . - var_export($char_set, true) . ', ' . var_export($double_encode, true) . - '), "HTML-ENTITIES", ' . var_export($char_set, true) . ')'; - } elseif ($double_encode) { - // php <5.2.3 - only handle double encoding - return 'mb_convert_encoding(htmlspecialchars(' . $params[ 0 ] . ', ENT_QUOTES, ' . - var_export($char_set, true) . '), "HTML-ENTITIES", ' . var_export($char_set, true) . ')'; - } else { - // fall back to modifier.escape.php - } + return 'htmlentities(mb_convert_encoding((string)' . $params[ 0 ] . ', \'UTF-8\', ' . + var_export($char_set, true) . '), ENT_QUOTES, \'UTF-8\', ' . + var_export($double_encode, true) . ')'; } // no MBString fallback - if ($_double_encode) { - // php >=5.2.3 - go native - return 'htmlentities(' . $params[ 0 ] . ', ENT_QUOTES, ' . var_export($char_set, true) . ', ' . - var_export($double_encode, true) . ')'; - } elseif ($double_encode) { - // php <5.2.3 - only handle double encoding - return 'htmlentities(' . $params[ 0 ] . ', ENT_QUOTES, ' . var_export($char_set, true) . ')'; - } else { - // fall back to modifier.escape.php - } + return 'htmlentities((string)' . $params[ 0 ] . ', ENT_QUOTES, ' . var_export($char_set, true) . ', ' . + var_export($double_encode, true) . ')'; // no break case 'url': - return 'rawurlencode(' . $params[ 0 ] . ')'; + return 'rawurlencode((string)' . $params[ 0 ] . ')'; case 'urlpathinfo': - return 'str_replace("%2F", "/", rawurlencode(' . $params[ 0 ] . '))'; + return 'str_replace("%2F", "/", rawurlencode((string)' . $params[ 0 ] . '))'; case 'quotes': // escape unescaped single quotes - return 'preg_replace("%(? "\\\\\\\\", "\'" => "\\\\\'", "\"" => "\\\\\"", "\\r" => "\\\\r", "\\n" => "\\\n", " "<\/", "

#{$vars@key}#

- {if isset($vars['scope'])}Origin: {$vars['scope']|debug_print_var nofilter}{/if} +
+

#{$vars@key}#

+ {if isset($vars['scope'])}Origin: {$vars['scope']|debug_print_var nofilter}{/if} +
+ {$vars['value']|debug_print_var:10:80 nofilter} {$vars['value']|debug_print_var:10:80 nofilter}