From 761d451e6382b59fd96f133b5c9dc9cca3ccb753 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Thu, 30 Jun 2022 14:00:35 +0000 Subject: [PATCH 001/105] 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 002/105] 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 003/105] 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 004/105] 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 005/105] 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 006/105] 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 007/105] 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 008/105] 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 009/105] 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 010/105] 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 011/105] 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 014/105] 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 015/105] 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 016/105] 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 012/105] 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 013/105] 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 017/105] 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 018/105] 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 019/105] 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 020/105] 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 022/105] 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 023/105] 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 024/105] 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 025/105] 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 021/105] 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 026/105] 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 027/105] 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 028/105] 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 029/105] 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 030/105] 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 031/105] 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 032/105] 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 033/105] 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 034/105] 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 035/105] 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 036/105] 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 037/105] 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 038/105] 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 039/105] 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 040/105] 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 041/105] 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 042/105] 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 043/105] 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 044/105] 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 045/105] 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 046/105] 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 047/105] 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 048/105] 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 049/105] 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 050/105] 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 051/105] 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 052/105] 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 053/105] 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 054/105] 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 055/105] 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 056/105] 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 057/105] 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 058/105] 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 059/105] 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}