Release 2.0.17 (#3992)

* prevent empty title for select options
* removed css for unused sweetalert
* fix isWeekend() check with DateTimeInterface
* reset timesheet rates on "create copy"
* wrap long names in multi-selects - fix #4001
* rename profile menus
* show all possible links in user profile dropdown
* check if plugin is compatible before displaying the buy link
This commit is contained in:
Kevin Papst
2023-05-02 00:40:43 +02:00
committed by GitHub
parent 7112e932a2
commit 66413d789c
43 changed files with 76 additions and 93 deletions

View File

@@ -347,7 +347,13 @@ export default class KimaiFormSelect extends KimaiFormPlugin {
--end;
}
return (start > 0 || end < title.length) ? title.substring(start, end) : title;
let result = (start > 0 || end < title.length) ? title.substring(start, end) : title;
if (result === '' && entity['name'] !== undefined) {
return entity['name'];
}
return result;
}
/**

View File

@@ -14,7 +14,6 @@
@import 'tables';
@import 'calendar';
@import 'ticktac';
@import 'sweetalert';
@import 'selectpicker';
@import 'forms';
@import 'modal';

View File

@@ -9,9 +9,6 @@
> input {
min-width: unset;
}
.item {
white-space: nowrap!important;
}
}
.ts-dropdown {

View File

@@ -1,16 +0,0 @@
.swal2-popup {
font-size: unset;
}
body.swal2-toast-shown .swal2-container {
// never block the top two bars double-bars display (smaller displays)
@media only screen and (max-width: 768px) {
top: 95px !important;
width: 80%;
}
// never block the top bar on single-bar display (larger devices)
@media only screen and (min-width: 768px) {
top: 60px !important;
}
}

View File

@@ -2740,11 +2740,6 @@ parameters:
count: 1
path: src/Export/ExportFilename.php
-
message: "#^Method App\\\\Export\\\\ExportFilename\\:\\:getFilename\\(\\) has no return type specified\\.$#"
count: 1
path: src/Export/ExportFilename.php
-
message: "#^Parameter \\#1 \\$customer of method App\\\\Export\\\\ExportFilename\\:\\:getCustomerName\\(\\) expects App\\\\Entity\\\\Customer, App\\\\Entity\\\\Customer\\|null given\\.$#"
count: 1

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -3,10 +3,10 @@
"app": {
"js": [
"/build/runtime.f0079159.js",
"/build/app.902831bc.js"
"/build/app.c61734af.js"
],
"css": [
"/build/app.b10b16e5.css"
"/build/app.d3ccebf5.css"
]
},
"export-pdf": {
@@ -63,8 +63,8 @@
},
"integrity": {
"/build/runtime.f0079159.js": "sha384-H22sAW1aTvyIPqvHOvGXWSWTxf0y6mptp+MsVmyXCfjx/WJjBbhX9gbUZ+qIuihV",
"/build/app.902831bc.js": "sha384-t6GwvEu6PBNMxOHW4JHSScxFmGE7tGus/IZoBBZHrGFdtDZtKjweNAqJ00VmSnSp",
"/build/app.b10b16e5.css": "sha384-dwEgmHAQRm4PP7XTYkJfIoB+Iumd+0WuH5FyoC/+qC3WQ/e1gCTmlx9M2mIBxPuK",
"/build/app.c61734af.js": "sha384-EP0ldSWtoYJlwRuaj2IjQCncdMT5rlz4cfZgDzciCd0i2pGn6MEijujB4bUU0PCP",
"/build/app.d3ccebf5.css": "sha384-9ufQd+D9ebk0tkPhQvGxswVMeHGES5GgJGK5lsDFS9+dQmdKbxwB/58ZSCVA3N/2",
"/build/export-pdf.587575e7.js": "sha384-J50GStmmfVwUTN4dIRQ02eg9hyzGFPSzpTtpPody92j0V6zCqw+s5l8+ZhVTugeW",
"/build/export-pdf.d8a6c23b.css": "sha384-ztepocHE4rnGE9eKZ4kL6jTKaePUyiwiB9TjJjstjpf/ckcKg1HedrEOOk/8ElJg",
"/build/invoice.c8ae95ad.js": "sha384-2eVY7MBiMQxo1vhfizU+fYfEZbz9bYUdzqxnpTQhxpYiLaxeSV4WnaObq2G7/Pks",

View File

@@ -1,6 +1,6 @@
{
"build/app.css": "/build/app.b10b16e5.css",
"build/app.js": "/build/app.902831bc.js",
"build/app.css": "/build/app.d3ccebf5.css",
"build/app.js": "/build/app.c61734af.js",
"build/export-pdf.css": "/build/export-pdf.d8a6c23b.css",
"build/export-pdf.js": "/build/export-pdf.587575e7.js",
"build/invoice.css": "/build/invoice.d0ae14e1.css",

View File

@@ -17,11 +17,11 @@ class Constants
/**
* The current release version
*/
public const VERSION = '2.0.16';
public const VERSION = '2.0.17';
/**
* The current release: major * 10000 + minor * 100 + patch
*/
public const VERSION_ID = 20016;
public const VERSION_ID = 20017;
/**
* The software name
*/

View File

@@ -202,6 +202,7 @@ abstract class TimesheetAbstractController extends AbstractController
protected function duplicate(Timesheet $timesheet, Request $request): Response
{
$copyTimesheet = clone $timesheet;
$copyTimesheet->resetRates();
$event = new TimesheetMetaDefinitionEvent($copyTimesheet);
$this->dispatcher->dispatch($event);

View File

@@ -615,6 +615,15 @@ class Timesheet implements EntityWithMetaFields, ExportableItem
return $all;
}
public function resetRates(): void
{
$this->setRate(0.00);
$this->setInternalRate(null);
$this->setHourlyRate(null);
$this->setFixedRate(null);
$this->setBillableMode(Timesheet::BILLABLE_AUTOMATIC);
}
public function getMetaField(string $name): ?MetaTableTypeInterface
{
foreach ($this->meta as $field) {

View File

@@ -997,7 +997,7 @@ class User implements UserInterface, EquatableInterface, ThemeUserInterface, Pas
$this->password = $data['password'];
}
public function __toString()
public function __toString(): string
{
return $this->getDisplayName();
}

View File

@@ -43,21 +43,22 @@ class UserDetailsSubscriber implements EventSubscriberInterface
$event->setUser($user);
if ($this->auth->isGranted('view', $user)) {
$event->addLink(
new MenuItemModel('user_profile', 'my.profile', 'user_profile', ['username' => $user->getUserIdentifier()], 'fas fa-tachometer-alt')
);
$event->addLink(new MenuItemModel('user_profile', 'my.profile', 'user_profile', ['username' => $user->getUserIdentifier()]));
}
if ($this->auth->isGranted('edit', $user)) {
$event->addLink(
new MenuItemModel('user_profile_edit', 'action.edit', 'user_profile_edit', ['username' => $user->getUserIdentifier()], 'fas fa-tachometer-alt')
);
$event->addLink(new MenuItemModel('user_profile_edit', 'action.edit', 'user_profile_edit', ['username' => $user->getUserIdentifier()]));
}
if ($this->auth->isGranted('password', $user)) {
$event->addLink(new MenuItemModel('password', 'profile.password', 'user_profile_password', ['username' => $user->getUserIdentifier()]));
}
if ($this->auth->isGranted('2fa', $user)) {
$event->addLink(new MenuItemModel('2fa', 'profile.2fa', 'user_profile_2fa', ['username' => $user->getUserIdentifier()]));
}
if ($this->auth->isGranted('api-token', $user)) {
$event->addLink(new MenuItemModel('api-token', 'profile.api-token', 'user_profile_api_token', ['username' => $user->getUserIdentifier()]));
}
if ($this->auth->isGranted('preferences', $user)) {
$event->addLink(
new MenuItemModel('user_profile_preferences', 'profile.preferences', 'user_profile_preferences', ['username' => $user->getUserIdentifier()], 'fas fa-tachometer-alt')
);
$event->addLink(new MenuItemModel('user_profile_preferences', 'profile.preferences', 'user_profile_preferences', ['username' => $user->getUserIdentifier()]));
}
}
}

View File

@@ -55,7 +55,7 @@ final class ExportFilename
return FileHelper::convertToAsciiFilename($filename);
}
public function getFilename()
public function getFilename(): string
{
if ($this->filename === null) {
$filename = date('Ymd');
@@ -91,7 +91,7 @@ final class ExportFilename
return $this->filename;
}
public function __toString()
public function __toString(): string
{
return $this->getFilename();
}

View File

@@ -17,29 +17,20 @@ final class RateResetCalculator implements CalculatorInterface
public function calculate(Timesheet $record, array $changeset): void
{
// check if the rate was changed manually
$changedRate = false;
foreach (['hourlyRate', 'fixedRate', 'internalRate', 'rate'] as $field) {
if (\array_key_exists($field, $changeset)) {
$changedRate = true;
break;
return;
}
}
// if no manual rate changed was applied:
// check if a field changed, that is relevant for the rate calculation: if one was changed =>
// check if a field changed, that is relevant for the rate calculation
// reset all rates, because most users do not even see their rates and would not be able
// to fix or empty the rate, even if they knew that the changed project has another base rate
if (!$changedRate) {
foreach (['project', 'activity', 'user'] as $field) {
if (\array_key_exists($field, $changeset)) {
// this has room for minor improvements: entries with a manual rate might be changed
$record->setRate(0.00);
$record->setInternalRate(null);
$record->setHourlyRate(null);
$record->setFixedRate(null);
$record->setBillableMode(Timesheet::BILLABLE_AUTOMATIC);
break;
}
// to change the rate, even if they knew that the changed project has another base rate
foreach (['project', 'activity', 'user'] as $field) {
if (\array_key_exists($field, $changeset)) {
$record->resetRates();
break;
}
}
}

View File

@@ -118,7 +118,7 @@ final class LocaleFormatExtensions extends AbstractExtension implements LocaleAw
public function isWeekend(\DateTimeInterface|string|null $dateTime): bool
{
if (!$dateTime instanceof \DateTime) {
if (!$dateTime instanceof \DateTimeInterface) {
return false;
}

View File

@@ -72,7 +72,7 @@ final class SearchTerm
return $this->originalTerm;
}
public function __toString()
public function __toString(): string
{
return $this->originalTerm;
}

View File

@@ -72,7 +72,7 @@
</td>
<td class="{{ tables.data_table_column_class(tableName, columns, 'description') }}">{{ extension.description }}</td>
<td class="{{ tables.data_table_column_class(tableName, columns, 'actions') }}">
{% if extension.buy is defined and extension.buy is not empty %}
{% if extension.buy is defined and extension.buy is not empty and (extension.minimum_version is not defined or (extension.minimum_version is not empty and constant('App\\Constants::VERSION') >= extension.minimum_version)) %}
<a class="btn btn-icon btn-outline-secondary" href="{{ extension.buy }}" title="{{ 'buy'|trans({}, 'plugins') }}" target="_blank">{{ icon('shop') }}</a>
{% endif %}
</td>

View File

@@ -1406,7 +1406,7 @@
</trans-unit>
<trans-unit id="FeFBn_3" resname="profile.2fa">
<source>profile.2fa</source>
<target state="translated">الاستيثاق بخطوتين (TOTP)</target>
<target state="translated">الاستيثاق بخطوتين (2FA)</target>
</trans-unit>
<trans-unit id="Y0PRG9Z" resname="profile.2fa_confirmation">
<source>profile.2fa_confirmation</source>

View File

@@ -1252,7 +1252,7 @@
</trans-unit>
<trans-unit id="FeFBn_3" resname="profile.2fa">
<source>profile.2fa</source>
<target state="translated">Dvoufázové (TOTP)</target>
<target state="translated">Dvoufázové (2FA)</target>
</trans-unit>
<trans-unit id="IRzPWmL" resname="login.2fa_intro">
<source>login.2fa_intro</source>

View File

@@ -564,7 +564,7 @@
<!-- 2FA -->
<trans-unit id="FeFBn_3" resname="profile.2fa">
<source>profile.2fa</source>
<target>Zwei-Faktor (TOTP)</target>
<target>Zwei-Faktor (2FA)</target>
</trans-unit>
<trans-unit id="hPY2VcD" resname="profile.2fa_intro">
<source>profile.2fa_intro</source>
@@ -585,7 +585,7 @@
<!-- END 2FA -->
<trans-unit id="Z34ZpjK" resname="profile.api-token">
<source>profile.api-token</source>
<target>API</target>
<target>API Zugang</target>
</trans-unit>
<trans-unit id="ygtTz8." resname="profile.roles">
<source>profile.roles</source>

View File

@@ -547,7 +547,7 @@
</trans-unit>
<trans-unit id="Z34ZpjK" resname="profile.api-token">
<source>profile.api-token</source>
<target>API</target>
<target>API Zugang</target>
</trans-unit>
<trans-unit id="A6TLLQa" resname="profile.password">
<source>profile.password</source>

View File

@@ -564,7 +564,7 @@
<!-- 2FA -->
<trans-unit id="FeFBn_3" resname="profile.2fa">
<source>profile.2fa</source>
<target>Two-Factor (TOTP)</target>
<target>Two-Factor (2FA)</target>
</trans-unit>
<trans-unit id="hPY2VcD" resname="profile.2fa_intro">
<source>profile.2fa_intro</source>
@@ -585,7 +585,7 @@
<!-- END 2FA -->
<trans-unit id="Z34ZpjK" resname="profile.api-token">
<source>profile.api-token</source>
<target>API</target>
<target>API Access</target>
</trans-unit>
<trans-unit id="ygtTz8." resname="profile.roles">
<source>profile.roles</source>

View File

@@ -1316,7 +1316,7 @@
</trans-unit>
<trans-unit id="FeFBn_3" resname="profile.2fa">
<source>profile.2fa</source>
<target state="translated">Dos factores (TOTP)</target>
<target state="translated">Dos factores (2FA)</target>
</trans-unit>
<trans-unit id="Y0PRG9Z" resname="profile.2fa_confirmation">
<source>profile.2fa_confirmation</source>

View File

@@ -1253,7 +1253,7 @@
</trans-unit>
<trans-unit id="FeFBn_3" resname="profile.2fa">
<source>profile.2fa</source>
<target state="translated">دو عاملی (TOTP)</target>
<target state="translated">دو عاملی (2FA)</target>
</trans-unit>
<trans-unit id="Y0PRG9Z" resname="profile.2fa_confirmation">
<source>profile.2fa_confirmation</source>

View File

@@ -1292,7 +1292,7 @@
</trans-unit>
<trans-unit id="FeFBn_3" resname="profile.2fa">
<source>profile.2fa</source>
<target state="translated">double facteur (TOTP)</target>
<target state="translated">double facteur (2FA)</target>
</trans-unit>
<trans-unit id="hPY2VcD" resname="profile.2fa_intro">
<source>profile.2fa_intro</source>

View File

@@ -1220,7 +1220,7 @@
</trans-unit>
<trans-unit id="FeFBn_3" resname="profile.2fa">
<source>profile.2fa</source>
<target state="translated">דו־שלבי (TOTP)</target>
<target state="translated">דו־שלבי (2FA)</target>
</trans-unit>
<trans-unit id="Y0PRG9Z" resname="profile.2fa_confirmation">
<source>profile.2fa_confirmation</source>

View File

@@ -1308,7 +1308,7 @@
</trans-unit>
<trans-unit id="FeFBn_3" resname="profile.2fa">
<source>profile.2fa</source>
<target state="translated">Dvofaktorski (TOTP)</target>
<target state="translated">Dvofaktorski (2FA)</target>
</trans-unit>
<trans-unit id="hPY2VcD" resname="profile.2fa_intro">
<source>profile.2fa_intro</source>

View File

@@ -1283,7 +1283,7 @@
</trans-unit>
<trans-unit id="FeFBn_3" resname="profile.2fa">
<source>profile.2fa</source>
<target state="translated">A due fattori (TOTP)</target>
<target state="translated">A due fattori (2FA)</target>
</trans-unit>
<trans-unit id="hPY2VcD" resname="profile.2fa_intro">
<source>profile.2fa_intro</source>

View File

@@ -1274,7 +1274,7 @@
</trans-unit>
<trans-unit id="FeFBn_3" resname="profile.2fa">
<source>profile.2fa</source>
<target state="translated">To-faktor (TOTP)</target>
<target state="translated">To-faktor (2FA)</target>
</trans-unit>
<trans-unit id="oq1jcSQ" resname="help.billable">
<source>help.billable</source>

View File

@@ -1264,7 +1264,7 @@
</trans-unit>
<trans-unit id="FeFBn_3" resname="profile.2fa">
<source>profile.2fa</source>
<target state="translated">Dwuskładnikowe (TOTP)</target>
<target state="translated">Dwuskładnikowe (2FA)</target>
</trans-unit>
<trans-unit id="o0hsx2H" resname="mark_as_open">
<source>mark_as_open</source>

View File

@@ -1228,7 +1228,7 @@
</trans-unit>
<trans-unit id="FeFBn_3" resname="profile.2fa">
<source>profile.2fa</source>
<target state="translated">Dois fatores (TOTP)</target>
<target state="translated">Dois fatores (2FA)</target>
</trans-unit>
<trans-unit id="hPY2VcD" resname="profile.2fa_intro">
<source>profile.2fa_intro</source>

View File

@@ -1268,7 +1268,7 @@
</trans-unit>
<trans-unit id="FeFBn_3" resname="profile.2fa">
<source>profile.2fa</source>
<target state="translated">Dois fatores (TOTP)</target>
<target state="translated">Dois fatores (2FA)</target>
</trans-unit>
<trans-unit id="hPY2VcD" resname="profile.2fa_intro">
<source>profile.2fa_intro</source>

View File

@@ -1176,7 +1176,7 @@
</trans-unit>
<trans-unit id="FeFBn_3" resname="profile.2fa">
<source>profile.2fa</source>
<target state="translated">Doi factori (TOTP)</target>
<target state="translated">Doi factori (2FA)</target>
</trans-unit>
<trans-unit id="IRzPWmL" resname="login.2fa_intro">
<source>login.2fa_intro</source>

View File

@@ -1254,7 +1254,7 @@
</trans-unit>
<trans-unit id="FeFBn_3" resname="profile.2fa">
<source>profile.2fa</source>
<target state="translated">Двухфакторная (TOTP)</target>
<target state="translated">Двухфакторная (2FA)</target>
</trans-unit>
<trans-unit id="IRzPWmL" resname="login.2fa_intro">
<source>login.2fa_intro</source>

View File

@@ -1292,7 +1292,7 @@
</trans-unit>
<trans-unit id="FeFBn_3" resname="profile.2fa">
<source>profile.2fa</source>
<target state="translated">Tvåfaktor (TOTP)</target>
<target state="translated">Tvåfaktor (2FA)</target>
</trans-unit>
<trans-unit id="aGk9Aqt" resname="desktop">
<source>desktop</source>

View File

@@ -1244,7 +1244,7 @@
</trans-unit>
<trans-unit id="FeFBn_3" resname="profile.2fa">
<source>profile.2fa</source>
<target state="translated">İki Adımlı (TOTP)</target>
<target state="translated">İki Adımlı (2FA)</target>
</trans-unit>
<trans-unit id="hPY2VcD" resname="profile.2fa_intro">
<source>profile.2fa_intro</source>

View File

@@ -1180,7 +1180,7 @@
</trans-unit>
<trans-unit id="FeFBn_3" resname="profile.2fa">
<source>profile.2fa</source>
<target state="translated">Двофакторна аутентифікація (TOTP)</target>
<target state="translated">Двофакторна аутентифікація (2FA)</target>
</trans-unit>
<trans-unit id="IRzPWmL" resname="login.2fa_intro">
<source>login.2fa_intro</source>

View File

@@ -1224,7 +1224,7 @@
</trans-unit>
<trans-unit id="FeFBn_3" resname="profile.2fa">
<source>profile.2fa</source>
<target state="translated">双因素 (TOTP)</target>
<target state="translated">双因素 (2FA)</target>
</trans-unit>
<trans-unit id="IRzPWmL" resname="login.2fa_intro">
<source>login.2fa_intro</source>