prepare release 1.7 (#1388)

This commit is contained in:
Kevin Papst
2020-01-19 21:49:04 +01:00
committed by GitHub
parent 352fac26f3
commit 9eff89963c
44 changed files with 225 additions and 57 deletions

View File

@@ -21,7 +21,7 @@ class Constants
/**
* The current release status, either "stable" or "dev"
*/
public const STATUS = 'dev';
public const STATUS = 'stable';
/**
* The software name
*/

View File

@@ -55,6 +55,29 @@ class DoctorController extends AbstractController
$this->projectDirectory = $projectDirectory;
}
/**
* @Route(path="/flush-log", name="doctor_flush_log", methods={"GET"})
* @Security("is_granted('system_configuration')")
*/
public function deleteLogfileAction(): Response
{
$logfile = $this->getLogFilename();
if (file_exists($logfile)) {
if (!is_writable($logfile)) {
$this->flashError('action.delete.error', ['%reason%' => 'Logfile cannot be written']);
} else {
if (false === file_put_contents($logfile, '')) {
$this->flashError('action.delete.error', ['%reason%' => 'Failed writing to logfile']);
} else {
$this->flashSuccess('action.delete.success');
}
}
}
return $this->redirectToRoute('doctor');
}
/**
* @Route(path="", name="doctor", methods={"GET"})
*/
@@ -62,6 +85,8 @@ class DoctorController extends AbstractController
{
$logLines = 100;
$canDeleteLogfile = $this->isGranted('system_configuration') && is_writable($this->getLogFilename());
return $this->render('doctor/index.html.twig', array_merge(
[
'modules' => get_loaded_extensions(),
@@ -70,6 +95,7 @@ class DoctorController extends AbstractController
'settings' => $this->getIniSettings(),
'extensions' => $this->getLoadedExtensions(),
'directories' => $this->getFilePermissions(),
'log_delete' => $canDeleteLogfile,
'logs' => $this->getLog(),
'logLines' => $logLines,
'logSize' => $this->getLogSize(),
@@ -106,20 +132,31 @@ class DoctorController extends AbstractController
return filesize($logfile);
}
private function getLog(int $lines = 100)
private function getLogFilename(): string
{
// why is this check here ???
if (!in_array(getenv('APP_ENV'), ['test', 'dev', 'prod'])) {
return [
'Unsupported log environment'
];
throw new \RuntimeException('Unsupported log environment');
}
$logfileName = 'var/log/' . getenv('APP_ENV') . '.log';
$logfile = $this->projectDirectory . '/' . $logfileName;
return $this->projectDirectory . '/' . $logfileName;
}
private function getLog(int $lines = 100)
{
try {
$logfile = $this->getLogFilename();
} catch (\Exception $ex) {
return [
$ex->getMessage()
];
}
if (!file_exists($logfile)) {
return [
'Could not find logfile: ' . $logfileName
'Empty or missing logfile'
];
}

View File

@@ -64,6 +64,7 @@ class ThemeOptionsSubscriber implements EventSubscriberInterface
/** @var User $user */
$user = $this->storage->getToken()->getUser();
/** @var UserPreference $ref */
foreach ($user->getPreferences() as $ref) {
$name = $ref->getName();
switch ($name) {
@@ -73,6 +74,16 @@ class ThemeOptionsSubscriber implements EventSubscriberInterface
}
break;
case 'theme.layout':
if ($ref->getValue() === 'boxed') {
$this->helper->setOption('boxed_layout', true);
$this->helper->setOption('fixed_layout', false);
} elseif ($ref->getValue() === 'fixed') {
$this->helper->setOption('boxed_layout', false);
$this->helper->setOption('fixed_layout', true);
}
break;
case 'theme.collapsed_sidebar':
$this->helper->setOption('collapsed_sidebar', $ref->getValue());
break;

View File

@@ -18,6 +18,7 @@ use App\Form\Type\CalendarViewType;
use App\Form\Type\InitialViewType;
use App\Form\Type\LanguageType;
use App\Form\Type\SkinType;
use App\Form\Type\ThemeLayoutType;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
@@ -128,6 +129,12 @@ class UserPreferenceSubscriber implements EventSubscriberInterface
->setOrder(400)
->setType(SkinType::class),
(new UserPreference())
->setName('theme.layout')
->setValue('fixed')
->setOrder(450)
->setType(ThemeLayoutType::class),
(new UserPreference())
->setName('theme.collapsed_sidebar')
->setValue(false)

View File

@@ -0,0 +1,42 @@
<?php
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\OptionsResolver\OptionsResolver;
/**
* Custom form field type to select the theme layout.
*/
class ThemeLayoutType extends AbstractType
{
/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'required' => true,
'choices' => [
'fixed' => 'fixed',
'boxed' => 'boxed',
]
]);
}
/**
* {@inheritdoc}
*/
public function getParent()
{
return ChoiceType::class;
}
}

View File

@@ -37,8 +37,8 @@ class UserCreateType extends UserEditType
->add('plainPassword', RepeatedType::class, [
'required' => true,
'type' => PasswordType::class,
'first_options' => ['label' => 'label.password'],
'second_options' => ['label' => 'label.password_repeat'],
'first_options' => ['label' => 'label.password', 'attr' => ['autocomplete' => 'new-password']],
'second_options' => ['label' => 'label.password_repeat', 'attr' => ['autocomplete' => 'new-password']],
]);
parent::buildForm($builder, $options);

View File

@@ -29,8 +29,8 @@ class UserPasswordType extends AbstractType
$builder
->add('plainPassword', RepeatedType::class, [
'type' => PasswordType::class,
'first_options' => ['label' => 'label.password'],
'second_options' => ['label' => 'label.password_repeat'],
'first_options' => ['label' => 'label.password', 'attr' => ['autocomplete' => 'new-password']],
'second_options' => ['label' => 'label.password_repeat', 'attr' => ['autocomplete' => 'new-password']],
])
;
}

View File

@@ -304,6 +304,8 @@ final class InvoiceModel
'project.name' => $project->getName(),
'project.comment' => $project->getComment(),
'project.order_number' => $project->getOrderNumber(),
'project.start_date' => null !== $project->getStart() ? $formatter->getFormattedDateTime($project->getStart()) : '',
'project.end_date' => null !== $project->getEnd() ? $formatter->getFormattedDateTime($project->getEnd()) : '',
'project.order_date' => null !== $project->getOrderDate() ? $formatter->getFormattedDateTime($project->getOrderDate()) : '',
'project.fixed_rate' => $formatter->getFormattedMoney($project->getFixedRate(), $currency),
'project.fixed_rate_nc' => $formatter->getFormattedMoney($project->getFixedRate(), null),
@@ -408,14 +410,47 @@ final class InvoiceModel
'entry.user_name' => $user->getUsername(),
'entry.user_title' => $user->getTitle(),
'entry.user_alias' => $user->getAlias(),
'entry.activity' => $activity->getName(),
'entry.activity_id' => $activity->getId(),
'entry.project' => $project->getName(),
'entry.project_id' => $project->getId(),
'entry.customer' => $customer->getName(),
'entry.customer_id' => $customer->getId(),
];
if (null !== $activity) {
$values = array_merge($values, [
'entry.activity' => $activity->getName(),
'entry.activity_id' => $activity->getId(),
]);
foreach ($activity->getVisibleMetaFields() as $metaField) {
$values = array_merge($values, [
'entry.activity.meta.' . $metaField->getName() => $metaField->getValue(),
]);
}
}
if (null !== $project) {
$values = array_merge($values, [
'entry.project' => $project->getName(),
'entry.project_id' => $project->getId(),
]);
foreach ($project->getVisibleMetaFields() as $metaField) {
$values = array_merge($values, [
'entry.project.meta.' . $metaField->getName() => $metaField->getValue(),
]);
}
}
if (null !== $customer) {
$values = array_merge($values, [
'entry.customer' => $customer->getName(),
'entry.customer_id' => $customer->getId(),
]);
foreach ($customer->getVisibleMetaFields() as $metaField) {
$values = array_merge($values, [
'entry.customer.meta.' . $metaField->getName() => $metaField->getValue(),
]);
}
}
foreach ($invoiceItem->getAdditionalFields() as $name => $value) {
$values = array_merge($values, [
'entry.meta.' . $name => $value,