version 1.14.1 (#2532)

* no back links in modal pages
* remove unused service links to bountysource and gitter
* add validation for budget and time-budget fields
* display time budget if set
* remove console log
* sanitize DDE payloads
* do not show status and name in version string
This commit is contained in:
Kevin Papst
2021-04-29 18:29:03 +02:00
committed by GitHub
parent 22af82cb15
commit dad1b8b772
74 changed files with 1770 additions and 2076 deletions

View File

@@ -80,5 +80,5 @@ class Version
* @Serializer\Groups({"Default"})
* @Serializer\Type(name="string")
*/
protected $copyright = Constants::SOFTWARE . ' - ' . Constants::VERSION . ' ' . Constants::STATUS . ' (' . Constants::NAME . ') by Kevin Papst and contributors.';
protected $copyright = Constants::SOFTWARE . ' ' . Constants::VERSION . ' by Kevin Papst and contributors.';
}

View File

@@ -105,7 +105,7 @@ final class InstallCommand extends Command
}
$io->success(
sprintf('Congratulations! Successfully installed %s version %s (%s)', Constants::SOFTWARE, Constants::VERSION, Constants::STATUS)
sprintf('Congratulations! Successfully installed %s version %s', Constants::SOFTWARE, Constants::VERSION)
);
return 0;

View File

@@ -121,7 +121,7 @@ final class UpdateCommand extends Command
if ($cacheResult !== 0) {
$io->warning(
[
sprintf('Updated %s to version %s (%s) but the cache could not be rebuilt.', Constants::SOFTWARE, Constants::VERSION, Constants::STATUS),
sprintf('Updated %s to version %s but the cache could not be rebuilt.', Constants::SOFTWARE, Constants::VERSION),
'Please run the cache commands manually:',
'bin/console cache:clear --env=' . $environment . PHP_EOL .
'bin/console cache:warmup --env=' . $environment
@@ -129,7 +129,7 @@ final class UpdateCommand extends Command
);
} else {
$io->success(
sprintf('Congratulations! Successfully updated %s to version %s (%s)', Constants::SOFTWARE, Constants::VERSION, Constants::STATUS)
sprintf('Congratulations! Successfully updated %s to version %s', Constants::SOFTWARE, Constants::VERSION)
);
}

View File

@@ -31,9 +31,10 @@ class VersionCommand extends Command
->setDescription('Receive version information')
->setHelp('This command allows you to fetch various version information about Kimai.')
->addOption('name', null, InputOption::VALUE_NONE, 'Display the major release name')
->addOption('candidate', null, InputOption::VALUE_NONE, 'Display the current version candidate (e.g. "stable" or "dev")')
->addOption('short', null, InputOption::VALUE_NONE, 'Display the version only')
->addOption('semver', null, InputOption::VALUE_NONE, 'Semantical versioning (SEMVER) compatible version string')
// @deprecated since 1.14.1
->addOption('candidate', null, InputOption::VALUE_NONE, 'DEPRECATED: Display the current version candidate (e.g. "stable" or "dev")')
->addOption('semver', null, InputOption::VALUE_NONE, 'DEPRECATED: Semantical versioning (SEMVER) compatible version string')
;
}
@@ -68,7 +69,7 @@ class VersionCommand extends Command
return 0;
}
$io->writeln(Constants::SOFTWARE . ' - ' . Constants::VERSION . ' ' . Constants::STATUS . ' (' . Constants::NAME . ') by Kevin Papst and contributors.');
$io->writeln(Constants::SOFTWARE . ' ' . Constants::VERSION . ' by Kevin Papst and contributors.');
return 0;
}

View File

@@ -17,11 +17,11 @@ class Constants
/**
* The current release version
*/
public const VERSION = '1.14';
public const VERSION = '1.14.1';
/**
* The current release: major * 10000 + minor * 100 + patch
*/
public const VERSION_ID = 11400;
public const VERSION_ID = 11401;
/**
* The current release status, either "stable" or "dev"
*/

View File

@@ -135,11 +135,12 @@ class Activity implements EntityWithMetaFields
* @ Exporter\Expose(label="label.budget")
*
* @ORM\Column(name="budget", type="float", nullable=false)
* @Assert\Range(min=0.00, max=900000000000.00)
* @Assert\NotNull()
*/
private $budget = 0.00;
/**
* The time budget in seconds, will be be zero if unconfigured.
* The time budget in seconds, will be zero if unconfigured.
*
* @var int
*
@@ -149,6 +150,7 @@ class Activity implements EntityWithMetaFields
* @ Exporter\Expose(label="label.timeBudget", type="duration")
*
* @ORM\Column(name="time_budget", type="integer", nullable=false)
* @Assert\Range(min=0, max=2145600000)
* @Assert\NotNull()
*/
private $timeBudget = 0;

View File

@@ -266,11 +266,12 @@ class Customer implements EntityWithMetaFields
* @ Exporter\Expose(label="label.budget")
*
* @ORM\Column(name="budget", type="float", nullable=false)
* @Assert\Range(min=0.00, max=900000000000.00)
* @Assert\NotNull()
*/
private $budget = 0.00;
/**
* The time budget in seconds, will be be zero if not configured.
* The time budget in seconds, will be zero if not configured.
*
* @var int
*
@@ -280,6 +281,7 @@ class Customer implements EntityWithMetaFields
* @ Exporter\Expose(label="label.timeBudget", type="duration")
*
* @ORM\Column(name="time_budget", type="integer", nullable=false)
* @Assert\Range(min=0, max=2145600000)
* @Assert\NotNull()
*/
private $timeBudget = 0;

View File

@@ -207,11 +207,12 @@ class Project implements EntityWithMetaFields
* @ Exporter\Expose(label="label.budget")
*
* @ORM\Column(name="budget", type="float", nullable=false)
* @Assert\Range(min=0.00, max=900000000000.00)
* @Assert\NotNull()
*/
private $budget = 0.00;
/**
* The time budget in seconds, will be be zero if not configured.
* The time budget in seconds, will be zero if not configured.
*
* @var int
*
@@ -221,6 +222,7 @@ class Project implements EntityWithMetaFields
* @ Exporter\Expose(label="label.timeBudget", type="duration")
*
* @ORM\Column(name="time_budget", type="integer", nullable=false)
* @Assert\Range(min=0, max=2145600000)
* @Assert\NotNull()
*/
private $timeBudget = 0;

View File

@@ -20,10 +20,6 @@ abstract class AbstractTimesheetSubscriber extends AbstractActionsSubscriber
/** @var Timesheet $timesheet */
$timesheet = $payload['timesheet'];
if (!$event->isIndexView()) {
$event->addBack($this->path($routeListing));
}
if ($timesheet->getId() !== null) {
if ($this->isGranted('edit', $timesheet)) {
$class = $event->isView('edit') ? '' : 'modal-ajax-form';

View File

@@ -20,11 +20,6 @@ class PermissionsSubscriber extends AbstractActionsSubscriber
public function onActions(PageActionsEvent $event): void
{
// the "create role" page
if (!$event->isIndexView() && $this->isGranted('role_permissions')) {
//$event->addBack($this->path('admin_user_permissions'));
}
if ($this->isGranted('role_permissions')) {
$event->addCreate($this->path('admin_user_roles'), !$event->isView('role'));
}

View File

@@ -37,10 +37,6 @@ class TagSubscriber extends AbstractActionsSubscriber
$name = $tag->getName();
}
if (!$event->isIndexView() && $this->isGranted('view_tag')) {
//$event->addBack($this->path('tags'));
}
if ($id === null) {
return;
}

View File

@@ -26,10 +26,6 @@ class TeamSubscriber extends AbstractActionsSubscriber
/** @var Team $team */
$team = $payload['team'];
if (!$event->isIndexView() && $this->isGranted('view_tag')) {
//$event->addBack($this->path('admin_team'));
}
if ($team->getId() === null) {
return;
}

View File

@@ -20,6 +20,7 @@ use App\Export\ExportItemInterface;
use App\Repository\Query\CustomerQuery;
use App\Repository\Query\TimesheetQuery;
use App\Twig\LocaleFormatExtensions;
use App\Utils\StringHelper;
use DateTime;
use PhpOffice\PhpSpreadsheet\Cell\DataType;
use PhpOffice\PhpSpreadsheet\Shared\Date;
@@ -80,6 +81,7 @@ abstract class AbstractSpreadsheetRenderer
'description' => [
'maxWidth' => 50,
'wrapText' => false,
'sanitizeDDE' => true,
],
'exported' => [],
'billable' => [],
@@ -337,15 +339,21 @@ abstract class AbstractSpreadsheetRenderer
if (isset($columns['description']) && !isset($columns['description']['render'])) {
$maxWidth = \array_key_exists('maxWidth', $columns['description']) ? \intval($columns['description']['maxWidth']) : null;
$wrapText = \array_key_exists('wrapText', $columns['description']) ? (bool) $columns['description']['wrapText'] : false;
$sanitizeText = \array_key_exists('sanitizeDDE', $columns['description']) ? (bool) $columns['description']['sanitizeDDE'] : true;
// This column has a column-only formatter to set the maximum width of a column.
// It needs to be executed once, so we use this as a flag on when to skip it.
$isColumnFormatted = false;
$columns['description']['render'] = function (Worksheet $sheet, int $row, int $column, ExportItemInterface $entity) use (&$isColumnFormatted, $maxWidth, $wrapText) {
$columns['description']['render'] = function (Worksheet $sheet, int $row, int $column, ExportItemInterface $entity) use (&$isColumnFormatted, $maxWidth, $wrapText, $sanitizeText) {
$cell = $sheet->getCellByColumnAndRow($column, $row);
$desc = $entity->getDescription();
$cell->setValueExplicit($entity->getDescription(), DataType::TYPE_STRING);
if ($sanitizeText && null !== $desc) {
$desc = StringHelper::sanitizeDDE($desc);
}
$cell->setValueExplicit($desc, DataType::TYPE_STRING);
// Apply wrap text if configured
if ($wrapText) {
@@ -355,8 +363,7 @@ abstract class AbstractSpreadsheetRenderer
// Apply max width, only needs to be once per column
if (!$isColumnFormatted) {
if (null !== $maxWidth) {
$sheet->getColumnDimensionByColumn($column)
->setWidth($maxWidth);
$sheet->getColumnDimensionByColumn($column)->setWidth($maxWidth);
}
$isColumnFormatted = true;
}

View File

@@ -11,6 +11,9 @@ namespace App\Utils;
final class StringHelper
{
// @see https://github.com/payloadbox/csv-injection-payloads
private const DDE_PAYLOADS = ['=', '-', '@', '+', "\t", "\n", "\r", "\r\n"];
public static function ensureMaxLength(?string $string, int $length): ?string
{
if (null === $string) {
@@ -23,4 +26,22 @@ final class StringHelper
return $string;
}
public static function sanitizeDDE(string $text): string
{
$sanitize = false;
if (\in_array($text[0], self::DDE_PAYLOADS)) {
$sanitize = true;
} elseif (stripos($text, 'DDE') !== false) {
$sanitize = true;
}
if ($sanitize) {
// trying to prevent fucking Microsoft "feature" DDE
$text = "' " . $text;
}
return $text;
}
}