configure first day of week for calendar and date-picker (#1952)
This commit is contained in:
@@ -50,4 +50,12 @@ export default class KimaiPlugin {
|
|||||||
return this._core;
|
return this._core;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} name
|
||||||
|
* @returns {(string|number|boolean)}
|
||||||
|
*/
|
||||||
|
getConfiguration(name) {
|
||||||
|
return this.getContainer().getConfiguration().get(name);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,14 +11,25 @@
|
|||||||
|
|
||||||
export default class KimaiTranslation {
|
export default class KimaiTranslation {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Array<string, string>} translations
|
||||||
|
*/
|
||||||
constructor(translations) {
|
constructor(translations) {
|
||||||
this._translations = translations;
|
this._translations = translations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} name
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
get(name) {
|
get(name) {
|
||||||
return this._translations[name];
|
return this._translations[name];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} name
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
has(name) {
|
has(name) {
|
||||||
return name in this._translations;
|
return name in this._translations;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export default class KimaiAutocomplete extends KimaiPlugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
this.minChars = this.getContainer().getConfiguration().get('autoComplete');
|
this.minChars = this.getConfiguration('autoComplete');
|
||||||
}
|
}
|
||||||
|
|
||||||
getId() {
|
getId() {
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ export default class KimaiDatatable extends KimaiPlugin {
|
|||||||
document.addEventListener(eventName, handle);
|
document.addEventListener(eventName, handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.getContainer().getConfiguration().get('autoReloadDatatable')) {
|
if (this.getConfiguration('autoReloadDatatable')) {
|
||||||
document.addEventListener('toolbar-change', handle);
|
document.addEventListener('toolbar-change', handle);
|
||||||
} else {
|
} else {
|
||||||
document.addEventListener('pagination-change', handle);
|
document.addEventListener('pagination-change', handle);
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ export default class KimaiDatePicker extends KimaiPlugin {
|
|||||||
activateDatePicker(selector) {
|
activateDatePicker(selector) {
|
||||||
const TRANSLATE = this.getContainer().getTranslation();
|
const TRANSLATE = this.getContainer().getTranslation();
|
||||||
const DATE_UTILS = this.getContainer().getPlugin('date');
|
const DATE_UTILS = this.getContainer().getPlugin('date');
|
||||||
|
const firstDow = this.getConfiguration('first_dow_iso');
|
||||||
|
|
||||||
jQuery(selector + ' ' + this.selector).each(function(index) {
|
jQuery(selector + ' ' + this.selector).each(function(index) {
|
||||||
let localeFormat = jQuery(this).data('format');
|
let localeFormat = jQuery(this).data('format');
|
||||||
@@ -36,7 +37,7 @@ export default class KimaiDatePicker extends KimaiPlugin {
|
|||||||
autoUpdateInput: false,
|
autoUpdateInput: false,
|
||||||
locale: {
|
locale: {
|
||||||
format: localeFormat,
|
format: localeFormat,
|
||||||
firstDay: moment().startOf('week').format('d'),
|
firstDay: firstDow,
|
||||||
applyLabel: TRANSLATE.get('confirm'),
|
applyLabel: TRANSLATE.get('confirm'),
|
||||||
cancelLabel: TRANSLATE.get('cancel'),
|
cancelLabel: TRANSLATE.get('cancel'),
|
||||||
customRangeLabel: TRANSLATE.get('customRange'),
|
customRangeLabel: TRANSLATE.get('customRange'),
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ export default class KimaiDateRangePicker extends KimaiPlugin {
|
|||||||
activateDateRangePicker(selector) {
|
activateDateRangePicker(selector) {
|
||||||
const TRANSLATE = this.getContainer().getTranslation();
|
const TRANSLATE = this.getContainer().getTranslation();
|
||||||
const DATE_UTILS = this.getContainer().getPlugin('date');
|
const DATE_UTILS = this.getContainer().getPlugin('date');
|
||||||
|
const firstDow = this.getConfiguration('first_dow_iso');
|
||||||
|
|
||||||
jQuery(selector + ' ' + this.selector).each(function(index) {
|
jQuery(selector + ' ' + this.selector).each(function(index) {
|
||||||
let localeFormat = jQuery(this).data('format');
|
let localeFormat = jQuery(this).data('format');
|
||||||
@@ -50,7 +51,7 @@ export default class KimaiDateRangePicker extends KimaiPlugin {
|
|||||||
locale: {
|
locale: {
|
||||||
separator: separator,
|
separator: separator,
|
||||||
format: localeFormat,
|
format: localeFormat,
|
||||||
firstDay: moment().startOf('week').format('d'),
|
firstDay: firstDow,
|
||||||
applyLabel: TRANSLATE.get('confirm'),
|
applyLabel: TRANSLATE.get('confirm'),
|
||||||
cancelLabel: TRANSLATE.get('cancel'),
|
cancelLabel: TRANSLATE.get('cancel'),
|
||||||
customRangeLabel: TRANSLATE.get('customRange'),
|
customRangeLabel: TRANSLATE.get('customRange'),
|
||||||
|
|||||||
@@ -25,20 +25,21 @@ export default class KimaiDateTimePicker extends KimaiPlugin {
|
|||||||
|
|
||||||
activateDateTimePicker(selector) {
|
activateDateTimePicker(selector) {
|
||||||
const TRANSLATE = this.getContainer().getTranslation();
|
const TRANSLATE = this.getContainer().getTranslation();
|
||||||
const CONFIG = this.getContainer().getConfiguration();
|
|
||||||
const DATE_UTILS = this.getContainer().getPlugin('date');
|
const DATE_UTILS = this.getContainer().getPlugin('date');
|
||||||
|
const firstDow = this.getConfiguration('first_dow_iso');
|
||||||
|
const is24hours = this.getConfiguration('twentyFourHours');
|
||||||
|
|
||||||
jQuery(selector + ' ' + this.selector).each(function(index) {
|
jQuery(selector + ' ' + this.selector).each(function(index) {
|
||||||
let localeFormat = jQuery(this).data('format');
|
let localeFormat = jQuery(this).data('format');
|
||||||
jQuery(this).daterangepicker({
|
jQuery(this).daterangepicker({
|
||||||
singleDatePicker: true,
|
singleDatePicker: true,
|
||||||
timePicker: true,
|
timePicker: true,
|
||||||
timePicker24Hour: CONFIG.get('twentyFourHours'),
|
timePicker24Hour: is24hours,
|
||||||
showDropdowns: true,
|
showDropdowns: true,
|
||||||
autoUpdateInput: false,
|
autoUpdateInput: false,
|
||||||
locale: {
|
locale: {
|
||||||
format: localeFormat,
|
format: localeFormat,
|
||||||
firstDay: moment().startOf('week').format('d'),
|
firstDay: firstDow,
|
||||||
applyLabel: TRANSLATE.get('confirm'),
|
applyLabel: TRANSLATE.get('confirm'),
|
||||||
cancelLabel: TRANSLATE.get('cancel'),
|
cancelLabel: TRANSLATE.get('cancel'),
|
||||||
customRangeLabel: TRANSLATE.get('customRange'),
|
customRangeLabel: TRANSLATE.get('customRange'),
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ export default class KimaiFormSelect extends KimaiPlugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
options = {...options, ...{
|
options = {...options, ...{
|
||||||
language: this.getContainer().getConfiguration().get('locale').replace('_', '-'),
|
language: this.getConfiguration('locale').replace('_', '-'),
|
||||||
theme: "bootstrap",
|
theme: "bootstrap",
|
||||||
matcher: matcher
|
matcher: matcher
|
||||||
}};
|
}};
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export default class KimaiThemeInitializer extends KimaiPlugin {
|
|||||||
* redirect access denied / session timeouts to login page
|
* redirect access denied / session timeouts to login page
|
||||||
*/
|
*/
|
||||||
registerGlobalAjaxErrorHandler() {
|
registerGlobalAjaxErrorHandler() {
|
||||||
const loginUrl = this.getContainer().getConfiguration().get('login');
|
const loginUrl = this.getConfiguration('login');
|
||||||
const alert = this.getContainer().getPlugin('alert');
|
const alert = this.getContainer().getPlugin('alert');
|
||||||
const translation = this.getContainer().getTranslation().get('login.required');
|
const translation = this.getContainer().getTranslation().get('login.required');
|
||||||
jQuery(document).ajaxError(function(event, jqxhr, settings, thrownError) {
|
jQuery(document).ajaxError(function(event, jqxhr, settings, thrownError) {
|
||||||
|
|||||||
1
public/build/app.27fcc913.js
Normal file
1
public/build/app.27fcc913.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -5,7 +5,7 @@
|
|||||||
"build/runtime.098eaae1.js",
|
"build/runtime.098eaae1.js",
|
||||||
"build/0.79dbdbb9.js",
|
"build/0.79dbdbb9.js",
|
||||||
"build/1.512c747c.js",
|
"build/1.512c747c.js",
|
||||||
"build/app.cafc411b.js"
|
"build/app.27fcc913.js"
|
||||||
],
|
],
|
||||||
"css": [
|
"css": [
|
||||||
"build/app.554717a4.css"
|
"build/app.554717a4.css"
|
||||||
@@ -53,7 +53,7 @@
|
|||||||
"build/runtime.098eaae1.js": "sha384-xNNrNinl64G3nCUrIskgSjU0mUXXCB9lj6XCSInBTwxSKXk8uTMafnLHtdWdIGtd",
|
"build/runtime.098eaae1.js": "sha384-xNNrNinl64G3nCUrIskgSjU0mUXXCB9lj6XCSInBTwxSKXk8uTMafnLHtdWdIGtd",
|
||||||
"build/0.79dbdbb9.js": "sha384-U2Ao0ORAZ8PCeDmyRsqQFET3hc7pfUBimq0PrqFdG4/s0Bdi+qBj4TJK3o70bCd5",
|
"build/0.79dbdbb9.js": "sha384-U2Ao0ORAZ8PCeDmyRsqQFET3hc7pfUBimq0PrqFdG4/s0Bdi+qBj4TJK3o70bCd5",
|
||||||
"build/1.512c747c.js": "sha384-SneGUmydV7WMkwkQp+4ql0TCXq1Or/1kQeIObbiMjx9QFM0DruGsytf77g4OxrGI",
|
"build/1.512c747c.js": "sha384-SneGUmydV7WMkwkQp+4ql0TCXq1Or/1kQeIObbiMjx9QFM0DruGsytf77g4OxrGI",
|
||||||
"build/app.cafc411b.js": "sha384-sc27ZMn3ySrIZOjZ6G5bkSbG5yptYP4Cck2n5p2fOid056W/QaQZN2U4vDt0EipW",
|
"build/app.27fcc913.js": "sha384-+2s6xRVSRzLkvfRiACKfxZ1W45frODLcpIITzEJjROOjyM2EtkKMe2FE4HHZkG1m",
|
||||||
"build/app.554717a4.css": "sha384-dITltEp59C3NH6o/8ZcpZJgHPLx+/I5t9xDQVsbQn7YoJ0GEV82gDDqe64ebjU29",
|
"build/app.554717a4.css": "sha384-dITltEp59C3NH6o/8ZcpZJgHPLx+/I5t9xDQVsbQn7YoJ0GEV82gDDqe64ebjU29",
|
||||||
"build/invoice.74279541.js": "sha384-2BXic5Sgorf2tXai6zSAN4wLY2dbg06L03/xMKW6itMcszvtnRArKzfBh6DNcF3f",
|
"build/invoice.74279541.js": "sha384-2BXic5Sgorf2tXai6zSAN4wLY2dbg06L03/xMKW6itMcszvtnRArKzfBh6DNcF3f",
|
||||||
"build/invoice.13d8ef4e.css": "sha384-B6RN/wZJToSBCZk2JeLokIqWEhbh+Eb9arYbt9dM+YoC2Z6PnCeTwTqSGyexWWJh",
|
"build/invoice.13d8ef4e.css": "sha384-B6RN/wZJToSBCZk2JeLokIqWEhbh+Eb9arYbt9dM+YoC2Z6PnCeTwTqSGyexWWJh",
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
"build/1.512c747c.js": "build/1.512c747c.js",
|
"build/1.512c747c.js": "build/1.512c747c.js",
|
||||||
"build/2.7ab75d0a.js": "build/2.7ab75d0a.js",
|
"build/2.7ab75d0a.js": "build/2.7ab75d0a.js",
|
||||||
"build/app.css": "build/app.554717a4.css",
|
"build/app.css": "build/app.554717a4.css",
|
||||||
"build/app.js": "build/app.cafc411b.js",
|
"build/app.js": "build/app.27fcc913.js",
|
||||||
"build/calendar.css": "build/calendar.1408f57e.css",
|
"build/calendar.css": "build/calendar.1408f57e.css",
|
||||||
"build/calendar.js": "build/calendar.0e49d4da.js",
|
"build/calendar.js": "build/calendar.0e49d4da.js",
|
||||||
"build/chart.js": "build/chart.34d60a88.js",
|
"build/chart.js": "build/chart.34d60a88.js",
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ class HomepageController extends AbstractController
|
|||||||
/** @var User $user */
|
/** @var User $user */
|
||||||
$user = $this->getUser();
|
$user = $this->getUser();
|
||||||
$userRoute = $user->getPreferenceValue('login.initial_view', InitialViewType::DEFAULT_VIEW);
|
$userRoute = $user->getPreferenceValue('login.initial_view', InitialViewType::DEFAULT_VIEW);
|
||||||
$userLanguage = $user->getLocale();
|
$userLanguage = $user->getLanguage();
|
||||||
$requestLanguage = $request->getLocale();
|
$requestLanguage = $request->getLocale();
|
||||||
|
|
||||||
if (empty($requestLanguage)) {
|
if (empty($requestLanguage)) {
|
||||||
|
|||||||
@@ -271,7 +271,7 @@ class ProfileController extends AbstractController
|
|||||||
/** @var UserPreference $pref */
|
/** @var UserPreference $pref */
|
||||||
foreach ($iterator as $pref) {
|
foreach ($iterator as $pref) {
|
||||||
if ($pref->isEnabled()) {
|
if ($pref->isEnabled()) {
|
||||||
$sections[$pref->getSection()][] = $pref->getName();
|
$sections[$pref->getSection()] = $pref->getSection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
namespace App\Entity;
|
namespace App\Entity;
|
||||||
|
|
||||||
|
use App\Constants;
|
||||||
use App\Export\Annotation as Exporter;
|
use App\Export\Annotation as Exporter;
|
||||||
use App\Utils\StringHelper;
|
use App\Utils\StringHelper;
|
||||||
use Doctrine\Common\Collections\ArrayCollection;
|
use Doctrine\Common\Collections\ArrayCollection;
|
||||||
@@ -69,7 +70,8 @@ class User extends BaseUser implements UserInterface
|
|||||||
public const ROLE_SUPER_ADMIN = 'ROLE_SUPER_ADMIN';
|
public const ROLE_SUPER_ADMIN = 'ROLE_SUPER_ADMIN';
|
||||||
|
|
||||||
public const DEFAULT_ROLE = self::ROLE_USER;
|
public const DEFAULT_ROLE = self::ROLE_USER;
|
||||||
public const DEFAULT_LANGUAGE = 'en';
|
public const DEFAULT_LANGUAGE = Constants::DEFAULT_LOCALE;
|
||||||
|
public const DEFAULT_FIRST_WEEKDAY = 'monday';
|
||||||
|
|
||||||
public const AUTH_INTERNAL = 'kimai';
|
public const AUTH_INTERNAL = 'kimai';
|
||||||
public const AUTH_LDAP = 'ldap';
|
public const AUTH_LDAP = 'ldap';
|
||||||
@@ -367,6 +369,11 @@ class User extends BaseUser implements UserInterface
|
|||||||
$this->setPreferenceValue(UserPreference::LOCALE, $language);
|
$this->setPreferenceValue(UserPreference::LOCALE, $language);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getFirstDayOfWeek(): string
|
||||||
|
{
|
||||||
|
return $this->getPreferenceValue(UserPreference::FIRST_WEEKDAY, User::DEFAULT_FIRST_WEEKDAY);
|
||||||
|
}
|
||||||
|
|
||||||
public function setTimezone(?string $timezone)
|
public function setTimezone(?string $timezone)
|
||||||
{
|
{
|
||||||
if ($timezone === null) {
|
if ($timezone === null) {
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ class UserPreference
|
|||||||
public const SKIN = 'skin';
|
public const SKIN = 'skin';
|
||||||
public const LOCALE = 'language';
|
public const LOCALE = 'language';
|
||||||
public const TIMEZONE = 'timezone';
|
public const TIMEZONE = 'timezone';
|
||||||
|
public const FIRST_WEEKDAY = 'first_weekday';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var int
|
* @var int
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ use App\Entity\UserPreference;
|
|||||||
use App\Event\PrepareUserEvent;
|
use App\Event\PrepareUserEvent;
|
||||||
use App\Event\UserPreferenceEvent;
|
use App\Event\UserPreferenceEvent;
|
||||||
use App\Form\Type\CalendarViewType;
|
use App\Form\Type\CalendarViewType;
|
||||||
|
use App\Form\Type\FirstWeekDayType;
|
||||||
use App\Form\Type\InitialViewType;
|
use App\Form\Type\InitialViewType;
|
||||||
use App\Form\Type\LanguageType;
|
use App\Form\Type\LanguageType;
|
||||||
use App\Form\Type\SkinType;
|
use App\Form\Type\SkinType;
|
||||||
@@ -126,10 +127,17 @@ class UserPreferenceSubscriber implements EventSubscriberInterface
|
|||||||
(new UserPreference())
|
(new UserPreference())
|
||||||
->setName(UserPreference::LOCALE)
|
->setName(UserPreference::LOCALE)
|
||||||
->setValue($this->getDefaultLanguage())
|
->setValue($this->getDefaultLanguage())
|
||||||
->setOrder(300)
|
->setOrder(250)
|
||||||
->setSection('locale')
|
->setSection('locale')
|
||||||
->setType(LanguageType::class),
|
->setType(LanguageType::class),
|
||||||
|
|
||||||
|
(new UserPreference())
|
||||||
|
->setName(UserPreference::FIRST_WEEKDAY)
|
||||||
|
->setValue(User::DEFAULT_FIRST_WEEKDAY)
|
||||||
|
->setOrder(300)
|
||||||
|
->setSection('locale')
|
||||||
|
->setType(FirstWeekDayType::class),
|
||||||
|
|
||||||
(new UserPreference())
|
(new UserPreference())
|
||||||
->setName(UserPreference::SKIN)
|
->setName(UserPreference::SKIN)
|
||||||
->setValue($this->getDefaultTheme())
|
->setValue($this->getDefaultTheme())
|
||||||
|
|||||||
46
src/Form/Type/FirstWeekDayType.php
Normal file
46
src/Form/Type/FirstWeekDayType.php
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<?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 first of the week.
|
||||||
|
*/
|
||||||
|
class FirstWeekDayType extends AbstractType
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function configureOptions(OptionsResolver $resolver)
|
||||||
|
{
|
||||||
|
$choices = [
|
||||||
|
'Monday' => 'monday',
|
||||||
|
'Sunday' => 'sunday'
|
||||||
|
];
|
||||||
|
|
||||||
|
$resolver->setDefaults([
|
||||||
|
'multiple' => false,
|
||||||
|
'choices' => $choices,
|
||||||
|
'label' => 'first_weekday',
|
||||||
|
'translation_domain' => 'system-configuration'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getParent()
|
||||||
|
{
|
||||||
|
return ChoiceType::class;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -42,7 +42,7 @@ final class UserMails implements FOSMailerInterface
|
|||||||
public function sendConfirmationEmailMessage(UserInterface $user)
|
public function sendConfirmationEmailMessage(UserInterface $user)
|
||||||
{
|
{
|
||||||
$username = $user->getUsername();
|
$username = $user->getUsername();
|
||||||
$language = 'en';
|
$language = User::DEFAULT_LANGUAGE;
|
||||||
|
|
||||||
if ($user instanceof User) {
|
if ($user instanceof User) {
|
||||||
$username = $user->getDisplayName();
|
$username = $user->getDisplayName();
|
||||||
@@ -70,7 +70,7 @@ final class UserMails implements FOSMailerInterface
|
|||||||
public function sendResettingEmailMessage(UserInterface $user)
|
public function sendResettingEmailMessage(UserInterface $user)
|
||||||
{
|
{
|
||||||
$username = $user->getUsername();
|
$username = $user->getUsername();
|
||||||
$language = 'en';
|
$language = User::DEFAULT_LANGUAGE;
|
||||||
|
|
||||||
if ($user instanceof User) {
|
if ($user instanceof User) {
|
||||||
$username = $user->getDisplayName();
|
$username = $user->getDisplayName();
|
||||||
|
|||||||
@@ -42,9 +42,24 @@ class Extensions extends AbstractExtension
|
|||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
new TwigFunction('class_name', [$this, 'getClassName']),
|
new TwigFunction('class_name', [$this, 'getClassName']),
|
||||||
|
new TwigFunction('iso_day_by_name', [$this, 'getIsoDayByName']),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getIsoDayByName(string $weekDay): int
|
||||||
|
{
|
||||||
|
$key = array_search(
|
||||||
|
strtolower($weekDay),
|
||||||
|
['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday']
|
||||||
|
);
|
||||||
|
|
||||||
|
if (false === $key) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ++$key;
|
||||||
|
}
|
||||||
|
|
||||||
public function color(EntityWithMetaFields $entity): ?string
|
public function color(EntityWithMetaFields $entity): ?string
|
||||||
{
|
{
|
||||||
if ($entity instanceof Activity) {
|
if ($entity instanceof Activity) {
|
||||||
@@ -77,7 +92,7 @@ class Extensions extends AbstractExtension
|
|||||||
* @param object $object
|
* @param object $object
|
||||||
* @return null|string
|
* @return null|string
|
||||||
*/
|
*/
|
||||||
public function getClassName($object)
|
public function getClassName($object): ?string
|
||||||
{
|
{
|
||||||
if (!\is_object($object)) {
|
if (!\is_object($object)) {
|
||||||
return null;
|
return null;
|
||||||
@@ -107,11 +122,7 @@ class Extensions extends AbstractExtension
|
|||||||
return implode(PHP_EOL, $parts);
|
return implode(PHP_EOL, $parts);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function documentationLink(?string $url = ''): string
|
||||||
* @param string $url
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function documentationLink($url = '')
|
|
||||||
{
|
{
|
||||||
return Constants::HOMEPAGE . '/documentation/' . $url;
|
return Constants::HOMEPAGE . '/documentation/' . $url;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -203,6 +203,7 @@
|
|||||||
{
|
{
|
||||||
login: '{{ path('fos_user_security_login') }}',
|
login: '{{ path('fos_user_security_login') }}',
|
||||||
locale: '{{ app.request.locale }}',
|
locale: '{{ app.request.locale }}',
|
||||||
|
first_dow_iso: {{ iso_day_by_name(app.user.firstDayOfWeek) }},
|
||||||
twentyFourHours: {{ 'true'|hour24('false') }},
|
twentyFourHours: {{ 'true'|hour24('false') }},
|
||||||
autoReloadDatatable: {% if theme_config('auto_reload_datatable') %}true{% else %}false{% endif %},
|
autoReloadDatatable: {% if theme_config('auto_reload_datatable') %}true{% else %}false{% endif %},
|
||||||
autoComplete: {{ theme_config('autocomplete_chars') }}
|
autoComplete: {{ theme_config('autocomplete_chars') }}
|
||||||
|
|||||||
@@ -57,12 +57,45 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
document.addEventListener('kimai.initialized', function(event) {
|
document.addEventListener('kimai.initialized', function(event) {
|
||||||
var API = event.detail.kimai.getPlugin('api');
|
var kimai = event.detail.kimai;
|
||||||
|
var API = kimai.getPlugin('api');
|
||||||
$('#timesheet_calendar').fullCalendar({
|
$('#timesheet_calendar').fullCalendar({
|
||||||
defaultView: '{{ app.user.getPreferenceValue('calendar.initial_view') }}',
|
defaultView: '{{ app.user.getPreferenceValue('calendar.initial_view') }}',
|
||||||
{% if not google.apiKey is null %}
|
{% if not google.apiKey is null %}
|
||||||
googleCalendarApiKey: '{{ google.apiKey }}',
|
googleCalendarApiKey: '{{ google.apiKey }}',
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
header : {
|
||||||
|
left : 'prev,next today',
|
||||||
|
center: 'title',
|
||||||
|
right : 'month,agendaWeek,agendaDay'
|
||||||
|
},
|
||||||
|
eventLimit: {{ config.dayLimit }},
|
||||||
|
weekNumbers: {% if config.showWeekNumbers %}true{% else %}false{% endif %},
|
||||||
|
allDaySlot: false,
|
||||||
|
navLinks: true,
|
||||||
|
locale: kimai.getConfiguration().get('locale').replace('_', '-').toLowerCase(),
|
||||||
|
firstDay: kimai.getConfiguration().get('first_dow_iso'),
|
||||||
|
/*
|
||||||
|
// TODO calculate height properly instead of relying on the default aspectRatio
|
||||||
|
height: function() {
|
||||||
|
return 'parent';
|
||||||
|
},
|
||||||
|
*/
|
||||||
|
height: 'auto',
|
||||||
|
nowIndicator: true,
|
||||||
|
now: '{{ now|date_format('c') }}',
|
||||||
|
timezone: '{{ app.user.preferenceValue("timezone") }}',
|
||||||
|
weekends: {% if config.showWeekends %}true{% else %}false{% endif %},
|
||||||
|
businessHours: {
|
||||||
|
dow: [{{ config.businessDays|join(',') }}],
|
||||||
|
start: '{{ config.businessTimeBegin }}',
|
||||||
|
end: '{{ config.businessTimeEnd }}'
|
||||||
|
},
|
||||||
|
slotDuration: '{{ config.slotDuration }}',
|
||||||
|
slotLabelInterval: '{{ config.slotDuration }}',
|
||||||
|
minTime: '{{ config.timeframeBegin }}',
|
||||||
|
maxTime: '{{ config.timeframeEnd }}',
|
||||||
|
defaultTimedEventDuration: '00:30',
|
||||||
eventSources: [
|
eventSources: [
|
||||||
{
|
{
|
||||||
events: function(start, end, timezone, callback) {
|
events: function(start, end, timezone, callback) {
|
||||||
@@ -107,32 +140,6 @@
|
|||||||
}
|
}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
],
|
],
|
||||||
header : {
|
|
||||||
left : 'prev,next today',
|
|
||||||
center: 'title',
|
|
||||||
right : 'month,agendaWeek,agendaDay'
|
|
||||||
},
|
|
||||||
eventLimit: {{ config.dayLimit }},
|
|
||||||
weekNumbers: {% if config.showWeekNumbers %}true{% else %}false{% endif %},
|
|
||||||
allDaySlot: false,
|
|
||||||
navLinks: true,
|
|
||||||
locale: '{{ app.request.locale|replace({'_': '-'})|lower }}',
|
|
||||||
/*
|
|
||||||
// TODO calculate height properly instead of relying on the default aspectRatio
|
|
||||||
height: function() {
|
|
||||||
return 'parent';
|
|
||||||
},
|
|
||||||
*/
|
|
||||||
height: 'auto',
|
|
||||||
nowIndicator: true,
|
|
||||||
now: '{{ now|date_format('c') }}',
|
|
||||||
timezone: '{{ app.user.preferenceValue("timezone") }}',
|
|
||||||
weekends: {% if config.showWeekends %}true{% else %}false{% endif %},
|
|
||||||
businessHours: {
|
|
||||||
dow: [{{ config.businessDays|join(',') }}],
|
|
||||||
start: '{{ config.businessTimeBegin }}',
|
|
||||||
end: '{{ config.businessTimeEnd }}'
|
|
||||||
},
|
|
||||||
eventRender: function(eventObj, $el) {
|
eventRender: function(eventObj, $el) {
|
||||||
// don't show popover for google calendar
|
// don't show popover for google calendar
|
||||||
if (eventObj.source.ajaxSettings !== undefined) {
|
if (eventObj.source.ajaxSettings !== undefined) {
|
||||||
@@ -214,11 +221,6 @@
|
|||||||
eventResize: changeHandler,
|
eventResize: changeHandler,
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
slotDuration: '{{ config.slotDuration }}',
|
|
||||||
slotLabelInterval: '{{ config.slotDuration }}',
|
|
||||||
minTime: '{{ config.timeframeBegin }}',
|
|
||||||
maxTime: '{{ config.timeframeEnd }}',
|
|
||||||
defaultTimedEventDuration: '00:30'
|
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -2,18 +2,15 @@
|
|||||||
|
|
||||||
{% block main %}
|
{% block main %}
|
||||||
{{ form_start(form) }}
|
{{ form_start(form) }}
|
||||||
{% for section, entries in sections %}
|
{% for section in sections %}
|
||||||
{% embed '@AdminLTE/Widgets/box-widget.html.twig' %}
|
{% embed '@AdminLTE/Widgets/box-widget.html.twig' %}
|
||||||
{% block box_body %}
|
{% block box_body %}
|
||||||
{% for pref in form.children.preferences %}
|
{% for pref in form.children.preferences|filter(pref => pref.vars.data.section == section)|sort((a, b) => a.vars.data.order <=> b.vars.data.order) %}
|
||||||
{% if pref.vars.data.name in entries %}
|
|
||||||
{{ form_row(pref) }}
|
{{ form_row(pref) }}
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% endembed %}
|
{% endembed %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{{ form_widget(form) }}
|
|
||||||
<input type="submit" value="{{ 'action.save'|trans }}" class="btn btn-primary" />
|
<input type="submit" value="{{ 'action.save'|trans }}" class="btn btn-primary" />
|
||||||
{{ form_end(form) }}
|
{{ form_end(form) }}
|
||||||
|
|
||||||
|
|||||||
@@ -444,7 +444,8 @@ class ProfileControllerTest extends ControllerBaseTest
|
|||||||
1 => ['name' => UserPreference::INTERNAL_RATE, 'value' => 19.54],
|
1 => ['name' => UserPreference::INTERNAL_RATE, 'value' => 19.54],
|
||||||
2 => ['name' => UserPreference::TIMEZONE, 'value' => 'America/Creston'],
|
2 => ['name' => UserPreference::TIMEZONE, 'value' => 'America/Creston'],
|
||||||
3 => ['name' => UserPreference::LOCALE, 'value' => 'ar'],
|
3 => ['name' => UserPreference::LOCALE, 'value' => 'ar'],
|
||||||
4 => ['name' => UserPreference::SKIN, 'value' => 'blue'],
|
4 => ['name' => UserPreference::FIRST_WEEKDAY, 'value' => 'sunday'],
|
||||||
|
5 => ['name' => UserPreference::SKIN, 'value' => 'blue'],
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
]);
|
]);
|
||||||
@@ -467,5 +468,7 @@ class ProfileControllerTest extends ControllerBaseTest
|
|||||||
$this->assertEquals('ar', $user->getLanguage());
|
$this->assertEquals('ar', $user->getLanguage());
|
||||||
$this->assertEquals('ar', $user->getLocale());
|
$this->assertEquals('ar', $user->getLocale());
|
||||||
$this->assertEquals('blue', $user->getPreferenceValue(UserPreference::SKIN));
|
$this->assertEquals('blue', $user->getPreferenceValue(UserPreference::SKIN));
|
||||||
|
$this->assertEquals('sunday', $user->getPreferenceValue(UserPreference::FIRST_WEEKDAY));
|
||||||
|
$this->assertEquals('sunday', $user->getFirstDayOfWeek());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ class UserPreferenceSubscriberTest extends TestCase
|
|||||||
self::assertSame($user, $event->getUser());
|
self::assertSame($user, $event->getUser());
|
||||||
|
|
||||||
$prefs = $sut->getDefaultPreferences($user);
|
$prefs = $sut->getDefaultPreferences($user);
|
||||||
self::assertCount(11, $prefs);
|
self::assertCount(12, $prefs);
|
||||||
|
|
||||||
foreach ($prefs as $pref) {
|
foreach ($prefs as $pref) {
|
||||||
switch ($pref->getName()) {
|
switch ($pref->getName()) {
|
||||||
@@ -49,6 +49,10 @@ class UserPreferenceSubscriberTest extends TestCase
|
|||||||
self::assertTrue($pref->isEnabled());
|
self::assertTrue($pref->isEnabled());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case UserPreference::FIRST_WEEKDAY:
|
||||||
|
self::assertEquals('monday', $pref->getValue());
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
self::assertTrue($pref->isEnabled());
|
self::assertTrue($pref->isEnabled());
|
||||||
}
|
}
|
||||||
@@ -66,7 +70,7 @@ class UserPreferenceSubscriberTest extends TestCase
|
|||||||
// TODO test merging values
|
// TODO test merging values
|
||||||
$sut->loadUserPreferences($event);
|
$sut->loadUserPreferences($event);
|
||||||
$prefs = $event->getUser()->getPreferences();
|
$prefs = $event->getUser()->getPreferences();
|
||||||
self::assertCount(11, $prefs);
|
self::assertCount(12, $prefs);
|
||||||
|
|
||||||
foreach ($prefs as $pref) {
|
foreach ($prefs as $pref) {
|
||||||
switch ($pref->getName()) {
|
switch ($pref->getName()) {
|
||||||
|
|||||||
@@ -89,7 +89,6 @@ class DateTimeFactoryTest extends TestCase
|
|||||||
$this->assertEquals($expected->format('Y'), $dateTime->format('Y'));
|
$this->assertEquals($expected->format('Y'), $dateTime->format('Y'));
|
||||||
$this->assertEquals(self::TEST_TIMEZONE, $dateTime->getTimezone()->getName());
|
$this->assertEquals(self::TEST_TIMEZONE, $dateTime->getTimezone()->getName());
|
||||||
|
|
||||||
$expected = new DateTime('now', new DateTimeZone(self::TEST_TIMEZONE));
|
|
||||||
$dateTime = $sut->getStartOfWeek();
|
$dateTime = $sut->getStartOfWeek();
|
||||||
|
|
||||||
$this->assertEquals(0, $dateTime->format('H'));
|
$this->assertEquals(0, $dateTime->format('H'));
|
||||||
@@ -97,8 +96,7 @@ class DateTimeFactoryTest extends TestCase
|
|||||||
$this->assertEquals(0, $dateTime->format('s'));
|
$this->assertEquals(0, $dateTime->format('s'));
|
||||||
$this->assertEquals(1, $dateTime->format('N'));
|
$this->assertEquals(1, $dateTime->format('N'));
|
||||||
$this->assertEquals('Monday', $dateTime->format('l'));
|
$this->assertEquals('Monday', $dateTime->format('l'));
|
||||||
$this->assertEquals($expected->format('m'), $dateTime->format('m'));
|
// month and year can be different when the week started at the end of the month
|
||||||
$this->assertEquals($expected->format('Y'), $dateTime->format('Y'));
|
|
||||||
$this->assertEquals(self::TEST_TIMEZONE, $dateTime->getTimezone()->getName());
|
$this->assertEquals(self::TEST_TIMEZONE, $dateTime->getTimezone()->getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ class ExtensionsTest extends TestCase
|
|||||||
|
|
||||||
public function testGetFunctions()
|
public function testGetFunctions()
|
||||||
{
|
{
|
||||||
$functions = ['class_name'];
|
$functions = ['class_name', 'iso_day_by_name'];
|
||||||
$sut = $this->getSut();
|
$sut = $this->getSut();
|
||||||
$twigFunctions = $sut->getFunctions();
|
$twigFunctions = $sut->getFunctions();
|
||||||
$this->assertCount(\count($functions), $twigFunctions);
|
$this->assertCount(\count($functions), $twigFunctions);
|
||||||
@@ -156,4 +156,20 @@ sdfsdf' . PHP_EOL . "\n" .
|
|||||||
$activity->setColor('#000002');
|
$activity->setColor('#000002');
|
||||||
self::assertEquals('#000002', $sut->color($activity));
|
self::assertEquals('#000002', $sut->color($activity));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testIsoDayByName()
|
||||||
|
{
|
||||||
|
$sut = $this->getSut();
|
||||||
|
|
||||||
|
self::assertEquals(1, $sut->getIsoDayByName('MoNdAy'));
|
||||||
|
self::assertEquals(2, $sut->getIsoDayByName('tuesDAY'));
|
||||||
|
self::assertEquals(3, $sut->getIsoDayByName('wednesday'));
|
||||||
|
self::assertEquals(4, $sut->getIsoDayByName('thursday'));
|
||||||
|
self::assertEquals(5, $sut->getIsoDayByName('FRIday'));
|
||||||
|
self::assertEquals(6, $sut->getIsoDayByName('saturday'));
|
||||||
|
self::assertEquals(7, $sut->getIsoDayByName('SUNDAY'));
|
||||||
|
// invalid days will return 'monday'
|
||||||
|
self::assertEquals(1, $sut->getIsoDayByName(''));
|
||||||
|
self::assertEquals(1, $sut->getIsoDayByName('sdfgsdf'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -222,6 +222,10 @@
|
|||||||
<source>label.invoice.number_format</source>
|
<source>label.invoice.number_format</source>
|
||||||
<target>Rechnungsnummer Format</target>
|
<target>Rechnungsnummer Format</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="first_weekday">
|
||||||
|
<source>first_weekday</source>
|
||||||
|
<target>Erster Tag der Woche</target>
|
||||||
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
</xliff>
|
</xliff>
|
||||||
|
|||||||
@@ -222,6 +222,10 @@
|
|||||||
<source>label.invoice.number_format</source>
|
<source>label.invoice.number_format</source>
|
||||||
<target>Invoice number format</target>
|
<target>Invoice number format</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="first_weekday">
|
||||||
|
<source>first_weekday</source>
|
||||||
|
<target>First day of the week</target>
|
||||||
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
</xliff>
|
</xliff>
|
||||||
|
|||||||
Reference in New Issue
Block a user