Compare commits
541 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
64893e0a95 | ||
|
|
6fdc304313 | ||
|
|
fb265ed732 | ||
|
|
e2be5b401c | ||
|
|
8539938b8a | ||
|
|
bbf25823b8 | ||
|
|
6b066046c6 | ||
|
|
9226daa891 | ||
|
|
843cd8fb67 | ||
|
|
6d7d441d42 | ||
|
|
1c4da30b61 | ||
|
|
84e25851d3 | ||
|
|
3a52daedab | ||
|
|
b7cf9cd776 | ||
|
|
ccb184b17f | ||
|
|
162ff07bd5 | ||
|
|
7326a452c3 | ||
|
|
9453947309 | ||
|
|
d25653b6ed | ||
|
|
ea415e33f5 | ||
|
|
b6430d5622 | ||
|
|
6fcd50ecdf | ||
|
|
6857d92c4f | ||
|
|
4bb9edb2b5 | ||
|
|
dd881bb856 | ||
|
|
204e0bc179 | ||
|
|
d026460106 | ||
|
|
934a7855b9 | ||
|
|
54d7c4490f | ||
|
|
fa6af3d148 | ||
|
|
063753b569 | ||
|
|
baff2d78d9 | ||
|
|
8f832856a5 | ||
|
|
f2eec4f315 | ||
|
|
2536105ad0 | ||
|
|
98a3fc99a2 | ||
|
|
56524a9773 | ||
|
|
fae525c82f | ||
|
|
5023fa6189 | ||
|
|
f743503705 | ||
|
|
f6a82ba119 | ||
|
|
305d3ba6fe | ||
|
|
7873bd812e | ||
|
|
b31ded22b9 | ||
|
|
f80fe0e4d0 | ||
|
|
c063aed867 | ||
|
|
b2d3272151 | ||
|
|
e9986c92d6 | ||
|
|
3a7dba437c | ||
|
|
cefd747e91 | ||
|
|
21f785638c | ||
|
|
df62c8a428 | ||
|
|
37e02365c7 | ||
|
|
7352c9a394 | ||
|
|
5b8510be56 | ||
|
|
6d5244aa5d | ||
|
|
f1f60042d8 | ||
|
|
6558bcae1d | ||
|
|
828b9c8b4a | ||
|
|
7d052eba00 | ||
|
|
43a31efeac | ||
|
|
f12f1ae60d | ||
|
|
9dc94084c1 | ||
|
|
03d5d3c70c | ||
|
|
27cea996c0 | ||
|
|
7661d43327 | ||
|
|
a1f90ffaba | ||
|
|
2583fdf20e | ||
|
|
9ddb87a6fd | ||
|
|
cc6031bfde | ||
|
|
6d196879b4 | ||
|
|
3d1fba650b | ||
|
|
0d5e0b3e80 | ||
|
|
7460647d58 | ||
|
|
53e01177d9 | ||
|
|
6709ef4c4c | ||
|
|
0b519e9d08 | ||
|
|
7f20cb045c | ||
|
|
286b63e2c8 | ||
|
|
c1eb5ba90a | ||
|
|
4859ac8ecb | ||
|
|
df6bd30d03 | ||
|
|
70fd0ae269 | ||
|
|
dd83872339 | ||
|
|
f7aa3c1e13 | ||
|
|
a9da5f8476 | ||
|
|
06f3a1ecd4 | ||
|
|
bed3ade221 | ||
|
|
f2b32b211b | ||
|
|
f81f9ba492 | ||
|
|
f10aad927f | ||
|
|
4fcd97b142 | ||
|
|
dad1b8b772 | ||
|
|
22af82cb15 | ||
|
|
e4b176a4d5 | ||
|
|
bb94b11e37 | ||
|
|
67d23ec6b2 | ||
|
|
ff7a402bd4 | ||
|
|
305a2fd4f6 | ||
|
|
09c9a95fcf | ||
|
|
08a144ac31 | ||
|
|
7ddcde62dd | ||
|
|
b6462b406d | ||
|
|
0330f45c6a | ||
|
|
8664d94ea6 | ||
|
|
af9dea9226 | ||
|
|
2ce6e815ff | ||
|
|
cdad5913ca | ||
|
|
68bb01d064 | ||
|
|
f4dbdbb427 | ||
|
|
d84beb957c | ||
|
|
7029bde555 | ||
|
|
7427187647 | ||
|
|
f8a5ff7315 | ||
|
|
87d07ffaaf | ||
|
|
954ba7c937 | ||
|
|
16e8e8a5e5 | ||
|
|
75e62b800c | ||
|
|
e1c9af795c | ||
|
|
9a2fd9ba91 | ||
|
|
c0378b4f51 | ||
|
|
0a6ea6be42 | ||
|
|
ec0eca39ff | ||
|
|
3e9371bd6a | ||
|
|
db1344573f | ||
|
|
c34e9f576d | ||
|
|
0b7d551048 | ||
|
|
30ac5e4c24 | ||
|
|
70f7f35009 | ||
|
|
5583caa9e4 | ||
|
|
d3099aca39 | ||
|
|
765ea63aed | ||
|
|
adf8c09fd4 | ||
|
|
5a90b36734 | ||
|
|
2e7bfc822c | ||
|
|
607d09aefb | ||
|
|
9eb25c412e | ||
|
|
8d41fa20bd | ||
|
|
a034b3519e | ||
|
|
ebf0b4de51 | ||
|
|
c9ccdb5f77 | ||
|
|
88b4d3842f | ||
|
|
3cd7e01a2c | ||
|
|
bd39666759 | ||
|
|
67d5cba09f | ||
|
|
8d72d114c7 | ||
|
|
e2324b7d51 | ||
|
|
1eae149c75 | ||
|
|
2a986517f7 | ||
|
|
bf47bb1840 | ||
|
|
cb6924e6d6 | ||
|
|
1de6b5d385 | ||
|
|
4c73fc825c | ||
|
|
d0a0786cce | ||
|
|
b7390ece39 | ||
|
|
1aae4f4d0f | ||
|
|
244169d6d5 | ||
|
|
a5fdf81df5 | ||
|
|
c1a54abc58 | ||
|
|
7e649dff3b | ||
|
|
40eda01c22 | ||
|
|
74b3917e69 | ||
|
|
6ada0b649c | ||
|
|
e21fc656e4 | ||
|
|
f4f5c8fa88 | ||
|
|
64d1aecf16 | ||
|
|
26c2d5aa81 | ||
|
|
78d8212e57 | ||
|
|
7a3388646b | ||
|
|
c3fe8d1c39 | ||
|
|
fcd9974a7c | ||
|
|
53dc322fd1 | ||
|
|
4cab1d323c | ||
|
|
686d55de6e | ||
|
|
0d2d40791a | ||
|
|
4302ecd6ea | ||
|
|
fc12056ba2 | ||
|
|
72edce86ba | ||
|
|
288fc5dd08 | ||
|
|
7503899a9b | ||
|
|
49a4d1069a | ||
|
|
f916e0ce25 | ||
|
|
bd4efad22b | ||
|
|
66865b553e | ||
|
|
62bfe0af2b | ||
|
|
db8714a87b | ||
|
|
5868741fe3 | ||
|
|
08e284c51b | ||
|
|
1757ffd8ee | ||
|
|
2d3c46957f | ||
|
|
ae0a6ebfb5 | ||
|
|
aba06ba3f0 | ||
|
|
b012332d82 | ||
|
|
4a94525320 | ||
|
|
1444593bbd | ||
|
|
82e1d6b917 | ||
|
|
61c0cbc887 | ||
|
|
8c39b4197e | ||
|
|
e24c647b18 | ||
|
|
6db21d7b34 | ||
|
|
7ae55c1980 | ||
|
|
bd167b6088 | ||
|
|
b9b7bf845c | ||
|
|
fc5ebb619e | ||
|
|
abd31e3785 | ||
|
|
8a8f5cb6ce | ||
|
|
2ad8f8a4bb | ||
|
|
4533c994a3 | ||
|
|
6ed7ee76e7 | ||
|
|
167a826f50 | ||
|
|
032db11b48 | ||
|
|
996f05c73a | ||
|
|
0ce02b4f06 | ||
|
|
fb31319fcf | ||
|
|
46bf01d93e | ||
|
|
c2ec15aeb3 | ||
|
|
bb97611349 | ||
|
|
825e492891 | ||
|
|
0aa6baea2f | ||
|
|
cafd84cee4 | ||
|
|
0751c14907 | ||
|
|
de0a9f8303 | ||
|
|
145116f657 | ||
|
|
6a8c820e09 | ||
|
|
2b033710e2 | ||
|
|
23f484a14d | ||
|
|
4cfa7347e9 | ||
|
|
aa1ea98579 | ||
|
|
38249e6b5e | ||
|
|
c02a882eff | ||
|
|
9ef32e75c5 | ||
|
|
14b3de4300 | ||
|
|
02d54fd208 | ||
|
|
d1e9e3e43f | ||
|
|
f279302009 | ||
|
|
e2a742b5c1 | ||
|
|
217960de60 | ||
|
|
ed528eb19a | ||
|
|
b69ea0648c | ||
|
|
193d193352 | ||
|
|
8e801c6829 | ||
|
|
e7da2b408a | ||
|
|
14255d71f7 | ||
|
|
a258516322 | ||
|
|
d25bcfe29c | ||
|
|
8464cd925e | ||
|
|
c2c4f087e4 | ||
|
|
064d083db6 | ||
|
|
ede21fe46a | ||
|
|
5c79e68c77 | ||
|
|
e6d01f2a23 | ||
|
|
af4101c9fe | ||
|
|
b9ee811cbf | ||
|
|
7a1dada9dc | ||
|
|
1de76ba266 | ||
|
|
ec612d1046 | ||
|
|
15c3acfa0b | ||
|
|
6aac85bddb | ||
|
|
d8ae7635d1 | ||
|
|
fbb3dd89cf | ||
|
|
8d1e24ae90 | ||
|
|
367a901207 | ||
|
|
9597015413 | ||
|
|
08696651fd | ||
|
|
661c3897b0 | ||
|
|
4200208c7b | ||
|
|
aba7e05ee2 | ||
|
|
ed4b82f4b1 | ||
|
|
2d4cfbe821 | ||
|
|
34a1e07824 | ||
|
|
bb4233ddfe | ||
|
|
2449a1e8bf | ||
|
|
dc9bc8a8ca | ||
|
|
0914ebf737 | ||
|
|
dc162bf385 | ||
|
|
86add9b9f7 | ||
|
|
476e6ca150 | ||
|
|
6f8c0e3cb6 | ||
|
|
6bd2390acf | ||
|
|
599b18ac99 | ||
|
|
130e6ac057 | ||
|
|
ecea92942c | ||
|
|
12e348544d | ||
|
|
4b8f743fa6 | ||
|
|
aee063bb3c | ||
|
|
93a23f3fa3 | ||
|
|
7907b43ef7 | ||
|
|
c843dba29a | ||
|
|
5864c7e127 | ||
|
|
f57b314466 | ||
|
|
f2dd2331a5 | ||
|
|
164af7ae02 | ||
|
|
b97d9f692d | ||
|
|
32c1e3258e | ||
|
|
19e4ebf88c | ||
|
|
ea39d5c74d | ||
|
|
682681afa5 | ||
|
|
091740f407 | ||
|
|
e22830790b | ||
|
|
e566305426 | ||
|
|
8530a03449 | ||
|
|
1a24aabb6f | ||
|
|
381f46cdd8 | ||
|
|
470a6b30e0 | ||
|
|
ea4e930cbb | ||
|
|
a75bdc52f6 | ||
|
|
21d8e8bbb8 | ||
|
|
57b4473bc8 | ||
|
|
ae6beff5d8 | ||
|
|
da5563960b | ||
|
|
3b6df1cb84 | ||
|
|
91736e89cc | ||
|
|
e2a4998341 | ||
|
|
3c511df7f7 | ||
|
|
e61ffe4db4 | ||
|
|
9da46bbdcd | ||
|
|
e3749c8e8f | ||
|
|
4e1ffedc20 | ||
|
|
35f864256f | ||
|
|
ad2698dae2 | ||
|
|
46a7b13293 | ||
|
|
cbd7124cec | ||
|
|
0debc70ead | ||
|
|
f8331b74c7 | ||
|
|
12c4205785 | ||
|
|
8f5c4dc4ec | ||
|
|
711139ad4c | ||
|
|
cfe0724620 | ||
|
|
aeef1dae2e | ||
|
|
0f3fa8fdbe | ||
|
|
7b25e9acaf | ||
|
|
be00a519b3 | ||
|
|
4254c2590a | ||
|
|
57254bf175 | ||
|
|
fe8fecafeb | ||
|
|
0bc9e74853 | ||
|
|
1dfceeea99 | ||
|
|
68d703d1e3 | ||
|
|
82c713031e | ||
|
|
5579c3d04f | ||
|
|
5e7b0c7826 | ||
|
|
9e1e1a7a96 | ||
|
|
7becddc3a4 | ||
|
|
dfe949264f | ||
|
|
163fb1be4a | ||
|
|
33f76f426c | ||
|
|
1ca1b00d11 | ||
|
|
c89750fe94 | ||
|
|
fb2ce0d30a | ||
|
|
b8c5323ece | ||
|
|
7b13ea860a | ||
|
|
4b1cfa5840 | ||
|
|
6eeb01ad48 | ||
|
|
e6e6a1eeea | ||
|
|
dd64eb98f9 | ||
|
|
9cd111bae5 | ||
|
|
137dcbf7e1 | ||
|
|
fbdc668a22 | ||
|
|
7f931cbfd3 | ||
|
|
7b7a962e04 | ||
|
|
e4610af235 | ||
|
|
e7458e76e1 | ||
|
|
04a43378d8 | ||
|
|
809acb47bf | ||
|
|
ace2788b57 | ||
|
|
d60e738bd2 | ||
|
|
a50a59bbca | ||
|
|
76a6ea57a1 | ||
|
|
9907694ef6 | ||
|
|
20b3c31cbd | ||
|
|
25a74ffeee | ||
|
|
b8273c4441 | ||
|
|
8d3a305ec3 | ||
|
|
72b73bc9a6 | ||
|
|
ec31d206e2 | ||
|
|
bf11de82fc | ||
|
|
34d2228d5d | ||
|
|
0e507804b1 | ||
|
|
6c7477f1a9 | ||
|
|
886fc8d74e | ||
|
|
7b218c33f9 | ||
|
|
c67617262f | ||
|
|
057c824e9f | ||
|
|
1b4f4bba22 | ||
|
|
847798eec1 | ||
|
|
52c7437076 | ||
|
|
465d7166d4 | ||
|
|
733aa58de8 | ||
|
|
9620627ea6 | ||
|
|
e06314e802 | ||
|
|
986d922855 | ||
|
|
75dcae6fbe | ||
|
|
375691365e | ||
|
|
5c5af05408 | ||
|
|
69cf7015d8 | ||
|
|
6a533579b7 | ||
|
|
3ff46e06c0 | ||
|
|
9e2fccb105 | ||
|
|
f2c6d8aee1 | ||
|
|
836a530b86 | ||
|
|
a31e027c54 | ||
|
|
a1b554b3ac | ||
|
|
79c4c237fa | ||
|
|
85a35a3063 | ||
|
|
e0e391efbe | ||
|
|
9eff89963c | ||
|
|
352fac26f3 | ||
|
|
8c16bf126a | ||
|
|
42de968f8f | ||
|
|
ed8f024e42 | ||
|
|
e534369c5b | ||
|
|
fed797df93 | ||
|
|
6a44dbfe83 | ||
|
|
28c5357f11 | ||
|
|
a8824f92a1 | ||
|
|
e57b69973f | ||
|
|
d6798eed1e | ||
|
|
50383673ca | ||
|
|
022dbe32e5 | ||
|
|
baac4eb698 | ||
|
|
8aa8f452d5 | ||
|
|
54255cf923 | ||
|
|
bd3d82d224 | ||
|
|
b14ad9f3ab | ||
|
|
261b4646e4 | ||
|
|
d62e1c4468 | ||
|
|
5b0266c462 | ||
|
|
06118dcabb | ||
|
|
984c852ab6 | ||
|
|
47414cfd0e | ||
|
|
fdf4fdfb6a | ||
|
|
5ee0396a02 | ||
|
|
724eb9c658 | ||
|
|
936ab05882 | ||
|
|
817a43b178 | ||
|
|
0f16f42887 | ||
|
|
641c199c8c | ||
|
|
04dd6b900f | ||
|
|
536a05210e | ||
|
|
60d16016e8 | ||
|
|
9222075c7a | ||
|
|
641dcdff85 | ||
|
|
668649be7a | ||
|
|
0de936d474 | ||
|
|
47bd8b0ce7 | ||
|
|
2287c9951c | ||
|
|
94c28ebbc5 | ||
|
|
b4739f8f03 | ||
|
|
c32cd36861 | ||
|
|
945562b34d | ||
|
|
af0f89774e | ||
|
|
c6c4098759 | ||
|
|
fa1c79e15c | ||
|
|
0705c26513 | ||
|
|
140fa4b7c0 | ||
|
|
cd88901868 | ||
|
|
8eec97deb5 | ||
|
|
4331a7c9f4 | ||
|
|
f4fcfeba9d | ||
|
|
48aefb1862 | ||
|
|
bda9584346 | ||
|
|
ed1154af67 | ||
|
|
c368eacd87 | ||
|
|
949c59230f | ||
|
|
1114538f41 | ||
|
|
b0f83291ee | ||
|
|
e91e4ff430 | ||
|
|
4ad290d4bd | ||
|
|
37c6e2c0af | ||
|
|
b4a45afce9 | ||
|
|
330ff6eb47 | ||
|
|
c681eb1bdb | ||
|
|
de533e7031 | ||
|
|
ae6ed3f23f | ||
|
|
32aa209b75 | ||
|
|
e8c25d8ac5 | ||
|
|
718ad4b398 | ||
|
|
8d81a394ee | ||
|
|
d661c8b54e | ||
|
|
e45e782e7d | ||
|
|
5aa422dde1 | ||
|
|
24cbe3d281 | ||
|
|
13f5e76015 | ||
|
|
35c65ba890 | ||
|
|
8f2b82b832 | ||
|
|
7f1a206a52 | ||
|
|
8c503a226e | ||
|
|
cdcf9eaf67 | ||
|
|
a651e55dc9 | ||
|
|
d041a3f4f9 | ||
|
|
3ea05cb705 | ||
|
|
3f27ab1e44 | ||
|
|
e99b170d0a | ||
|
|
49e1a1c410 | ||
|
|
93ab3d5666 | ||
|
|
94f8159fdc | ||
|
|
8fb0b1445a | ||
|
|
15673555a2 | ||
|
|
ceeec7b8a6 | ||
|
|
46e5650882 | ||
|
|
405ea0076b | ||
|
|
8cd7f7de3f | ||
|
|
7cd17154fc | ||
|
|
299fcb091a | ||
|
|
025c6b0e7b | ||
|
|
388c3ec188 | ||
|
|
b6d9bb0d2a | ||
|
|
561ec3b1e9 | ||
|
|
9611646bc6 | ||
|
|
b5972bb680 | ||
|
|
455861ec69 | ||
|
|
5affa0de82 | ||
|
|
e11a2a6089 | ||
|
|
614a50ce0b | ||
|
|
66594b286a | ||
|
|
8e06f94bdd | ||
|
|
b0e03bb0b8 | ||
|
|
22e30b1871 | ||
|
|
8104a67e3e | ||
|
|
654ffda454 | ||
|
|
3f51437569 | ||
|
|
1bd784eef3 | ||
|
|
0d11aded40 | ||
|
|
00925d38aa | ||
|
|
12803b3957 | ||
|
|
7583498735 | ||
|
|
53eda31179 | ||
|
|
56e276939b | ||
|
|
39abccb35d | ||
|
|
dae8866053 | ||
|
|
a0e8aa3a43 | ||
|
|
e68b7da1f8 | ||
|
|
fcce7a531a | ||
|
|
f23dcbec17 | ||
|
|
8a1d28563d | ||
|
|
c33a87a07c | ||
|
|
b833e1ce27 | ||
|
|
6de1a55d73 | ||
|
|
cbf9086e10 | ||
|
|
eb4aa23577 | ||
|
|
bf9ae44775 |
@@ -7,15 +7,6 @@ coverage:
|
||||
round: down
|
||||
range: "80...100"
|
||||
|
||||
status:
|
||||
project:
|
||||
default:
|
||||
threshold: 0.5%
|
||||
patch:
|
||||
default:
|
||||
threshold: 50%
|
||||
changes: no
|
||||
|
||||
parsers:
|
||||
gcov:
|
||||
branch_detection:
|
||||
|
||||
@@ -3,7 +3,8 @@ root = true
|
||||
|
||||
; Unix-style newlines
|
||||
[*]
|
||||
end_of_line = LF
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
|
||||
[*.php]
|
||||
indent_style = space
|
||||
|
||||
47
.env.dist
47
.env.dist
@@ -1,31 +1,30 @@
|
||||
# This file is a "template" of which env vars need to be defined for your application
|
||||
# Copy this file to .env file for development, create environment variables when deploying to production
|
||||
# https://symfony.com/doc/current/best_practices/configuration.html#infrastructure-related-configuration
|
||||
### DATABASE CONFIGURATION
|
||||
# Replace "user", "password" and "database" with your database connection.
|
||||
# Configure the server version, MariaDB requires the "mariadb-" prefix, eg:
|
||||
# for MySQL "serverVersion=5.7" and for MariaDB "serverVersion=mariadb-10.5.8"
|
||||
DATABASE_URL=mysql://user:password@127.0.0.1:3306/database?charset=utf8&serverVersion=5.7
|
||||
|
||||
### EMAIL CONFIGURATION
|
||||
# Emails will be sent "from":
|
||||
MAILER_FROM=kimai@example.com
|
||||
|
||||
###> symfony/framework-bundle ###
|
||||
# Email connection (disabled by default with MAILER_URL=null://null)
|
||||
# SMTP: smtp://localhost:25?encryption=&auth_mode=
|
||||
# Google: gmail://username:password@default
|
||||
# Amazon: ses://ACCESS_KEY:SECRET_KEY@default?region=eu-west-1
|
||||
# Mailchimp: mandrill://KEY@default
|
||||
# Mailgun: mailgun://KEY:DOMAIN@default
|
||||
# Postmark: postmark://ID@default
|
||||
# Sendgrid: sendgrid://KEY@default
|
||||
# Disable emails: null://null
|
||||
MAILER_URL=null://null
|
||||
|
||||
### APPLICATION CONFIGURATION
|
||||
APP_ENV=prod
|
||||
APP_SECRET=change_this_to_something_unique
|
||||
#TRUSTED_PROXIES=127.0.0.1,127.0.0.2
|
||||
#TRUSTED_HOSTS=localhost,example.com
|
||||
###< symfony/framework-bundle ###
|
||||
|
||||
###> doctrine/doctrine-bundle ###
|
||||
# Format described at http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url
|
||||
# For an SQLite database, use: "sqlite:///%kernel.project_dir%/var/data/kimai.sqlite"
|
||||
# Configure your db driver and server_version in config/packages/doctrine.yaml
|
||||
# DATABASE_URL=mysql://db_user:db_password@127.0.0.1:3306/db_name
|
||||
DATABASE_URL=sqlite:///%kernel.project_dir%/var/data/kimai.sqlite
|
||||
###< doctrine/doctrine-bundle ###
|
||||
|
||||
###> symfony/swiftmailer-bundle ###
|
||||
# For Gmail as a transport, use: "gmail://username:password@localhost"
|
||||
# For a generic SMTP server, use: "smtp://localhost:25?encryption=&auth_mode="
|
||||
# Delivery is disabled by default via "null://localhost"
|
||||
MAILER_URL=null://localhost
|
||||
###< symfony/swiftmailer-bundle ###
|
||||
|
||||
###> nelmio/cors-bundle ###
|
||||
# Running in a "special" environment, eg. behind reverse proxies?
|
||||
# Check those:
|
||||
# TRUSTED_PROXIES=127.0.0.1,127.0.0.2
|
||||
# TRUSTED_HOSTS=localhost,example.com
|
||||
CORS_ALLOW_ORIGIN=^https?://localhost(:[0-9]+)?$
|
||||
###< nelmio/cors-bundle ###
|
||||
|
||||
4
.github/FUNDING.yml
vendored
4
.github/FUNDING.yml
vendored
@@ -1,2 +1,2 @@
|
||||
#github: [kevinpapst]
|
||||
custom: https://www.kimai.org/donate/
|
||||
github: [kevinpapst]
|
||||
custom: ["https://www.kimai.org/donate/", "https://www.paypal.me/kevinpapst"]
|
||||
|
||||
25
.github/ISSUE_TEMPLATE/bug_report.md
vendored
25
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,6 +1,9 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve Kimai
|
||||
about: Found a problem? Create a report to help us improve Kimai
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
@@ -8,27 +11,17 @@ about: Create a report to help us improve Kimai
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. See error
|
||||
|
||||
**Logfile**
|
||||
```
|
||||
Add the last lines from your logfile at var/log/prod.log, around the time when the problem happened.
|
||||
Add the last lines from your logfile at "var/log/prod.log" or "Doctor > Logs", around the time when the problem happened.
|
||||
```
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Desktop/Smartphone**
|
||||
Please complete the following information if this seems to be a frontend issue:
|
||||
- Device: [e.g. iPhone6s]
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
- Kimai version
|
||||
- PHP version
|
||||
- Kimai version: ?.?
|
||||
- PHP version: ?.?
|
||||
- Device: [Ubuntu Laptop 16 inch, Windows Desktop 27 inch, iPhone 6s]
|
||||
- Browser [e.g. Firefox 81, Chrome 85, Safari 14]
|
||||
|
||||
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: "Support / Questions / Discussions"
|
||||
url: https://github.com/kevinpapst/kimai2/discussions
|
||||
about: "This forum is for support questions: eg. if you need help with your Kimai installation, want to know how to use a Kimai feature or wonder where to start creating a new Kimai plugin."
|
||||
7
.github/ISSUE_TEMPLATE/feature_request.md
vendored
7
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,6 +1,9 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea to make Kimai better
|
||||
title: ''
|
||||
labels: feature request
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
@@ -13,5 +16,5 @@ A clear and concise description of what you want to happen.
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
**Screenshot**
|
||||
Add a mockup / screenshot of your idea here.
|
||||
|
||||
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -7,6 +7,6 @@ A clear and concise description of what this pull request adds or changes.
|
||||
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
|
||||
|
||||
## Checklist
|
||||
- [ ] I verified that my code applies to the guidelines (`composer kimai:code-check`)
|
||||
- [ ] I verified that my code applies to the guidelines (`composer code-check`)
|
||||
- [ ] I updated the documentation (see [here](https://github.com/kimai/www.kimai.org/tree/master/_documentation))
|
||||
- [ ] I agree that this code is used in Kimai and will be published under the [MIT license](https://github.com/kevinpapst/kimai2/blob/master/LICENSE)
|
||||
|
||||
24
.github/lock.yml
vendored
24
.github/lock.yml
vendored
@@ -1,24 +0,0 @@
|
||||
# Configuration for lock-threads - https://github.com/dessant/lock-threads
|
||||
|
||||
# Number of days of inactivity before a closed issue or pull request is locked
|
||||
daysUntilLock: 60
|
||||
|
||||
# Issues and pull requests with these labels will not be locked. Set to `[]` to disable
|
||||
exemptLabels: []
|
||||
|
||||
# Label to add before locking, such as `outdated`. Set to `false` to disable
|
||||
lockLabel: false
|
||||
|
||||
# Comment to post before locking. Set to `false` to disable
|
||||
lockComment: >
|
||||
This thread has been automatically locked since there has not been
|
||||
any recent activity after it was closed. Please open a new issue for
|
||||
related bugs.
|
||||
If you use Kimai on a daily basis, please [consider donating](https://www.kimai.org/donate/) to
|
||||
support further development of Kimai.
|
||||
|
||||
# Assign `resolved` as the reason for locking. Set to `false` to disable
|
||||
setLockReason: false
|
||||
|
||||
# Limit to only `issues` or `pulls`
|
||||
# only: pulls
|
||||
48
.github/workflows/coverage.yaml
vendored
Normal file
48
.github/workflows/coverage.yaml
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
name: Coverage
|
||||
on:
|
||||
pull_request: null
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
jobs:
|
||||
tests:
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
mysql:
|
||||
image: mysql:5.7
|
||||
env:
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: false
|
||||
MYSQL_ROOT_PASSWORD: kimai
|
||||
MYSQL_DATABASE: kimai
|
||||
ports:
|
||||
- 3306/tcp
|
||||
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
|
||||
strategy:
|
||||
matrix:
|
||||
php: ['7.4']
|
||||
|
||||
name: Coverage (${{ matrix.php }})
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
coverage: pcov
|
||||
extensions: mbstring, xml, ctype, iconv, intl, mysql, zip, gd, ldap
|
||||
- uses: ramsey/composer-install@v1
|
||||
- run: composer require laminas/laminas-ldap
|
||||
- name: Setup problem matchers for PHPUnit
|
||||
run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
|
||||
- name: Run tests
|
||||
run: vendor/bin/phpunit tests/ --coverage-clover=coverage.xml
|
||||
env:
|
||||
DATABASE_URL: mysql://root:kimai@127.0.0.1:${{ job.services.mysql.ports['3306'] }}/kimai?serverVersion=5.7
|
||||
APP_ENV: dev
|
||||
MAILER_URL: null://localhost
|
||||
TEST_WITH_BUNDLES: 1
|
||||
- name: Upload code coverage
|
||||
uses: codecov/codecov-action@v2
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
files: ./coverage.xml
|
||||
fail_ci_if_error: true
|
||||
56
.github/workflows/lock.yaml
vendored
Normal file
56
.github/workflows/lock.yaml
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
name: 'Lock Threads'
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
concurrency:
|
||||
group: lock
|
||||
|
||||
jobs:
|
||||
action:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: dessant/lock-threads@v3
|
||||
with:
|
||||
github-token: ${{ github.token }}
|
||||
issue-inactive-days: '180'
|
||||
exclude-issue-created-before: ''
|
||||
exclude-issue-created-after: ''
|
||||
exclude-issue-created-between: ''
|
||||
exclude-issue-closed-before: ''
|
||||
exclude-issue-closed-after: ''
|
||||
exclude-issue-closed-between: ''
|
||||
include-any-issue-labels: ''
|
||||
include-all-issue-labels: ''
|
||||
exclude-any-issue-labels: ''
|
||||
add-issue-labels: ''
|
||||
remove-issue-labels: ''
|
||||
issue-comment: >
|
||||
This thread has been automatically locked since there has not been
|
||||
any recent activity after it was closed. Please open a new issue for
|
||||
related bugs.
|
||||
If you use Kimai on a daily basis, please [consider donating](https://www.kimai.org/donate/) to
|
||||
support further development of Kimai.
|
||||
issue-lock-reason: 'resolved'
|
||||
pr-inactive-days: '180'
|
||||
exclude-pr-created-before: ''
|
||||
exclude-pr-created-after: ''
|
||||
exclude-pr-created-between: ''
|
||||
exclude-pr-closed-before: ''
|
||||
exclude-pr-closed-after: ''
|
||||
exclude-pr-closed-between: ''
|
||||
include-any-pr-labels: ''
|
||||
include-all-pr-labels: ''
|
||||
exclude-any-pr-labels: ''
|
||||
add-pr-labels: ''
|
||||
remove-pr-labels: ''
|
||||
pr-comment: ''
|
||||
pr-lock-reason: ''
|
||||
process-only: ''
|
||||
log-output: false
|
||||
85
.github/workflows/testing.yaml
vendored
Normal file
85
.github/workflows/testing.yaml
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
name: Tests
|
||||
on:
|
||||
pull_request: null
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
jobs:
|
||||
integration:
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
mysql:
|
||||
image: mysql:5.7
|
||||
env:
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: false
|
||||
MYSQL_ROOT_PASSWORD: kimai
|
||||
MYSQL_DATABASE: kimai
|
||||
ports:
|
||||
- 3306/tcp
|
||||
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
|
||||
strategy:
|
||||
matrix:
|
||||
php: ['7.3', '7.4', '8.0']
|
||||
|
||||
name: Integration (${{ matrix.php }})
|
||||
steps:
|
||||
|
||||
- name: Clone Kimai
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
coverage: none
|
||||
extensions: mbstring, xml, ctype, iconv, intl, mysql, zip, gd, ldap
|
||||
tools: cs2pr:1.1.0
|
||||
|
||||
- name: Install dependencies
|
||||
uses: ramsey/composer-install@v1
|
||||
|
||||
- name: Validate Composer
|
||||
run: composer validate --strict
|
||||
|
||||
- name: Check codestyles
|
||||
run: vendor/bin/php-cs-fixer fix --dry-run --verbose --config=.php-cs-fixer.dist.php --using-cache=no --show-progress=none --format=checkstyle | cs2pr
|
||||
|
||||
- name: Run PHPStan on Codebase
|
||||
run: vendor/bin/phpstan analyse src -c phpstan.neon --level=5 --no-progress --error-format=checkstyle | cs2pr
|
||||
|
||||
- name: Run PHPStan on Tests
|
||||
run: vendor/bin/phpstan analyse tests -c tests/phpstan.neon --level=5 --no-progress --error-format=checkstyle | cs2pr
|
||||
|
||||
- name: Lint codebase
|
||||
run: composer linting
|
||||
|
||||
- name: Install LDAP package
|
||||
run: composer require laminas/laminas-ldap
|
||||
|
||||
- name: Setup problem matchers for PHPUnit
|
||||
run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
|
||||
|
||||
- name: Run unit tests
|
||||
run: composer kimai:tests-unit
|
||||
env:
|
||||
DATABASE_URL: mysql://root:kimai@127.0.0.1:${{ job.services.mysql.ports['3306'] }}/kimai?serverVersion=5.7
|
||||
APP_ENV: dev
|
||||
MAILER_URL: null://localhost
|
||||
|
||||
- name: Run integration tests
|
||||
run: composer kimai:tests-integration
|
||||
env:
|
||||
DATABASE_URL: mysql://root:kimai@127.0.0.1:${{ job.services.mysql.ports['3306'] }}/kimai?serverVersion=5.7
|
||||
APP_ENV: dev
|
||||
MAILER_URL: null://localhost
|
||||
|
||||
- name: Run migrations on MySQL
|
||||
run: |
|
||||
bin/console doctrine:database:drop --if-exists --force -n
|
||||
bin/console doctrine:database:create --if-not-exists -n
|
||||
bin/console doctrine:migrations:migrate -n
|
||||
bin/console doctrine:migrations:migrate first -n
|
||||
env:
|
||||
DATABASE_URL: mysql://root:kimai@127.0.0.1:${{ job.services.mysql.ports['3306'] }}/kimai?serverVersion=5.7
|
||||
APP_ENV: dev
|
||||
MAILER_URL: null://localhost
|
||||
@@ -1,4 +1,5 @@
|
||||
unreleased=true
|
||||
future-release=1.0.1
|
||||
exclude-labels=duplicate,question,invalid,wontfix,release
|
||||
enhancement_labels=>enhancement,Enhancement,feature request
|
||||
future-release=1.15.6
|
||||
exclude-labels=duplicate,support,question,invalid,wontfix,release,waiting for feedback,documentation
|
||||
enhancement_labels=>enhancement,Enhancement,feature request,translation i18n,technical debt,documentation
|
||||
issues-wo-labels=false
|
||||
|
||||
24
.gitignore
vendored
24
.gitignore
vendored
@@ -1,23 +1,37 @@
|
||||
# ========== KIMAI ==========
|
||||
# IDE
|
||||
# some hosters require a htaccess to change the PHP version
|
||||
.htaccess
|
||||
.env-*
|
||||
.idea/
|
||||
.DS_Store
|
||||
|
||||
# for symfony local webserver
|
||||
php.ini
|
||||
|
||||
/bin/*
|
||||
!bin/console
|
||||
/config/packages/local.yaml
|
||||
/config/packages/*-local.yaml
|
||||
/config/packages/*/local.yaml
|
||||
/config/packages/*/*-local.yaml
|
||||
/config/bundles-local.php
|
||||
|
||||
public/avatars/*.png
|
||||
|
||||
templates/invoice/renderer/.~lock*
|
||||
translations/branding.*.xlf
|
||||
|
||||
/var/data/*
|
||||
!/var/data/.gitkeep
|
||||
!/var/data/kimai_test.sqlite
|
||||
/var/coverage/
|
||||
/var/cache/*
|
||||
!var/cache/.gitkeep
|
||||
|
||||
/var/invoices/*
|
||||
/var/export/*
|
||||
|
||||
/var/packages/*
|
||||
!var/packages/.gitkeep
|
||||
/var/plugins/*
|
||||
!var/plugins/.gitkeep
|
||||
|
||||
@@ -34,10 +48,6 @@ templates/invoice/renderer/.~lock*
|
||||
/vendor/
|
||||
###< symfony/framework-bundle ###
|
||||
|
||||
###> symfony/web-server-bundle ###
|
||||
.web-server-pid
|
||||
###< symfony/web-server-bundle ###
|
||||
|
||||
###> phpunit/phpunit ###
|
||||
/phpunit.xml
|
||||
/.phpunit.result.cache
|
||||
@@ -46,6 +56,7 @@ templates/invoice/renderer/.~lock*
|
||||
###> friendsofphp/php-cs-fixer ###
|
||||
.php_cs
|
||||
.php_cs.cache
|
||||
.php-cs-fixer.cache
|
||||
###< friendsofphp/php-cs-fixer ###
|
||||
|
||||
###> symfony/phpunit-bridge ###
|
||||
@@ -58,3 +69,4 @@ templates/invoice/renderer/.~lock*
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
###< symfony/webpack-encore-bundle ###
|
||||
/nbproject/*
|
||||
@@ -7,7 +7,8 @@ For the full copyright and license information, please view the LICENSE
|
||||
file that was distributed with this source code.
|
||||
COMMENT;
|
||||
|
||||
return PhpCsFixer\Config::create()
|
||||
$fixer = new PhpCsFixer\Config();
|
||||
$fixer
|
||||
->setRiskyAllowed(true)
|
||||
->setRules([
|
||||
'encoding' => true,
|
||||
@@ -19,7 +20,7 @@ return PhpCsFixer\Config::create()
|
||||
'function_declaration' => true,
|
||||
'indentation_type' => true,
|
||||
'line_ending' => true,
|
||||
'lowercase_constants' => true,
|
||||
'constant_case' => ['case' => 'lower'],
|
||||
'lowercase_keywords' => true,
|
||||
'method_argument_space' => ['on_multiline' => 'ensure_fully_multiline'],
|
||||
'header_comment' => ['header' => $fileHeaderComment, 'separate' => 'both'],
|
||||
@@ -46,7 +47,7 @@ return PhpCsFixer\Config::create()
|
||||
'statements' => ['return'],
|
||||
],
|
||||
'cast_spaces' => true,
|
||||
'class_attributes_separation' => ['elements' => ['method']],
|
||||
'class_attributes_separation' => ['elements' => ['method' => 'one']],
|
||||
'concat_space' => ['spacing' => 'one'],
|
||||
'declare_equal_normalize' => true,
|
||||
'function_typehint_space' => true,
|
||||
@@ -100,10 +101,10 @@ return PhpCsFixer\Config::create()
|
||||
],
|
||||
'phpdoc_annotation_without_dot' => true,
|
||||
'phpdoc_indent' => true,
|
||||
'phpdoc_inline_tag' => true,
|
||||
'phpdoc_inline_tag_normalizer' => true,
|
||||
'phpdoc_no_access' => true,
|
||||
'phpdoc_no_alias_tag' => true,
|
||||
'phpdoc_no_empty_return' => true,
|
||||
'phpdoc_no_empty_return' => false,
|
||||
'phpdoc_no_package' => true,
|
||||
'phpdoc_no_useless_inheritdoc' => true,
|
||||
'phpdoc_return_self_reference' => true,
|
||||
@@ -130,7 +131,7 @@ return PhpCsFixer\Config::create()
|
||||
'standardize_increment' => true,
|
||||
'standardize_not_equals' => true,
|
||||
'ternary_operator_spaces' => true,
|
||||
'trailing_comma_in_multiline_array' => false,
|
||||
'trailing_comma_in_multiline' => false,
|
||||
'trim_array_spaces' => true,
|
||||
'unary_operator_spaces' => true,
|
||||
'whitespace_after_comma_in_array' => true,
|
||||
@@ -141,6 +142,18 @@ return PhpCsFixer\Config::create()
|
||||
'method',
|
||||
'property',
|
||||
]],
|
||||
'native_function_invocation' => [
|
||||
'include' => [
|
||||
'@compiler_optimized'
|
||||
],
|
||||
'scope' => 'namespaced'
|
||||
],
|
||||
'native_function_type_declaration_casing' => true,
|
||||
'no_alias_functions' => [
|
||||
'sets' => [
|
||||
'@internal'
|
||||
]
|
||||
],
|
||||
])
|
||||
->setFinder(
|
||||
PhpCsFixer\Finder::create()
|
||||
@@ -151,3 +164,5 @@ return PhpCsFixer\Config::create()
|
||||
)
|
||||
->setFormat('checkstyle')
|
||||
;
|
||||
|
||||
return $fixer;
|
||||
50
.travis.yml
50
.travis.yml
@@ -1,50 +0,0 @@
|
||||
language: php
|
||||
sudo: false
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.composer/cache/files
|
||||
|
||||
addons:
|
||||
mariadb: '10.2'
|
||||
|
||||
services:
|
||||
- mysql
|
||||
|
||||
env:
|
||||
global:
|
||||
- CODECOVERAGE=0
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
include:
|
||||
- php: 7.2
|
||||
- php: 7.3
|
||||
env: CODECOVERAGE=1
|
||||
|
||||
before_install:
|
||||
# - phpenv config-rm xdebug.ini
|
||||
- composer self-update
|
||||
- php -i
|
||||
- php -m
|
||||
|
||||
install:
|
||||
- composer install
|
||||
- composer require zendframework/zend-ldap
|
||||
|
||||
script:
|
||||
- composer validate --no-check-all --strict
|
||||
- composer kimai:codestyle
|
||||
- composer kimai:phpstan
|
||||
- if [[ $CODECOVERAGE == 1 ]]; then vendor/bin/phpunit tests/ --coverage-clover=coverage.xml; else vendor/bin/phpunit tests/; fi;
|
||||
- cp tests/.env.dist.sqlite .env
|
||||
- bin/console doctrine:database:create -n
|
||||
- bin/console doctrine:migrations:migrate -n
|
||||
- bin/console doctrine:migrations:migrate first -n
|
||||
- cp tests/.env.dist.mysql .env
|
||||
- bin/console doctrine:database:create -n
|
||||
- bin/console doctrine:migrations:migrate -n
|
||||
- bin/console doctrine:migrations:migrate first -n
|
||||
|
||||
after_success:
|
||||
- if [[ $CODECOVERAGE == 1 ]]; then bash <(curl -s https://codecov.io/bash); fi
|
||||
835
CHANGELOG.md
835
CHANGELOG.md
@@ -1,834 +1,3 @@
|
||||
# Change Log
|
||||
# Changelog
|
||||
|
||||
## [1.0.1](https://github.com/kevinpapst/kimai2/tree/1.0.1) (2019-07-05)
|
||||
[Full Changelog](https://github.com/kevinpapst/kimai2/compare/1.0...1.0.1)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- allow to configure default values for users [\#919](https://github.com/kevinpapst/kimai2/pull/919) ([kevinpapst](https://github.com/kevinpapst))
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- default value for end of visible time range is not valid [\#917](https://github.com/kevinpapst/kimai2/issues/917)
|
||||
- fix default calendar config [\#918](https://github.com/kevinpapst/kimai2/pull/918) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- fix 500 error happening in error pages [\#914](https://github.com/kevinpapst/kimai2/pull/914) ([kevinpapst](https://github.com/kevinpapst))
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- prod log config [\#921](https://github.com/kevinpapst/kimai2/pull/921) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- updated all composer packages [\#920](https://github.com/kevinpapst/kimai2/pull/920) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- remove scrutinizer [\#915](https://github.com/kevinpapst/kimai2/pull/915) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- use pre-defined icons in menu as well [\#913](https://github.com/kevinpapst/kimai2/pull/913) ([kevinpapst](https://github.com/kevinpapst))
|
||||
|
||||
## [1.0](https://github.com/kevinpapst/kimai2/tree/1.0) (2019-07-02)
|
||||
[Full Changelog](https://github.com/kevinpapst/kimai2/compare/0.9...1.0)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Multiple Work Orders under Project for a Customer [\#720](https://github.com/kevinpapst/kimai2/issues/720)
|
||||
- View cut off on 4:3 monitor [\#575](https://github.com/kevinpapst/kimai2/issues/575)
|
||||
- Admin Timesheets Toolbar: order users by username [\#876](https://github.com/kevinpapst/kimai2/issues/876)
|
||||
- import project "budget" field [\#841](https://github.com/kevinpapst/kimai2/issues/841)
|
||||
- Add tag data to exports [\#825](https://github.com/kevinpapst/kimai2/issues/825)
|
||||
- Additional status indicators for a field \(tag field\) more like lables which are globally assigable and appear for each task a user is working on or has started [\#769](https://github.com/kevinpapst/kimai2/issues/769)
|
||||
- Include option of tags field to link various timesheet entries together and use them for filtering data [\#768](https://github.com/kevinpapst/kimai2/issues/768)
|
||||
- Disable manual To date and duration entry option in timer [\#764](https://github.com/kevinpapst/kimai2/issues/764)
|
||||
- Custom data for external references [\#760](https://github.com/kevinpapst/kimai2/issues/760)
|
||||
- Refactor active entries dropdown [\#754](https://github.com/kevinpapst/kimai2/issues/754)
|
||||
- Stop entries without page reload [\#753](https://github.com/kevinpapst/kimai2/issues/753)
|
||||
- More actions on data records resp. table rows [\#748](https://github.com/kevinpapst/kimai2/issues/748)
|
||||
- Action menu instead of icons [\#747](https://github.com/kevinpapst/kimai2/issues/747)
|
||||
- Add custom fields in timesheet record [\#697](https://github.com/kevinpapst/kimai2/issues/697)
|
||||
- Lock exported objects [\#686](https://github.com/kevinpapst/kimai2/issues/686)
|
||||
- Option to allow Markdown in Project/Activity-comments [\#677](https://github.com/kevinpapst/kimai2/issues/677)
|
||||
- Recalculate rates on every change [\#673](https://github.com/kevinpapst/kimai2/issues/673)
|
||||
- Show on the title if there is an active recording [\#660](https://github.com/kevinpapst/kimai2/issues/660)
|
||||
- Allow edit Date From for saved time records [\#627](https://github.com/kevinpapst/kimai2/issues/627)
|
||||
- Restarting a entry- missing description [\#626](https://github.com/kevinpapst/kimai2/issues/626)
|
||||
- Calendar: Return to last view after creating a new time entry [\#622](https://github.com/kevinpapst/kimai2/issues/622)
|
||||
- Unable to select «projects» while filtering in timesheets [\#601](https://github.com/kevinpapst/kimai2/issues/601)
|
||||
- Calendar view: more space for business hours [\#530](https://github.com/kevinpapst/kimai2/issues/530)
|
||||
- maximale Anzahl Projektstunden als Vorgabe [\#467](https://github.com/kevinpapst/kimai2/issues/467)
|
||||
- don't allow start end dates, just punch in or out [\#448](https://github.com/kevinpapst/kimai2/issues/448)
|
||||
- Refresh of Duration time [\#420](https://github.com/kevinpapst/kimai2/issues/420)
|
||||
- Statistic of time-used vs. time-budget [\#416](https://github.com/kevinpapst/kimai2/issues/416)
|
||||
- Colors for customers / projects [\#374](https://github.com/kevinpapst/kimai2/issues/374)
|
||||
- allow to disable user preferences by permission [\#895](https://github.com/kevinpapst/kimai2/pull/895) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- order users by username [\#880](https://github.com/kevinpapst/kimai2/pull/880) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- use default\_begin in duration only mode [\#873](https://github.com/kevinpapst/kimai2/pull/873) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Custom fields for timesheets, customers, projects and activities [\#871](https://github.com/kevinpapst/kimai2/pull/871) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- export tags in spreadsheets [\#860](https://github.com/kevinpapst/kimai2/pull/860) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- add tracking mode \(duration + fixed start time\) [\#859](https://github.com/kevinpapst/kimai2/pull/859) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- branding options [\#856](https://github.com/kevinpapst/kimai2/pull/856) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Widget refactoring [\#847](https://github.com/kevinpapst/kimai2/pull/847) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added hourly and money budgets to activity, project and customer [\#843](https://github.com/kevinpapst/kimai2/pull/843) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- updated dockerfile [\#840](https://github.com/kevinpapst/kimai2/pull/840) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added installation command [\#838](https://github.com/kevinpapst/kimai2/pull/838) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- add ldap support [\#815](https://github.com/kevinpapst/kimai2/pull/815) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added punch-in punch-out / time-clock mode [\#812](https://github.com/kevinpapst/kimai2/pull/812) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- allow markdown for customer, project and activity comments [\#807](https://github.com/kevinpapst/kimai2/pull/807) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- improved html export [\#802](https://github.com/kevinpapst/kimai2/pull/802) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- use dropdown for entity actions on detail pages [\#801](https://github.com/kevinpapst/kimai2/pull/801) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- lock exported timesheets [\#798](https://github.com/kevinpapst/kimai2/pull/798) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Calendar improvements [\#784](https://github.com/kevinpapst/kimai2/pull/784) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- restart via API allows to copy description [\#782](https://github.com/kevinpapst/kimai2/pull/782) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- delete timesheets via api [\#776](https://github.com/kevinpapst/kimai2/pull/776) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- javascript and api to stop and display active records [\#772](https://github.com/kevinpapst/kimai2/pull/772) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- recent activities via API [\#761](https://github.com/kevinpapst/kimai2/pull/761) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- ES6 javascript conversion [\#733](https://github.com/kevinpapst/kimai2/pull/733) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Use tags for every timesheet entry [\#604](https://github.com/kevinpapst/kimai2/pull/604) ([infastra](https://github.com/infastra))
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- adding a new timesheet entry is broken in current master [\#778](https://github.com/kevinpapst/kimai2/issues/778)
|
||||
- Invoice calculator that differs between fixed and hourly rates [\#884](https://github.com/kevinpapst/kimai2/issues/884)
|
||||
- Timesheets table empty after update [\#878](https://github.com/kevinpapst/kimai2/issues/878)
|
||||
- Ability to hide the revenue stats to users [\#853](https://github.com/kevinpapst/kimai2/issues/853)
|
||||
- Error on update database migration: Syntax error or access violation: 1071 Specified key was too long; max key length is 767 bytes [\#788](https://github.com/kevinpapst/kimai2/issues/788)
|
||||
- calendar week timesheet task with duration=true error 500 [\#704](https://github.com/kevinpapst/kimai2/issues/704)
|
||||
- Inter-month entries don't get exported in single month [\#652](https://github.com/kevinpapst/kimai2/issues/652)
|
||||
- fix selectbox is not resettable [\#901](https://github.com/kevinpapst/kimai2/pull/901) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- fixed short invoice [\#891](https://github.com/kevinpapst/kimai2/pull/891) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- fix sqlite migration [\#881](https://github.com/kevinpapst/kimai2/pull/881) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added LDAP config to set filter for finding user attributes [\#877](https://github.com/kevinpapst/kimai2/pull/877) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- fix LDAP install for systems without ldap extension [\#846](https://github.com/kevinpapst/kimai2/pull/846) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- fixed mpdf temp directory [\#845](https://github.com/kevinpapst/kimai2/pull/845) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- enhance tag query to show all tags of an item [\#810](https://github.com/kevinpapst/kimai2/pull/810) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- fix column and max key length [\#789](https://github.com/kevinpapst/kimai2/pull/789) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- bugfix for API selects [\#780](https://github.com/kevinpapst/kimai2/pull/780) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- translate date picker and charts [\#775](https://github.com/kevinpapst/kimai2/pull/775) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- refactored javascript to ES6 classes [\#759](https://github.com/kevinpapst/kimai2/pull/759) ([kevinpapst](https://github.com/kevinpapst))
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Cannot find bundle in plugin tests [\#863](https://github.com/kevinpapst/kimai2/issues/863)
|
||||
- Allowed memory in reset-dev [\#857](https://github.com/kevinpapst/kimai2/issues/857)
|
||||
- 503 Error Service Unavailable [\#834](https://github.com/kevinpapst/kimai2/issues/834)
|
||||
- Create documentation for layman users to deploy the master code on their servers [\#827](https://github.com/kevinpapst/kimai2/issues/827)
|
||||
- Allow export of data in timesheet view directly from the frontend [\#826](https://github.com/kevinpapst/kimai2/issues/826)
|
||||
- Adding a command as Plugin [\#797](https://github.com/kevinpapst/kimai2/issues/797)
|
||||
- Older Kimai2 version compatible with PHP 7.0? [\#790](https://github.com/kevinpapst/kimai2/issues/790)
|
||||
- POLL: Global vs. customer currency [\#781](https://github.com/kevinpapst/kimai2/issues/781)
|
||||
- Finally got this installed! [\#743](https://github.com/kevinpapst/kimai2/issues/743)
|
||||
- Grouping Users when Exporting [\#680](https://github.com/kevinpapst/kimai2/issues/680)
|
||||
- Update Spanish translation [\#898](https://github.com/kevinpapst/kimai2/issues/898)
|
||||
- Update Hungarian translation [\#897](https://github.com/kevinpapst/kimai2/issues/897)
|
||||
- Authentication to Active Directory [\#875](https://github.com/kevinpapst/kimai2/issues/875)
|
||||
- LDAP Documentation Enhancement for Active Directory [\#867](https://github.com/kevinpapst/kimai2/issues/867)
|
||||
- Documentation for native wrapper like nativefier [\#830](https://github.com/kevinpapst/kimai2/issues/830)
|
||||
- connect kimai to ldap [\#795](https://github.com/kevinpapst/kimai2/issues/795)
|
||||
- Docker refactor [\#763](https://github.com/kevinpapst/kimai2/issues/763)
|
||||
- Possible to translate login page [\#736](https://github.com/kevinpapst/kimai2/issues/736)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Added last changed strings [\#906](https://github.com/kevinpapst/kimai2/pull/906) ([yayitazale](https://github.com/yayitazale))
|
||||
- Update japanese translation 190702 [\#905](https://github.com/kevinpapst/kimai2/pull/905) ([nabbisen](https://github.com/nabbisen))
|
||||
- Update Hungarian translation [\#902](https://github.com/kevinpapst/kimai2/pull/902) ([infeeeee](https://github.com/infeeeee))
|
||||
- performance tuning on timesheets [\#874](https://github.com/kevinpapst/kimai2/pull/874) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Removed lines that skip plugins in test env [\#864](https://github.com/kevinpapst/kimai2/pull/864) ([tobybatch](https://github.com/tobybatch))
|
||||
- fix/LDAP multivalues Email Aliases [\#862](https://github.com/kevinpapst/kimai2/pull/862) ([jacoblanco](https://github.com/jacoblanco))
|
||||
- Spanish translate updated [\#852](https://github.com/kevinpapst/kimai2/pull/852) ([yayitazale](https://github.com/yayitazale))
|
||||
- Update of the French translation [\#850](https://github.com/kevinpapst/kimai2/pull/850) ([jeau](https://github.com/jeau))
|
||||
- improved fixture data for devs [\#848](https://github.com/kevinpapst/kimai2/pull/848) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Implement japanese translation 190601 [\#837](https://github.com/kevinpapst/kimai2/pull/837) ([nabbisen](https://github.com/nabbisen))
|
||||
- use codecov for coverage [\#833](https://github.com/kevinpapst/kimai2/pull/833) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- code cleanup [\#811](https://github.com/kevinpapst/kimai2/pull/811) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- refactored permissions for simpler customizations [\#808](https://github.com/kevinpapst/kimai2/pull/808) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added phpstan for code analyzes [\#806](https://github.com/kevinpapst/kimai2/pull/806) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added SECURITY.md [\#805](https://github.com/kevinpapst/kimai2/pull/805) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added funding yaml [\#804](https://github.com/kevinpapst/kimai2/pull/804) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- timesheet controller refactoring [\#796](https://github.com/kevinpapst/kimai2/pull/796) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- fix phpunit annotations [\#794](https://github.com/kevinpapst/kimai2/pull/794) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Modify Japanese translation [\#779](https://github.com/kevinpapst/kimai2/pull/779) ([nabbisen](https://github.com/nabbisen))
|
||||
- Add Japanese translation [\#774](https://github.com/kevinpapst/kimai2/pull/774) ([nabbisen](https://github.com/nabbisen))
|
||||
- fix toolbar requests for empty choice [\#758](https://github.com/kevinpapst/kimai2/pull/758) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added more entity actions [\#757](https://github.com/kevinpapst/kimai2/pull/757) ([bjoerne2](https://github.com/bjoerne2))
|
||||
- Database interoperability [\#756](https://github.com/kevinpapst/kimai2/pull/756) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- dynamically update title and visible durations for running records [\#752](https://github.com/kevinpapst/kimai2/pull/752) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- colors for visual grouping of customer, projects and activities [\#751](https://github.com/kevinpapst/kimai2/pull/751) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- moved shared entity fields and functions to trait [\#750](https://github.com/kevinpapst/kimai2/pull/750) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Entity actions re-designed \(table buttons\) [\#746](https://github.com/kevinpapst/kimai2/pull/746) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- navbar block cleanup [\#742](https://github.com/kevinpapst/kimai2/pull/742) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- use bootstrap tooltips instead of own solution [\#741](https://github.com/kevinpapst/kimai2/pull/741) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- always query timesheets by begin date [\#740](https://github.com/kevinpapst/kimai2/pull/740) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- prevent jumping icons in navbar between collapsed and open state [\#739](https://github.com/kevinpapst/kimai2/pull/739) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- translate time-tracking in login screen and browser title [\#738](https://github.com/kevinpapst/kimai2/pull/738) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- theme update for swedish auth screens [\#737](https://github.com/kevinpapst/kimai2/pull/737) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Testing: Webpack upgrade [\#729](https://github.com/kevinpapst/kimai2/pull/729) ([kevinpapst](https://github.com/kevinpapst))
|
||||
|
||||
## [0.9](https://github.com/kevinpapst/kimai2/tree/0.9) (2019-04-29)
|
||||
[Full Changelog](https://github.com/kevinpapst/kimai2/compare/0.8.1...0.9)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Translation to Swedish [\#731](https://github.com/kevinpapst/kimai2/issues/731)
|
||||
- Remove active flag in user preferences form for non admin user [\#694](https://github.com/kevinpapst/kimai2/issues/694)
|
||||
- Add floor and ceil rounding [\#692](https://github.com/kevinpapst/kimai2/issues/692)
|
||||
- POST/PUT API endpoints for projects and activities [\#683](https://github.com/kevinpapst/kimai2/issues/683)
|
||||
- API is not able to handle CORS [\#646](https://github.com/kevinpapst/kimai2/issues/646)
|
||||
- Recompile the frontend assets for usage in a sub-directory [\#635](https://github.com/kevinpapst/kimai2/issues/635)
|
||||
- Remove database table prefix [\#633](https://github.com/kevinpapst/kimai2/issues/633)
|
||||
- Autofocus on first entry of forms [\#620](https://github.com/kevinpapst/kimai2/issues/620)
|
||||
- Activity admin - filter for globals [\#598](https://github.com/kevinpapst/kimai2/issues/598)
|
||||
- Non visible customer/projects etc are shown in dropdowns [\#597](https://github.com/kevinpapst/kimai2/issues/597)
|
||||
- remove duplicate confirm dialog of deletion [\#591](https://github.com/kevinpapst/kimai2/issues/591)
|
||||
- remove mobile navbar header [\#588](https://github.com/kevinpapst/kimai2/issues/588)
|
||||
- Support 12h time format [\#578](https://github.com/kevinpapst/kimai2/issues/578)
|
||||
- add timerange filter to timesheet export api [\#577](https://github.com/kevinpapst/kimai2/issues/577)
|
||||
- Extend rounding options with "to closest" mode [\#576](https://github.com/kevinpapst/kimai2/issues/576)
|
||||
- Custom CSS [\#516](https://github.com/kevinpapst/kimai2/issues/516)
|
||||
- reduce config complexity by converting php format to js format [\#734](https://github.com/kevinpapst/kimai2/pull/734) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Updated authorization screens [\#727](https://github.com/kevinpapst/kimai2/pull/727) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added delete timesheet API endpoint [\#726](https://github.com/kevinpapst/kimai2/pull/726) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Design updates [\#723](https://github.com/kevinpapst/kimai2/pull/723) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- API: changed date-format, camelCase instead of snake\_case, null values, update and create for customer and project [\#718](https://github.com/kevinpapst/kimai2/pull/718) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added api/activities POST and PATCH [\#717](https://github.com/kevinpapst/kimai2/pull/717) ([horlabs](https://github.com/horlabs))
|
||||
- allow to overwrite config via prepend [\#715](https://github.com/kevinpapst/kimai2/pull/715) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Load export type ids dynamically [\#703](https://github.com/kevinpapst/kimai2/pull/703) ([simone-gasparini](https://github.com/simone-gasparini))
|
||||
- fixing default env var for cors header [\#656](https://github.com/kevinpapst/kimai2/pull/656) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added cors bundle to allow external api access [\#655](https://github.com/kevinpapst/kimai2/pull/655) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added route to redirect locale to configured homepage [\#654](https://github.com/kevinpapst/kimai2/pull/654) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added database driven system configurations with admin screen [\#647](https://github.com/kevinpapst/kimai2/pull/647) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added new rounding mode: closest [\#611](https://github.com/kevinpapst/kimai2/pull/611) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- order datatable items by name [\#594](https://github.com/kevinpapst/kimai2/pull/594) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- UI improvements for datatables [\#593](https://github.com/kevinpapst/kimai2/pull/593) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Plugins support [\#592](https://github.com/kevinpapst/kimai2/pull/592) ([kevinpapst](https://github.com/kevinpapst))
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- Export date parsing does not care about timezone [\#707](https://github.com/kevinpapst/kimai2/issues/707)
|
||||
- Error on importing v1 timeSheets [\#645](https://github.com/kevinpapst/kimai2/issues/645)
|
||||
- 500 \(Internal Server Error\) create customer [\#711](https://github.com/kevinpapst/kimai2/issues/711)
|
||||
- API docs are wrong for GET entity \(ignores serialization group\) [\#701](https://github.com/kevinpapst/kimai2/issues/701)
|
||||
- API date and datetime format [\#700](https://github.com/kevinpapst/kimai2/issues/700)
|
||||
- totals do not match [\#691](https://github.com/kevinpapst/kimai2/issues/691)
|
||||
- Validate email when creating user via cmd-line [\#663](https://github.com/kevinpapst/kimai2/issues/663)
|
||||
- Datepicker "From" not working [\#651](https://github.com/kevinpapst/kimai2/issues/651)
|
||||
- Error export PDF [\#648](https://github.com/kevinpapst/kimai2/issues/648)
|
||||
- Time selection dropdown not working in Firefox [\#618](https://github.com/kevinpapst/kimai2/issues/618)
|
||||
- Revoke of start\_own\_timesheet permission doesn't prevent start active entry [\#606](https://github.com/kevinpapst/kimai2/issues/606)
|
||||
- resize mobile navbar buttons [\#582](https://github.com/kevinpapst/kimai2/issues/582)
|
||||
- fix tests that failed around midnight [\#735](https://github.com/kevinpapst/kimai2/pull/735) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- remove invalid cache header [\#722](https://github.com/kevinpapst/kimai2/pull/722) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- fix modal - autofocus problem, datepicker not shown on begin date [\#653](https://github.com/kevinpapst/kimai2/pull/653) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- fix pdf export cache directory [\#650](https://github.com/kevinpapst/kimai2/pull/650) ([kevinpapst](https://github.com/kevinpapst))
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- 403 error after installation [\#725](https://github.com/kevinpapst/kimai2/issues/725)
|
||||
- AppExtension Error after bin/console cache:clear --env=prod on Line 43 [\#721](https://github.com/kevinpapst/kimai2/issues/721)
|
||||
- Kimai-v1 Import Failure [\#719](https://github.com/kevinpapst/kimai2/issues/719)
|
||||
- Review export lifecycle for plugin injection [\#702](https://github.com/kevinpapst/kimai2/issues/702)
|
||||
- Missing database-table kimai2\_configuration while trying to run import/migration script [\#698](https://github.com/kevinpapst/kimai2/issues/698)
|
||||
- Import from v1 fails if two users have the same email or blank email [\#688](https://github.com/kevinpapst/kimai2/issues/688)
|
||||
- mysql limit of max amount of activities ? [\#681](https://github.com/kevinpapst/kimai2/issues/681)
|
||||
- Filter by project broken [\#670](https://github.com/kevinpapst/kimai2/issues/670)
|
||||
- Group of user [\#669](https://github.com/kevinpapst/kimai2/issues/669)
|
||||
- Demo not loading [\#665](https://github.com/kevinpapst/kimai2/issues/665)
|
||||
- Admin Panel [\#662](https://github.com/kevinpapst/kimai2/issues/662)
|
||||
- Some activites don't show up in last activities [\#609](https://github.com/kevinpapst/kimai2/issues/609)
|
||||
- Wrong link in the CSS [\#605](https://github.com/kevinpapst/kimai2/issues/605)
|
||||
- Failed to import users: failed to validate user [\#600](https://github.com/kevinpapst/kimai2/issues/600)
|
||||
- Edit timesheet , delete user edit own timesheet [\#498](https://github.com/kevinpapst/kimai2/issues/498)
|
||||
- mixed date formats in the api for the timesheet [\#658](https://github.com/kevinpapst/kimai2/issues/658)
|
||||
- Add demo bundle [\#442](https://github.com/kevinpapst/kimai2/issues/442)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- added svenska [\#732](https://github.com/kevinpapst/kimai2/pull/732) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- apply users timezone to toolbar queries [\#716](https://github.com/kevinpapst/kimai2/pull/716) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- remove unused role ROLE\_CUSTOMER [\#712](https://github.com/kevinpapst/kimai2/pull/712) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Update to Symfony 4.2 [\#710](https://github.com/kevinpapst/kimai2/pull/710) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- fix validation for create user command [\#709](https://github.com/kevinpapst/kimai2/pull/709) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- remove the total counter from all admin pages [\#708](https://github.com/kevinpapst/kimai2/pull/708) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- removed active flag in user preferences form for non admin user. \(\#694\) [\#696](https://github.com/kevinpapst/kimai2/pull/696) ([simone-gasparini](https://github.com/simone-gasparini))
|
||||
- added floor and ceil rounding with tests \(\#692\) [\#695](https://github.com/kevinpapst/kimai2/pull/695) ([simone-gasparini](https://github.com/simone-gasparini))
|
||||
- fixed activity-project assignment for kimai v1 importer [\#679](https://github.com/kevinpapst/kimai2/pull/679) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added content-type to allowed headers for api [\#672](https://github.com/kevinpapst/kimai2/pull/672) ([hmr-it-jr](https://github.com/hmr-it-jr))
|
||||
- added plugin screen [\#671](https://github.com/kevinpapst/kimai2/pull/671) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Updated hungarian translation [\#661](https://github.com/kevinpapst/kimai2/pull/661) ([infeeeee](https://github.com/infeeeee))
|
||||
- remove mailer url from about debug info [\#657](https://github.com/kevinpapst/kimai2/pull/657) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- delete directory with invalid case [\#643](https://github.com/kevinpapst/kimai2/pull/643) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added command to create release packages \(for FTP user\) [\#642](https://github.com/kevinpapst/kimai2/pull/642) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- removed dynamic database prefix [\#641](https://github.com/kevinpapst/kimai2/pull/641) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- set autofocus attribute on form fields for better keyboard support [\#640](https://github.com/kevinpapst/kimai2/pull/640) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- API - added begin end and export filter for timesheets [\#639](https://github.com/kevinpapst/kimai2/pull/639) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- replaced delete confirm dialog with modal [\#638](https://github.com/kevinpapst/kimai2/pull/638) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- convert-timezone: add a default value 'y' to the asked question [\#637](https://github.com/kevinpapst/kimai2/pull/637) ([blueowl04](https://github.com/blueowl04))
|
||||
- support subdirectories and subdomains for assets [\#636](https://github.com/kevinpapst/kimai2/pull/636) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- improved plugin support [\#634](https://github.com/kevinpapst/kimai2/pull/634) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- fix random order in recent activities [\#631](https://github.com/kevinpapst/kimai2/pull/631) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- fix for firefox datepicker focus problem in modals [\#630](https://github.com/kevinpapst/kimai2/pull/630) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- remove invisible entities from toolbars [\#628](https://github.com/kevinpapst/kimai2/pull/628) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- removed inline documentation [\#624](https://github.com/kevinpapst/kimai2/pull/624) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added support for Ante meridiem and Post meridiem format [\#615](https://github.com/kevinpapst/kimai2/pull/615) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added filter for global activities [\#614](https://github.com/kevinpapst/kimai2/pull/614) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Improve create and start permission handling [\#613](https://github.com/kevinpapst/kimai2/pull/613) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Edit and create timesheet records in modal [\#603](https://github.com/kevinpapst/kimai2/pull/603) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- code cleanup [\#602](https://github.com/kevinpapst/kimai2/pull/602) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- simplify theme settings in user profile [\#586](https://github.com/kevinpapst/kimai2/pull/586) ([kevinpapst](https://github.com/kevinpapst))
|
||||
|
||||
## [0.8.1](https://github.com/kevinpapst/kimai2/tree/0.8.1) (2019-02-22)
|
||||
[Full Changelog](https://github.com/kevinpapst/kimai2/compare/0.8...0.8.1)
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- deletion of customers leads to internal server error 500 [\#589](https://github.com/kevinpapst/kimai2/issues/589)
|
||||
- Entry editor label wrong \(from/to\) [\#587](https://github.com/kevinpapst/kimai2/issues/587)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Support foreign keys with SQLite [\#590](https://github.com/kevinpapst/kimai2/pull/590) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- post release fixes [\#584](https://github.com/kevinpapst/kimai2/pull/584) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Mobile navbar size [\#583](https://github.com/kevinpapst/kimai2/pull/583) ([kevinpapst](https://github.com/kevinpapst))
|
||||
|
||||
## [0.8](https://github.com/kevinpapst/kimai2/tree/0.8) (2019-02-19)
|
||||
[Full Changelog](https://github.com/kevinpapst/kimai2/compare/0.7...0.8)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Export other users' timesheets using the API [\#562](https://github.com/kevinpapst/kimai2/issues/562)
|
||||
- Some importer bugs/improvements [\#553](https://github.com/kevinpapst/kimai2/issues/553)
|
||||
- Export timesheet only exports entries on the current page [\#534](https://github.com/kevinpapst/kimai2/issues/534)
|
||||
- On the last activities dropdown show tha last ten DIFFERENT activities [\#533](https://github.com/kevinpapst/kimai2/issues/533)
|
||||
- Better user expierence on "My Times" [\#526](https://github.com/kevinpapst/kimai2/issues/526)
|
||||
- Highlight current day in datepickers and not only the selected day [\#522](https://github.com/kevinpapst/kimai2/issues/522)
|
||||
- Configuration of first page after login [\#501](https://github.com/kevinpapst/kimai2/issues/501)
|
||||
- Visual Grouping of entry from same day or same week with some stats of that group [\#495](https://github.com/kevinpapst/kimai2/issues/495)
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- Error 404 on 'My times' page 2+ if the time range is changed to something smaller [\#558](https://github.com/kevinpapst/kimai2/issues/558)
|
||||
- Timezone is not working correctly [\#554](https://github.com/kevinpapst/kimai2/issues/554)
|
||||
- New time picker not working on languages without updated translation [\#546](https://github.com/kevinpapst/kimai2/issues/546)
|
||||
- Export timesheet only exports entries on the current page [\#534](https://github.com/kevinpapst/kimai2/issues/534)
|
||||
- Kimai2 will not allow me to enter times in the future [\#531](https://github.com/kevinpapst/kimai2/issues/531)
|
||||
- No projects in filter section [\#525](https://github.com/kevinpapst/kimai2/issues/525)
|
||||
- This value should be greater than or equal to zero [\#511](https://github.com/kevinpapst/kimai2/issues/511)
|
||||
- Wrong time after migration kimai v1 data [\#507](https://github.com/kevinpapst/kimai2/issues/507)
|
||||
- SQL-Error for getRecentActivities\(\) caused by GROUP BY [\#488](https://github.com/kevinpapst/kimai2/issues/488)
|
||||
- fix sql for only\_full\_group\_by [\#581](https://github.com/kevinpapst/kimai2/pull/581) ([kevinpapst](https://github.com/kevinpapst))
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Feedback for improving installation setup [\#541](https://github.com/kevinpapst/kimai2/issues/541)
|
||||
- template entry.end\_time shows empty field [\#535](https://github.com/kevinpapst/kimai2/issues/535)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- handle deleted user during import from v1 [\#569](https://github.com/kevinpapst/kimai2/pull/569) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- fix pagination in combination with daterange picker [\#568](https://github.com/kevinpapst/kimai2/pull/568) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- allow to query other users timesheets via api [\#563](https://github.com/kevinpapst/kimai2/pull/563) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Updated HTML invoice templates [\#560](https://github.com/kevinpapst/kimai2/pull/560) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- updated composer packages [\#559](https://github.com/kevinpapst/kimai2/pull/559) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- fix timezone problems in timesheet forms [\#555](https://github.com/kevinpapst/kimai2/pull/555) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Daily stats in timesheet [\#552](https://github.com/kevinpapst/kimai2/pull/552) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Added more php requirements to documentation [\#551](https://github.com/kevinpapst/kimai2/pull/551) ([infeeeee](https://github.com/infeeeee))
|
||||
- improve recent activities [\#550](https://github.com/kevinpapst/kimai2/pull/550) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- scss fixes: year selector width, dropdown menu width, navbar refactoring [\#549](https://github.com/kevinpapst/kimai2/pull/549) ([infeeeee](https://github.com/infeeeee))
|
||||
- improved installation docs [\#548](https://github.com/kevinpapst/kimai2/pull/548) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- fix daterange-picker for fr, hu and ar [\#547](https://github.com/kevinpapst/kimai2/pull/547) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- do not limit users timesheet export page size [\#545](https://github.com/kevinpapst/kimai2/pull/545) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Use Symfony formatter for currency symbol placement [\#542](https://github.com/kevinpapst/kimai2/pull/542) ([sanjitlpatel](https://github.com/sanjitlpatel))
|
||||
- Localized date-inputs and daterange-picker [\#540](https://github.com/kevinpapst/kimai2/pull/540) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added export module [\#538](https://github.com/kevinpapst/kimai2/pull/538) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- updated documentation [\#536](https://github.com/kevinpapst/kimai2/pull/536) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- support remote data in beta-test selectpicker [\#529](https://github.com/kevinpapst/kimai2/pull/529) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Improve allowed running records [\#528](https://github.com/kevinpapst/kimai2/pull/528) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Improve daterangepicker [\#527](https://github.com/kevinpapst/kimai2/pull/527) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added configurable view after login [\#523](https://github.com/kevinpapst/kimai2/pull/523) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- convert timesheets to UTC with support for user timezone [\#372](https://github.com/kevinpapst/kimai2/pull/372) ([kevinpapst](https://github.com/kevinpapst))
|
||||
|
||||
## [0.7](https://github.com/kevinpapst/kimai2/tree/0.7) (2019-01-28)
|
||||
[Full Changelog](https://github.com/kevinpapst/kimai2/compare/0.6.1...0.7)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Editing from calendar view will return to my times table instead of calendar [\#515](https://github.com/kevinpapst/kimai2/issues/515)
|
||||
- Timesheet Export for Admins [\#503](https://github.com/kevinpapst/kimai2/issues/503)
|
||||
- Customer List Not Alphabetic [\#499](https://github.com/kevinpapst/kimai2/issues/499)
|
||||
- sorting of ${entry.X} values [\#487](https://github.com/kevinpapst/kimai2/issues/487)
|
||||
- ${entry.description} needed in Word-Docx [\#485](https://github.com/kevinpapst/kimai2/issues/485)
|
||||
- Automatic sort for activities, customers, etc. [\#477](https://github.com/kevinpapst/kimai2/issues/477)
|
||||
- User title in the timesheet invoice [\#461](https://github.com/kevinpapst/kimai2/issues/461)
|
||||
- global variables for reports/invoices [\#438](https://github.com/kevinpapst/kimai2/issues/438)
|
||||
- Configuration option: Only one active record for each user [\#427](https://github.com/kevinpapst/kimai2/issues/427)
|
||||
- User creating activity and projects [\#393](https://github.com/kevinpapst/kimai2/issues/393)
|
||||
- Configuration option to disable fixed rate and hourly rate from "edit timesheet" [\#330](https://github.com/kevinpapst/kimai2/issues/330)
|
||||
- Set other users hourly rate [\#303](https://github.com/kevinpapst/kimai2/issues/303)
|
||||
- Feature request - Make "Rate" hideable [\#217](https://github.com/kevinpapst/kimai2/issues/217)
|
||||
- fixed null project for advanced invoice calculator [\#462](https://github.com/kevinpapst/kimai2/pull/462) ([kevinpapst](https://github.com/kevinpapst))
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- Timesheet Export for Admins [\#503](https://github.com/kevinpapst/kimai2/issues/503)
|
||||
- admin activity: visibility "none" \(no filter\) causes sql-error [\#491](https://github.com/kevinpapst/kimai2/issues/491)
|
||||
- login-screen optimizations [\#483](https://github.com/kevinpapst/kimai2/issues/483)
|
||||
- Configuration of roles to add/edit customers, projects, activities... [\#479](https://github.com/kevinpapst/kimai2/issues/479)
|
||||
- Line breaks for address and payment information fields [\#464](https://github.com/kevinpapst/kimai2/issues/464)
|
||||
- Possible to use a decimal in hourly rate field? [\#458](https://github.com/kevinpapst/kimai2/issues/458)
|
||||
- Invoice Number Generator possibly not compatible [\#454](https://github.com/kevinpapst/kimai2/issues/454)
|
||||
- users can change their role to system-admin [\#440](https://github.com/kevinpapst/kimai2/issues/440)
|
||||
- fix wrong include filename in user registration [\#520](https://github.com/kevinpapst/kimai2/pull/520) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- fix segmentation fault - rollback composer dependencies [\#463](https://github.com/kevinpapst/kimai2/pull/463) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- fixed null project for advanced invoice calculator [\#462](https://github.com/kevinpapst/kimai2/pull/462) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- fix the restart timesheet button [\#436](https://github.com/kevinpapst/kimai2/pull/436) ([kevinpapst](https://github.com/kevinpapst))
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Redirecting when using Kimai on a subdirectory + reverse proxy [\#492](https://github.com/kevinpapst/kimai2/issues/492)
|
||||
- Installation \(Cannot declare.... in use\) [\#455](https://github.com/kevinpapst/kimai2/issues/455)
|
||||
- New Number Generator not recognized [\#453](https://github.com/kevinpapst/kimai2/issues/453)
|
||||
- de/help/invoices returns 404 error [\#452](https://github.com/kevinpapst/kimai2/issues/452)
|
||||
- Can you Add Brazilian Portuguese Translation? I can help it.. [\#444](https://github.com/kevinpapst/kimai2/issues/444)
|
||||
- replace all selects with smart-selects and live-search [\#441](https://github.com/kevinpapst/kimai2/issues/441)
|
||||
- use parsedown-extra for rendering markdown [\#388](https://github.com/kevinpapst/kimai2/issues/388)
|
||||
- Think about a cooler name [\#133](https://github.com/kevinpapst/kimai2/issues/133)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- pagination without reload while keeping filters applied [\#521](https://github.com/kevinpapst/kimai2/pull/521) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- go back to calendar after editing and creation of time-records [\#519](https://github.com/kevinpapst/kimai2/pull/519) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- fetch toolbar results without page reload [\#518](https://github.com/kevinpapst/kimai2/pull/518) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Form and theme improvements [\#513](https://github.com/kevinpapst/kimai2/pull/513) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- validation for future and negative times [\#512](https://github.com/kevinpapst/kimai2/pull/512) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- alphabetical order for selectboxes [\#510](https://github.com/kevinpapst/kimai2/pull/510) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Export team timesheets [\#508](https://github.com/kevinpapst/kimai2/pull/508) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- fix broken sql in activity admin [\#506](https://github.com/kevinpapst/kimai2/pull/506) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- display invoice entries in ascending order [\#505](https://github.com/kevinpapst/kimai2/pull/505) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Improve DOCX template row [\#504](https://github.com/kevinpapst/kimai2/pull/504) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- do not display admin menu if it has no children [\#500](https://github.com/kevinpapst/kimai2/pull/500) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- login-screen optimizations [\#493](https://github.com/kevinpapst/kimai2/pull/493) ([lduer](https://github.com/lduer))
|
||||
- preg\_replace for md-file-extensions when rendering correct link… [\#482](https://github.com/kevinpapst/kimai2/pull/482) ([lduer](https://github.com/lduer))
|
||||
- Better composer install in the docker [\#481](https://github.com/kevinpapst/kimai2/pull/481) ([tobybatch](https://github.com/tobybatch))
|
||||
- Update dockerfile for https in composer installer [\#478](https://github.com/kevinpapst/kimai2/pull/478) ([scolson](https://github.com/scolson))
|
||||
- support line breaks in docx [\#466](https://github.com/kevinpapst/kimai2/pull/466) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- updated README and docu [\#465](https://github.com/kevinpapst/kimai2/pull/465) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- allow decimals in users hourly rate [\#460](https://github.com/kevinpapst/kimai2/pull/460) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- updated all composer packages [\#459](https://github.com/kevinpapst/kimai2/pull/459) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- moved some packages to dev requirements [\#456](https://github.com/kevinpapst/kimai2/pull/456) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Added portuguese translations [\#446](https://github.com/kevinpapst/kimai2/pull/446) ([marquesmatheus](https://github.com/marquesmatheus))
|
||||
- Simplify timesheet edit form if only one customer is existing [\#443](https://github.com/kevinpapst/kimai2/pull/443) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added project variables for invoice templates [\#439](https://github.com/kevinpapst/kimai2/pull/439) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added configurable permission system [\#424](https://github.com/kevinpapst/kimai2/pull/424) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Only one running timesheet: automatically stop others [\#386](https://github.com/kevinpapst/kimai2/pull/386) ([lduer](https://github.com/lduer))
|
||||
|
||||
## [0.6.1](https://github.com/kevinpapst/kimai2/tree/0.6.1) (2018-11-19)
|
||||
[Full Changelog](https://github.com/kevinpapst/kimai2/compare/0.6...0.6.1)
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- Error importing v1 Database [\#428](https://github.com/kevinpapst/kimai2/issues/428)
|
||||
- Redo button missing? [\#426](https://github.com/kevinpapst/kimai2/issues/426)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Error importing v1 Database \(could not convert to string\) [\#431](https://github.com/kevinpapst/kimai2/issues/431)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- fix migration for MySQL [\#430](https://github.com/kevinpapst/kimai2/pull/430) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- fix importer for MySQL [\#429](https://github.com/kevinpapst/kimai2/pull/429) ([kevinpapst](https://github.com/kevinpapst))
|
||||
|
||||
## [0.6](https://github.com/kevinpapst/kimai2/tree/0.6) (2018-11-18)
|
||||
[Full Changelog](https://github.com/kevinpapst/kimai2/compare/0.5...0.6)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Language setting does not work immediately [\#418](https://github.com/kevinpapst/kimai2/issues/418)
|
||||
- Merge projects and/or activities [\#406](https://github.com/kevinpapst/kimai2/issues/406)
|
||||
- Global activities support for Kimai 1 importer [\#400](https://github.com/kevinpapst/kimai2/issues/400)
|
||||
- Invoice Grouped by Activity [\#379](https://github.com/kevinpapst/kimai2/issues/379)
|
||||
- Set the order of columns \(in my times page\) [\#371](https://github.com/kevinpapst/kimai2/issues/371)
|
||||
- Columns on small/narrow displays \(mobile\) on „my times“ page [\#370](https://github.com/kevinpapst/kimai2/issues/370)
|
||||
- Make settings more accessible [\#365](https://github.com/kevinpapst/kimai2/issues/365)
|
||||
- Data validation for register page username field. [\#360](https://github.com/kevinpapst/kimai2/issues/360)
|
||||
- Option for setting default calendar view [\#359](https://github.com/kevinpapst/kimai2/issues/359)
|
||||
- Activities just Duration [\#334](https://github.com/kevinpapst/kimai2/issues/334)
|
||||
- API: Add filtering/sorting/limits [\#333](https://github.com/kevinpapst/kimai2/issues/333)
|
||||
- API: Timesheets [\#315](https://github.com/kevinpapst/kimai2/issues/315)
|
||||
- Feature request: Quick search box for activities when creating new timesheet [\#307](https://github.com/kevinpapst/kimai2/issues/307)
|
||||
- Separate Customer / Project / Activity input when logging time [\#250](https://github.com/kevinpapst/kimai2/issues/250)
|
||||
- Interactive help after fresh installation [\#94](https://github.com/kevinpapst/kimai2/issues/94)
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- Invoicing export - 500: Internal Server Error [\#390](https://github.com/kevinpapst/kimai2/issues/390)
|
||||
- invalid CSRF-Token [\#344](https://github.com/kevinpapst/kimai2/issues/344)
|
||||
- fix column visibility if unconfigured \(no cookie existing\) [\#423](https://github.com/kevinpapst/kimai2/pull/423) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- fix duration and rate for html invoices [\#414](https://github.com/kevinpapst/kimai2/pull/414) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- fixed broken toolbar form with empty date [\#391](https://github.com/kevinpapst/kimai2/pull/391) ([kevinpapst](https://github.com/kevinpapst))
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- After installation no graphic/background/icons shown [\#417](https://github.com/kevinpapst/kimai2/issues/417)
|
||||
- Form fields should be always visible [\#408](https://github.com/kevinpapst/kimai2/issues/408)
|
||||
- Active filter should stay open [\#405](https://github.com/kevinpapst/kimai2/issues/405)
|
||||
- Make project field optional [\#401](https://github.com/kevinpapst/kimai2/issues/401)
|
||||
- Unrecognized Options "renderer, number\_generator" under "kimai.invoice" [\#381](https://github.com/kevinpapst/kimai2/issues/381)
|
||||
- 500: Internal Server Error after login \(new install\) [\#342](https://github.com/kevinpapst/kimai2/issues/342)
|
||||
- Invoices export error [\#341](https://github.com/kevinpapst/kimai2/issues/341)
|
||||
- add the same activity for multiple projects [\#325](https://github.com/kevinpapst/kimai2/issues/325)
|
||||
- API: Endpoint for API docs needs session cookie [\#319](https://github.com/kevinpapst/kimai2/issues/319)
|
||||
- docker image for kimai2 [\#284](https://github.com/kevinpapst/kimai2/issues/284)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- redirect to user language after profile update [\#421](https://github.com/kevinpapst/kimai2/pull/421) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added more timesheet related invoice fields [\#411](https://github.com/kevinpapst/kimai2/pull/411) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- toolbar improvements [\#410](https://github.com/kevinpapst/kimai2/pull/410) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- move entries from one entity to another upon deletion [\#409](https://github.com/kevinpapst/kimai2/pull/409) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- show toolbar filter if they were submitted [\#407](https://github.com/kevinpapst/kimai2/pull/407) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- full configurable data columns in all screen sizes [\#404](https://github.com/kevinpapst/kimai2/pull/404) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added global activity support in importer [\#402](https://github.com/kevinpapst/kimai2/pull/402) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added parsedown extension for header ids \(\#388\) [\#399](https://github.com/kevinpapst/kimai2/pull/399) ([lduer](https://github.com/lduer))
|
||||
- Fix for \#397 [\#398](https://github.com/kevinpapst/kimai2/pull/398) ([tobybatch](https://github.com/tobybatch))
|
||||
- Added links to docker hub and my docker repo [\#396](https://github.com/kevinpapst/kimai2/pull/396) ([tobybatch](https://github.com/tobybatch))
|
||||
- fixed issues- & milestone links [\#395](https://github.com/kevinpapst/kimai2/pull/395) ([lduer](https://github.com/lduer))
|
||||
- added links to further docker images by the community [\#394](https://github.com/kevinpapst/kimai2/pull/394) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- beta test: added quick search box for customers, projects and activities [\#392](https://github.com/kevinpapst/kimai2/pull/392) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- updated the documentation, reflecting the changes in \#306 [\#387](https://github.com/kevinpapst/kimai2/pull/387) ([lduer](https://github.com/lduer))
|
||||
- Added Hungarian translation and fixed the path of kimai.yaml in translation documentation [\#385](https://github.com/kevinpapst/kimai2/pull/385) ([infeeeee](https://github.com/infeeeee))
|
||||
- improved docs and cross-linked docker [\#383](https://github.com/kevinpapst/kimai2/pull/383) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Doc update of install/update instructions re: username [\#382](https://github.com/kevinpapst/kimai2/pull/382) ([srdco](https://github.com/srdco))
|
||||
- invoice calculator to group entries by activity [\#380](https://github.com/kevinpapst/kimai2/pull/380) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- basic about screen with server and debug infos [\#378](https://github.com/kevinpapst/kimai2/pull/378) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Dev/demo dockerfile [\#377](https://github.com/kevinpapst/kimai2/pull/377) ([tobybatch](https://github.com/tobybatch))
|
||||
- Sidebar - user-profile links and language settings [\#369](https://github.com/kevinpapst/kimai2/pull/369) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added configurable initial calendar view [\#363](https://github.com/kevinpapst/kimai2/pull/363) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- decrease minimum username to 3 character [\#362](https://github.com/kevinpapst/kimai2/pull/362) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Default country \(\#2\) [\#358](https://github.com/kevinpapst/kimai2/pull/358) ([tobybatch](https://github.com/tobybatch))
|
||||
- composer: removed deprecated symfony/lt, updated others [\#354](https://github.com/kevinpapst/kimai2/pull/354) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- updated admin-lte theme bundle to 2.1.1 [\#350](https://github.com/kevinpapst/kimai2/pull/350) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- updated timesheet and email documentation [\#349](https://github.com/kevinpapst/kimai2/pull/349) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- updated required dependencies [\#346](https://github.com/kevinpapst/kimai2/pull/346) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Fix password reset [\#345](https://github.com/kevinpapst/kimai2/pull/345) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added arabic translations and locale support in datepicker [\#337](https://github.com/kevinpapst/kimai2/pull/337) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- API endpoint for Timesheet entries [\#332](https://github.com/kevinpapst/kimai2/pull/332) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Support global activities [\#259](https://github.com/kevinpapst/kimai2/pull/259) ([kevinpapst](https://github.com/kevinpapst))
|
||||
|
||||
## [0.5](https://github.com/kevinpapst/kimai2/tree/0.5) (2018-09-27)
|
||||
[Full Changelog](https://github.com/kevinpapst/kimai2/compare/0.4...0.5)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- API: Kimai metadata endpoint [\#320](https://github.com/kevinpapst/kimai2/issues/320)
|
||||
- Support Markdown in timesheet description [\#295](https://github.com/kevinpapst/kimai2/issues/295)
|
||||
- Add support for MS Office templates [\#283](https://github.com/kevinpapst/kimai2/issues/283)
|
||||
- Adding rates to customers, projects and tasks [\#271](https://github.com/kevinpapst/kimai2/issues/271)
|
||||
- Delete invoice templates [\#267](https://github.com/kevinpapst/kimai2/issues/267)
|
||||
- Sum of hours in timesheet invoice [\#262](https://github.com/kevinpapst/kimai2/issues/262)
|
||||
- Feature request - API [\#251](https://github.com/kevinpapst/kimai2/issues/251)
|
||||
- Feature request - PDF Download for monthly report [\#244](https://github.com/kevinpapst/kimai2/issues/244)
|
||||
- Add support for OpenOffice templates [\#223](https://github.com/kevinpapst/kimai2/issues/223)
|
||||
- Feature Request - Excel Output of times [\#221](https://github.com/kevinpapst/kimai2/issues/221)
|
||||
- Add hourly rate options [\#111](https://github.com/kevinpapst/kimai2/issues/111)
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- Excel Output not containing data [\#327](https://github.com/kevinpapst/kimai2/issues/327)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Creating database schema throws exceptions [\#322](https://github.com/kevinpapst/kimai2/issues/322)
|
||||
- Add permission voter for invoices and templates [\#293](https://github.com/kevinpapst/kimai2/issues/293)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- added copy invoice template action [\#331](https://github.com/kevinpapst/kimai2/pull/331) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- fix spreadsheet renderer for invoices with one entry [\#328](https://github.com/kevinpapst/kimai2/pull/328) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- invoice VAT as float and database key length [\#323](https://github.com/kevinpapst/kimai2/pull/323) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added version command + api endpoint [\#321](https://github.com/kevinpapst/kimai2/pull/321) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Timesheet export [\#317](https://github.com/kevinpapst/kimai2/pull/317) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added invoice voter [\#316](https://github.com/kevinpapst/kimai2/pull/316) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Refactored invoice system [\#306](https://github.com/kevinpapst/kimai2/pull/306) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- add stable controller tests [\#305](https://github.com/kevinpapst/kimai2/pull/305) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Fixed rate and Hourly rates [\#304](https://github.com/kevinpapst/kimai2/pull/304) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- add hourly\_rate and fixed\_rate to timesheet entries [\#302](https://github.com/kevinpapst/kimai2/pull/302) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- allow to switch language on any page [\#299](https://github.com/kevinpapst/kimai2/pull/299) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Fix duration\_short format for french translation [\#297](https://github.com/kevinpapst/kimai2/pull/297) ([jeau](https://github.com/jeau))
|
||||
- allow markdown in timesheet descriptions [\#296](https://github.com/kevinpapst/kimai2/pull/296) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added option to delete invoice template [\#294](https://github.com/kevinpapst/kimai2/pull/294) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Improved invoice preview [\#254](https://github.com/kevinpapst/kimai2/pull/254) ([kevinpapst](https://github.com/kevinpapst))
|
||||
|
||||
## [0.4](https://github.com/kevinpapst/kimai2/tree/0.4) (2018-09-01)
|
||||
[Full Changelog](https://github.com/kevinpapst/kimai2/compare/0.3...0.4)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Delete user [\#225](https://github.com/kevinpapst/kimai2/issues/225)
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- Don't allow to stop an already stopped entry [\#282](https://github.com/kevinpapst/kimai2/issues/282)
|
||||
- installed it, and then what??? [\#255](https://github.com/kevinpapst/kimai2/issues/255)
|
||||
- fixed calendar for empty timesheet descriptions [\#265](https://github.com/kevinpapst/kimai2/pull/265) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- fixed .htaccess for apache users [\#260](https://github.com/kevinpapst/kimai2/pull/260) ([kevinpapst](https://github.com/kevinpapst))
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Spanish Translation Contrib [\#248](https://github.com/kevinpapst/kimai2/issues/248)
|
||||
- Administration of projects throws error if locale is "de" [\#247](https://github.com/kevinpapst/kimai2/issues/247)
|
||||
- Web Installer Package for near-automated installs from browser [\#235](https://github.com/kevinpapst/kimai2/issues/235)
|
||||
- Improve dashboard display [\#280](https://github.com/kevinpapst/kimai2/issues/280)
|
||||
- Feature request - Monthly overview / report [\#207](https://github.com/kevinpapst/kimai2/issues/207)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- fixed money display in dashboard widgets [\#291](https://github.com/kevinpapst/kimai2/pull/291) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Symfony update 4.1.4 [\#290](https://github.com/kevinpapst/kimai2/pull/290) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- do not allow to stop already stopped timesheets [\#289](https://github.com/kevinpapst/kimai2/pull/289) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added configurable formats for duration [\#287](https://github.com/kevinpapst/kimai2/pull/287) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Updated russian translations [\#286](https://github.com/kevinpapst/kimai2/pull/286) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Update french translation [\#279](https://github.com/kevinpapst/kimai2/pull/279) ([jeau](https://github.com/jeau))
|
||||
- Fix standard date format in french [\#278](https://github.com/kevinpapst/kimai2/pull/278) ([jeau](https://github.com/jeau))
|
||||
- new translations: spanish, french and italian [\#276](https://github.com/kevinpapst/kimai2/pull/276) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- French translation for Kimai [\#275](https://github.com/kevinpapst/kimai2/pull/275) ([jeau](https://github.com/jeau))
|
||||
- updated to AdminLTE 2.4.8 [\#274](https://github.com/kevinpapst/kimai2/pull/274) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- fixing migration\_v1.md url [\#270](https://github.com/kevinpapst/kimai2/pull/270) ([ltsavar](https://github.com/ltsavar))
|
||||
- dashboard widgets are configurable via config [\#269](https://github.com/kevinpapst/kimai2/pull/269) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Spanish translation for Kimai [\#266](https://github.com/kevinpapst/kimai2/pull/266) ([cobaltos](https://github.com/cobaltos))
|
||||
- moved theme settings to dynamic compiler pass [\#264](https://github.com/kevinpapst/kimai2/pull/264) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Added config setting to disable password-reset and user-registration [\#261](https://github.com/kevinpapst/kimai2/pull/261) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Added basic API endpoints [\#258](https://github.com/kevinpapst/kimai2/pull/258) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Hotfix for Symfony Flex incompatibility with latest composer changes [\#257](https://github.com/kevinpapst/kimai2/pull/257) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added italian translation [\#253](https://github.com/kevinpapst/kimai2/pull/253) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Delete user [\#249](https://github.com/kevinpapst/kimai2/pull/249) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- dashboard widgets from event [\#246](https://github.com/kevinpapst/kimai2/pull/246) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Added timesheet-calendar view [\#236](https://github.com/kevinpapst/kimai2/pull/236) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Improved data fixtures [\#234](https://github.com/kevinpapst/kimai2/pull/234) ([kevinpapst](https://github.com/kevinpapst))
|
||||
|
||||
## [0.3](https://github.com/kevinpapst/kimai2/tree/0.3) (2018-07-22)
|
||||
[Full Changelog](https://github.com/kevinpapst/kimai2/compare/0.2...0.3)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Install Kimai as WebApp [\#203](https://github.com/kevinpapst/kimai2/issues/203)
|
||||
- Replace AvanzuAdminTheme with AdminLTE bundle [\#201](https://github.com/kevinpapst/kimai2/issues/201)
|
||||
- Add dynamic column filter [\#174](https://github.com/kevinpapst/kimai2/issues/174)
|
||||
- Add register user function [\#164](https://github.com/kevinpapst/kimai2/issues/164)
|
||||
- Add forgot password function - login screen [\#163](https://github.com/kevinpapst/kimai2/issues/163)
|
||||
- Integrate FOSUserBundle \(for registration, password-reset, etc.\) [\#144](https://github.com/kevinpapst/kimai2/issues/144)
|
||||
- added dynamic column filter \#174 [\#184](https://github.com/kevinpapst/kimai2/pull/184) ([kevinpapst](https://github.com/kevinpapst))
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- Missing assets in fresh installation [\#213](https://github.com/kevinpapst/kimai2/issues/213)
|
||||
- Cannot remove payment terms from invoice template [\#188](https://github.com/kevinpapst/kimai2/issues/188)
|
||||
- Set PHP locale for date format and month names [\#110](https://github.com/kevinpapst/kimai2/issues/110)
|
||||
- Show revenue only for Admin \(wrong currency shown\) [\#19](https://github.com/kevinpapst/kimai2/issues/19)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Installation Issue of version1.3.1 [\#208](https://github.com/kevinpapst/kimai2/issues/208)
|
||||
- Error on bin/console doctrine:schema:create [\#191](https://github.com/kevinpapst/kimai2/issues/191)
|
||||
- Verifying email configuration [\#226](https://github.com/kevinpapst/kimai2/issues/226)
|
||||
- Add favicon [\#205](https://github.com/kevinpapst/kimai2/issues/205)
|
||||
- Document DB requirements in install docu [\#196](https://github.com/kevinpapst/kimai2/issues/196)
|
||||
- easier creation of tasks [\#195](https://github.com/kevinpapst/kimai2/issues/195)
|
||||
- Integrate lock bot [\#183](https://github.com/kevinpapst/kimai2/issues/183)
|
||||
- Upgrade to FontAwesome 5 [\#179](https://github.com/kevinpapst/kimai2/issues/179)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Easier creation of Tasks/Projects/User [\#230](https://github.com/kevinpapst/kimai2/pull/230) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Added email configuration docs [\#228](https://github.com/kevinpapst/kimai2/pull/228) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Show revenue only for ROLE\_ADMIN [\#227](https://github.com/kevinpapst/kimai2/pull/227) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- hotfix responsiveness [\#220](https://github.com/kevinpapst/kimai2/pull/220) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Integrated FOSUserBundle [\#216](https://github.com/kevinpapst/kimai2/pull/216) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added auto-script to install assets [\#214](https://github.com/kevinpapst/kimai2/pull/214) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added yunohost installation link [\#212](https://github.com/kevinpapst/kimai2/pull/212) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added favicons and web app metadata [\#211](https://github.com/kevinpapst/kimai2/pull/211) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added FAQ and database requirement docu \#196 [\#210](https://github.com/kevinpapst/kimai2/pull/210) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Updated to AdminLTEBundle 1.0 [\#206](https://github.com/kevinpapst/kimai2/pull/206) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- replaced AvanzuAdminTheme with AdminLTE bundle [\#202](https://github.com/kevinpapst/kimai2/pull/202) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- allow empty invoice template terms \#188 [\#189](https://github.com/kevinpapst/kimai2/pull/189) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- improved install docs and composer [\#187](https://github.com/kevinpapst/kimai2/pull/187) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added lock-bot config \#183 [\#186](https://github.com/kevinpapst/kimai2/pull/186) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Composer package name [\#185](https://github.com/kevinpapst/kimai2/pull/185) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- updated to font-awesome 5 \#179 [\#181](https://github.com/kevinpapst/kimai2/pull/181) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- language specific money and date display [\#180](https://github.com/kevinpapst/kimai2/pull/180) ([kevinpapst](https://github.com/kevinpapst))
|
||||
|
||||
## [0.2](https://github.com/kevinpapst/kimai2/tree/0.2) (2018-06-23)
|
||||
[Full Changelog](https://github.com/kevinpapst/kimai2/compare/0.1...0.2)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Add english translation [\#8](https://github.com/kevinpapst/kimai2/issues/8)
|
||||
- Add configurable system configuration [\#6](https://github.com/kevinpapst/kimai2/issues/6)
|
||||
- Hide notifications after some seconds [\#169](https://github.com/kevinpapst/kimai2/issues/169)
|
||||
- Setup Scrutinizer to run code sniffer [\#158](https://github.com/kevinpapst/kimai2/issues/158)
|
||||
- Local config to overwrite settings [\#153](https://github.com/kevinpapst/kimai2/issues/153)
|
||||
- Improve mobile view [\#151](https://github.com/kevinpapst/kimai2/issues/151)
|
||||
- Add help text to form elements [\#138](https://github.com/kevinpapst/kimai2/issues/138)
|
||||
- Add duration-only mode [\#131](https://github.com/kevinpapst/kimai2/issues/131)
|
||||
- Add help controller [\#129](https://github.com/kevinpapst/kimai2/issues/129)
|
||||
- Secure create-user command [\#123](https://github.com/kevinpapst/kimai2/issues/123)
|
||||
- Add command for building a production release [\#115](https://github.com/kevinpapst/kimai2/issues/115)
|
||||
- Add webpack and webpack-encore for frontend assets [\#113](https://github.com/kevinpapst/kimai2/issues/113)
|
||||
- Add time rounding option [\#112](https://github.com/kevinpapst/kimai2/issues/112)
|
||||
- Add field order number in Projects [\#107](https://github.com/kevinpapst/kimai2/issues/107)
|
||||
- Create docu for invoice templates [\#106](https://github.com/kevinpapst/kimai2/issues/106)
|
||||
- Add invoice template: timesheet [\#104](https://github.com/kevinpapst/kimai2/issues/104)
|
||||
- Add importer from Kimai v1 [\#102](https://github.com/kevinpapst/kimai2/issues/102)
|
||||
- Setup error pages [\#100](https://github.com/kevinpapst/kimai2/issues/100)
|
||||
- Add basic HTML invoice rendering [\#97](https://github.com/kevinpapst/kimai2/issues/97)
|
||||
- Filter user in timesheet admin [\#93](https://github.com/kevinpapst/kimai2/issues/93)
|
||||
- Support to switch theme settings [\#89](https://github.com/kevinpapst/kimai2/issues/89)
|
||||
- Roles should become a real table [\#86](https://github.com/kevinpapst/kimai2/issues/86)
|
||||
- Fix Doctrine extensions config loader [\#84](https://github.com/kevinpapst/kimai2/issues/84)
|
||||
- Upgrade to Symfony 4 and Flex [\#74](https://github.com/kevinpapst/kimai2/issues/74)
|
||||
- Add remember me login [\#53](https://github.com/kevinpapst/kimai2/issues/53)
|
||||
- Add preferences sidebar [\#10](https://github.com/kevinpapst/kimai2/issues/10)
|
||||
- Add user settings [\#9](https://github.com/kevinpapst/kimai2/issues/9)
|
||||
- Mobile alignments and toolbar \#151 [\#157](https://github.com/kevinpapst/kimai2/pull/157) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Local config \#153 [\#156](https://github.com/kevinpapst/kimai2/pull/156) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Filter in hidden toolbar - improved mobile \#151 [\#155](https://github.com/kevinpapst/kimai2/pull/155) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Improved mobile view \#151 [\#150](https://github.com/kevinpapst/kimai2/pull/150) ([kevinpapst](https://github.com/kevinpapst))
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- Change to users language after login [\#7](https://github.com/kevinpapst/kimai2/issues/7)
|
||||
- Prevent end time before start time [\#152](https://github.com/kevinpapst/kimai2/issues/152)
|
||||
- Importing old data fails [\#145](https://github.com/kevinpapst/kimai2/issues/145)
|
||||
- Translate LanguageType choices [\#83](https://github.com/kevinpapst/kimai2/issues/83)
|
||||
- Mobile alignments and toolbar \\#151 [\#157](https://github.com/kevinpapst/kimai2/pull/157) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Improved mobile view \\#151 [\#150](https://github.com/kevinpapst/kimai2/pull/150) ([kevinpapst](https://github.com/kevinpapst))
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Add CHANGELOG.md [\#177](https://github.com/kevinpapst/kimai2/issues/177)
|
||||
- Add preview blog message and info text on old website [\#162](https://github.com/kevinpapst/kimai2/issues/162)
|
||||
- Upgrade to Symfony 4.1 [\#149](https://github.com/kevinpapst/kimai2/issues/149)
|
||||
- Replace has\_role\(\) with is\_granted\(\) [\#148](https://github.com/kevinpapst/kimai2/issues/148)
|
||||
- Duration mode: creating new timesheet records prefills duration with some seconds [\#137](https://github.com/kevinpapst/kimai2/issues/137)
|
||||
- \[RFC\] Encore: configureFilenames with hash after filename instead of in the filename [\#125](https://github.com/kevinpapst/kimai2/issues/125)
|
||||
- \[RFC\] Kimai license header [\#124](https://github.com/kevinpapst/kimai2/issues/124)
|
||||
- Setup travis to run phpunit tests [\#120](https://github.com/kevinpapst/kimai2/issues/120)
|
||||
- Add russian translation [\#95](https://github.com/kevinpapst/kimai2/issues/95)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- \[WIP\] Translations [\#173](https://github.com/kevinpapst/kimai2/pull/173) ([simonschaufi](https://github.com/simonschaufi))
|
||||
- various documentation updates [\#176](https://github.com/kevinpapst/kimai2/pull/176) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- code styles: header\_comment and ordered\_imports [\#175](https://github.com/kevinpapst/kimai2/pull/175) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Code sniffer rules \#158 [\#172](https://github.com/kevinpapst/kimai2/pull/172) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Set default locale for unit-tests [\#171](https://github.com/kevinpapst/kimai2/pull/171) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Hide success notification after 5 seconds [\#170](https://github.com/kevinpapst/kimai2/pull/170) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Added validation for end date before start date [\#167](https://github.com/kevinpapst/kimai2/pull/167) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Security expression \#148 [\#166](https://github.com/kevinpapst/kimai2/pull/166) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Composer install [\#165](https://github.com/kevinpapst/kimai2/pull/165) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added scrutinizer config \#158 [\#161](https://github.com/kevinpapst/kimai2/pull/161) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Update to Symfony 4.1 \#149 [\#160](https://github.com/kevinpapst/kimai2/pull/160) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Updated README [\#159](https://github.com/kevinpapst/kimai2/pull/159) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added russian translation \#95 [\#147](https://github.com/kevinpapst/kimai2/pull/147) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Bugfix importer \(\#145\) [\#146](https://github.com/kevinpapst/kimai2/pull/146) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- help text and link for form labels \#138 [\#143](https://github.com/kevinpapst/kimai2/pull/143) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- bugfix for duration\_only mode \#137 [\#142](https://github.com/kevinpapst/kimai2/pull/142) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Make the back link appear like a normal link instead of a button [\#139](https://github.com/kevinpapst/kimai2/pull/139) ([simonschaufi](https://github.com/simonschaufi))
|
||||
- Use short array syntax [\#135](https://github.com/kevinpapst/kimai2/pull/135) ([simonschaufi](https://github.com/simonschaufi))
|
||||
- Duration only mode \#131 [\#134](https://github.com/kevinpapst/kimai2/pull/134) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added controller to render markdown documentation \#129 [\#132](https://github.com/kevinpapst/kimai2/pull/132) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Rounding rules [\#128](https://github.com/kevinpapst/kimai2/pull/128) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Secure create-user command [\#127](https://github.com/kevinpapst/kimai2/pull/127) ([simonschaufi](https://github.com/simonschaufi))
|
||||
- ConfigureFilenames with hash after filename [\#126](https://github.com/kevinpapst/kimai2/pull/126) ([simonschaufi](https://github.com/simonschaufi))
|
||||
- added webpack-encore for managing frontend assets \#113 [\#122](https://github.com/kevinpapst/kimai2/pull/122) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Remove personal mentions in license header [\#119](https://github.com/kevinpapst/kimai2/pull/119) ([simonschaufi](https://github.com/simonschaufi))
|
||||
- Add github issue and pull request template [\#118](https://github.com/kevinpapst/kimai2/pull/118) ([simonschaufi](https://github.com/simonschaufi))
|
||||
- Improvements [\#114](https://github.com/kevinpapst/kimai2/pull/114) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added first docu on extending kimai with bundles \#106 [\#109](https://github.com/kevinpapst/kimai2/pull/109) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added order number to projects \#107 [\#108](https://github.com/kevinpapst/kimai2/pull/108) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added short invoice calculator and timesheet invoice template \#104 [\#105](https://github.com/kevinpapst/kimai2/pull/105) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added command to import data from kimai v1 \#102 [\#103](https://github.com/kevinpapst/kimai2/pull/103) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Error pages \#100 [\#101](https://github.com/kevinpapst/kimai2/pull/101) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added basic invoice rendering \#97 [\#99](https://github.com/kevinpapst/kimai2/pull/99) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added remember me feature \#53 [\#98](https://github.com/kevinpapst/kimai2/pull/98) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- code-cleanup: removed unused classes, added role constants, phpcs fixes [\#96](https://github.com/kevinpapst/kimai2/pull/96) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- load doctrine extensions during kernel bootstrap \#84 [\#92](https://github.com/kevinpapst/kimai2/pull/92) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- support theme options via user preferences \#89 [\#91](https://github.com/kevinpapst/kimai2/pull/91) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added dynamic user preferences \#7 \#9 [\#90](https://github.com/kevinpapst/kimai2/pull/90) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added control sidebar \#10 [\#87](https://github.com/kevinpapst/kimai2/pull/87) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added english translation \#8 [\#82](https://github.com/kevinpapst/kimai2/pull/82) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- upgraded to symfony 4 \#74 [\#81](https://github.com/kevinpapst/kimai2/pull/81) ([kevinpapst](https://github.com/kevinpapst))
|
||||
|
||||
## [0.1](https://github.com/kevinpapst/kimai2/tree/0.1) (2018-01-10)
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Hide inactive delete user button [\#78](https://github.com/kevinpapst/kimai2/issues/78)
|
||||
- Delete activity [\#71](https://github.com/kevinpapst/kimai2/issues/71)
|
||||
- Delete other users timesheet entries [\#70](https://github.com/kevinpapst/kimai2/issues/70)
|
||||
- Delete Project [\#69](https://github.com/kevinpapst/kimai2/issues/69)
|
||||
- Delete Customer [\#68](https://github.com/kevinpapst/kimai2/issues/68)
|
||||
- Delete timesheet entries [\#67](https://github.com/kevinpapst/kimai2/issues/67)
|
||||
- Add installation docu [\#64](https://github.com/kevinpapst/kimai2/issues/64)
|
||||
- Disallow login for inactive user [\#57](https://github.com/kevinpapst/kimai2/issues/57)
|
||||
- Add toolbar to filter user screen [\#56](https://github.com/kevinpapst/kimai2/issues/56)
|
||||
- Upgrade to latest Avanzu version [\#55](https://github.com/kevinpapst/kimai2/issues/55)
|
||||
- AdminLTE login screen [\#52](https://github.com/kevinpapst/kimai2/issues/52)
|
||||
- Add edit Timesheet entry for Teamleads [\#45](https://github.com/kevinpapst/kimai2/issues/45)
|
||||
- Add Timesheet Voter [\#44](https://github.com/kevinpapst/kimai2/issues/44)
|
||||
- Developer improvements [\#42](https://github.com/kevinpapst/kimai2/issues/42)
|
||||
- Base class for repository and query objects [\#40](https://github.com/kevinpapst/kimai2/issues/40)
|
||||
- Add console command to create user [\#36](https://github.com/kevinpapst/kimai2/issues/36)
|
||||
- Add basic security Voter [\#34](https://github.com/kevinpapst/kimai2/issues/34)
|
||||
- Do not show hidden Activities [\#33](https://github.com/kevinpapst/kimai2/issues/33)
|
||||
- Do not show hidden Projects [\#32](https://github.com/kevinpapst/kimai2/issues/32)
|
||||
- Add "create timesheet record" form [\#31](https://github.com/kevinpapst/kimai2/issues/31)
|
||||
- Add filter to Customer admin [\#28](https://github.com/kevinpapst/kimai2/issues/28)
|
||||
- Do not show hidden Customer [\#27](https://github.com/kevinpapst/kimai2/issues/27)
|
||||
- Rename Timesheet query class [\#25](https://github.com/kevinpapst/kimai2/issues/25)
|
||||
- Add "create activity" functionality [\#24](https://github.com/kevinpapst/kimai2/issues/24)
|
||||
- Add "create project" functionality [\#23](https://github.com/kevinpapst/kimai2/issues/23)
|
||||
- Add "create customer" functionality [\#22](https://github.com/kevinpapst/kimai2/issues/22)
|
||||
- Translate dashboard widgets [\#20](https://github.com/kevinpapst/kimai2/issues/20)
|
||||
- Display user avatar in navbar [\#16](https://github.com/kevinpapst/kimai2/issues/16)
|
||||
- Add role SUPER\_ADMIN [\#15](https://github.com/kevinpapst/kimai2/issues/15)
|
||||
- Teamlead can see all user times [\#13](https://github.com/kevinpapst/kimai2/issues/13)
|
||||
- Add filter to Timesheet \(user\) [\#11](https://github.com/kevinpapst/kimai2/issues/11)
|
||||
- Add "create user" functionality [\#5](https://github.com/kevinpapst/kimai2/issues/5)
|
||||
- Edit users role [\#4](https://github.com/kevinpapst/kimai2/issues/4)
|
||||
- Add filter to Timesheet admin [\#3](https://github.com/kevinpapst/kimai2/issues/3)
|
||||
- Add filter to Activity admin [\#2](https://github.com/kevinpapst/kimai2/issues/2)
|
||||
- Add filter to Project admin [\#1](https://github.com/kevinpapst/kimai2/issues/1)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- hide inactive delete user button \#78 [\#79](https://github.com/kevinpapst/kimai2/pull/79) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added delete customer action \#68 [\#77](https://github.com/kevinpapst/kimai2/pull/77) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added delete action for projects \#69 [\#76](https://github.com/kevinpapst/kimai2/pull/76) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Delete actions \#71 [\#75](https://github.com/kevinpapst/kimai2/pull/75) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added delete timesheet record for admins \#70 [\#73](https://github.com/kevinpapst/kimai2/pull/73) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added delete timesheet records action for users \#67 [\#72](https://github.com/kevinpapst/kimai2/pull/72) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added toolbar to user screen \#56 [\#66](https://github.com/kevinpapst/kimai2/pull/66) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- document installation options \#64 [\#65](https://github.com/kevinpapst/kimai2/pull/65) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- update avanzu admin bundle \#55 [\#63](https://github.com/kevinpapst/kimai2/pull/63) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- prevent inactive user from login \#57 [\#61](https://github.com/kevinpapst/kimai2/pull/61) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- handle hidden customer \#27 [\#60](https://github.com/kevinpapst/kimai2/pull/60) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- handle hidden projects \#32 [\#59](https://github.com/kevinpapst/kimai2/pull/59) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Handle hidden activities \#33 [\#58](https://github.com/kevinpapst/kimai2/pull/58) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- using the AdminLTE login screen \#52 [\#54](https://github.com/kevinpapst/kimai2/pull/54) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added toolbar for activity screen \#2 [\#51](https://github.com/kevinpapst/kimai2/pull/51) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Added toolbar to project screen \#1 [\#50](https://github.com/kevinpapst/kimai2/pull/50) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added toolbar to customer screen \#28 [\#49](https://github.com/kevinpapst/kimai2/pull/49) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added form to edit timesheet entries for all user \#45 [\#48](https://github.com/kevinpapst/kimai2/pull/48) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Create timesheet for User \#31 [\#47](https://github.com/kevinpapst/kimai2/pull/47) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added timesheet voter \#44 [\#46](https://github.com/kevinpapst/kimai2/pull/46) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Developer cleanup \#42 [\#43](https://github.com/kevinpapst/kimai2/pull/43) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Repository cleanup [\#41](https://github.com/kevinpapst/kimai2/pull/41) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Added voter for customer, project and activity [\#39](https://github.com/kevinpapst/kimai2/pull/39) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added create user console command [\#38](https://github.com/kevinpapst/kimai2/pull/38) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added "create user" functionality [\#37](https://github.com/kevinpapst/kimai2/pull/37) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Added "create activity" form [\#35](https://github.com/kevinpapst/kimai2/pull/35) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Added create project functionality [\#30](https://github.com/kevinpapst/kimai2/pull/30) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Rename TimesheetQuery class [\#29](https://github.com/kevinpapst/kimai2/pull/29) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Add customer functionality [\#26](https://github.com/kevinpapst/kimai2/pull/26) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Dashboard widgets [\#21](https://github.com/kevinpapst/kimai2/pull/21) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Added form to edit user roles [\#18](https://github.com/kevinpapst/kimai2/pull/18) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- added role ROLE\_SUPER\_ADMIN [\#17](https://github.com/kevinpapst/kimai2/pull/17) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- allow TEAMLEAD to see user times [\#14](https://github.com/kevinpapst/kimai2/pull/14) ([kevinpapst](https://github.com/kevinpapst))
|
||||
- Timesheet toolbar [\#12](https://github.com/kevinpapst/kimai2/pull/12) ([kevinpapst](https://github.com/kevinpapst))
|
||||
|
||||
|
||||
|
||||
\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*
|
||||
See [GitHub release page](https://github.com/kevinpapst/kimai2/releases).
|
||||
|
||||
54
Dockerfile
54
Dockerfile
@@ -1,54 +0,0 @@
|
||||
# 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.
|
||||
FROM php:7.2.9-apache-stretch AS tmp_kimai2_base
|
||||
|
||||
RUN apt update && \
|
||||
apt install -y --allow-unauthenticated \
|
||||
git \
|
||||
haveged \
|
||||
libicu-dev \
|
||||
libjpeg-dev \
|
||||
libldap2-dev \
|
||||
libldb-dev \
|
||||
libpng-dev \
|
||||
mysql-client \
|
||||
unzip \
|
||||
wget \
|
||||
zip \
|
||||
&& \
|
||||
EXPECTED_SIGNATURE="$(wget -q -O - https://composer.github.io/installer.sig)" && \
|
||||
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" && \
|
||||
ACTUAL_SIGNATURE="$(php -r "echo hash_file('sha384', 'composer-setup.php');")" && \
|
||||
if [ "$EXPECTED_SIGNATURE" != "$ACTUAL_SIGNATURE" ]; then >&2 echo 'ERROR: Invalid installer signature'; rm composer-setup.php; exit 1; fi && \
|
||||
php composer-setup.php --quiet && \
|
||||
rm composer-setup.php && \
|
||||
mv /var/www/html/composer.phar /usr/bin/composer && \
|
||||
docker-php-ext-install \
|
||||
gd \
|
||||
intl \
|
||||
ldap \
|
||||
pdo_mysql \
|
||||
zip && \
|
||||
apt remove -y wget && \
|
||||
apt -y autoremove && \
|
||||
apt clean
|
||||
|
||||
FROM tmp_kimai2_base
|
||||
|
||||
RUN git clone https://github.com/kevinpapst/kimai2.git /opt/kimai && \
|
||||
sed "s/prod/dev/g" /opt/kimai/.env.dist > /opt/kimai/.env && \
|
||||
composer install --working-dir=/opt/kimai --dev --optimize-autoloader && \
|
||||
/opt/kimai/bin/console doctrine:database:create && \
|
||||
/opt/kimai/bin/console doctrine:schema:create && \
|
||||
/opt/kimai/bin/console doctrine:migrations:version --add --all && \
|
||||
/opt/kimai/bin/console cache:warmup && \
|
||||
chown -R www-data:www-data /opt/kimai/var && \
|
||||
chown www-data:www-data /opt/kimai/vendor/mpdf/mpdf/tmp
|
||||
|
||||
WORKDIR /opt/kimai
|
||||
|
||||
EXPOSE 8001
|
||||
USER www-data
|
||||
CMD /opt/kimai/bin/console server:run 0.0.0.0:8001
|
||||
79
README.md
79
README.md
@@ -1,38 +1,45 @@
|
||||
# Kimai 2 - online time-tracker
|
||||
<p align="center">
|
||||
<img src="https://raw.githubusercontent.com/kimai/images/master/repository-header.png" alt="Kimai logo">
|
||||
</p>
|
||||
|
||||
[](https://packagist.org/packages/kevinpapst/kimai2)
|
||||
[](https://packagist.org/packages/kevinpapst/kimai2)
|
||||
[](https://travis-ci.org/kevinpapst/kimai2)
|
||||
[](https://codecov.io/gh/kevinpapst/kimai2)
|
||||
[](https://gitter.im/kimai2/support)
|
||||
<p align="center">
|
||||
<a href="https://github.com/kevinpapst/kimai2/actions"><img alt="CI Status" src="https://github.com/kevinpapst/kimai2/workflows/CI/badge.svg"></a>
|
||||
<a href="https://codecov.io/gh/kevinpapst/kimai2"><img alt="Code Coverage" src="https://codecov.io/gh/kevinpapst/kimai2/branch/master/graph/badge.svg"></a>
|
||||
<a href="https://packagist.org/packages/kevinpapst/kimai2"><img alt="Latest stable version" src="https://poser.pugx.org/kevinpapst/kimai2/v/stable"></a>
|
||||
<a href="https://packagist.org/packages/kevinpapst/kimai2"><img alt="License" src="https://poser.pugx.org/kevinpapst/kimai2/license"></a>
|
||||
<a href="https://twitter.com/kimai_org"><img alt="Twitter" src="https://img.shields.io/badge/follow-%40kimai__org-00acee"></a>
|
||||
</p>
|
||||
|
||||
<h1 align="center">Kimai - time-tracker</h1>
|
||||
|
||||
Kimai is a free, open source and online time-tracking software designed for small businesses and freelancers.
|
||||
It is built with modern technologies such as Symfony, Bootstrap, RESTful API, Doctrine, AdminLTE, Webpack, ES6 etc.
|
||||
It is built with modern technologies such as [Symfony](https://github.com/symfony/symfony), [Bootstrap](https://github.com/twbs/bootstrap),
|
||||
[RESTful API](https://github.com/FriendsOfSymfony/FOSRestBundle), [Doctrine](https://github.com/doctrine/),
|
||||
[AdminLTE](https://github.com/kevinpapst/AdminLTEBundle/), [Webpack](https://github.com/webpack/webpack), ES6 and [many](composer.json) [more](package.json).
|
||||
|
||||
## Introduction
|
||||
|
||||
- [Home](https://www.kimai.org) - The house of Kimai
|
||||
- [Blog](https://www.kimai.org/blog/) - Get the latest news
|
||||
- [Documentation](https://www.kimai.org/documentation/) - Learn how to use
|
||||
- [Translations](https://www.kimai.org/documentation/translations.html) - Kimai in your language
|
||||
- [Migration](https://www.kimai.org/documentation/migration-v1.html) - Import data from v1
|
||||
- [Home](https://www.kimai.org) - Kimai project homepage
|
||||
- [Blog](https://www.kimai.org/blog/) - Read the latest news
|
||||
- [Documentation](https://www.kimai.org/documentation/) - Learn how to use Kimai
|
||||
- [Translations](https://hosted.weblate.org/projects/kimai/#languages) - Kimai in your language
|
||||
- [Migration](https://www.kimai.org/documentation/migration-v1.html) - Import data from Kimai 1
|
||||
|
||||
### Requirements
|
||||
|
||||
- PHP 7.2 or higher
|
||||
- Database (MySQL, MariaDB, SQLite)
|
||||
- Webserver (nginx, Apache)
|
||||
- A modern browser
|
||||
- [Other libraries](https://www.kimai.org/download/)
|
||||
- PHP 7.3 or higher (PHP 8 supported)
|
||||
- MariaDB or MySQL
|
||||
- A webserver and subdomain
|
||||
- PHP extensions: `gd`, `intl`, `json`, `mbstring`, `pdo`, `xsl`, `zip`
|
||||
|
||||
### About
|
||||
|
||||
This is new version of the open source timetracker Kimai. It is stable and production ready, ships
|
||||
with most advanced features from Kimai 1 and many new ones, including but not limited to:
|
||||
This is the new version of the open source time tracker Kimai. It is stable and production ready, ships
|
||||
with many advanced features, including but not limited to:
|
||||
|
||||
JSON API, invoicing, data exports, multi-timer and punch-in punch-out mode, tagging, multi-user and multi-timezones,
|
||||
LDAP and built-in authentication, customizable role permissions, responsive and ready for your mobile device,
|
||||
hourly and fixed rates, advanced filtering, money and time budgets with report, support for plugins and many more.
|
||||
authentication via SAML/LDAP/Database, customizable role and team permissions, responsive and ready for your mobile device,
|
||||
user specific rates, advanced search & filtering, money and time budgets, reporting, support for plugins and many more.
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -40,11 +47,10 @@ hourly and fixed rates, advanced filtering, money and time budgets with report,
|
||||
- [Docker](https://www.kimai.org/documentation/docker.html) - containerized
|
||||
- [Development](https://www.kimai.org/documentation/installation.html#development-installation) - on your local machine
|
||||
- [1-click installer](https://www.kimai.org/documentation/installation.html#hosting-and-1-click-installations) - hosted environments
|
||||
- [FTP](https://www.kimai.org/documentation/installation.html#ftp-installation) - unfortunately still widely used ;-)
|
||||
|
||||
### Updating Kimai
|
||||
|
||||
- [Update Kimai](https://www.kimai.org/documentation/updates.html) - the documentation
|
||||
- [Update Kimai](https://www.kimai.org/documentation/updates.html) - get the latest version
|
||||
- [UPGRADING guide](UPGRADING.md) - version specific steps
|
||||
|
||||
### Plugins
|
||||
@@ -59,15 +65,24 @@ It is open for changes and input from the community, your [ideas and questions](
|
||||
|
||||
> Kimai 2 uses a rolling release concept for delivering updates.
|
||||
> You can upgrade Kimai at any time, you don't need to wait for the next official release.
|
||||
> The master branch is always deployable, release tags are only snapshots of the current development version.
|
||||
|
||||
Release versions will be created on a regular base (approx. every second month) and you can use these tags if you are familiar with git.
|
||||
Every code change, whether it's a new feature or a bug fix, will be done on the master branch.
|
||||
I have to do it this way, as I develop Kimai in my free time and want to put my effort into the software instead of backporting changes for old versions.
|
||||
Release versions will be created on a regular base (approx. one release every 2 months).
|
||||
Every code change, whether it's a new feature or a bugfix, will be done on the master branch.
|
||||
Kimai is actively developed in my spare time, I put my effort into the software instead of backporting changes.
|
||||
|
||||
## Credits
|
||||
## Contributing
|
||||
|
||||
Kimai 2 is developed with modern frameworks like
|
||||
[Symfony v4](https://github.com/symfony/symfony),
|
||||
[Doctrine](https://github.com/doctrine/),
|
||||
[AdminLTEBundle](https://github.com/kevinpapst/AdminLTEBundle/) (based on [AdminLTE theme](https://github.com/almasaeed2010/AdminLTE)) and
|
||||
[many](composer.json) [more](package.json).
|
||||
You want to contribute to this repository? This is so great!
|
||||
The best way to start is to [open a new issue](https://github.com/kevinpapst/kimai2/issues) for bugs or feature requests or a [discussion](https://github.com/kevinpapst/kimai2/discussions) for questions, support and such.
|
||||
|
||||
In case you want to contribute, but you wouldn't know how, here are some suggestions:
|
||||
|
||||
- Spread the word: More user means more people testing and contributing to Kimai - which in turn means better stability and more and better features. Please vote for Kimai on platforms lie Slant, Product Hunt, Softpedia or AlternativeTo, you can tweet about it, share it on LinkedIn, reddit or any of your favorite social media platforms. Every little bit helps!
|
||||
- Answer questions: You know the answer to another user's problem? Share your knowledge!
|
||||
- Make a feature request: Something can be done better? Something essential missing? Let us know!
|
||||
- Report bugs
|
||||
- Contribute: You don't have to be programmer to help. The documentation and translation could use some love as well.
|
||||
- Sponsor the project
|
||||
|
||||
There is one simple rule in our "Code of conduct": Don't be an ass!
|
||||
|
||||
@@ -7,8 +7,8 @@ As announced in the [README](README.md) I only support the latest available rele
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| master | :white_check_mark: |
|
||||
| 1.0.x | :white_check_mark: |
|
||||
| < 1.0 | :x: |
|
||||
| 1.15 | :white_check_mark: |
|
||||
| < 1.15 | :x: |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
@@ -17,7 +17,7 @@ Please report any security related vulnerability in the [advisories section at G
|
||||
I will work as fast as I can to fix the problem and publish a bugfix release / security update.
|
||||
Depending on the size of the required fixes, this might take a couple of hours or a couple of days.
|
||||
|
||||
You can expect that your message will be answered ASAP, but please take into account that I am living in the timezone Europe/Berlin.
|
||||
You can expect that your message will be answered ASAP, but please take into account that I am living in the timezone CET.
|
||||
|
||||
If your issue is valid and I verified and fixed it, you will be mentioned in the release notes within the Security section at the top of the release.
|
||||
If your issue is valid and after I verified and fixed it, you will be mentioned in the release notes.
|
||||
I am grateful for any (discrete) disclosure of vulnerabilities!
|
||||
210
UPGRADING.md
210
UPGRADING.md
@@ -5,10 +5,214 @@ _Make sure to create a backup before you start!_
|
||||
Read the [updates documentation](https://www.kimai.org/documentation/updates.html) to find out how
|
||||
you can upgrade your Kimai installation to the latest stable release.
|
||||
|
||||
**Before you leave ...**
|
||||
Check below if there are more version specific steps required, which need to be executed after the normal update process.
|
||||
Perform EACH version specific task between your version and the new one, otherwise you risk data inconsistency or a broken installation.
|
||||
|
||||
There might be more steps required which are version specific and need to be executed after [the update](https://www.kimai.org/documentation/updates.html).
|
||||
Read and perform EACH version specific task below, otherwise you risk data inconsistency or a broken installation!
|
||||
## [1.15](https://github.com/kevinpapst/kimai2/releases/tag/1.15)
|
||||
|
||||
**Many database changes: don't forget to [run the updater](https://www.kimai.org/documentation/updates.html).**
|
||||
|
||||
Updating the database might take quite a while, depending on the amount of timesheet entries and speed of your database server (~1 minute per 100k records).
|
||||
|
||||
**ATTENTION**
|
||||
|
||||
- This release bumps the minimum required [PHP version to 7.3](https://www.kimai.org/blog/2021/php8-support-php72-dropped/)
|
||||
- Self-registration is disabled by default
|
||||
- Self-registration now always requires email confirmation
|
||||
- All plugins that use own databases need to be updated as well
|
||||
- Removed the YearChart widget and the related configs named `userRecapThisYear`, `userRecapLastYear`, `userRecapTwoYears`, `userRecapThreeYears`
|
||||
|
||||
**LDAP & SAML**
|
||||
|
||||
Please verify your config with the [LDAP](https://www.kimai.org/documentation/ldap.html) and [SAML](https://www.kimai.org/documentation/saml.html) documentation, especially:
|
||||
|
||||
- SAML users: activate it by setting the `kimai.saml.activate: true` config key
|
||||
- LDAP users: activate it by setting the `kimai.ldap.activate: true` config key
|
||||
- LDAP and SAML users need to remove the complete `security` section from their `local.yaml`
|
||||
|
||||
**DEVELOPER**
|
||||
|
||||
PHP 8 compatibility forced to upgrade MANY libraries, including but not limited to:
|
||||
|
||||
- Removed FOSUserBundle and hslavich/oneloginsaml
|
||||
- Doctrine Migrations, whose new major version forces plugin updates
|
||||
- Gedmo v3 (which include BC breaks in definitions)
|
||||
- Doctrine DBAL and others, which required PHP 7.3 as well
|
||||
|
||||
**API BC break**: Due to team structure changes, it was impossible to keep the (writing) API structure. Please adjust your code accordingly!
|
||||
|
||||
## [1.14](https://github.com/kevinpapst/kimai2/releases/tag/1.14)
|
||||
|
||||
**CRITICAL BC break**: SQLite support was removed. If you are using SQLite, you have to [read this blog post](https://www.kimai.org/blog/2021/sqlite-and-ftp-support-removed/) and migrate to MySQL/MariaDB first!
|
||||
|
||||
**New database tables and fields: don't forget to [run the updater](https://www.kimai.org/documentation/updates.html).**
|
||||
|
||||
Permission changes:
|
||||
- `history_invoice` - removed permission entirely
|
||||
|
||||
## [1.13](https://github.com/kevinpapst/kimai2/releases/tag/1.13)
|
||||
|
||||
- Deprecated `now` variable in export templates: create it yourself with `{% set now = create_date('now', app.user) %}`
|
||||
- Changed invoice filename generation (check if you use cronjob for invoices)
|
||||
- **BC break**: duration entered as plain numbers will now be treated as decimal duration in hours instead of seconds
|
||||
|
||||
## [1.12](https://github.com/kevinpapst/kimai2/releases/tag/1.12)
|
||||
|
||||
- Export templates can now include items from plugins (eg. Expenses).
|
||||
|
||||
## [1.10](https://github.com/kevinpapst/kimai2/releases/tag/1.10)
|
||||
|
||||
**New database tables and fields were created, don't forget to [run the updater](https://www.kimai.org/documentation/updates.html).**
|
||||
|
||||
- Invoice renderer `CSV` was removed
|
||||
- Sessions are now stored in the database (all users have to re-login after upgrade)
|
||||
- New permissions: `lockdown_grace_timesheet`, `lockdown_override_timesheet`, `view_all_data`
|
||||
- Fixed team permissions on user queries: depending on your previous team & permission setup your users might see less data (SUPER_ADMINS see all data, but new: ADMINS only see all data if they own the `view_all_data` permission)
|
||||
- Markdown does not support headings anymore, text like `# foo` is not converted to `<h1 id="foo">foo</h1>` anymore
|
||||
|
||||
### Developer
|
||||
|
||||
- **BC break**: removed registration of `.env` with `putenv()` - do not rely on `getenv()` as it is not thread-safe
|
||||
- **BC break**: interface method signature `HtmlToPdfConverter::convertToPdf()` changed
|
||||
- **BC break**: the macros `badge` and `label` do not apply the `|trans` filter any more
|
||||
- **BC Break**: removed `getVisible()` (deprecated since 1.4) method on Customer, Project and Activity (use `isVisible()` instead, templates are still working)
|
||||
- **BC Break**: API changes
|
||||
- some representation names changed (eg. from `ActivityMetaField` to `ActivityMeta`, `TimesheetSubCollection` vs `TimesheetCollectionExpanded`), you could use `class_alias()` if you use auto-generated code from Swagger-Gen or alike
|
||||
- new result types were introduced
|
||||
- result data changed in some areas to smooth out inconsistencies (eg. TeamEntity fields changed in nested results)
|
||||
|
||||
## [1.9](https://github.com/kevinpapst/kimai2/releases/tag/1.9)
|
||||
|
||||
**New database tables and fields were created, don't forget to [run the updater](https://www.kimai.org/documentation/updates.html).**
|
||||
|
||||
- The directory `var/data/invoices/` will be used to store archived invoice files (check file permissions)
|
||||
- The default invoice number format changed. If you want to use the old one: configure `{date}` as format - see [invoice documentation](https://www.kimai.org/documentation/invoices.html)
|
||||
- HTML invoice templates are now treated like other files and offered as download. If you are using relative URLs for including assets (CSS, images) you need to either inline them (see the default templates) or use absolute URLs
|
||||
- Invoice templates that use the templates variables `${activity.X}` or `${project.X}` should be checked and possibly adapted, as multi-select is now possible for filtering
|
||||
- Invoice templates have access to all meta-fields as variables, not only the ones marked as visible
|
||||
- Rates configuration/structure changed for customer, project and activity
|
||||
- **Invoice templates**: rates variables were removed
|
||||
|
||||
Permission changes:
|
||||
- `history_invoice` - NEW: grants all features for the new invoice archive (by default for all admins)
|
||||
|
||||
### Developer
|
||||
|
||||
- **BC break**: `InvoiceItemInterface` has new methods `getType()` and `getCategory()`
|
||||
- **BC break**: API fields changed - see new `/rates` endpoints
|
||||
|
||||
## [1.8](https://github.com/kevinpapst/kimai2/releases/tag/1.8)
|
||||
|
||||
**New database tables and fields were created, don't forget to [run the updater](https://www.kimai.org/documentation/updates.html).**
|
||||
|
||||
- New PHP requirement: `ext-xsl` - which should be pre-installed in most environments when `ext-xml` is loaded
|
||||
- New mailer library: check if emails are still working (eg. by using the "password forgotten" function) or if you need to adjust your configuration, [see docs at symfony.com](https://symfony.com/doc/current/components/mailer.html#transport)
|
||||
- Support for line breaks in multiline invoice fields for spreadsheets (check your invoice templates after the update)
|
||||
|
||||
Permission changes:
|
||||
|
||||
- `comments_create_customer` - NEW: permission that allows to add new comments for customers
|
||||
- `comments_create_team_customer` - NEW: permission that allows to add new comments for team members of the current customer
|
||||
- `comments_create_teamlead_customer` - NEW: permission that allows to add new comments for a teamlead of the current customer
|
||||
- `comments_create_project` - NEW: permission that allows to add new comments for project
|
||||
- `comments_create_team_project` - NEW: permission that allows to add new comments for team members of the current project
|
||||
- `comments_create_teamlead_project` - NEW: permission that allows to add new comments for a teamlead of the current project
|
||||
- `edit_teamlead_project` - removed default permission from ROLE_TEAMLEAD (if you use it: change it in the Role & Permission UI)
|
||||
- `edit_teamlead_customer` - removed default permission from ROLE_TEAMLEAD (if you use it: change it in the Role & Permission UI)
|
||||
- `upload_invoice_template` - NEW: permission that allows to upload invoice documents from the UI
|
||||
|
||||
## [1.7](https://github.com/kevinpapst/kimai2/releases/tag/1.7)
|
||||
|
||||
**New database tables and fields were created, don't forget to [run the updater](https://www.kimai.org/documentation/updates.html).**
|
||||
|
||||
New permissions:
|
||||
|
||||
- `comments_customer` - show comment list on customer detail page (new feature)
|
||||
- `details_customer` - show detail information for customers (customer number, vat, rates, meta-fields, assigned teams ...)
|
||||
- `comments_project` - show comment list on project detail page (new feature)
|
||||
- `details_project` - show detail information for projects (rates, meta-fields, assigned teams ...)
|
||||
|
||||
If you are using teams, please read on: The following list of permissions are now also available in the UI and they (can) replace the `X_project` and `X_customer` permissions.
|
||||
They are more strict, as they allow only access to team specific items, the older permissions without `_teamlead_`/`_team_` work on a global level instead.
|
||||
|
||||
- `view_teamlead_customer`, `edit_teamlead_customer`, `budget_teamlead_customer`, `permissions_teamlead_customer`, `comments_teamlead_customer`, `details_teamlead_customer` - allows access to customer data when user is teamlead of a team assigned to the customer (replaces more global permission like `view_customer` for teamleads)
|
||||
- `view_team_customer`, `edit_team_customer`, `budget_team_customer`, `comments_team_customer`, `details_team_customer` - allows access to customer data when user is member of a team assigned to the customer (replaces more global permission like `view_customer` for users)
|
||||
- `view_teamlead_project`, `edit_teamlead_project`, `budget_teamlead_project`, `permissions_teamlead_project`, `comments_teamlead_project`, `details_teamlead_project` - allows access to customer data when user is teamlead of a team assigned to the project (replaces more global permission like `view_project` for teamleads)
|
||||
- `view_team_project`, `edit_team_project`, `budget_team_project`, `comments_team_project`, `details_team_project` - allows access to customer data when user is member of a team assigned to the project (replaces more global permission like `view_project` for users)
|
||||
|
||||
### ExpenseBundle
|
||||
|
||||
**ATTENTION** due to incompatibilities in the underlying frameworks users of the ExpenseBundle need to do one more step:
|
||||
You need to delete the bundle before updating: `rm -r var/plugins/ExpenseBundle`, otherwise you will run into errors during the update.
|
||||
After the Kimai update was successful, you have to re-install the latest bundle version, which is compatible with Kimai 1.7 only.
|
||||
|
||||
### Developer
|
||||
|
||||
- Projects now have a start and end date and the API will only return those, which are either unconfigured or currently active, you might want to reload the list of projects once the user entered begin and end datetime OR use the new `ignoreDates` parameter.
|
||||
- Doctrine bundle was updated to v2, check your code for [the usage of RegistryInterface and ObjectManager](https://github.com/doctrine/DoctrineBundle/blob/master/UPGRADE-2.0.md)
|
||||
- Removed the webserver bundle and the command `server:run` - see [docs](https://www.kimai.org/documentation/developers.html)
|
||||
|
||||
## [1.6](https://github.com/kevinpapst/kimai2/releases/tag/1.6), [1.6.1](https://github.com/kevinpapst/kimai2/releases/tag/1.6.1), [1.6.2](https://github.com/kevinpapst/kimai2/releases/tag/1.6.2)
|
||||
|
||||
**New database tables and fields were created, don't forget to [run the updater](https://www.kimai.org/documentation/updates.html).**
|
||||
|
||||
- Invoice changes:
|
||||
- Moved CSV, ODS and XSLX invoice templates to [another repository](https://github.com/Keleo/kimai2-invoice-templates). Using them? Install them manually (see [invoice documentation](https://www.kimai.org/documentation/invoices.html)).
|
||||
- Added new invoice fields (VAT, contact, payment details) and customer field (VAT). Used the twig settings before? Move them to the respective invoice template settings.
|
||||
- Permissions can be managed via Admin UI. Please move your permission settings from [local.yaml to your database](https://www.kimai.org/documentation/permissions.html).
|
||||
- Important permission change: regular users with the `view_other_timesheet` permission could see all timesheets. This was a legacy from the time before team permissions were introduced. If you rely on this behavior, you need to create a team with all users and the teamlead being the user who needs access to all timesheets.
|
||||
|
||||
### Developer
|
||||
|
||||
Please add default permissions to your [plugin](https://www.kimai.org/documentation/plugins.html).
|
||||
|
||||
## [1.5](https://github.com/kevinpapst/kimai2/releases/tag/1.5)
|
||||
|
||||
[Update as usual](https://www.kimai.org/documentation/updates.html)
|
||||
|
||||
## [1.4](https://github.com/kevinpapst/kimai2/releases/tag/1.4)
|
||||
|
||||
**There is a new directory, which needs to be writable by the webserver: `public/avatars/`.**
|
||||
|
||||
New permission (used in new dashboard widget):
|
||||
- `view_team_member` - display team assignments (names, teamleads and members) for the current user
|
||||
|
||||
Activated Javascript select component by default (check mobile devices).
|
||||
|
||||
### Developer: BC breaks
|
||||
|
||||
- Dashboard widgets and rows need to define their `type` by FQCN
|
||||
- Switched to Symfony 4.3 event types, this could fail in plugins, but only if they didn't use the official constants for event names
|
||||
|
||||
## [1.3](https://github.com/kevinpapst/kimai2/releases/tag/1.3)
|
||||
|
||||
Added `manage_tag` permission for new tag features
|
||||
|
||||
### Developer: BC breaks
|
||||
|
||||
- Refactored toolbars and search, plugins needs to be checked
|
||||
- Invoices now supports multiple repositories, some method signatures had to be changed (eg. `calculateSumIdentifier()`)
|
||||
|
||||
## [1.2](https://github.com/kevinpapst/kimai2/releases/tag/1.2)
|
||||
|
||||
**If you are still using 0.7 or below, you need to upgrade to 1.1 before upgrading to this version.**
|
||||
|
||||
- Deleted timezone conversion command.
|
||||
- Minimum password length raised from 5 to 8 character (applies only for password changes and new users)
|
||||
- Maximum customer name length lowered to 150 character
|
||||
- Maximum project name length lowered to 150 character
|
||||
- Maximum activity name length lowered to 150 character
|
||||
- Added new permission: `manage_invoice_template`
|
||||
- Removed permissions: `view_invoice_template`, `create_invoice_template`, `edit_invoice_template`, `delete_invoice_template`
|
||||
- Removed permission: `view_export` (using `create_export` only)
|
||||
|
||||
### Developer: BC breaks
|
||||
|
||||
- Custom export renderer need to check for usage of `Timesheet::getEnd()` as running entries can now be exported as well
|
||||
|
||||
## [1.1](https://github.com/kevinpapst/kimai2/releases/tag/1.1)
|
||||
|
||||
[Update as usual](https://www.kimai.org/documentation/updates.html), nothing special for this release if you upgrade from 1.0 / 1.0.1.
|
||||
|
||||
## [1.0](https://github.com/kevinpapst/kimai2/releases/tag/1.0)
|
||||
|
||||
|
||||
@@ -11,25 +11,71 @@ global.$ = global.jQuery = $;
|
||||
|
||||
require('bootstrap-sass');
|
||||
require('jquery-slimscroll');
|
||||
require('bootstrap-select');
|
||||
|
||||
require('select2');
|
||||
require('select2/dist/js/i18n/ar');
|
||||
require('select2/dist/js/i18n/cs');
|
||||
require('select2/dist/js/i18n/da');
|
||||
require('select2/dist/js/i18n/de');
|
||||
require('select2/dist/js/i18n/el');
|
||||
require('select2/dist/js/i18n/es');
|
||||
require('select2/dist/js/i18n/eu');
|
||||
require('select2/dist/js/i18n/fi');
|
||||
require('select2/dist/js/i18n/fr');
|
||||
require('select2/dist/js/i18n/he');
|
||||
require('select2/dist/js/i18n/hu');
|
||||
require('select2/dist/js/i18n/it');
|
||||
require('select2/dist/js/i18n/ja');
|
||||
require('select2/dist/js/i18n/ko');
|
||||
require('select2/dist/js/i18n/nl');
|
||||
require('select2/dist/js/i18n/pl');
|
||||
require('select2/dist/js/i18n/pt');
|
||||
require('select2/dist/js/i18n/pt-BR');
|
||||
require('select2/dist/js/i18n/ro');
|
||||
require('select2/dist/js/i18n/ru');
|
||||
require('select2/dist/js/i18n/sk');
|
||||
require('select2/dist/js/i18n/sv');
|
||||
require('select2/dist/js/i18n/tr');
|
||||
require('select2/dist/js/i18n/vi');
|
||||
require('select2/dist/js/i18n/zh-CN');
|
||||
|
||||
const Moment = require('moment');
|
||||
global.moment = Moment;
|
||||
require('moment/locale/de');
|
||||
require('moment/locale/it');
|
||||
require('moment/locale/fr');
|
||||
require('moment/locale/es');
|
||||
require('moment/locale/ru');
|
||||
require('moment/locale/ar');
|
||||
require('moment/locale/cs');
|
||||
require('moment/locale/da');
|
||||
require('moment/locale/de');
|
||||
require('moment/locale/de-at');
|
||||
require('moment/locale/de-ch');
|
||||
require('moment/locale/el');
|
||||
require('moment/locale/eo');
|
||||
require('moment/locale/es');
|
||||
require('moment/locale/eu');
|
||||
require('moment/locale/fi');
|
||||
require('moment/locale/fo');
|
||||
require('moment/locale/fr');
|
||||
require('moment/locale/he');
|
||||
require('moment/locale/hu');
|
||||
require('moment/locale/pt-br');
|
||||
require('moment/locale/sv');
|
||||
require('moment/locale/it');
|
||||
require('moment/locale/ja');
|
||||
require('moment/locale/ko');
|
||||
require('moment/locale/nl');
|
||||
require('moment/locale/pl');
|
||||
require('moment/locale/pt');
|
||||
require('moment/locale/pt-br');
|
||||
require('moment/locale/ro');
|
||||
require('moment/locale/ru');
|
||||
require('moment/locale/sk');
|
||||
require('moment/locale/sv');
|
||||
require('moment/locale/tr');
|
||||
require('moment/locale/vi');
|
||||
require('moment/locale/zh-cn');
|
||||
|
||||
require('daterangepicker');
|
||||
|
||||
// ------ AdminLTE framework ------
|
||||
require('../vendor/kevinpapst/adminlte-bundle/Resources/assets/admin-lte.scss');
|
||||
require('./sass/bootstrap.scss');
|
||||
require('./sass/fontawesome.scss');
|
||||
require('admin-lte/dist/css/AdminLTE.min.css');
|
||||
require('admin-lte/dist/css/skins/_all-skins.css');
|
||||
require('../vendor/kevinpapst/adminlte-bundle/Resources/assets/admin-lte-extensions.scss');
|
||||
@@ -44,6 +90,10 @@ require('./sass/app.scss');
|
||||
|
||||
// ------ Kimai itself ------
|
||||
require('./js/KimaiWebLoader.js');
|
||||
global.KimaiPaginatedBoxWidget = require('./js/widgets/KimaiPaginatedBoxWidget').default;
|
||||
global.KimaiReloadPageWidget = require('./js/widgets/KimaiReloadPageWidget').default;
|
||||
global.KimaiCookies = require('./js/widgets/KimaiCookies').default;
|
||||
global.KimaiStorage = require('./js/widgets/KimaiStorage').default;
|
||||
|
||||
// ------ Autocomplete for tags only ------
|
||||
require('jquery-ui/ui/widgets/autocomplete');
|
||||
require('jquery-ui/ui/widgets/autocomplete');
|
||||
|
||||
@@ -1,16 +1,37 @@
|
||||
|
||||
// can be removed, once fullcalendar was updated and works without draggable jquery ui objects
|
||||
require('jquery-ui/ui/widgets/draggable');
|
||||
|
||||
require('fullcalendar');
|
||||
require('fullcalendar/dist/gcal.min');
|
||||
|
||||
require('fullcalendar/dist/locale/de');
|
||||
require('fullcalendar/dist/locale/it');
|
||||
require('fullcalendar/dist/locale/fr');
|
||||
require('fullcalendar/dist/locale/es');
|
||||
require('fullcalendar/dist/locale/ru');
|
||||
require('fullcalendar/dist/locale/ar');
|
||||
require('fullcalendar/dist/locale/cs');
|
||||
require('fullcalendar/dist/locale/da');
|
||||
require('fullcalendar/dist/locale/de');
|
||||
require('fullcalendar/dist/locale/de-at');
|
||||
require('fullcalendar/dist/locale/de-ch');
|
||||
require('fullcalendar/dist/locale/el');
|
||||
require('fullcalendar/dist/locale/es');
|
||||
require('fullcalendar/dist/locale/eu');
|
||||
require('fullcalendar/dist/locale/fi');
|
||||
require('fullcalendar/dist/locale/fr');
|
||||
require('fullcalendar/dist/locale/he');
|
||||
require('fullcalendar/dist/locale/hu');
|
||||
require('fullcalendar/dist/locale/pt-br');
|
||||
require('fullcalendar/dist/locale/sv');
|
||||
require('fullcalendar/dist/locale/it');
|
||||
require('fullcalendar/dist/locale/ja');
|
||||
require('fullcalendar/dist/locale/ko');
|
||||
require('fullcalendar/dist/locale/nl');
|
||||
require('fullcalendar/dist/locale/pl');
|
||||
require('fullcalendar/dist/locale/pt');
|
||||
require('fullcalendar/dist/locale/pt-br');
|
||||
require('fullcalendar/dist/locale/ro');
|
||||
require('fullcalendar/dist/locale/ru');
|
||||
require('fullcalendar/dist/locale/sk');
|
||||
require('fullcalendar/dist/locale/sv');
|
||||
require('fullcalendar/dist/locale/tr');
|
||||
require('fullcalendar/dist/locale/zh-cn');
|
||||
require('fullcalendar/dist/locale/vi');
|
||||
require('fullcalendar/dist/locale/en-gb'); // the last imported file is used as fallback for locales that do not exist (like EO)
|
||||
|
||||
require('fullcalendar/dist/fullcalendar.min.css');
|
||||
|
||||
1348
assets/css/emails.css
Normal file
1348
assets/css/emails.css
Normal file
File diff suppressed because it is too large
Load Diff
2
assets/invoice-pdf.js
Normal file
2
assets/invoice-pdf.js
Normal file
@@ -0,0 +1,2 @@
|
||||
|
||||
require('./sass/invoice-pdf.scss');
|
||||
2
assets/invoice.js
Normal file
2
assets/invoice.js
Normal file
@@ -0,0 +1,2 @@
|
||||
|
||||
require('./sass/invoice.scss');
|
||||
@@ -16,7 +16,6 @@ import KimaiContainer from "./KimaiContainer";
|
||||
import KimaiActiveRecordsDuration from './plugins/KimaiActiveRecordsDuration.js';
|
||||
import KimaiDatatableColumnView from './plugins/KimaiDatatableColumnView.js';
|
||||
import KimaiThemeInitializer from "./plugins/KimaiThemeInitializer";
|
||||
import KimaiJqueryPluginInitializer from "./plugins/KimaiJqueryPluginInitializer";
|
||||
import KimaiDateRangePicker from "./plugins/KimaiDateRangePicker";
|
||||
import KimaiDatatable from "./plugins/KimaiDatatable";
|
||||
import KimaiToolbar from "./plugins/KimaiToolbar";
|
||||
@@ -31,13 +30,18 @@ import KimaiEvent from "./plugins/KimaiEvent";
|
||||
import KimaiAPILink from "./plugins/KimaiAPILink";
|
||||
import KimaiAlert from "./plugins/KimaiAlert";
|
||||
import KimaiAutocomplete from "./plugins/KimaiAutocomplete";
|
||||
import KimaiToolbarAction from "./plugins/KimaiToolbarAction";
|
||||
import KimaiFormSelect from "./plugins/KimaiFormSelect";
|
||||
import KimaiForm from "./plugins/KimaiForm";
|
||||
import KimaiDatePicker from "./plugins/KimaiDatePicker";
|
||||
import KimaiConfirmationLink from "./plugins/KimaiConfirmationLink";
|
||||
import KimaiMultiUpdateTable from "./plugins/KimaiMultiUpdateTable";
|
||||
import KimaiDateUtils from "./plugins/KimaiDateUtils";
|
||||
|
||||
export default class KimaiLoader {
|
||||
|
||||
constructor(configurations, translations) {
|
||||
// set the current locale for all javascript components
|
||||
moment.locale(configurations['locale']);
|
||||
moment.locale(configurations['locale'].replace('_', '-').toLowerCase());
|
||||
|
||||
const kimai = new KimaiContainer(
|
||||
new KimaiConfiguration(configurations),
|
||||
@@ -47,14 +51,16 @@ export default class KimaiLoader {
|
||||
kimai.registerPlugin(new KimaiEvent());
|
||||
kimai.registerPlugin(new KimaiAPI());
|
||||
kimai.registerPlugin(new KimaiAlert());
|
||||
kimai.registerPlugin(new KimaiActiveRecordsDuration('[data-since]'));
|
||||
kimai.registerPlugin(new KimaiDateUtils());
|
||||
kimai.registerPlugin(new KimaiFormSelect('.selectpicker'));
|
||||
kimai.registerPlugin(new KimaiConfirmationLink('confirmation-link'));
|
||||
kimai.registerPlugin(new KimaiActiveRecordsDuration());
|
||||
kimai.registerPlugin(new KimaiDatatableColumnView('data-column-visibility'));
|
||||
kimai.registerPlugin(new KimaiThemeInitializer());
|
||||
kimai.registerPlugin(new KimaiJqueryPluginInitializer());
|
||||
kimai.registerPlugin(new KimaiDateRangePicker('.content-wrapper'));
|
||||
kimai.registerPlugin(new KimaiDateTimePicker('.content-wrapper'));
|
||||
kimai.registerPlugin(new KimaiDatatable('table.dataTable'));
|
||||
kimai.registerPlugin(new KimaiToolbar());
|
||||
kimai.registerPlugin(new KimaiDateRangePicker('input[data-daterangepickerenable="on"]'));
|
||||
kimai.registerPlugin(new KimaiDateTimePicker('input[data-datetimepicker="on"]'));
|
||||
kimai.registerPlugin(new KimaiDatePicker('input[data-datepickerenable="on"]'));
|
||||
kimai.registerPlugin(new KimaiDatatable('section.content', 'table.dataTable'));
|
||||
kimai.registerPlugin(new KimaiToolbar('form.searchform', 'toolbar-action'));
|
||||
kimai.registerPlugin(new KimaiSelectDataAPI('select[data-related-select]'));
|
||||
kimai.registerPlugin(new KimaiAlternativeLinks('.alternative-link'));
|
||||
kimai.registerPlugin(new KimaiAjaxModalForm('.modal-ajax-form'));
|
||||
@@ -62,7 +68,9 @@ export default class KimaiLoader {
|
||||
kimai.registerPlugin(new KimaiActiveRecords('li.messages-menu', 'li.messages-menu-empty'));
|
||||
kimai.registerPlugin(new KimaiAPILink('api-link'));
|
||||
kimai.registerPlugin(new KimaiAutocomplete('.js-autocomplete'));
|
||||
kimai.registerPlugin(new KimaiToolbarAction('toolbar-action'));
|
||||
kimai.registerPlugin(new KimaiForm());
|
||||
kimai.registerPlugin(new KimaiThemeInitializer());
|
||||
kimai.registerPlugin(new KimaiMultiUpdateTable());
|
||||
//kimai.registerPlugin(new KimaiPauseRecord('li.messages-menu ul.menu li'));
|
||||
|
||||
// notify all listeners that Kimai plugins can now be registered
|
||||
|
||||
@@ -50,4 +50,20 @@ export default class KimaiPlugin {
|
||||
return this._core;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @returns {(string|number|boolean)}
|
||||
*/
|
||||
getConfiguration(name) {
|
||||
return this.getContainer().getConfiguration().get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @returns {KimaiPlugin}
|
||||
*/
|
||||
getPlugin(name) {
|
||||
return this.getContainer().getPlugin(name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -11,14 +11,25 @@
|
||||
|
||||
export default class KimaiTranslation {
|
||||
|
||||
/**
|
||||
* @param {Array<string, string>} translations
|
||||
*/
|
||||
constructor(translations) {
|
||||
this._translations = translations;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @returns {string}
|
||||
*/
|
||||
get(name) {
|
||||
return this._translations[name];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @returns {boolean}
|
||||
*/
|
||||
has(name) {
|
||||
return name in this._translations;
|
||||
}
|
||||
|
||||
@@ -33,7 +33,30 @@ export default class KimaiAPI extends KimaiPlugin {
|
||||
});
|
||||
}
|
||||
|
||||
post(url, data, callbackSuccess, callbackError) {
|
||||
if (callbackError === null || callbackError === undefined) {
|
||||
callbackError = this.getPostErrorHandler();
|
||||
}
|
||||
|
||||
jQuery.ajax({
|
||||
url: url,
|
||||
headers: {
|
||||
'X-AUTH-SESSION': true,
|
||||
'Content-Type':'application/json'
|
||||
},
|
||||
method: 'POST',
|
||||
data: data,
|
||||
dataType: 'json',
|
||||
success: callbackSuccess,
|
||||
error: callbackError
|
||||
});
|
||||
}
|
||||
|
||||
patch(url, data, callbackSuccess, callbackError) {
|
||||
if (callbackError === null || callbackError === undefined) {
|
||||
callbackError = this.getPatchErrorHandler();
|
||||
}
|
||||
|
||||
jQuery.ajax({
|
||||
url: url,
|
||||
headers: {
|
||||
@@ -49,6 +72,10 @@ export default class KimaiAPI extends KimaiPlugin {
|
||||
}
|
||||
|
||||
delete(url, callbackSuccess, callbackError) {
|
||||
if (callbackError === null || callbackError === undefined) {
|
||||
callbackError = this.getDeleteErrorHandler();
|
||||
}
|
||||
|
||||
jQuery.ajax({
|
||||
url: url,
|
||||
headers: {
|
||||
@@ -62,4 +89,64 @@ export default class KimaiAPI extends KimaiPlugin {
|
||||
});
|
||||
}
|
||||
|
||||
getDeleteErrorHandler() {
|
||||
const self = this;
|
||||
return function(xhr, err) {
|
||||
self.handleError('action.delete.error', xhr, err);
|
||||
};
|
||||
}
|
||||
|
||||
getPatchErrorHandler() {
|
||||
const self = this;
|
||||
return function(xhr, err) {
|
||||
self.handleError('action.update.error', xhr, err);
|
||||
};
|
||||
}
|
||||
|
||||
getPostErrorHandler() {
|
||||
const self = this;
|
||||
return function(xhr, err) {
|
||||
self.handleError('action.update.error', xhr, err);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} message
|
||||
* @param {jqXHR} xhr
|
||||
* @param {string} err
|
||||
*/
|
||||
handleError(message, xhr, err) {
|
||||
let resultError = err;
|
||||
if (xhr.responseJSON && xhr.responseJSON.message) {
|
||||
resultError = xhr.responseJSON.message;
|
||||
// find validation errors
|
||||
if (xhr.status === 400 && xhr.responseJSON.errors) {
|
||||
let collected = ['<u>' + resultError + '</u>'];
|
||||
// form errors that are not attached to a field (like extra fields)
|
||||
if (xhr.responseJSON.errors.errors) {
|
||||
for (let error of xhr.responseJSON.errors.errors) {
|
||||
collected.push(error);
|
||||
}
|
||||
}
|
||||
if (xhr.responseJSON.errors.children) {
|
||||
for (let field in xhr.responseJSON.errors.children) {
|
||||
let tmpField = xhr.responseJSON.errors.children[field];
|
||||
if (tmpField.hasOwnProperty('errors') && tmpField.errors.length > 0) {
|
||||
for (let error of tmpField.errors) {
|
||||
collected.push(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (collected.length > 0) {
|
||||
resultError = collected;
|
||||
}
|
||||
}
|
||||
} else if (xhr.status && xhr.statusText) {
|
||||
resultError = '[' + xhr.status + '] ' + xhr.statusText;
|
||||
}
|
||||
|
||||
this.getPlugin('alert').error(message, resultError);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
import KimaiClickHandlerReducedInTableRow from "./KimaiClickHandlerReducedInTableRow";
|
||||
import KimaiPlugin from "../KimaiPlugin";
|
||||
|
||||
/**
|
||||
* Needs to be initialized with a class name.
|
||||
@@ -18,7 +18,7 @@ import KimaiClickHandlerReducedInTableRow from "./KimaiClickHandlerReducedInTabl
|
||||
*
|
||||
* @param selector
|
||||
*/
|
||||
export default class KimaiAPILink extends KimaiClickHandlerReducedInTableRow {
|
||||
export default class KimaiAPILink extends KimaiPlugin {
|
||||
|
||||
constructor(selector) {
|
||||
super();
|
||||
@@ -75,10 +75,7 @@ export default class KimaiAPILink extends KimaiClickHandlerReducedInTableRow {
|
||||
if (attributes.msgError) {
|
||||
message = attributes.msgError;
|
||||
}
|
||||
if (xhr.responseJSON && xhr.responseJSON.message) {
|
||||
err = xhr.responseJSON.message;
|
||||
}
|
||||
alert.error(message, err);
|
||||
API.handleError(message, xhr, err);
|
||||
};
|
||||
|
||||
if (method === 'PATCH') {
|
||||
@@ -87,6 +84,12 @@ export default class KimaiAPILink extends KimaiClickHandlerReducedInTableRow {
|
||||
data = attributes.payload;
|
||||
}
|
||||
API.patch(url, data, successHandle, errorHandle);
|
||||
} else if (method === 'POST') {
|
||||
let data = {};
|
||||
if (attributes.payload) {
|
||||
data = attributes.payload;
|
||||
}
|
||||
API.post(url, data, successHandle, errorHandle);
|
||||
} else if (method === 'DELETE') {
|
||||
API.delete(url, successHandle, errorHandle);
|
||||
}
|
||||
|
||||
@@ -24,37 +24,39 @@ export default class KimaiActiveRecords extends KimaiPlugin {
|
||||
}
|
||||
|
||||
init() {
|
||||
const menu = document.querySelector(this.selector);
|
||||
this.menu = document.querySelector(this.selector);
|
||||
|
||||
// the menu can be hidden if user has no permissions to see it
|
||||
if (menu === null) {
|
||||
if (this.menu === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const dropdown = menu.querySelector('ul.dropdown-menu');
|
||||
|
||||
this.attributes = dropdown.dataset;
|
||||
this.itemList = dropdown.querySelector('li > ul.menu');
|
||||
this.label = menu.querySelector('a > span.label');
|
||||
this.attributes = this.menu.dataset;
|
||||
|
||||
const self = this;
|
||||
const handle = function() { self.reloadActiveRecords(); };
|
||||
|
||||
document.addEventListener('kimai.timesheetUpdate', handle);
|
||||
document.addEventListener('kimai.timesheetDelete', handle);
|
||||
document.addEventListener('kimai.activityUpdate', handle);
|
||||
document.addEventListener('kimai.activityDelete', handle);
|
||||
document.addEventListener('kimai.projectUpdate', handle);
|
||||
document.addEventListener('kimai.projectDelete', handle);
|
||||
document.addEventListener('kimai.customerUpdate', handle);
|
||||
}
|
||||
|
||||
emptyList() {
|
||||
this.itemList.innerHTML = '';
|
||||
document.addEventListener('kimai.customerDelete', handle);
|
||||
}
|
||||
|
||||
_toggleMenu(hasEntries) {
|
||||
const menu = document.querySelector(this.selector);
|
||||
const menuEmpty = document.querySelector(this.selectorEmpty);
|
||||
this.menu.style.display = hasEntries ? 'inline-block' : 'none';
|
||||
if (!hasEntries) {
|
||||
// make sure that template entries in the menu are removed, otherwise they
|
||||
// might still be shown in the browsers title
|
||||
for (let record of this.menu.querySelectorAll('[data-since]')) {
|
||||
record.dataset['since'] = '';
|
||||
}
|
||||
}
|
||||
|
||||
menu.style.display = hasEntries ? 'inline-block' : 'none';
|
||||
const menuEmpty = document.querySelector(this.selectorEmpty);
|
||||
if (menuEmpty !== null) {
|
||||
menuEmpty.style.display = !hasEntries ? 'inline-block' : 'none';
|
||||
}
|
||||
@@ -63,42 +65,52 @@ export default class KimaiActiveRecords extends KimaiPlugin {
|
||||
setEntries(entries) {
|
||||
this._toggleMenu(entries.length > 0);
|
||||
|
||||
const template = this.menu.querySelector('[data-template="active-record"]');
|
||||
|
||||
const label = this.menu.querySelector('a > span.label');
|
||||
if (label !== null) {
|
||||
label.innerText = entries.length === 0 ? '' : entries.length;
|
||||
}
|
||||
|
||||
if (entries.length === 0) {
|
||||
this.label.innerText = '';
|
||||
this.emptyList();
|
||||
return;
|
||||
}
|
||||
|
||||
let htmlToInsert = '';
|
||||
const durations = this.getContainer().getPlugin('timesheet-duration');
|
||||
|
||||
for (let timesheet of entries) {
|
||||
htmlToInsert +=
|
||||
`<li>` +
|
||||
`<a href="${ this.attributes['href'].replace('000', timesheet.id) }" data-event="kimai.timesheetStop kimai.timesheetUpdate" class="api-link" data-method="PATCH" data-msg-error="timesheet.stop.error" data-msg-success="timesheet.stop.success">` +
|
||||
`<div class="pull-left">` +
|
||||
`<i class="${ this.attributes['icon'] } fa-2x"></i>` +
|
||||
`</div>` +
|
||||
`<h4>` +
|
||||
`<span>${ timesheet.activity.name }</span>` +
|
||||
`<small>` +
|
||||
`<span data-title="true" data-since="${ timesheet.begin }" data-format="${ this.attributes['format'] }">${ durations.formatDuration(timesheet.duration, this.attributes['format']) }</span>` +
|
||||
`</small>` +
|
||||
`</h4>` +
|
||||
`<p>${ timesheet.project.name } (${ timesheet.project.customer.name })</p>` +
|
||||
`</a>` +
|
||||
`</li>`;
|
||||
}
|
||||
|
||||
if (this.label.dataset.warning < entries.length) {
|
||||
this.label.classList = 'label label-danger';
|
||||
if (template === null) {
|
||||
this._replaceInNode(this.menu, entries[0]);
|
||||
} else {
|
||||
this.label.classList = 'label label-warning';
|
||||
}
|
||||
this.label.innerText = entries.length;
|
||||
this.itemList.innerHTML = htmlToInsert;
|
||||
const container = template.parentElement;
|
||||
container.innerHTML = '';
|
||||
|
||||
durations.updateRecords();
|
||||
for (let timesheet of entries) {
|
||||
const newNode = template.cloneNode(true);
|
||||
container.appendChild(this._replaceInNode(newNode, timesheet));
|
||||
}
|
||||
}
|
||||
|
||||
this.getContainer().getPlugin('timesheet-duration').updateRecords();
|
||||
}
|
||||
|
||||
_replaceInNode(node, timesheet) {
|
||||
const date = this.getContainer().getPlugin('date');
|
||||
const allReplacer = node.querySelectorAll('[data-replacer]');
|
||||
for (let node of allReplacer) {
|
||||
const replacerName = node.dataset['replacer'];
|
||||
if (replacerName === 'url') {
|
||||
node.href = this.attributes['href'].replace('000', timesheet.id);
|
||||
} else if (replacerName === 'activity') {
|
||||
node.innerText = timesheet.activity.name;
|
||||
} else if (replacerName === 'project') {
|
||||
node.innerText = timesheet.project.name;
|
||||
} else if (replacerName === 'customer') {
|
||||
node.innerText = timesheet.project.customer.name;
|
||||
} else if (replacerName === 'duration') {
|
||||
node.dataset['since'] = timesheet.begin;
|
||||
node.innerText = date.formatDuration(timesheet.duration);
|
||||
}
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
reloadActiveRecords() {
|
||||
|
||||
@@ -9,21 +9,16 @@
|
||||
* [KIMAI] KimaiActiveRecordsDuration: activate the updates for all active timesheet records on this page
|
||||
*/
|
||||
|
||||
import moment from 'moment';
|
||||
import KimaiPlugin from '../KimaiPlugin';
|
||||
|
||||
export default class KimaiActiveRecordsDuration extends KimaiPlugin {
|
||||
|
||||
constructor(selector) {
|
||||
super();
|
||||
this.selector = selector;
|
||||
}
|
||||
|
||||
getId() {
|
||||
return 'timesheet-duration';
|
||||
}
|
||||
|
||||
init() {
|
||||
this.updateBrowserTitle = !!this.getConfiguration('updateBrowserTitle');
|
||||
this.updateRecords();
|
||||
const self = this;
|
||||
const handle = function() { self.updateRecords(); };
|
||||
@@ -38,20 +33,25 @@ export default class KimaiActiveRecordsDuration extends KimaiPlugin {
|
||||
|
||||
updateRecords() {
|
||||
let durations = [];
|
||||
const activeRecords = document.querySelectorAll(this.selector);
|
||||
const activeRecords = document.querySelectorAll('[data-since]:not([data-since=""])');
|
||||
|
||||
if (activeRecords.length === 0) {
|
||||
document.title = document.querySelector('body').dataset['title'];
|
||||
if (this.updateBrowserTitle) {
|
||||
document.title = document.querySelector('body').dataset['title'];
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for(let record of activeRecords) {
|
||||
const since = record.getAttribute('data-since');
|
||||
const format = record.getAttribute('data-format');
|
||||
const duration = this.formatDuration(since, format);
|
||||
if (record.getAttribute('data-title') !== null) {
|
||||
const DATE = this.getPlugin('date');
|
||||
|
||||
for (let record of activeRecords) {
|
||||
const since = record.dataset['since'];
|
||||
const duration = DATE.formatDuration(since);
|
||||
// only use the ones from the menu for the title
|
||||
if (record.dataset['replacer'] !== undefined && record.dataset['title'] !== null && duration !== '?') {
|
||||
durations.push(duration);
|
||||
}
|
||||
// but update all on the page (running entries in list pages)
|
||||
record.textContent = duration;
|
||||
}
|
||||
|
||||
@@ -59,6 +59,10 @@ export default class KimaiActiveRecordsDuration extends KimaiPlugin {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.updateBrowserTitle) {
|
||||
return;
|
||||
}
|
||||
|
||||
let title = durations.shift();
|
||||
let prefix = ' | ';
|
||||
|
||||
@@ -67,19 +71,4 @@ export default class KimaiActiveRecordsDuration extends KimaiPlugin {
|
||||
}
|
||||
document.title = title;
|
||||
}
|
||||
|
||||
formatDuration(since, format) {
|
||||
const duration = moment.duration(moment(new Date()).diff(moment(since)));
|
||||
|
||||
let hours = parseInt(duration.asHours()).toString();
|
||||
let minutes = duration.minutes();
|
||||
let seconds = duration.seconds();
|
||||
|
||||
// special case for hours, as they can overflow the 24h barrier - Kimai does not support days as duration unit
|
||||
if (hours.length === 1) {
|
||||
hours = '0' + hours;
|
||||
}
|
||||
|
||||
return format.replace('%h', hours).replace('%m', ('0'+minutes).substr(-2)).replace('%s', ('0'+seconds).substr(-2));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
*/
|
||||
|
||||
import jQuery from 'jquery';
|
||||
import KimaiClickHandlerReducedInTableRow from "./KimaiClickHandlerReducedInTableRow";
|
||||
import KimaiReducedClickHandler from "./KimaiReducedClickHandler";
|
||||
|
||||
export default class KimaiAjaxModalForm extends KimaiClickHandlerReducedInTableRow {
|
||||
export default class KimaiAjaxModalForm extends KimaiReducedClickHandler {
|
||||
|
||||
constructor(selector) {
|
||||
super();
|
||||
@@ -28,8 +28,33 @@ export default class KimaiAjaxModalForm extends KimaiClickHandlerReducedInTableR
|
||||
|
||||
init() {
|
||||
const self = this;
|
||||
this.isDirty = false;
|
||||
|
||||
this._addClickHandlerReducedInTableRow(this.selector, function(href) {
|
||||
this.modal = jQuery('#remote_form_modal');
|
||||
this.modal
|
||||
.on('hide.bs.modal', function (e) {
|
||||
if (self.isDirty) {
|
||||
if (jQuery('#remote_form_modal .modal-body .remote_modal_is_dirty_warning').length === 0) {
|
||||
const msg = self.getContainer().getTranslation().get('modal.dirty');
|
||||
jQuery('#remote_form_modal .modal-body').prepend('<p class="'+(self.modal.hasClass('modal-danger') ? 'well well-sm ' : '') + 'text-danger small remote_modal_is_dirty_warning">' + msg + '</p>');
|
||||
}
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
jQuery(self._getFormIdentifier()).off('change', self._isDirtyHandler);
|
||||
self.isDirty = false;
|
||||
self.getContainer().getPlugin('event').trigger('modal-hide');
|
||||
})
|
||||
.on('hidden.bs.modal', function () {
|
||||
// kill all references, so GC can kick in
|
||||
self.getContainer().getPlugin('form').destroyForm(self._getFormIdentifier());
|
||||
jQuery('#remote_form_modal .modal-body').replaceWith('');
|
||||
})
|
||||
.on('show.bs.modal', function () {
|
||||
self.getContainer().getPlugin('event').trigger('modal-show');
|
||||
});
|
||||
|
||||
this._addClickHandler(this.selector, function(href) {
|
||||
self.openUrlInModal(href);
|
||||
});
|
||||
}
|
||||
@@ -39,12 +64,9 @@ export default class KimaiAjaxModalForm extends KimaiClickHandlerReducedInTableR
|
||||
|
||||
if (errorHandler === undefined) {
|
||||
errorHandler = function(xhr, err) {
|
||||
if (xhr.status !== undefined && xhr.status === 403) {
|
||||
const alert = self.getContainer().getPlugin('alert');
|
||||
alert.error(xhr.statusText);
|
||||
return;
|
||||
if (xhr.status === undefined || xhr.status !== 403) {
|
||||
window.location = url;
|
||||
}
|
||||
window.location = url;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -57,24 +79,33 @@ export default class KimaiAjaxModalForm extends KimaiClickHandlerReducedInTableR
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the CSS selector for the modal form.
|
||||
*
|
||||
* @returns {string}
|
||||
* @private
|
||||
*/
|
||||
_getFormIdentifier() {
|
||||
return '#remote_form_modal .modal-content form';
|
||||
}
|
||||
|
||||
_openFormInModal(html) {
|
||||
const self = this;
|
||||
|
||||
// the modal that we use to render the form in
|
||||
let formIdentifier = '#remote_form_modal .modal-content form';
|
||||
let formIdentifier = this._getFormIdentifier();
|
||||
// if any of these is found in a response, the form will be re-displayed
|
||||
let flashErrorIdentifier = 'div.alert-error';
|
||||
// messages to show above the form
|
||||
let flashMessageIdentifier = 'div.alert';
|
||||
let form = jQuery(formIdentifier);
|
||||
let remoteModal = jQuery('#remote_form_modal');
|
||||
let remoteModal = this.modal;
|
||||
|
||||
// will be (re-)activated later
|
||||
form.off('submit');
|
||||
|
||||
// load new form from given content
|
||||
if (jQuery(html).find('#form_modal .modal-content').length > 0 ) {
|
||||
// switch classes, in case the modal type changed
|
||||
if (jQuery(html).find('#form_modal .modal-content').length > 0) {
|
||||
// Support changing modal importance/types
|
||||
remoteModal.on('hidden.bs.modal', function () {
|
||||
if (remoteModal.hasClass('modal-danger')) {
|
||||
remoteModal.removeClass('modal-danger');
|
||||
@@ -85,19 +116,26 @@ export default class KimaiAjaxModalForm extends KimaiClickHandlerReducedInTableR
|
||||
remoteModal.addClass('modal-danger');
|
||||
}
|
||||
|
||||
// Support changing modal sizes
|
||||
let modalDialog = remoteModal.find('.modal-dialog');
|
||||
let largeModal = jQuery(html).find('.modal-dialog').hasClass('modal-lg');
|
||||
if (largeModal && !modalDialog.hasClass('modal-lg')) {
|
||||
modalDialog.addClass('modal-lg');
|
||||
}
|
||||
if (!largeModal && modalDialog.hasClass('modal-lg')) {
|
||||
modalDialog.removeClass('modal-lg');
|
||||
}
|
||||
|
||||
jQuery('#remote_form_modal .modal-content').replaceWith(
|
||||
jQuery(html).find('#form_modal .modal-content')
|
||||
);
|
||||
|
||||
// TODO these should be handled with Events, probably using Custom Events
|
||||
// https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events
|
||||
jQuery('#remote_form_modal [data-dismiss=modal]').on('click', function() {
|
||||
self.isDirty = false;
|
||||
});
|
||||
|
||||
// activate new loaded widgets
|
||||
self.getContainer().getPlugin('date-time-picker').activateDateTimePicker(formIdentifier);
|
||||
self.getContainer().getPlugin('autocomplete').activateAutocomplete(formIdentifier + " .js-autocomplete");
|
||||
|
||||
// activate selectpicker if beta test is active
|
||||
jQuery('.selectpicker').selectpicker('refresh');
|
||||
self.getContainer().getPlugin('form').activateForm(formIdentifier);
|
||||
}
|
||||
|
||||
// show error flash messages
|
||||
@@ -116,18 +154,24 @@ export default class KimaiAjaxModalForm extends KimaiClickHandlerReducedInTableR
|
||||
});
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
// workaround for autofocus attribute, as the modal "steals" it
|
||||
remoteModal.on('shown.bs.modal', function () {
|
||||
jQuery(this).find('input[type=text],textarea,select').filter(':not("[data-datetimepicker=on]")').filter(':visible:first').focus().delay(1000).focus();
|
||||
});
|
||||
|
||||
remoteModal.modal('show');
|
||||
|
||||
// the new form that was loaded via ajax
|
||||
form = jQuery(formIdentifier);
|
||||
|
||||
this._isDirtyHandler = function(e) {
|
||||
self.isDirty = true;
|
||||
}
|
||||
form.on('change', this._isDirtyHandler);
|
||||
|
||||
// click handler for modal save button, to send forms via ajax
|
||||
form.on('submit', function(event){
|
||||
form.on('submit', function(event) {
|
||||
// if the form has a target, we let the normal HTML flow happen
|
||||
if (form.attr('target') !== undefined) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// otherwise we do some AJAX magic to process the form in the background
|
||||
const btn = jQuery(formIdentifier + ' button[type=submit]').button('loading');
|
||||
const eventName = form.attr('data-form-event');
|
||||
const events = self.getContainer().getPlugin('event');
|
||||
@@ -168,6 +212,7 @@ export default class KimaiAjaxModalForm extends KimaiClickHandlerReducedInTableR
|
||||
if (msg === null || msg === undefined) {
|
||||
msg = 'action.update.success';
|
||||
}
|
||||
self.isDirty = false;
|
||||
remoteModal.modal('hide');
|
||||
alert.success(msg);
|
||||
}
|
||||
@@ -184,6 +229,10 @@ export default class KimaiAjaxModalForm extends KimaiClickHandlerReducedInTableR
|
||||
err = '[' + xhr.status +'] ' + xhr.statusText;
|
||||
}
|
||||
alert.error(message, err);
|
||||
// this is useful for changing form fields and retrying to save (and in development to test form changes)
|
||||
setTimeout(function() {
|
||||
btn.button('reset');
|
||||
}, 1500);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -18,6 +18,10 @@ export default class KimaiAlert extends KimaiPlugin {
|
||||
return 'alert';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} title
|
||||
* @param {string|array} message
|
||||
*/
|
||||
error(title, message) {
|
||||
const translation = this.getContainer().getTranslation();
|
||||
if (translation.has(title)) {
|
||||
@@ -26,14 +30,48 @@ export default class KimaiAlert extends KimaiPlugin {
|
||||
if (translation.has(message)) {
|
||||
message = translation.get(message);
|
||||
}
|
||||
Swal.fire({
|
||||
type: 'error',
|
||||
title: title.replace('%reason%', ''),
|
||||
text: message,
|
||||
});
|
||||
|
||||
if (Array.isArray(message)) {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: title.replace('%reason%', ''),
|
||||
html: message.join('<br>'),
|
||||
});
|
||||
} else {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: title.replace('%reason%', ''),
|
||||
text: message,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
warning(message) {
|
||||
this._show('warning', message);
|
||||
}
|
||||
|
||||
success(message) {
|
||||
this._toast('success', message);
|
||||
}
|
||||
|
||||
info(message) {
|
||||
this._show('info', message);
|
||||
}
|
||||
|
||||
_show(type, message) {
|
||||
const translation = this.getContainer().getTranslation();
|
||||
|
||||
if (translation.has(message)) {
|
||||
message = translation.get(message);
|
||||
}
|
||||
|
||||
Swal.fire({
|
||||
icon: type,
|
||||
title: message,
|
||||
});
|
||||
}
|
||||
|
||||
_toast(type, message) {
|
||||
const translation = this.getContainer().getTranslation();
|
||||
|
||||
if (translation.has(message)) {
|
||||
@@ -42,10 +80,11 @@ export default class KimaiAlert extends KimaiPlugin {
|
||||
|
||||
Swal.fire({
|
||||
timer: 2000,
|
||||
timerProgressBar: true,
|
||||
toast: true,
|
||||
position: 'top-end',
|
||||
position: 'top',
|
||||
showConfirmButton: false,
|
||||
type: 'success',
|
||||
icon: type,
|
||||
title: message,
|
||||
});
|
||||
}
|
||||
@@ -65,7 +104,7 @@ export default class KimaiAlert extends KimaiPlugin {
|
||||
|
||||
Swal.fire({
|
||||
title: message,
|
||||
type: 'question',
|
||||
icon: 'question',
|
||||
showCancelButton: true,
|
||||
confirmButtonText: translation.get('confirm'),
|
||||
cancelButtonText: translation.get('cancel')
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
*/
|
||||
|
||||
import jQuery from 'jquery';
|
||||
import KimaiClickHandlerReducedInTableRow from "./KimaiClickHandlerReducedInTableRow";
|
||||
import KimaiReducedClickHandler from "./KimaiReducedClickHandler";
|
||||
|
||||
export default class KimaiAlternativeLinks extends KimaiClickHandlerReducedInTableRow {
|
||||
export default class KimaiAlternativeLinks extends KimaiReducedClickHandler {
|
||||
|
||||
constructor(selector) {
|
||||
super();
|
||||
@@ -23,7 +23,7 @@ export default class KimaiAlternativeLinks extends KimaiClickHandlerReducedInTab
|
||||
}
|
||||
|
||||
init() {
|
||||
this._addClickHandlerReducedInTableRow(this.selector, function(href) {
|
||||
this._addClickHandler(this.selector, function(href) {
|
||||
window.location = href;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ export default class KimaiAutocomplete extends KimaiPlugin {
|
||||
}
|
||||
|
||||
init() {
|
||||
this.activateAutocomplete(this.selector);
|
||||
this.minChars = this.getConfiguration('autoComplete');
|
||||
}
|
||||
|
||||
getId() {
|
||||
@@ -35,60 +35,72 @@ export default class KimaiAutocomplete extends KimaiPlugin {
|
||||
return this.splitTagList(term).pop();
|
||||
}
|
||||
|
||||
activateAutocomplete(selector)
|
||||
{
|
||||
const apiUrl = jQuery(selector).attr('data-autocomplete-url');
|
||||
activateAutocomplete(selector) {
|
||||
const self = this;
|
||||
const API = self.getContainer().getPlugin('api');
|
||||
|
||||
jQuery(selector + ' ' + this.selector).each(function(index) {
|
||||
const currentField = jQuery(this);
|
||||
const apiUrl = currentField.attr('data-autocomplete-url');
|
||||
const API = self.getContainer().getPlugin('api');
|
||||
|
||||
jQuery(selector)
|
||||
// don't navigate away from the field on tab when selecting an item
|
||||
.on("keydown", function (event) {
|
||||
if (event.keyCode === jQuery.ui.keyCode.TAB &&
|
||||
jQuery(this).autocomplete("instance").menu.active) {
|
||||
event.preventDefault();
|
||||
}
|
||||
})
|
||||
.autocomplete({
|
||||
source: function (request, response) {
|
||||
const lastEntry = self.extractLastTag(request.term);
|
||||
API.get(apiUrl, {'name': lastEntry}, function(data){
|
||||
response(data);
|
||||
});
|
||||
},
|
||||
search: function () {
|
||||
// custom minLength
|
||||
var term = self.extractLastTag(this.value);
|
||||
if (term.length < 2) {
|
||||
return false;
|
||||
currentField
|
||||
// don't navigate away from the field on tab when selecting an item
|
||||
.on("keydown", function (event) {
|
||||
if (event.keyCode === jQuery.ui.keyCode.TAB &&
|
||||
jQuery(this).autocomplete("instance").menu.active) {
|
||||
event.preventDefault();
|
||||
}
|
||||
},
|
||||
focus: function () {
|
||||
// prevent value inserted on focus
|
||||
return false;
|
||||
},
|
||||
select: function (event, ui) {
|
||||
var terms = self.splitTagList(this.value);
|
||||
})
|
||||
.autocomplete({
|
||||
source: function (request, response) {
|
||||
const lastEntry = self.extractLastTag(request.term);
|
||||
API.get(apiUrl, {'name': lastEntry}, function(data){
|
||||
response(data);
|
||||
});
|
||||
},
|
||||
search: function () {
|
||||
// custom minLength
|
||||
var term = self.extractLastTag(this.value);
|
||||
if (term.length < self.minChars) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
focus: function () {
|
||||
// prevent value inserted on focus
|
||||
return false;
|
||||
},
|
||||
select: function (event, ui) {
|
||||
var terms = self.splitTagList(this.value);
|
||||
|
||||
// remove the current input
|
||||
terms.pop();
|
||||
// remove the current input
|
||||
terms.pop();
|
||||
|
||||
// check if selected tag is already in list
|
||||
if (!terms.includes(ui.item.value)) {
|
||||
// add the selected item
|
||||
terms.push(ui.item.value);
|
||||
// check if selected tag is already in list
|
||||
if (!terms.includes(ui.item.value)) {
|
||||
// add the selected item
|
||||
terms.push(ui.item.value);
|
||||
}
|
||||
// add placeholder to get the comma-and-space at the end
|
||||
terms.push("");
|
||||
|
||||
this.value = terms.join(", ");
|
||||
|
||||
$(this).trigger('change');
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// add placeholder to get the comma-and-space at the end
|
||||
terms.push("");
|
||||
)
|
||||
;
|
||||
});
|
||||
}
|
||||
|
||||
this.value = terms.join(", ");
|
||||
|
||||
$(this).trigger('change');
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
);
|
||||
destroyAutocomplete(selector) {
|
||||
jQuery(selector + ' ' + this.selector).each(function(index) {
|
||||
const currentField = jQuery(this);
|
||||
currentField.autocomplete("destroy");
|
||||
currentField.removeData('autocomplete');
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
70
assets/js/plugins/KimaiConfirmationLink.js
Normal file
70
assets/js/plugins/KimaiConfirmationLink.js
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import KimaiPlugin from "../KimaiPlugin";
|
||||
|
||||
/**
|
||||
* Needs to be initialized with a class name.
|
||||
*
|
||||
* Allows to assign the given selector to any element, which then is used as click-handler
|
||||
* calling an API method and trigger the event from data-event attribute afterwards.
|
||||
*
|
||||
* @param selector
|
||||
*/
|
||||
export default class KimaiConfirmationLink extends KimaiPlugin {
|
||||
|
||||
constructor(selector) {
|
||||
super();
|
||||
this.selector = selector;
|
||||
}
|
||||
|
||||
init() {
|
||||
const self = this;
|
||||
document.addEventListener('click', function(event) {
|
||||
let target = event.target;
|
||||
while (target !== null && !target.matches('body')) {
|
||||
if (target.classList.contains(self.selector)) {
|
||||
const attributes = target.dataset;
|
||||
|
||||
// is this a link?
|
||||
let url = attributes['href'];
|
||||
// or another HTML element with a custom href
|
||||
if (!url) {
|
||||
url = target.getAttribute('href');
|
||||
}
|
||||
|
||||
// or is this a button?
|
||||
let form = null;
|
||||
if (target.type === 'submit' && target.form !== undefined) {
|
||||
form = target.form;
|
||||
}
|
||||
|
||||
if (attributes.question !== undefined) {
|
||||
self.getContainer().getPlugin('alert').question(attributes.question, function(value) {
|
||||
if (value) {
|
||||
if (form === null) {
|
||||
document.location = url;
|
||||
} else {
|
||||
if (url !== null) {
|
||||
form.action = url;
|
||||
}
|
||||
form.submit();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
target = target.parentNode;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,9 +14,10 @@ import KimaiPlugin from "../KimaiPlugin";
|
||||
|
||||
export default class KimaiDatatable extends KimaiPlugin {
|
||||
|
||||
constructor(selector) {
|
||||
constructor(contentAreaSelector, tableSelector) {
|
||||
super();
|
||||
this.selector = selector;
|
||||
this.contentArea = contentAreaSelector;
|
||||
this.selector = tableSelector;
|
||||
}
|
||||
|
||||
getId() {
|
||||
@@ -34,6 +35,8 @@ export default class KimaiDatatable extends KimaiPlugin {
|
||||
const attributes = dataTable.dataset;
|
||||
const events = attributes['reloadEvent'];
|
||||
|
||||
this.fixDropdowns();
|
||||
|
||||
if (events === undefined) {
|
||||
return;
|
||||
}
|
||||
@@ -44,19 +47,25 @@ export default class KimaiDatatable extends KimaiPlugin {
|
||||
for (let eventName of events.split(' ')) {
|
||||
document.addEventListener(eventName, handle);
|
||||
}
|
||||
|
||||
document.addEventListener('pagination-change', handle);
|
||||
document.addEventListener('filter-change', handle);
|
||||
}
|
||||
|
||||
reloadDatatable() {
|
||||
// FIXME remove query
|
||||
const self = this;
|
||||
const contentArea = this.contentArea;
|
||||
const durations = this.getContainer().getPlugin('timesheet-duration');
|
||||
const form = jQuery('.toolbar form');
|
||||
const toolbarSelector = this.getContainer().getPlugin('toolbar').getSelector();
|
||||
|
||||
const form = jQuery(toolbarSelector);
|
||||
let loading = '<div class="overlay"><i class="fas fa-sync fa-spin"></i></div>';
|
||||
jQuery('section.content').append(loading);
|
||||
jQuery(contentArea).append(loading);
|
||||
|
||||
// remove the empty fields to prevent errors
|
||||
let formData = jQuery('.toolbar form :input')
|
||||
let formData = jQuery(toolbarSelector + ' :input')
|
||||
.filter(function(index, element) {
|
||||
return jQuery(element).val() != '';
|
||||
return jQuery(element).val() !== '';
|
||||
})
|
||||
.serialize();
|
||||
|
||||
@@ -65,10 +74,11 @@ export default class KimaiDatatable extends KimaiPlugin {
|
||||
type: form.attr('method'),
|
||||
data: formData,
|
||||
success: function(html) {
|
||||
jQuery('section.content').replaceWith(
|
||||
jQuery(html).find('section.content')
|
||||
jQuery(contentArea).replaceWith(
|
||||
jQuery(html).find(contentArea)
|
||||
);
|
||||
durations.updateRecords();
|
||||
self.fixDropdowns();
|
||||
},
|
||||
error: function(xhr, err) {
|
||||
form.submit();
|
||||
@@ -76,4 +86,21 @@ export default class KimaiDatatable extends KimaiPlugin {
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* show dropdown menu upwards, if it is outside the visible viewport
|
||||
*/
|
||||
fixDropdowns() {
|
||||
const docHeight = jQuery(document).height();
|
||||
jQuery(this.selector + ' [data-toggle=dropdown]').each(function() {
|
||||
const parent = jQuery(this).parent();
|
||||
const menu = parent.find('.dropdown-menu');
|
||||
|
||||
if (parent && menu) {
|
||||
if ((parent.offset().top + parent.outerHeight() + menu.outerHeight()) > docHeight) {
|
||||
parent.addClass('dropup').removeClass('dropdown');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ export default class KimaiDatatableColumnView extends KimaiPlugin {
|
||||
});
|
||||
for (let checkbox of this.modal.querySelectorAll('form input[type=checkbox]')) {
|
||||
checkbox.addEventListener('click', function () {
|
||||
self.changeVisibility(checkbox.getAttribute('name'));
|
||||
self.changeVisibility(checkbox.getAttribute('name'), checkbox.checked);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -55,7 +55,7 @@ export default class KimaiDatatableColumnView extends KimaiPlugin {
|
||||
for (let checkbox of form.querySelectorAll('input[type=checkbox]')) {
|
||||
settings[checkbox.getAttribute('name')] = checkbox.checked;
|
||||
}
|
||||
Cookies.set(form.getAttribute('name'), JSON.stringify(settings), {expires: 365});
|
||||
Cookies.set(form.getAttribute('name'), JSON.stringify(settings), {expires: 365, SameSite: 'Strict'});
|
||||
jQuery(this.modal).modal('toggle');
|
||||
}
|
||||
|
||||
@@ -70,24 +70,40 @@ export default class KimaiDatatableColumnView extends KimaiPlugin {
|
||||
jQuery(this.modal).modal('toggle');
|
||||
}
|
||||
|
||||
changeVisibility(columnName) {
|
||||
const table = document.getElementById('datatable_' + this.id).getElementsByClassName('dataTable')[0];
|
||||
let column = 0;
|
||||
let foundColumn = false;
|
||||
for (let columnElement of table.getElementsByTagName('th')) {
|
||||
if (columnElement.getAttribute('data-field') === columnName) {
|
||||
foundColumn = true;
|
||||
break;
|
||||
changeVisibility(columnName, checked) {
|
||||
const tables = document.getElementsByClassName('datatable_' + this.id);
|
||||
for (let tableBox of tables) {
|
||||
let column = 0;
|
||||
let foundColumn = false;
|
||||
let table = tableBox.getElementsByClassName('dataTable')[0];
|
||||
for (let columnElement of table.getElementsByTagName('th')) {
|
||||
if (columnElement.getAttribute('data-field') === columnName) {
|
||||
foundColumn = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (columnElement.getAttribute('colspan') !== null) {
|
||||
console.log('Tables with colspans are not supported!');
|
||||
}
|
||||
|
||||
column++;
|
||||
}
|
||||
column++;
|
||||
}
|
||||
|
||||
if (!foundColumn) {
|
||||
return;
|
||||
}
|
||||
if (!foundColumn) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let rowElement of table.getElementsByTagName('tr')) {
|
||||
rowElement.children[column].classList.toggle('hidden');
|
||||
for (let rowElement of table.getElementsByTagName('tr')) {
|
||||
if (rowElement.children[column] === undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (checked) {
|
||||
rowElement.children[column].classList.remove('hidden');
|
||||
} else {
|
||||
rowElement.children[column].classList.add('hidden');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
|
||||
import jQuery from 'jquery';
|
||||
import KimaiPlugin from '../KimaiPlugin';
|
||||
import moment from 'moment';
|
||||
|
||||
export default class KimaiDatePicker extends KimaiPlugin {
|
||||
|
||||
@@ -24,13 +23,12 @@ export default class KimaiDatePicker extends KimaiPlugin {
|
||||
return 'date-picker';
|
||||
}
|
||||
|
||||
init() {
|
||||
this.activateDatePicker(this.selector);
|
||||
}
|
||||
|
||||
activateDatePicker(selector) {
|
||||
let translator = this.getContainer().getTranslation();
|
||||
jQuery(selector + ' input[data-datepickerenable="on"]').each(function(index) {
|
||||
const TRANSLATE = this.getContainer().getTranslation();
|
||||
const DATE_UTILS = this.getContainer().getPlugin('date');
|
||||
const firstDow = this.getConfiguration('first_dow_iso') % 7;
|
||||
|
||||
jQuery(selector + ' ' + this.selector).each(function(index) {
|
||||
let localeFormat = jQuery(this).data('format');
|
||||
jQuery(this).daterangepicker({
|
||||
singleDatePicker: true,
|
||||
@@ -38,12 +36,19 @@ export default class KimaiDatePicker extends KimaiPlugin {
|
||||
autoUpdateInput: false,
|
||||
locale: {
|
||||
format: localeFormat,
|
||||
firstDay: 1,
|
||||
applyLabel: translator.get('confirm'),
|
||||
cancelLabel: translator.get('cancel'),
|
||||
customRangeLabel: translator.get('customRange'),
|
||||
daysOfWeek: moment.weekdaysShort(),
|
||||
monthNames: moment.months(),
|
||||
firstDay: firstDow,
|
||||
applyLabel: TRANSLATE.get('confirm'),
|
||||
cancelLabel: TRANSLATE.get('cancel'),
|
||||
customRangeLabel: TRANSLATE.get('customRange'),
|
||||
daysOfWeek: DATE_UTILS.getWeekDaysShort(),
|
||||
monthNames: DATE_UTILS.getMonthNames(),
|
||||
}
|
||||
});
|
||||
|
||||
jQuery(this).on('show.daterangepicker', function (ev, picker) {
|
||||
if (picker.element.offset().top - jQuery(window).scrollTop() + picker.container.outerHeight() + 30 > jQuery(window).height()) {
|
||||
picker.drops = 'up';
|
||||
picker.move();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -54,4 +59,13 @@ export default class KimaiDatePicker extends KimaiPlugin {
|
||||
});
|
||||
}
|
||||
|
||||
destroyDatePicker(selector) {
|
||||
jQuery(selector + ' ' + this.selector).each(function(index) {
|
||||
if (jQuery(this).data('daterangepicker') !== undefined) {
|
||||
jQuery(this).daterangepicker('destroy');
|
||||
jQuery(this).data('daterangepicker').remove();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,50 +24,71 @@ export default class KimaiDateRangePicker extends KimaiPlugin {
|
||||
return 'date-range-picker';
|
||||
}
|
||||
|
||||
init() {
|
||||
this.activateDateRangePicker(this.selector);
|
||||
}
|
||||
|
||||
activateDateRangePicker(selector) {
|
||||
let translator = this.getContainer().getTranslation();
|
||||
jQuery(selector + ' input[data-daterangepickerenable="on"]').each(function(index) {
|
||||
const TRANSLATE = this.getContainer().getTranslation();
|
||||
const DATE_UTILS = this.getContainer().getPlugin('date');
|
||||
const firstDow = this.getConfiguration('first_dow_iso') % 7;
|
||||
|
||||
jQuery(selector + ' ' + this.selector).each(function(index) {
|
||||
let localeFormat = jQuery(this).data('format');
|
||||
let separator = jQuery(this).data('separator');
|
||||
let rangesList = {};
|
||||
|
||||
rangesList[translator.get('today')] = [moment(), moment()];
|
||||
rangesList[translator.get('yesterday')] = [moment().subtract(1, 'days'), moment().subtract(1, 'days')];
|
||||
rangesList[translator.get('thisWeek')] = [moment().startOf('week'), moment().endOf('week')];
|
||||
rangesList[translator.get('lastWeek')] = [moment().subtract(1, 'week').startOf('week'), moment().subtract(1, 'week').endOf('week')];
|
||||
rangesList[translator.get('thisMonth')] = [moment().startOf('month'), moment().endOf('month')];
|
||||
rangesList[translator.get('lastMonth')] = [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')];
|
||||
rangesList[translator.get('thisYear')] = [moment().startOf('year'), moment().endOf('year')];
|
||||
rangesList[translator.get('lastYear')] = [moment().subtract(1, 'year').startOf('year'), moment().subtract(1, 'year').endOf('year')];
|
||||
rangesList[TRANSLATE.get('today')] = [moment(), moment()];
|
||||
rangesList[TRANSLATE.get('yesterday')] = [moment().subtract(1, 'days'), moment().subtract(1, 'days')];
|
||||
rangesList[TRANSLATE.get('thisWeek')] = [moment().startOf('isoWeek'), moment().endOf('isoWeek')];
|
||||
rangesList[TRANSLATE.get('lastWeek')] = [moment().subtract(1, 'week').startOf('isoWeek'), moment().subtract(1, 'week').endOf('isoWeek')];
|
||||
if (firstDow === 0) { // sunday = 0
|
||||
rangesList[TRANSLATE.get('thisWeek')] = [moment().startOf('isoWeek').subtract(1, 'day'), moment().endOf('isoWeek').subtract(1, 'day')];
|
||||
rangesList[TRANSLATE.get('lastWeek')] = [moment().subtract(1, 'week').startOf('isoWeek').subtract(1, 'day'), moment().subtract(1, 'week').endOf('isoWeek').subtract(1, 'day')];
|
||||
}
|
||||
rangesList[TRANSLATE.get('thisMonth')] = [moment().startOf('month'), moment().endOf('month')];
|
||||
rangesList[TRANSLATE.get('lastMonth')] = [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')];
|
||||
rangesList[TRANSLATE.get('thisYear')] = [moment().startOf('year'), moment().endOf('year')];
|
||||
rangesList[TRANSLATE.get('lastYear')] = [moment().subtract(1, 'year').startOf('year'), moment().subtract(1, 'year').endOf('year')];
|
||||
|
||||
jQuery(this).daterangepicker({
|
||||
showDropdowns: true,
|
||||
autoUpdateInput: false,
|
||||
autoApply: false,
|
||||
linkedCalendars: false,
|
||||
linkedCalendars: true,
|
||||
locale: {
|
||||
separator: separator,
|
||||
format: localeFormat,
|
||||
firstDay: 1,
|
||||
applyLabel: translator.get('confirm'),
|
||||
cancelLabel: translator.get('cancel'),
|
||||
customRangeLabel: translator.get('customRange'),
|
||||
daysOfWeek: moment.weekdaysShort(),
|
||||
monthNames: moment.months(),
|
||||
firstDay: firstDow,
|
||||
applyLabel: TRANSLATE.get('confirm'),
|
||||
cancelLabel: TRANSLATE.get('cancel'),
|
||||
customRangeLabel: TRANSLATE.get('customRange'),
|
||||
daysOfWeek: DATE_UTILS.getWeekDaysShort(),
|
||||
monthNames: DATE_UTILS.getMonthNames(),
|
||||
},
|
||||
ranges: rangesList,
|
||||
alwaysShowCalendars: true
|
||||
});
|
||||
|
||||
jQuery(this).on('show.daterangepicker', function (ev, picker) {
|
||||
if (picker.element.offset().top - jQuery(window).scrollTop() + picker.container.outerHeight() + 30 > jQuery(window).height()) {
|
||||
picker.drops = 'up';
|
||||
picker.move();
|
||||
}
|
||||
});
|
||||
|
||||
jQuery(this).on('apply.daterangepicker', function(ev, picker) {
|
||||
jQuery(this).val(picker.startDate.format(localeFormat) + ' - ' + picker.endDate.format(localeFormat));
|
||||
jQuery(this).data('begin', picker.startDate.format(localeFormat));
|
||||
jQuery(this).data('end', picker.endDate.format(localeFormat));
|
||||
jQuery(this).trigger("change");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
destroyDateRangePicker(selector) {
|
||||
jQuery(selector + ' ' + this.selector).each(function(index) {
|
||||
if (jQuery(this).data('daterangepicker') !== undefined) {
|
||||
jQuery(this).daterangepicker('destroy');
|
||||
jQuery(this).data('daterangepicker').remove();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
|
||||
import jQuery from 'jquery';
|
||||
import KimaiPlugin from '../KimaiPlugin';
|
||||
import moment from 'moment';
|
||||
|
||||
export default class KimaiDateTimePicker extends KimaiPlugin {
|
||||
|
||||
@@ -24,30 +23,35 @@ export default class KimaiDateTimePicker extends KimaiPlugin {
|
||||
return 'date-time-picker';
|
||||
}
|
||||
|
||||
init() {
|
||||
this.activateDateTimePicker(this.selector);
|
||||
}
|
||||
|
||||
activateDateTimePicker(selector) {
|
||||
let translator = this.getContainer().getTranslation();
|
||||
let configuration = this.getContainer().getConfiguration();
|
||||
const TRANSLATE = this.getContainer().getTranslation();
|
||||
const DATE_UTILS = this.getContainer().getPlugin('date');
|
||||
const firstDow = this.getConfiguration('first_dow_iso') % 7;
|
||||
const is24hours = this.getConfiguration('twentyFourHours');
|
||||
|
||||
jQuery(selector + ' input[data-datetimepicker="on"]').each(function(index) {
|
||||
jQuery(selector + ' ' + this.selector).each(function(index) {
|
||||
let localeFormat = jQuery(this).data('format');
|
||||
jQuery(this).daterangepicker({
|
||||
singleDatePicker: true,
|
||||
timePicker: true,
|
||||
timePicker24Hour: configuration.get('twentyFourHours'),
|
||||
timePicker24Hour: is24hours,
|
||||
showDropdowns: true,
|
||||
autoUpdateInput: false,
|
||||
locale: {
|
||||
format: localeFormat,
|
||||
firstDay: 1,
|
||||
applyLabel: translator.get('confirm'),
|
||||
cancelLabel: translator.get('cancel'),
|
||||
customRangeLabel: translator.get('customRange'),
|
||||
daysOfWeek: moment.weekdaysShort(),
|
||||
monthNames: moment.months(),
|
||||
firstDay: firstDow,
|
||||
applyLabel: TRANSLATE.get('confirm'),
|
||||
cancelLabel: TRANSLATE.get('cancel'),
|
||||
customRangeLabel: TRANSLATE.get('customRange'),
|
||||
daysOfWeek: DATE_UTILS.getWeekDaysShort(),
|
||||
monthNames: DATE_UTILS.getMonthNames(),
|
||||
}
|
||||
});
|
||||
|
||||
jQuery(this).on('show.daterangepicker', function (ev, picker) {
|
||||
if (picker.element.offset().top - jQuery(window).scrollTop() + picker.container.outerHeight() + 30 > jQuery(window).height()) {
|
||||
picker.drops = 'up';
|
||||
picker.move();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -58,4 +62,13 @@ export default class KimaiDateTimePicker extends KimaiPlugin {
|
||||
});
|
||||
}
|
||||
|
||||
destroyDateTimePicker(selector) {
|
||||
jQuery(selector + ' ' + this.selector).each(function(index) {
|
||||
if (jQuery(this).data('daterangepicker') !== undefined) {
|
||||
jQuery(this).daterangepicker('destroy');
|
||||
jQuery(this).data('daterangepicker').remove();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
64
assets/js/plugins/KimaiDateUtils.js
Normal file
64
assets/js/plugins/KimaiDateUtils.js
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* [KIMAI] KimaiDateUtils: responsible for handling date specific tasks
|
||||
*/
|
||||
|
||||
import KimaiPlugin from '../KimaiPlugin';
|
||||
import moment from 'moment';
|
||||
|
||||
export default class KimaiDateUtils extends KimaiPlugin {
|
||||
|
||||
getId() {
|
||||
return 'date';
|
||||
}
|
||||
|
||||
getWeekDaysShort() {
|
||||
return moment.localeData().weekdaysShort();
|
||||
}
|
||||
|
||||
getMonthNames() {
|
||||
return moment.localeData().months();
|
||||
}
|
||||
|
||||
formatDuration(since) {
|
||||
const duration = moment.duration(moment(new Date()).diff(moment(since)));
|
||||
|
||||
return this.formatMomentDuration(duration);
|
||||
}
|
||||
|
||||
formatSeconds(seconds) {
|
||||
const duration = moment.duration('PT' + seconds + 'S');
|
||||
|
||||
return this.formatMomentDuration(duration);
|
||||
}
|
||||
|
||||
formatMomentDuration(duration) {
|
||||
const hours = parseInt(duration.asHours()).toString();
|
||||
const minutes = duration.minutes();
|
||||
const seconds = duration.seconds();
|
||||
|
||||
return this.formatTime(hours, minutes, seconds);
|
||||
}
|
||||
|
||||
formatTime(hours, minutes, seconds) {
|
||||
if (hours < 0 || minutes < 0 || seconds < 0) {
|
||||
return '?';
|
||||
}
|
||||
|
||||
// special case for hours, as they can overflow the 24h barrier - Kimai does not support days as duration unit
|
||||
if (hours.length === 1) {
|
||||
hours = '0' + hours;
|
||||
}
|
||||
|
||||
const format = this.getConfiguration('formatDuration');
|
||||
|
||||
return format.replace('%h', hours).replace('%m', ('0'+minutes).substr(-2)).replace('%s', ('0'+seconds).substr(-2));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -25,7 +25,7 @@ export default class KimaiEvent extends KimaiPlugin {
|
||||
for(let event of name.split(' ')) {
|
||||
let triggerEvent = new Event(event);
|
||||
if (details !== undefined) {
|
||||
triggerEvent = new CustomEvent(name, {detail: details});
|
||||
triggerEvent = new CustomEvent(event, {detail: details});
|
||||
}
|
||||
document.dispatchEvent(triggerEvent);
|
||||
}
|
||||
|
||||
80
assets/js/plugins/KimaiForm.js
Normal file
80
assets/js/plugins/KimaiForm.js
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* [KIMAI] KimaiForm: basic functions for all forms
|
||||
*/
|
||||
|
||||
import KimaiPlugin from "../KimaiPlugin";
|
||||
|
||||
export default class KimaiForm extends KimaiPlugin {
|
||||
|
||||
getId() {
|
||||
return 'form';
|
||||
}
|
||||
|
||||
activateForm(formSelector, container) {
|
||||
this.getContainer().getPlugin('date-range-picker').activateDateRangePicker(formSelector);
|
||||
this.getContainer().getPlugin('date-time-picker').activateDateTimePicker(formSelector);
|
||||
this.getContainer().getPlugin('date-picker').activateDatePicker(formSelector);
|
||||
this.getContainer().getPlugin('autocomplete').activateAutocomplete(formSelector);
|
||||
this.getContainer().getPlugin('form-select').activateSelectPicker(formSelector, container);
|
||||
}
|
||||
|
||||
destroyForm(formSelector) {
|
||||
this.getContainer().getPlugin('form-select').destroySelectPicker(formSelector);
|
||||
this.getContainer().getPlugin('autocomplete').destroyAutocomplete(formSelector);
|
||||
this.getContainer().getPlugin('date-picker').destroyDatePicker(formSelector);
|
||||
this.getContainer().getPlugin('date-time-picker').destroyDateTimePicker(formSelector);
|
||||
this.getContainer().getPlugin('date-range-picker').destroyDateRangePicker(formSelector);
|
||||
}
|
||||
|
||||
getFormData(form) {
|
||||
let serialized = [];
|
||||
|
||||
// Loop through each field in the form
|
||||
for (let i = 0; i < form.elements.length; i++) {
|
||||
|
||||
let field = form.elements[i];
|
||||
|
||||
// Don't serialize a couple of field types (button and submit are important to exclude, eg. invoice preview would fail otherwise)
|
||||
if (!field.name || field.disabled || field.type === 'file' || field.type === 'reset' || field.type === 'submit' || field.type === 'button') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If a multi-select, get all selections
|
||||
if (field.type === 'select-multiple') {
|
||||
for (var n = 0; n < field.options.length; n++) {
|
||||
if (!field.options[n].selected) {
|
||||
continue;
|
||||
}
|
||||
serialized.push({
|
||||
name: field.name,
|
||||
value: field.options[n].value
|
||||
});
|
||||
}
|
||||
} else if ((field.type !== 'checkbox' && field.type !== 'radio') || field.checked) {
|
||||
serialized.push({
|
||||
name: field.name,
|
||||
value: field.value
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return serialized;
|
||||
}
|
||||
|
||||
convertFormDataToQueryString(formData) {
|
||||
let serialized = [];
|
||||
|
||||
for (let row of formData) {
|
||||
serialized.push(encodeURIComponent(row.name) + "=" + encodeURIComponent(row.value));
|
||||
}
|
||||
|
||||
return serialized.join('&');
|
||||
}
|
||||
}
|
||||
203
assets/js/plugins/KimaiFormSelect.js
Normal file
203
assets/js/plugins/KimaiFormSelect.js
Normal file
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* [KIMAI] KimaiFormSelect: enhanced functionality for HTML select's
|
||||
*/
|
||||
|
||||
import KimaiPlugin from "../KimaiPlugin";
|
||||
import jQuery from "jquery";
|
||||
|
||||
export default class KimaiFormSelect extends KimaiPlugin {
|
||||
|
||||
constructor(selector) {
|
||||
super();
|
||||
this.selector = selector;
|
||||
}
|
||||
|
||||
getId() {
|
||||
return 'form-select';
|
||||
}
|
||||
|
||||
activateSelectPicker(selector, container) {
|
||||
const elementSelector = this.selector;
|
||||
let options = {};
|
||||
if (container !== undefined) {
|
||||
options = {
|
||||
dropdownParent: $(container),
|
||||
};
|
||||
}
|
||||
|
||||
// Function to match the name of the parent and not only the names of the children
|
||||
// Based on the original matcher function of Select2: https://github.com/select2/select2/blob/5765090318c4d382ae56463cfa25ba8ca7bdd495/src/js/select2/defaults.js#L272
|
||||
// More information: https://select2.org/searching | https://github.com/select2/docs/blob/develop/pages/11.searching/docs.md
|
||||
function matcher(params, data) {
|
||||
// Always return the object if there is nothing to compare
|
||||
if (jQuery.trim(params.term) === '') {
|
||||
return data;
|
||||
}
|
||||
|
||||
// Check whether options has children
|
||||
let hasChildren = data.children && data.children.length > 0;
|
||||
|
||||
// Split search param by space to search for all terms and convert all to uppercase
|
||||
let terms = params.term.toUpperCase().split(' ');
|
||||
let original = data.text.toUpperCase();
|
||||
|
||||
// Always return the parent option including its children, when the name matches one of the params
|
||||
// Check if the text contains all or at least one of the terms
|
||||
let foundAll = true;
|
||||
let foundOne = false;
|
||||
let missingTerms = [];
|
||||
terms.forEach(function(item, index) {
|
||||
if (original.indexOf(item) > -1) {
|
||||
foundOne = true;
|
||||
} else {
|
||||
foundAll = false;
|
||||
missingTerms.push(item);
|
||||
}
|
||||
});
|
||||
|
||||
// If the option element contains all terms, return it
|
||||
if (foundAll) {
|
||||
return data;
|
||||
}
|
||||
|
||||
// Do a recursive check for options with children
|
||||
if (hasChildren) {
|
||||
// If the parent already contains one or more search terms, proceed only with the missing ones
|
||||
// First: Clone the original params object...
|
||||
let newParams = jQuery.extend(true, {}, params);
|
||||
if (foundOne) {
|
||||
newParams.term = missingTerms.join(' ');
|
||||
} else {
|
||||
newParams.term = params.term;
|
||||
}
|
||||
|
||||
// Clone the data object if there are children
|
||||
// This is required as we modify the object to remove any non-matches
|
||||
let match = jQuery.extend(true, {}, data);
|
||||
|
||||
// Check each child of the option
|
||||
for (let c = data.children.length - 1; c >= 0; c--) {
|
||||
let child = data.children[c];
|
||||
|
||||
let matches = matcher(newParams, child);
|
||||
|
||||
// If there wasn't a match, remove the object in the array
|
||||
if (matches === null) {
|
||||
match.children.splice(c, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// If any children matched, return the new object
|
||||
if (match.children.length > 0) {
|
||||
return match;
|
||||
}
|
||||
}
|
||||
|
||||
// If the option or its children do not contain the term, don't return anything
|
||||
return null;
|
||||
}
|
||||
|
||||
options = {...options, ...{
|
||||
language: this.getConfiguration('locale').replace('_', '-'),
|
||||
theme: "bootstrap",
|
||||
matcher: matcher
|
||||
}};
|
||||
|
||||
const templateResultFunc = function (state) {
|
||||
return jQuery('<span><span style="background-color:'+state.id+'; width: 20px; height: 20px; display: inline-block; margin-right: 10px;"> </span>' + state.text + '</span>');
|
||||
};
|
||||
|
||||
let optionsColor = {...options, ...{
|
||||
templateSelection: templateResultFunc,
|
||||
templateResult: templateResultFunc
|
||||
}};
|
||||
|
||||
jQuery(selector + ' ' + elementSelector + ':not([data-renderer=color])').select2(options);
|
||||
jQuery(selector + ' ' + elementSelector + '[data-renderer=color]').select2(optionsColor);
|
||||
|
||||
jQuery('body').on('reset', 'form', function(event){
|
||||
setTimeout(function() {
|
||||
jQuery(event.target).find(elementSelector).trigger('change');
|
||||
}, 10);
|
||||
});
|
||||
}
|
||||
|
||||
destroySelectPicker(selector) {
|
||||
jQuery(selector + ' ' + this.selector).select2('destroy');
|
||||
}
|
||||
|
||||
updateOptions(selectIdentifier, data) {
|
||||
let select = jQuery(selectIdentifier);
|
||||
let emptyOption = jQuery(selectIdentifier + ' option[value=""]');
|
||||
const selectedValue = select.val();
|
||||
|
||||
select.find('option').remove().end().find('optgroup').remove().end();
|
||||
|
||||
if (emptyOption.length !== 0) {
|
||||
select.append(this._createOption(emptyOption.text(), ''));
|
||||
}
|
||||
|
||||
let emptyOpts = [];
|
||||
let options = [];
|
||||
|
||||
for (const [key, value] of Object.entries(data)) {
|
||||
if (key === '__empty__') {
|
||||
for (const entity of value) {
|
||||
emptyOpts.push(this._createOption(entity.name, entity.id));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
let optGroup = this._createOptgroup(key);
|
||||
for (const entity of value) {
|
||||
optGroup.appendChild(this._createOption(entity.name, entity.id));
|
||||
}
|
||||
options.push(optGroup);
|
||||
}
|
||||
|
||||
select.append(options);
|
||||
select.append(emptyOpts);
|
||||
|
||||
// if available, re-select the previous selected option (mostly usable for global activities)
|
||||
select.val(selectedValue);
|
||||
|
||||
// if we don't trigger the change, the other selects won't reset
|
||||
select.trigger('change');
|
||||
|
||||
// if select2 is active, this will tell the select to refresh
|
||||
if (select.hasClass('selectpicker')) {
|
||||
select.trigger('change.select2');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} label
|
||||
* @param {string} value
|
||||
* @returns {HTMLElement}
|
||||
* @private
|
||||
*/
|
||||
_createOption(label, value) {
|
||||
let option = document.createElement('option');
|
||||
option.innerText = label;
|
||||
option.value = value;
|
||||
return option;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} label
|
||||
* @returns {HTMLElement}
|
||||
* @private
|
||||
*/
|
||||
_createOptgroup(label) {
|
||||
let optGroup = document.createElement('optgroup');
|
||||
optGroup.label = label;
|
||||
return optGroup;
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* [KIMAI] KimaiJqueryPluginInitializer: initialize jQuery plugins
|
||||
*/
|
||||
|
||||
import jQuery from 'jquery';
|
||||
import KimaiPlugin from '../KimaiPlugin';
|
||||
|
||||
export default class KimaiJqueryPluginInitializer extends KimaiPlugin {
|
||||
|
||||
init() {
|
||||
// activate the dropdown functionality
|
||||
jQuery('.dropdown-toggle').dropdown();
|
||||
// activate the tooltip functionality
|
||||
jQuery('[data-toggle="tooltip"]').tooltip();
|
||||
}
|
||||
|
||||
}
|
||||
73
assets/js/plugins/KimaiMultiUpdateTable.js
Normal file
73
assets/js/plugins/KimaiMultiUpdateTable.js
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* [KIMAI] KimaiMultiUpdateForm: handle the multi update checkbox list and form
|
||||
*/
|
||||
|
||||
import KimaiPlugin from '../KimaiPlugin';
|
||||
import jQuery from "jquery";
|
||||
|
||||
export default class KimaiMultiUpdateTable extends KimaiPlugin {
|
||||
|
||||
init() {
|
||||
const self = this;
|
||||
|
||||
jQuery('body').
|
||||
on('change', '#multi_update_all', function(event) {
|
||||
jQuery('.multi_update_single').prop('checked', jQuery(event.target).prop('checked'));
|
||||
self.toggleForm();
|
||||
})
|
||||
.on('change', '.multi_update_single', function(event) {
|
||||
self.toggleForm();
|
||||
})
|
||||
.on('change', '#multi_update_table_action', function(event) {
|
||||
const selectedItem = jQuery('#multi_update_table_action option:selected');
|
||||
const selectedVal = selectedItem.val();
|
||||
|
||||
if (selectedVal === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
const form = jQuery('#multi_update_form form');
|
||||
const selectedText = selectedItem.text();
|
||||
const ids = self.getSelectedIds();
|
||||
const question = form.attr('data-question').replace(/%action%/, selectedText).replace(/%count%/, ids.length);
|
||||
|
||||
self.getContainer().getPlugin('alert').question(question, function(value) {
|
||||
if (value) {
|
||||
form.attr('action', selectedVal).submit();
|
||||
} else {
|
||||
jQuery('#multi_update_table_action').val('').trigger('change');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getSelectedIds()
|
||||
{
|
||||
let ids = [];
|
||||
jQuery('.multi_update_single:checked').each(function(i){
|
||||
ids[i] = $(this).val();
|
||||
});
|
||||
|
||||
return ids;
|
||||
}
|
||||
|
||||
toggleForm()
|
||||
{
|
||||
const ids = this.getSelectedIds();
|
||||
jQuery('#multi_update_table_entities').val(ids.join(','));
|
||||
|
||||
if (ids.length > 0) {
|
||||
jQuery('#multi_update_form').show();
|
||||
} else {
|
||||
jQuery('#multi_update_form').hide();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -40,10 +40,15 @@ export default class KimaiRecentActivities extends KimaiPlugin {
|
||||
// don't block initial browser rendering
|
||||
setTimeout(handle, 500);
|
||||
|
||||
document.addEventListener('kimai.timesheetStop', handle);
|
||||
document.addEventListener('kimai.recentActivities', handle);
|
||||
document.addEventListener('kimai.timesheetUpdate', handle);
|
||||
document.addEventListener('kimai.timesheetDelete', handle);
|
||||
document.addEventListener('kimai.activityUpdate', handle);
|
||||
document.addEventListener('kimai.activityDelete', handle);
|
||||
document.addEventListener('kimai.projectUpdate', handle);
|
||||
document.addEventListener('kimai.projectDelete', handle);
|
||||
document.addEventListener('kimai.customerUpdate', handle);
|
||||
document.addEventListener('kimai.customerDelete', handle);
|
||||
}
|
||||
|
||||
emptyList() {
|
||||
|
||||
@@ -6,27 +6,28 @@
|
||||
*/
|
||||
|
||||
/*!
|
||||
* [KIMAI] KimaiClickHandlerReducedInTableRow: abstract class
|
||||
* [KIMAI] KimaiReducedClickHandler: abstract class
|
||||
*/
|
||||
|
||||
import jQuery from 'jquery';
|
||||
import KimaiPlugin from "../KimaiPlugin";
|
||||
|
||||
export default class KimaiClickHandlerReducedInTableRow extends KimaiPlugin {
|
||||
export default class KimaiReducedClickHandler extends KimaiPlugin {
|
||||
|
||||
_addClickHandlerReducedInTableRow(selector, callback) {
|
||||
_addClickHandler(selector, callback) {
|
||||
jQuery('body').on('click', selector, function(event) {
|
||||
// just in case an inner element is editable, than this should not be triggered
|
||||
if (event.target.parentNode.isContentEditable || event.target.isContentEditable) {
|
||||
if (event.target.parentNode.isContentEditable || event.target.isContentEditable) {
|
||||
return;
|
||||
}
|
||||
|
||||
// handles the "click" on table rows to open an entry for editing: when a button within a row is clicked,
|
||||
// we don't want the table row event to be processed - so we intercept it
|
||||
// handles the "click" on table rows or list elements
|
||||
let target = event.target;
|
||||
if (event.currentTarget.matches('tr')) {
|
||||
if (event.currentTarget.matches('tr') || event.currentTarget.matches('li')) {
|
||||
while (target !== null && !target.matches('body')) {
|
||||
if (target.matches('a') || target.matches ('button')) {
|
||||
// when an element within the row is clicked, that can trigger stuff itself, we don't want the event to be processed
|
||||
// don't act if a link, button or form element was clicked
|
||||
if (target.matches('a') || target.matches ('button') || target.matches ('input')) {
|
||||
return;
|
||||
}
|
||||
target = target.parentNode;
|
||||
@@ -41,6 +42,10 @@ export default class KimaiClickHandlerReducedInTableRow extends KimaiPlugin {
|
||||
href = jQuery(this).attr('href');
|
||||
}
|
||||
|
||||
if (href === undefined || href === null || href === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
callback(href);
|
||||
});
|
||||
}
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
import jQuery from 'jquery';
|
||||
import KimaiPlugin from "../KimaiPlugin";
|
||||
import moment from 'moment';
|
||||
|
||||
export default class KimaiSelectDataAPI extends KimaiPlugin {
|
||||
|
||||
@@ -32,50 +33,116 @@ export default class KimaiSelectDataAPI extends KimaiPlugin {
|
||||
const API = this.getContainer().getPlugin('api');
|
||||
|
||||
jQuery('body').on('change', selector, function(event) {
|
||||
let apiUrl = jQuery(this).attr('data-api-url').replace('-s-', jQuery(this).val());
|
||||
const targetSelect = '#' + jQuery(this).attr('data-related-select');
|
||||
const targetSelect = '#' + this.dataset['relatedSelect'];
|
||||
|
||||
// if the related target select does not exist, we do not need to load the related data
|
||||
if (jQuery(targetSelect).length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (jQuery(this).val() === '') {
|
||||
if (jQuery(this).attr('data-empty-url') === undefined) {
|
||||
let formPrefix = jQuery(this).parents('form').first().attr('name');
|
||||
if (formPrefix === undefined || formPrefix === null) {
|
||||
formPrefix = '';
|
||||
} else {
|
||||
formPrefix += '_';
|
||||
}
|
||||
|
||||
let newApiUrl = self._buildUrlWithFormFields(this.dataset['apiUrl'], formPrefix);
|
||||
|
||||
const selectValue = jQuery(this).val();
|
||||
|
||||
// Problem: select a project with activities and then select a customer that has no project
|
||||
// results in a wrong URL, it triggers "activities?project=" instead of using the "emptyUrl"
|
||||
if (selectValue === undefined || selectValue === null || selectValue === '' || (Array.isArray(selectValue) && selectValue.length === 0)) {
|
||||
if (this.dataset['emptyUrl'] === undefined) {
|
||||
self._updateSelect(targetSelect, {});
|
||||
jQuery(targetSelect).attr('disabled', 'disabled');
|
||||
return;
|
||||
}
|
||||
apiUrl = jQuery(this).attr('data-empty-url').replace('-s-', jQuery(this).val());
|
||||
newApiUrl = self._buildUrlWithFormFields(this.dataset['emptyUrl'], formPrefix);
|
||||
}
|
||||
|
||||
jQuery(targetSelect).removeAttr('disabled');
|
||||
|
||||
API.get(apiUrl, {}, function(data){
|
||||
API.get(newApiUrl, {}, function(data){
|
||||
self._updateSelect(targetSelect, data);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
_buildUrlWithFormFields(apiUrl, formPrefix) {
|
||||
let newApiUrl = apiUrl;
|
||||
|
||||
_updateSelect(selectName, data) {
|
||||
let select = jQuery(selectName);
|
||||
let emptyOption = jQuery(selectName + ' option[value=""]');
|
||||
apiUrl.split('?')[1].split('&').forEach(item => {
|
||||
let [key, value] = item.split('=');
|
||||
let decoded = decodeURIComponent(value);
|
||||
let test = decoded.match(/%(.*)%/);
|
||||
if (test !== null) {
|
||||
let targetField = jQuery('#' + formPrefix + test[1]);
|
||||
let newValue = '';
|
||||
if (targetField.length === 0) {
|
||||
// happens for example:
|
||||
// - in duration only mode, when the end field is not found
|
||||
// console.log('ERROR: Cannot find field with name "' + test[1] + '" by selector: #' + formPrefix + test[1]);
|
||||
} else {
|
||||
if (targetField.val() !== null) {
|
||||
newValue = targetField.val();
|
||||
|
||||
select.find('option').remove().end().find('optgroup').remove().end();
|
||||
if (newValue !== '') {
|
||||
// having that special case here is far from being perfect... but for now it works ;-)
|
||||
if (targetField.data('daterangepicker') !== undefined) {
|
||||
if (key === 'begin' || key === 'start' || targetField.data('daterangepicker').singleDatePicker) {
|
||||
newValue = targetField.data('daterangepicker').startDate.format(moment.HTML5_FMT.DATETIME_LOCAL_SECONDS);
|
||||
} else if (key === 'end') {
|
||||
newValue = targetField.data('daterangepicker').endDate.format(moment.HTML5_FMT.DATETIME_LOCAL_SECONDS);
|
||||
}
|
||||
} else if (targetField.data('format') !== undefined) {
|
||||
if (moment(newValue, targetField.data('format')).isValid()) {
|
||||
newValue = moment(newValue, targetField.data('format')).format(moment.HTML5_FMT.DATETIME_LOCAL_SECONDS);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// happens for example:
|
||||
// - when the end date is not set on a timesheet record and the project list is loaded (as the URL contains the %end% replacer)
|
||||
// console.log('Empty value found for field with name "' + test[1] + '" by selector: #' + formPrefix + test[1]);
|
||||
}
|
||||
} else {
|
||||
// happens for example:
|
||||
// - when a customer without projects is selected
|
||||
// console.log('ERROR: Empty field with name "' + test[1] + '" by selector: #' + formPrefix + test[1]);
|
||||
}
|
||||
}
|
||||
|
||||
if (emptyOption.length !== 0) {
|
||||
select.append('<option value="">' + emptyOption.text() + '</option>');
|
||||
}
|
||||
if (Array.isArray(newValue)) {
|
||||
newValue = newValue.join(',');
|
||||
}
|
||||
|
||||
jQuery.each(data, function(i, obj) {
|
||||
select.append('<option value="' + obj.id + '">' + obj.name + '</option>');
|
||||
newApiUrl = newApiUrl.replace(value, newValue);
|
||||
}
|
||||
});
|
||||
|
||||
// if we don't trigger the change, the other selects won't be resetted
|
||||
select.trigger('change');
|
||||
return newApiUrl;
|
||||
}
|
||||
|
||||
// if the beta test kimai.theme.select_type is active, this will tell the selects to refresh
|
||||
jQuery('.selectpicker').selectpicker('refresh');
|
||||
_updateSelect(selectName, data) {
|
||||
const options = {};
|
||||
for (const apiData of data) {
|
||||
let title = '__empty__';
|
||||
if (apiData.hasOwnProperty('parentTitle') && apiData.parentTitle !== null) {
|
||||
title = apiData.parentTitle;
|
||||
}
|
||||
if (!options.hasOwnProperty(title)) {
|
||||
options[title] = [];
|
||||
}
|
||||
options[title].push(apiData);
|
||||
}
|
||||
|
||||
const ordered = {};
|
||||
Object.keys(options).sort().forEach(function(key) {
|
||||
ordered[key] = options[key];
|
||||
});
|
||||
|
||||
this.getContainer().getPlugin('form-select').updateOptions(selectName, ordered);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,7 +15,60 @@ import KimaiPlugin from '../KimaiPlugin';
|
||||
export default class KimaiThemeInitializer extends KimaiPlugin {
|
||||
|
||||
init() {
|
||||
this.registerGlobalAjaxErrorHandler();
|
||||
this.registerAutomaticAlertRemove('div.alert-success', 5000);
|
||||
// activate the dropdown functionality
|
||||
jQuery('.dropdown-toggle').dropdown();
|
||||
// activate the tooltip functionality
|
||||
jQuery('[data-toggle="tooltip"]').tooltip();
|
||||
// activate all form plugins
|
||||
this.getContainer().getPlugin('form').activateForm('.content-wrapper form', 'body');
|
||||
this.getContainer().getPlugin('form').activateForm('form.searchform', 'body');
|
||||
|
||||
this.registerModalAutofocus('#modal_search');
|
||||
this.registerModalAutofocus('#remote_form_modal');
|
||||
}
|
||||
|
||||
/**
|
||||
* workaround for autofocus attribute, as the modal "steals" it
|
||||
*
|
||||
* @param {string} selector
|
||||
*/
|
||||
registerModalAutofocus(selector) {
|
||||
let modal = jQuery(selector);
|
||||
if (modal.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
modal.on('shown.bs.modal', function () {
|
||||
let form = modal.find('form');
|
||||
let formAutofocus = form.find('[autofocus]');
|
||||
if (formAutofocus.length < 1) {
|
||||
formAutofocus = form.find('input[type=text],textarea,select');
|
||||
}
|
||||
formAutofocus.filter(':not("[data-datetimepicker=on]")').filter(':visible:first').focus().delay(1000).focus();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* redirect access denied / session timeouts to login page
|
||||
*/
|
||||
registerGlobalAjaxErrorHandler() {
|
||||
const loginUrl = this.getConfiguration('login');
|
||||
const alert = this.getContainer().getPlugin('alert');
|
||||
const translation = this.getContainer().getTranslation().get('login.required');
|
||||
jQuery(document).ajaxError(function(event, jqxhr, settings, thrownError) {
|
||||
if (jqxhr.status !== undefined && jqxhr.status === 403) {
|
||||
const loginRequired = jqxhr.getResponseHeader('login-required');
|
||||
if (loginRequired !== null) {
|
||||
alert.question(translation, function (result) {
|
||||
if (result === true) {
|
||||
window.location.replace(loginUrl);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,12 +14,131 @@ import KimaiPlugin from "../KimaiPlugin";
|
||||
|
||||
export default class KimaiToolbar extends KimaiPlugin {
|
||||
|
||||
init() {
|
||||
const datatable = this.getContainer().getPlugin('datatable');
|
||||
constructor(formSelector, formSubmitActionClass) {
|
||||
super();
|
||||
this.formSelector = formSelector;
|
||||
this.actionClass = formSubmitActionClass;
|
||||
}
|
||||
|
||||
// This catches all clicks on the pagination and prevents the default action, as we want to relad the page via JS
|
||||
getId() {
|
||||
return 'toolbar';
|
||||
}
|
||||
|
||||
init() {
|
||||
const formSelector = this.getSelector();
|
||||
const self = this;
|
||||
const EVENT = self.getContainer().getPlugin('event');
|
||||
|
||||
this._registerPagination(formSelector, EVENT);
|
||||
this._registerSortableTables(formSelector, EVENT);
|
||||
this._registerAlternativeSubmitActions(formSelector, this.actionClass);
|
||||
|
||||
// Reset the page if filter values are changed, otherwise we might end up with a limited set of data,
|
||||
// which does not support the given page - and it would be just wrong to stay in the same page
|
||||
jQuery(formSelector +' input').change(function (event) {
|
||||
switch (event.target.id) {
|
||||
case 'order':
|
||||
case 'orderBy':
|
||||
case 'page':
|
||||
break;
|
||||
default:
|
||||
jQuery(formSelector + ' input#page').val(1);
|
||||
break;
|
||||
}
|
||||
self.triggerChange();
|
||||
});
|
||||
|
||||
// when user selected a new customer or project, reset the pagination back to 1
|
||||
// and then find out if the results should be reloaded
|
||||
jQuery(formSelector + ' select').change(function (event) {
|
||||
let reload = true;
|
||||
switch (event.target.id) {
|
||||
case 'customer':
|
||||
if (jQuery(formSelector + ' select#project').length > 0) {
|
||||
reload = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'project':
|
||||
if (jQuery(formSelector + ' select#activity').length > 0) {
|
||||
reload = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
jQuery(formSelector + ' input#page').val(1);
|
||||
|
||||
if (reload) {
|
||||
self.triggerChange();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Some actions utilize the filter from the search form and submit it to another URL.
|
||||
* @private
|
||||
*/
|
||||
_registerAlternativeSubmitActions(toolbarSelector, actionBtnClass) {
|
||||
document.addEventListener('click', function(event) {
|
||||
let target = event.target;
|
||||
while (target !== null && !target.matches('body')) {
|
||||
if (target.classList.contains(actionBtnClass)) {
|
||||
const form = document.querySelector(toolbarSelector);
|
||||
if (form === null) {
|
||||
return;
|
||||
}
|
||||
const prevAction = form.action;
|
||||
const prevMethod = form.method;
|
||||
form.target = '_blank';
|
||||
form.action = target.href;
|
||||
if (target.dataset.method !== undefined) {
|
||||
form.method = target.dataset.method;
|
||||
}
|
||||
form.submit();
|
||||
form.target = '';
|
||||
form.action = prevAction;
|
||||
form.method = prevMethod;
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
target = target.parentNode;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sortable datatables use hidden fields in the toolbar filter/search form
|
||||
* @private
|
||||
*/
|
||||
_registerSortableTables(formSelector, EVENT) {
|
||||
jQuery('body').on('click', 'th.sortable', function(event){
|
||||
var $header = jQuery(event.target);
|
||||
var order = 'DESC';
|
||||
var orderBy = $header.data('order');
|
||||
if ($header.hasClass('sorting_desc')) {
|
||||
order = 'ASC';
|
||||
}
|
||||
|
||||
jQuery(formSelector + ' #orderBy').val(orderBy);
|
||||
jQuery(formSelector + ' #order').val(order);
|
||||
|
||||
// re-render the selectboxes
|
||||
jQuery(formSelector + ' #orderBy').trigger('change');
|
||||
jQuery(formSelector + ' #order').trigger('change');
|
||||
|
||||
// triggers the datatable reload - search for the event name
|
||||
EVENT.trigger('filter-change');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This catches all clicks on the pagination and prevents the default action, as we want to reload the page via JS
|
||||
* @private
|
||||
*/
|
||||
_registerPagination(formSelector, EVENT) {
|
||||
jQuery('body').on('click', 'div.navigation ul.pagination li a', function(event) {
|
||||
let pager = jQuery(".toolbar form input[name='page']");
|
||||
let pager = jQuery(formSelector + " input#page");
|
||||
if (pager.length === 0) {
|
||||
return;
|
||||
}
|
||||
@@ -29,41 +148,26 @@ export default class KimaiToolbar extends KimaiPlugin {
|
||||
let page = urlParts[urlParts.length-1];
|
||||
pager.val(page);
|
||||
pager.trigger('change');
|
||||
EVENT.trigger('pagination-change');
|
||||
return false;
|
||||
});
|
||||
|
||||
// Reset the page if any other value is changed, otherwise we might end up with a limited set
|
||||
// of data which does not support the given page - and it would be just wrong to stay in the same page
|
||||
jQuery('.toolbar form input').change(function (event) {
|
||||
switch (event.target.id) {
|
||||
case 'page':
|
||||
break;
|
||||
default:
|
||||
jQuery('.toolbar form input#page').val(1);
|
||||
}
|
||||
datatable.reloadDatatable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers an event, that everyone can listen for.
|
||||
*/
|
||||
triggerChange() {
|
||||
this.getContainer().getPlugin('event').trigger('toolbar-change');
|
||||
}
|
||||
|
||||
jQuery('.toolbar form select').change(function (event) {
|
||||
let reload = true;
|
||||
switch (event.target.id) {
|
||||
case 'customer':
|
||||
if (jQuery('.toolbar form select#project').length > 0) {
|
||||
reload = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'project':
|
||||
if (jQuery('.toolbar form select#activity').length > 0) {
|
||||
reload = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
jQuery('.toolbar form input#page').val(1);
|
||||
if (reload) {
|
||||
datatable.reloadDatatable();
|
||||
}
|
||||
});
|
||||
/**
|
||||
* Returns the CSS selector to target the toolbar form.
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
getSelector() {
|
||||
return this.formSelector;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import KimaiPlugin from '../KimaiPlugin';
|
||||
|
||||
/**
|
||||
* Needs to be initialized with a class name.
|
||||
*
|
||||
* A link like <a href=# class=remoteLink> can be activated with:
|
||||
* new KimaiToolbarAction('remoteLink')
|
||||
*
|
||||
* @param selector
|
||||
*/
|
||||
export default class KimaiToolbarAction extends KimaiPlugin {
|
||||
|
||||
constructor(selector) {
|
||||
super();
|
||||
this.selector = selector;
|
||||
}
|
||||
|
||||
init() {
|
||||
const self = this;
|
||||
document.addEventListener('click', function(event) {
|
||||
let target = event.target;
|
||||
while (target !== null && !target.matches('body')) {
|
||||
if (target.classList.contains(self.selector)) {
|
||||
const form = document.querySelector('div.toolbar form.navbar-form');
|
||||
if (form === null) {
|
||||
return;
|
||||
}
|
||||
const prevAction = form.action;
|
||||
const prevMethod = form.method;
|
||||
form.target = '_blank';
|
||||
form.action = target.href;
|
||||
if (target.dataset.method !== undefined) {
|
||||
form.method = target.dataset.method;
|
||||
}
|
||||
form.submit();
|
||||
form.target = '';
|
||||
form.action = prevAction;
|
||||
form.method = prevMethod;
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
target = target.parentNode;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
28
assets/js/widgets/KimaiCookies.js
Normal file
28
assets/js/widgets/KimaiCookies.js
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* [KIMAI] KimaiCookies: simple wrapper to handle cookies
|
||||
*/
|
||||
|
||||
import Cookies from 'js-cookie';
|
||||
|
||||
export default class KimaiCookies {
|
||||
|
||||
static set(name, values, options) {
|
||||
Cookies.set(name, values, options);
|
||||
}
|
||||
|
||||
static get(name) {
|
||||
return Cookies.getJSON(name);
|
||||
}
|
||||
|
||||
static remove(name) {
|
||||
Cookies.remove(name);
|
||||
}
|
||||
|
||||
}
|
||||
97
assets/js/widgets/KimaiPaginatedBoxWidget.js
Normal file
97
assets/js/widgets/KimaiPaginatedBoxWidget.js
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* [KIMAI] KimaiPaginatedBoxWidget: handles box widgets that have a pagination
|
||||
*/
|
||||
|
||||
import jQuery from "jquery";
|
||||
|
||||
export default class KimaiPaginatedBoxWidget {
|
||||
|
||||
constructor(boxId) {
|
||||
this.selector = boxId;
|
||||
this.overlay = jQuery('<div class="overlay"><div class="fas fa-sync fa-spin"></div></div>');
|
||||
this.widget = jQuery(this.selector);
|
||||
this.href = this.widget.data('href');
|
||||
this.events = this.widget.data('reload').split(' ');
|
||||
|
||||
const self = this;
|
||||
|
||||
const reloadPage = function (event) {
|
||||
const page = jQuery(self.selector + ' .box-tools').data('page');
|
||||
const url = self._buildUrl(page);
|
||||
self.loadPage(url);
|
||||
};
|
||||
|
||||
for (const eventName of this.events) {
|
||||
document.addEventListener(eventName, reloadPage);
|
||||
}
|
||||
|
||||
this.widget.on('click', '.box-tools ul.pagination a', function (event) {
|
||||
event.preventDefault();
|
||||
self.loadPage(jQuery(event.currentTarget).attr('href'));
|
||||
});
|
||||
}
|
||||
|
||||
static create(elementId) {
|
||||
return new KimaiPaginatedBoxWidget(elementId);
|
||||
}
|
||||
|
||||
_showOverlay() {
|
||||
this.widget.append(this.overlay);
|
||||
}
|
||||
|
||||
_hideOverlay() {
|
||||
jQuery(this.overlay).remove();
|
||||
}
|
||||
|
||||
loadPage(url) {
|
||||
const self = this;
|
||||
const selector = this.selector;
|
||||
|
||||
self._showOverlay();
|
||||
|
||||
jQuery.ajax({
|
||||
url: url,
|
||||
data: {},
|
||||
success: function (response) {
|
||||
const html = jQuery(response);
|
||||
jQuery(selector + ' .box-tools').replaceWith(html.find('.box-tools'));
|
||||
jQuery(selector + ' .box-body').replaceWith(html.find('.box-body'));
|
||||
jQuery(selector + ' .box-title').replaceWith(html.find('.box-title'));
|
||||
if (jQuery(selector + ' .box-footer').length > 0) {
|
||||
jQuery(selector + ' .box-footer').replaceWith(html.find('.box-footer'));
|
||||
}
|
||||
jQuery(selector + ' .box-body [data-toggle="tooltip"]').tooltip();
|
||||
self.widget.removeData('error-retry');
|
||||
self._hideOverlay();
|
||||
},
|
||||
dataType: 'html',
|
||||
error: function(jqXHR, textStatus, errorThrown) {
|
||||
if (jqXHR.status !== undefined && jqXHR.status === 500) {
|
||||
if (self.widget.data('error-retry') !== undefined) {
|
||||
// TODO show error message ?
|
||||
return;
|
||||
}
|
||||
const page = jQuery(selector + ' .box-tools').data('page');
|
||||
if (page > 1) {
|
||||
self.widget.data('error-retry', 1);
|
||||
var url = self._buildUrl(page - 1);
|
||||
self.loadPage(url);
|
||||
}
|
||||
}
|
||||
self._hideOverlay();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_buildUrl(page) {
|
||||
return this.href.replace('1', page);
|
||||
}
|
||||
|
||||
}
|
||||
73
assets/js/widgets/KimaiReloadPageWidget.js
Normal file
73
assets/js/widgets/KimaiReloadPageWidget.js
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* [KIMAI] KimaiReloadPageWidget: a simple helper to reload the page on events
|
||||
*/
|
||||
|
||||
import jQuery from "jquery";
|
||||
|
||||
export default class KimaiReloadPageWidget {
|
||||
|
||||
constructor(events, fullReload) {
|
||||
this.overlay = jQuery('<div class="overlay-wrapper"><div class="overlay"><div class="fas fa-sync fa-spin"></div></div></div>');
|
||||
this.widget = jQuery('div.content-wrapper');
|
||||
|
||||
const self = this;
|
||||
|
||||
const reloadPage = function (event) {
|
||||
if (fullReload) {
|
||||
document.location.reload(true);
|
||||
} else {
|
||||
self.loadPage(document.location);
|
||||
}
|
||||
};
|
||||
|
||||
for (const eventName of events.split(' ')) {
|
||||
document.addEventListener(eventName, reloadPage);
|
||||
}
|
||||
}
|
||||
|
||||
static create(events, fullReload) {
|
||||
if (fullReload === undefined || fullReload === null) {
|
||||
fullReload = false;
|
||||
}
|
||||
return new KimaiReloadPageWidget(events, fullReload);
|
||||
}
|
||||
|
||||
_showOverlay() {
|
||||
this.widget.append(this.overlay);
|
||||
}
|
||||
|
||||
_hideOverlay() {
|
||||
jQuery(this.overlay).remove();
|
||||
}
|
||||
|
||||
loadPage(url) {
|
||||
const self = this;
|
||||
|
||||
self._showOverlay();
|
||||
|
||||
jQuery.ajax({
|
||||
url: url,
|
||||
data: {},
|
||||
success: function (response) {
|
||||
jQuery('section.content').replaceWith(
|
||||
jQuery(response).find('section.content')
|
||||
);
|
||||
document.dispatchEvent(new Event('kimai.reloadPage'));
|
||||
self._hideOverlay();
|
||||
},
|
||||
dataType: 'html',
|
||||
error: function(jqXHR, textStatus, errorThrown) {
|
||||
self._hideOverlay();
|
||||
document.location = url;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
30
assets/js/widgets/KimaiStorage.js
Normal file
30
assets/js/widgets/KimaiStorage.js
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* [KIMAI] KimaiStorage: simple wrapper to handle localStorage access
|
||||
*/
|
||||
|
||||
export default class KimaiStorage {
|
||||
|
||||
static set(name, values) {
|
||||
window.localStorage.setItem(name, JSON.stringify(values));
|
||||
}
|
||||
|
||||
static get(name) {
|
||||
let value = window.localStorage.getItem(name);
|
||||
if (value === undefined || value === null) {
|
||||
return null;
|
||||
}
|
||||
return JSON.parse(value);
|
||||
}
|
||||
|
||||
static remove(name) {
|
||||
window.localStorage.removeItem(name);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,15 +5,16 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
@import "~bootstrap-sass/assets/stylesheets/bootstrap/variables";
|
||||
@import 'variables';
|
||||
@import 'layout';
|
||||
@import 'error-page';
|
||||
@import 'print';
|
||||
@import 'content';
|
||||
@import 'box';
|
||||
@import 'content-header';
|
||||
@import 'toolbar';
|
||||
@import 'sidebar';
|
||||
@import 'footer';
|
||||
@import 'invoice';
|
||||
@import 'datatable';
|
||||
@import 'datatable-extensions';
|
||||
@import 'calendar';
|
||||
@@ -23,3 +24,11 @@
|
||||
@import 'skins';
|
||||
@import 'sweetalert';
|
||||
@import 'autocomplete';
|
||||
@import 'selectpicker';
|
||||
@import 'reporting';
|
||||
@import 'forms';
|
||||
@import 'modal';
|
||||
@import 'progressbar';
|
||||
@import 'avatar';
|
||||
@import 'security';
|
||||
@import 'tabs';
|
||||
|
||||
60
assets/sass/avatar.scss
Normal file
60
assets/sass/avatar.scss
Normal file
@@ -0,0 +1,60 @@
|
||||
|
||||
$avatar-base-size: 30;
|
||||
|
||||
.avatar {
|
||||
text-align: center;
|
||||
background-color: #d2d6de;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
height: ($avatar-base-size)+px;
|
||||
width: ($avatar-base-size)+px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.initials {
|
||||
line-height: 1;
|
||||
position: relative;
|
||||
font-weight: bold;
|
||||
font-size: ceil($avatar-base-size / 2 - 2)+px;
|
||||
top: floor($avatar-base-size / 4 - 1)+px;
|
||||
}
|
||||
|
||||
$avatarSizes: "xs" .75, "sm" 1.25, "md" 1.5, "lg" 2;
|
||||
|
||||
@each $avatarSizeName, $avatarBaseSize in $avatarSizes {
|
||||
.avatar-#{$avatarSizeName} {
|
||||
width: ceil($avatar-base-size * $avatarBaseSize)+px;
|
||||
height: ceil($avatar-base-size * $avatarBaseSize)+px;
|
||||
|
||||
.initials {
|
||||
font-size: ceil($avatar-base-size * $avatarBaseSize / 2 - 2)+px;
|
||||
top: ceil($avatar-base-size * $avatarBaseSize / 4 - 1)+px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.widget-user-image {
|
||||
border-radius: 50%;
|
||||
border: 3px solid #fff;
|
||||
.avatar {
|
||||
width: ceil($avatar-base-size * 2.75)+px;
|
||||
height: ceil($avatar-base-size * 2.75)+px;
|
||||
.initials {
|
||||
font-size: ceil($avatar-base-size * 2.75 / 2 - 2)+px;
|
||||
top: ceil($avatar-base-size * 2.75 / 4 - 1)+px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Detail tables like my teams (Dashboard) or */
|
||||
table.dataTable.table > tbody > tr > td {
|
||||
&.avatars {
|
||||
.avatar {
|
||||
margin: 1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.avatar.teamlead {
|
||||
box-shadow: 0px 0px 5px 1px rgba(0,0,0,.4),0 0px 3px 0 rgba(0,0,0,0.3);
|
||||
}
|
||||
9
assets/sass/bootstrap.scss
vendored
Normal file
9
assets/sass/bootstrap.scss
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
$icon-font-path: "~bootstrap-sass/assets/fonts/bootstrap/";
|
||||
@import '~bootstrap-sass/assets/stylesheets/bootstrap';
|
||||
22
assets/sass/box.scss
Normal file
22
assets/sass/box.scss
Normal file
@@ -0,0 +1,22 @@
|
||||
.box {
|
||||
/* used on detail pages for: customer, project and activity */
|
||||
.box-body.no-padding div.comment,
|
||||
.box-body.no-padding .box-padding {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.box-tools {
|
||||
/* a button that is directly connected with a pagination element inside the box tools (eg. project list on customer detail page) */
|
||||
.btn-pager {
|
||||
float: left;
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
#customer_details_box,
|
||||
#project_details_box,
|
||||
#activity_details_box {
|
||||
th {
|
||||
width: 30%;
|
||||
}
|
||||
}
|
||||
@@ -6,3 +6,46 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Drag and Drop events with styling */
|
||||
.external-events {
|
||||
.external-event {
|
||||
vertical-align: middle;
|
||||
padding: 0 !important;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
padding: 0 7px 4px 7px;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-size: 0.7em;
|
||||
}
|
||||
|
||||
.title {
|
||||
padding: 3px 7px 0;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.fc-bootstrap3 {
|
||||
.table-bordered > tbody > tr > td,
|
||||
.table-bordered > thead > tr > td,
|
||||
.table-bordered > thead > tr > th {
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
.fc-head {
|
||||
background-color: #fafafa;
|
||||
}
|
||||
td.fc-today {
|
||||
background-color: #fcf8e3!important;
|
||||
}
|
||||
}
|
||||
65
assets/sass/content-header.scss
Normal file
65
assets/sass/content-header.scss
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
.content-header {
|
||||
height: 50px;
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.1);
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1) !important;
|
||||
background-color: #fff !important;
|
||||
padding: 10px 0 0 10px;
|
||||
|
||||
h1 {
|
||||
padding-top: 3px;
|
||||
float: left;
|
||||
small {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Page based action buttons in the upper right corner of the content area */
|
||||
.content-header>.breadcrumb {
|
||||
position: absolute;
|
||||
float: right;
|
||||
background: transparent;
|
||||
top: 0;
|
||||
right: 0;
|
||||
padding-left: 10px;
|
||||
.btn {
|
||||
span.label {
|
||||
position: absolute;
|
||||
top: 1px;
|
||||
right: 1px;
|
||||
text-align: center;
|
||||
font-size: 9px;
|
||||
padding: 2px 3px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: $screen-md-max) {
|
||||
.content-header h1 {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
.content-header>.breadcrumb {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: $screen-sm-min) {
|
||||
.content-header>.breadcrumb {
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
.content-header {
|
||||
h1 {
|
||||
small {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,48 +8,19 @@
|
||||
.content {
|
||||
padding: 15px 0;
|
||||
}
|
||||
.content-header {
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.1);
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1) !important;
|
||||
background-color: #fff !important;
|
||||
padding: 15px 0 15px 10px;
|
||||
|
||||
h1 {
|
||||
small {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.content-header>.breadcrumb {
|
||||
position: relative;
|
||||
margin-top: 5px;
|
||||
top: 0;
|
||||
right: 0;
|
||||
float: none;
|
||||
background: #d2d6de;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
@media (min-width: $screen-sm-min) {
|
||||
.content-header>.breadcrumb {
|
||||
right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: $screen-sm-min) {
|
||||
.content-header {
|
||||
h1 {
|
||||
small {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
.content {
|
||||
padding: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: $screen-lg-min) {
|
||||
.content {
|
||||
padding: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
td {
|
||||
&.timesheet-description {
|
||||
ul {
|
||||
@@ -69,4 +40,48 @@ td {
|
||||
text-align: center;
|
||||
line-height: 35px;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.box-profile {
|
||||
img.img-circle {
|
||||
width: 100px;
|
||||
}
|
||||
.profile-username {
|
||||
margin-top: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.label-gray {
|
||||
background-color: $gray-lte;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
/* Delete link in action dropdowns */
|
||||
.dropdown-menu > li.delete > a {
|
||||
color: #dd4b39;
|
||||
}
|
||||
|
||||
.open-edit {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* allow minimized table columns, eg. customer team & visibility */
|
||||
th.w-min,
|
||||
td.w-min {
|
||||
width: 1%;
|
||||
white-space: nowrap;
|
||||
}
|
||||
table.dataTable thead > tr > th.hw-min {
|
||||
width: 1%;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.badge {
|
||||
font-weight: normal;
|
||||
color: #000;
|
||||
background-color: $kimai-default;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-weight: normal;
|
||||
}
|
||||
@@ -13,4 +13,8 @@ section {
|
||||
}
|
||||
}
|
||||
}
|
||||
.box-body-scrollable {
|
||||
overflow: auto;
|
||||
max-height: 300px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,35 +5,59 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
|
||||
table.dataTable.table > tbody > tr > td {
|
||||
vertical-align: middle;
|
||||
.img-circle {
|
||||
max-width: 30px;
|
||||
margin-right: 0;
|
||||
&.teamlead {
|
||||
box-shadow: 0 0 5px 3px $gray-lte;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
span.label-color,
|
||||
span.label-meta,
|
||||
span.label-tag,
|
||||
span.label-user,
|
||||
span.label-activity,
|
||||
span.label-project,
|
||||
span.label-customer {
|
||||
display: inline-block;
|
||||
.name {
|
||||
white-space: nowrap;
|
||||
.name-inner {
|
||||
white-space: normal;
|
||||
}
|
||||
}
|
||||
.dot {
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
background-color: $gray-lte;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
font-size: 9px;
|
||||
margin-right: 3px;
|
||||
color: $gray-lte;
|
||||
}
|
||||
}
|
||||
|
||||
table.dataTable {
|
||||
/* action column */
|
||||
.actions {
|
||||
width: 90px;
|
||||
li.delete a {
|
||||
color: #dd4b39;
|
||||
}
|
||||
width: 40px;
|
||||
}
|
||||
tr {
|
||||
/* summary row - provided in the users own timesheet table */
|
||||
&.summary td {
|
||||
font-weight: bold;
|
||||
border-bottom: 1px solid #ccc;
|
||||
background-color: #eaeaea;
|
||||
}
|
||||
&.open-edit {
|
||||
cursor: pointer;
|
||||
th.multiCheckbox {
|
||||
width: 15px;
|
||||
}
|
||||
&.overlapping {
|
||||
border-top: 2px dotted red;
|
||||
}
|
||||
&.exported {
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
td {
|
||||
@@ -46,8 +70,8 @@ table.dataTable {
|
||||
}
|
||||
.label {
|
||||
font-size: 85%;
|
||||
font-weight: 500;
|
||||
padding: .3em .6em .3em;
|
||||
line-height: 24px;
|
||||
}
|
||||
span.label-activity,
|
||||
span.label-project,
|
||||
@@ -65,4 +89,23 @@ table.dataTable {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
tfoot {
|
||||
tr {
|
||||
/* overwritten if used in tfoot, to switch the border from below to top */
|
||||
&.summary td {
|
||||
border-top: 1px solid #ccc;
|
||||
border-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
table.table-hover {
|
||||
tr {
|
||||
&.exported {
|
||||
&:hover {
|
||||
opacity: 1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ section.content .overlay > .fas,
|
||||
section.content > .overlay,
|
||||
.box > .loading-img,
|
||||
.overlay-wrapper > .loading-img {
|
||||
position: absolute;
|
||||
position: fixed; /* see #1330 */
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
@@ -125,46 +125,45 @@ table.dataTable thead .sorting_desc_disabled {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
table.dataTable thead .sorting_asc,
|
||||
table.dataTable thead .sorting_desc {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
table.dataTable thead .sorting:after,
|
||||
table.dataTable thead .sorting_asc:after,
|
||||
table.dataTable thead .sorting_desc:after {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
display: block;
|
||||
font-family: 'Glyphicons Halflings';
|
||||
padding-left: 5px;
|
||||
font-family: 'Font Awesome 5 Free';
|
||||
opacity: 0.5;
|
||||
font-size: 12px;
|
||||
}
|
||||
table.dataTable thead .sorting:after {
|
||||
opacity: 0.2;
|
||||
content: "\e150"; /* sort */
|
||||
content: "\f0dc"; /* sort */
|
||||
}
|
||||
table.dataTable thead .sorting_asc:after {
|
||||
content: "\e155"; /* sort-by-attributes */
|
||||
content: "\f062";
|
||||
}
|
||||
table.dataTable thead .sorting_desc:after {
|
||||
content: "\e156"; /* sort-by-attributes-alt */
|
||||
content: "\f063";
|
||||
}
|
||||
div.dataTables_scrollBody table.dataTable thead .sorting:after,
|
||||
div.dataTables_scrollBody table.dataTable thead .sorting_asc:after,
|
||||
div.dataTables_scrollBody table.dataTable thead .sorting_desc:after {
|
||||
div.dataTables_scrollBody table.dataTable thead .sorting:before,
|
||||
div.dataTables_scrollBody table.dataTable thead .sorting_asc:before,
|
||||
div.dataTables_scrollBody table.dataTable thead .sorting_desc:before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
table.dataTable thead .sorting_asc_disabled:after,
|
||||
table.dataTable thead .sorting_desc_disabled:after {
|
||||
table.dataTable thead .sorting_asc_disabled:before,
|
||||
table.dataTable thead .sorting_desc_disabled:before {
|
||||
color: #eee;
|
||||
}
|
||||
|
||||
table.dataTable thead > tr > th {
|
||||
vertical-align: top;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/*
|
||||
table.dataTable thead > tr > th {
|
||||
padding-right: 30px;
|
||||
}
|
||||
*/
|
||||
table.dataTable th:active {
|
||||
outline: none;
|
||||
}
|
||||
@@ -175,9 +174,9 @@ table.dataTable.table-condensed thead > tr > th {
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
table.dataTable.table-condensed thead .sorting:after,
|
||||
table.dataTable.table-condensed thead .sorting_asc:after,
|
||||
table.dataTable.table-condensed thead .sorting_desc:after {
|
||||
table.dataTable.table-condensed thead .sorting:before,
|
||||
table.dataTable.table-condensed thead .sorting_asc:before,
|
||||
table.dataTable.table-condensed thead .sorting_desc:before {
|
||||
top: 6px;
|
||||
right: 6px;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
@import '~daterangepicker/daterangepicker.css';
|
||||
|
||||
.drp-calendar {
|
||||
.calendar-table {
|
||||
|
||||
12
assets/sass/fontawesome.scss
vendored
Normal file
12
assets/sass/fontawesome.scss
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
$fa-font-path: "~@fortawesome/fontawesome-free/webfonts/";
|
||||
@import '~@fortawesome/fontawesome-free/scss/fontawesome';
|
||||
@import '~@fortawesome/fontawesome-free/scss/regular';
|
||||
@import '~@fortawesome/fontawesome-free/scss/solid';
|
||||
@import '~@fortawesome/fontawesome-free/scss/brands';
|
||||
15
assets/sass/forms.scss
Normal file
15
assets/sass/forms.scss
Normal file
@@ -0,0 +1,15 @@
|
||||
/* Currently only used in batch-update form for timesheets, to highlight sections of combined fields */
|
||||
form .box-body fieldset {
|
||||
border-radius: $box-border-radius;
|
||||
padding: 10px;
|
||||
&:hover {
|
||||
background-color: $highlight-bg;
|
||||
}
|
||||
}
|
||||
|
||||
/* To be used with .form-horizontal - to reduce the space between rows, eg. used in the search modals */
|
||||
form.form-narrow {
|
||||
.form-group {
|
||||
margin: 0 0 5px 0;
|
||||
}
|
||||
}
|
||||
116
assets/sass/invoice-pdf.scss
Normal file
116
assets/sass/invoice-pdf.scss
Normal file
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
font-size: 10pt;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
table, tr, td, th, p, h1, h2, h3, h4, h5 {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
th {
|
||||
border: none;
|
||||
text-align: left;
|
||||
}
|
||||
td {
|
||||
vertical-align: top;
|
||||
}
|
||||
p {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.text-small {
|
||||
font-size: 7pt;
|
||||
}
|
||||
.padding-left {
|
||||
padding-left: 15px;
|
||||
}
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
.text-right {
|
||||
text-align: right;
|
||||
}
|
||||
.text-nowrap {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.date {
|
||||
color: #666;
|
||||
font-size: 80%;
|
||||
text-align: right;
|
||||
padding-top: 6px;
|
||||
}
|
||||
.wrapper {
|
||||
margin: 4px;
|
||||
}
|
||||
table {
|
||||
width: 100%;
|
||||
border-spacing: 0;
|
||||
}
|
||||
table.addresses, p {
|
||||
line-height: 14pt;
|
||||
}
|
||||
/* The address table */
|
||||
table.addresses {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
/* The PDF header */
|
||||
table.header {
|
||||
padding-bottom: 4px;
|
||||
border-bottom: 1px solid #999;
|
||||
}
|
||||
table.header .title {
|
||||
font-weight: normal;
|
||||
font-size: 140%;
|
||||
}
|
||||
/* The PDF footer */
|
||||
table.footer {
|
||||
border-top: 1px solid #999;
|
||||
font-size: 80%;
|
||||
padding-top: 8px;
|
||||
line-height: 14px;
|
||||
}
|
||||
table.footer td {
|
||||
vertical-align: bottom;
|
||||
}
|
||||
/* The invoice items list */
|
||||
table.items {
|
||||
border-collapse: collapse;
|
||||
margin-top: 50px;
|
||||
margin-bottom: 60px;
|
||||
}
|
||||
table.items thead th,
|
||||
table.items tbody td {
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
table.items thead th.first,
|
||||
table.items tbody td.first {
|
||||
padding-left: 0;
|
||||
}
|
||||
table.items thead th.last,
|
||||
table.items tbody td.last,
|
||||
table.items tfoot td.last {
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
table.items td,
|
||||
table.items th {
|
||||
padding: 8px 6px;
|
||||
}
|
||||
table.items tfoot td,
|
||||
table.items tfoot th {
|
||||
padding: 20px 0 0 0;
|
||||
line-height: 1px;
|
||||
}
|
||||
table.items tfoot td.last {
|
||||
padding-left: 10px;
|
||||
}
|
||||
table.items tr.odd {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
@@ -5,301 +5,294 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
.invoice {
|
||||
margin: 0;
|
||||
border: none;
|
||||
}
|
||||
@import "~bootstrap-sass/assets/stylesheets/bootstrap/variables";
|
||||
@import "~bootstrap-sass/assets/stylesheets/bootstrap/mixins";
|
||||
@import "~bootstrap-sass/assets/stylesheets/bootstrap/normalize";
|
||||
@import "~bootstrap-sass/assets/stylesheets/bootstrap/print";
|
||||
@import "~bootstrap-sass/assets/stylesheets/bootstrap/scaffolding";
|
||||
@import "~bootstrap-sass/assets/stylesheets/bootstrap/type";
|
||||
@import "~bootstrap-sass/assets/stylesheets/bootstrap/grid";
|
||||
@import "~bootstrap-sass/assets/stylesheets/bootstrap/tables";
|
||||
@import "~bootstrap-sass/assets/stylesheets/bootstrap/list-group";
|
||||
@import "~bootstrap-sass/assets/stylesheets/bootstrap/panels";
|
||||
@import "~bootstrap-sass/assets/stylesheets/bootstrap/wells";
|
||||
@import "~bootstrap-sass/assets/stylesheets/bootstrap/utilities";
|
||||
@import "~bootstrap-sass/assets/stylesheets/bootstrap/responsive-utilities";
|
||||
|
||||
body.invoice_print {
|
||||
background-color: #eee;
|
||||
|
||||
.table.no-border, .table.no-border td, .table.no-border th {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
h1, h2, h3 {
|
||||
font-family: 'Source Sans Pro', sans-serif;
|
||||
}
|
||||
|
||||
@media (min-width: $screen-sm-min) {
|
||||
.invoice {
|
||||
margin: 25px;
|
||||
}
|
||||
}
|
||||
|
||||
.page-header-small{
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
div.invoice-address {
|
||||
margin-top: 30px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
table.invoice-records {}
|
||||
table.invoice-meta th {
|
||||
padding-right: 40px;
|
||||
}
|
||||
table.invoice-sum th {
|
||||
width: 70%;
|
||||
}
|
||||
table.invoice-sum th, table.invoice-sum td {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/* ================================ FREELANCER INVOICE ================================ */
|
||||
|
||||
#freelancer-invoice {
|
||||
font: 15px/1 $font-family-sans-serif;
|
||||
box-sizing: border-box;
|
||||
|
||||
h1 {
|
||||
font: bold 100% sans-serif;
|
||||
letter-spacing: 0.5em;
|
||||
text-align: center;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font: bold 1.5em sans-serif;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
/* table */
|
||||
|
||||
table {
|
||||
table-layout: fixed;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
/* header */
|
||||
|
||||
header {
|
||||
}
|
||||
|
||||
header:after {
|
||||
clear: both;
|
||||
content: "";
|
||||
display: table;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
header address {
|
||||
float: left;
|
||||
font-style: normal;
|
||||
line-height: 1.25;
|
||||
margin: 0 1em 1em 0;
|
||||
font-weight: bold;
|
||||
font-size: 0.7em;
|
||||
}
|
||||
|
||||
article address {
|
||||
font-size: 1em;
|
||||
float: left;
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
article.address {
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
|
||||
article.footer {
|
||||
margin-top: 60px;
|
||||
}
|
||||
|
||||
/* article */
|
||||
|
||||
article, table.meta, table.inventory {
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
article:after {
|
||||
clear: both;
|
||||
content: "";
|
||||
display: table;
|
||||
}
|
||||
|
||||
article h1 {
|
||||
clip: rect(0 0 0 0);
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
article p {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
/* table meta & balance */
|
||||
|
||||
table.meta:after, table.balance:after {
|
||||
clear: both;
|
||||
content: "";
|
||||
display: table;
|
||||
}
|
||||
|
||||
/* table meta */
|
||||
|
||||
table.meta {
|
||||
float: right;
|
||||
width: 50%;
|
||||
font-size: 85%;
|
||||
|
||||
th {
|
||||
text-align: right;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
td {
|
||||
width: 120px;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
/* table balance */
|
||||
|
||||
table.balance {
|
||||
float: right;
|
||||
th {
|
||||
text-align: right;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
td {
|
||||
width: 140px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.total {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
/* table items */
|
||||
|
||||
table.inventory {
|
||||
clear: both;
|
||||
width: 100%;
|
||||
margin-top: 2em;
|
||||
border-bottom: 1px solid #000;
|
||||
}
|
||||
|
||||
table.inventory th {
|
||||
font-weight: bold;
|
||||
border-width: 0 0 1px 0;
|
||||
border-bottom: 1px solid #000;
|
||||
}
|
||||
table.inventory th,
|
||||
table.inventory td {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
table.inventory td:nth-child(1),
|
||||
table.inventory th:nth-child(1) {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
table.inventory td:nth-child(2),
|
||||
table.inventory th:nth-child(2) {
|
||||
text-align: center;
|
||||
width: 20%;
|
||||
}
|
||||
|
||||
table.inventory td:nth-child(3),
|
||||
table.inventory th:nth-child(3) {
|
||||
text-align: center;
|
||||
width: 20%;
|
||||
}
|
||||
|
||||
table.inventory td:nth-child(4),
|
||||
table.inventory th:nth-child(4) {
|
||||
text-align: right;
|
||||
width: 20%;
|
||||
}
|
||||
|
||||
/* footer */
|
||||
|
||||
footer {
|
||||
margin-top: 50px;
|
||||
}
|
||||
|
||||
footer table {
|
||||
border-top: 1px solid #000;
|
||||
font-size: 0.8em;
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
footer th {
|
||||
text-align: left;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
footer table th:nth-child(1) {
|
||||
width: 32%;
|
||||
}
|
||||
|
||||
footer table th:nth-child(3) {
|
||||
width: 28%;
|
||||
}
|
||||
|
||||
footer td {
|
||||
text-align: left;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
/* aside */
|
||||
|
||||
aside {
|
||||
position: absolute;
|
||||
top: 10in;
|
||||
width: 7.5in;
|
||||
}
|
||||
|
||||
aside h1 {
|
||||
margin: 105px auto 30px auto;
|
||||
padding: 50px 65px;
|
||||
min-height: 297mm;
|
||||
position: relative;
|
||||
max-width: 210mm;
|
||||
box-shadow: 0 0 20px rgba(80,80,80,0.7);
|
||||
background: #fff;
|
||||
border: none;
|
||||
border-width: 0 0 1px;
|
||||
margin: 0 0 1em;
|
||||
}
|
||||
|
||||
aside h1 {
|
||||
border-color: #999;
|
||||
border-bottom-style: solid;
|
||||
.page-header {
|
||||
margin: 10px 0 20px 0;
|
||||
font-size: 22px;
|
||||
>small {
|
||||
color: #666;
|
||||
display: block;
|
||||
margin-top: 5px;
|
||||
}
|
||||
h3 {
|
||||
border-color: #ddd;
|
||||
}
|
||||
}
|
||||
/*
|
||||
.page-header {
|
||||
margin: 10px 0 20px 0;
|
||||
font-size: 22px;
|
||||
}
|
||||
*/
|
||||
div.invoice-address {
|
||||
margin-top: 30px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
aside p {
|
||||
font-size: 85%;
|
||||
table.invoice-meta th {
|
||||
padding-right: 40px;
|
||||
}
|
||||
table.invoice-sum th {
|
||||
width: 70%;
|
||||
}
|
||||
table.invoice-sum th, table.invoice-sum td {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
aside div {
|
||||
margin-top: 15px;
|
||||
.invoice-items {
|
||||
margin-top: 2em;
|
||||
margin-bottom: 3em;
|
||||
|
||||
.table {
|
||||
thead th {
|
||||
font-weight: bold;
|
||||
border-bottom: 1px solid #ddd;
|
||||
padding-bottom: 15px
|
||||
}
|
||||
tfoot {
|
||||
border-top: 1px solid #ddd;
|
||||
|
||||
tr:first-child th,
|
||||
tr:first-child td {
|
||||
padding-top: 15px
|
||||
}
|
||||
tr {
|
||||
th,td {
|
||||
border:none;
|
||||
padding: 4px 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
aside div p {
|
||||
line-height: 1.4;
|
||||
.footer {
|
||||
border-top: 1px solid #ccc;
|
||||
margin-top: 50px;
|
||||
font-size: 0.8em;
|
||||
padding-top: 10px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
aside p span {
|
||||
width: 130px;
|
||||
font-weight: bold;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
@media print {
|
||||
* {
|
||||
-webkit-print-color-adjust: exact;
|
||||
}
|
||||
body, .wrapper, #freelancer-invoice {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
/* ================================ FREELANCER INVOICE ================================ */
|
||||
|
||||
#freelancer-invoice {
|
||||
margin: 1em 2em;
|
||||
|
||||
span:empty {
|
||||
display: none;
|
||||
box-sizing: border-box;
|
||||
|
||||
h1 {
|
||||
font: bold 100% sans-serif;
|
||||
letter-spacing: 0.5em;
|
||||
text-align: center;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.add, .cut {
|
||||
display: none;
|
||||
h2 {
|
||||
font: bold 1.5em sans-serif;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
/* table */
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
/* header */
|
||||
|
||||
header {
|
||||
&:after {
|
||||
clear: both;
|
||||
content: "";
|
||||
display: table;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
address {
|
||||
float: left;
|
||||
font-style: normal;
|
||||
line-height: 1.25;
|
||||
margin: 0 1em 1em 0;
|
||||
font-size: 0.7em;
|
||||
}
|
||||
}
|
||||
|
||||
article address {
|
||||
font-size: 1em;
|
||||
float: left;
|
||||
}
|
||||
|
||||
article.address {
|
||||
margin:1em 0 1.5em 0;
|
||||
}
|
||||
|
||||
/* article */
|
||||
|
||||
article:after {
|
||||
clear: both;
|
||||
content: "";
|
||||
display: table;
|
||||
}
|
||||
|
||||
article h1 {
|
||||
clip: rect(0 0 0 0);
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
article p {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
/* table meta & balance */
|
||||
|
||||
table.meta:after, table.balance:after {
|
||||
clear: both;
|
||||
content: "";
|
||||
display: table;
|
||||
}
|
||||
|
||||
/* table meta */
|
||||
|
||||
table.meta {
|
||||
float: right;
|
||||
width: 50%;
|
||||
font-size: 85%;
|
||||
|
||||
th {
|
||||
text-align: right;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
td {
|
||||
width: 120px;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
/* table balance */
|
||||
|
||||
table.balance {
|
||||
float: right;
|
||||
margin-top: 1em;
|
||||
th {
|
||||
text-align: right;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
td {
|
||||
width: 140px;
|
||||
text-align: right;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.total {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
/* table items */
|
||||
|
||||
table.inventory {
|
||||
clear: both;
|
||||
width: 100%;
|
||||
margin-top: 2em;
|
||||
border-bottom: 1px solid #000;
|
||||
}
|
||||
|
||||
table.inventory th,
|
||||
table.inventory td {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
table.inventory thead th {
|
||||
font-weight: bold;
|
||||
border-width: 0 0 1px 0;
|
||||
border-bottom: 1px solid #000;
|
||||
padding-bottom: 15px
|
||||
}
|
||||
|
||||
table.inventory tbody tr:first-child td {
|
||||
padding-top: 15px
|
||||
}
|
||||
|
||||
table.inventory tbody tr:last-child td {
|
||||
padding-bottom: 15px
|
||||
}
|
||||
|
||||
.footer {
|
||||
border-color: #000;
|
||||
}
|
||||
}
|
||||
|
||||
.invoice {
|
||||
width: unset;
|
||||
/* ================================ PRINTING RULES ================================ */
|
||||
|
||||
@media print {
|
||||
* {
|
||||
-webkit-print-color-adjust: exact;
|
||||
}
|
||||
body, .wrapper {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: white;
|
||||
}
|
||||
#freelancer-invoice {
|
||||
span:empty {
|
||||
display: none;
|
||||
}
|
||||
.add, .cut {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.invoice {
|
||||
margin: 1em 2em;
|
||||
font-size: 90%;
|
||||
width: unset;
|
||||
padding: 0;
|
||||
min-height: unset;
|
||||
position: unset;
|
||||
max-width: unset;
|
||||
box-shadow: unset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
8
assets/sass/layout.scss
Normal file
8
assets/sass/layout.scss
Normal file
@@ -0,0 +1,8 @@
|
||||
@media (max-width: $screen-sm-max) {
|
||||
.main-header .logo {
|
||||
display: none;
|
||||
}
|
||||
.fixed .content-wrapper, .fixed .right-side, .control-sidebar, .main-sidebar {
|
||||
padding-top: 50px;
|
||||
}
|
||||
}
|
||||
9
assets/sass/modal.scss
Normal file
9
assets/sass/modal.scss
Normal file
@@ -0,0 +1,9 @@
|
||||
.modal-header,
|
||||
.modal-footer {
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
.modal-content {
|
||||
border-radius: 3px;
|
||||
box-shadow: 0 10px 80px rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
@@ -5,10 +5,34 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
.navbar-custom-menu {
|
||||
.navbar-nav {
|
||||
li>.ddt-small {
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-nav {
|
||||
li>a.ddt-large {
|
||||
padding: 15px 12px 11px 10px;
|
||||
font-size: 10px;
|
||||
img.img-circle {
|
||||
max-height: 26px;
|
||||
margin-top: -2px;
|
||||
}
|
||||
.avatar {
|
||||
max-height: 30px;
|
||||
max-width: 30px;
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
margin-top: 0;
|
||||
.initials {
|
||||
top: 5px;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
li>.ddt-small {
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
li.messages-menu ul.menu li {
|
||||
a:hover .pull-left i {
|
||||
@@ -31,17 +55,13 @@
|
||||
width: unset !important;
|
||||
}
|
||||
.user-menu {
|
||||
margin-right: 10px;
|
||||
ul {
|
||||
li {
|
||||
a {
|
||||
color: #444;
|
||||
&:hover {
|
||||
background-color: #f4f4f4;
|
||||
i {
|
||||
color: $blue;
|
||||
}
|
||||
}
|
||||
color: #444;
|
||||
i {
|
||||
padding-right: 10px;
|
||||
}
|
||||
@@ -52,41 +72,86 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
.notifications-menu>.dropdown-menu>li .menu>li>a:hover i {
|
||||
color: $green;
|
||||
}
|
||||
@keyframes ticktac-blink {
|
||||
0% { opacity: 1; }
|
||||
5% { opacity: 0.95; }
|
||||
10% { opacity: 0.9; }
|
||||
15% { opacity: 0.85; }
|
||||
20% { opacity: 0.8; }
|
||||
25% { opacity: 0.75; }
|
||||
30% { opacity: 0.7; }
|
||||
35% { opacity: 0.65; }
|
||||
40% { opacity: 0.6; }
|
||||
45% { opacity: 0.65; }
|
||||
50% { opacity: 0.7; }
|
||||
55% { opacity: 0.75; }
|
||||
60% { opacity: 0.8; }
|
||||
65% { opacity: 0.85; }
|
||||
70% { opacity: 0.9; }
|
||||
75% { opacity: 0.95; }
|
||||
100% { opacity: 1; }
|
||||
}
|
||||
.ticktac-single {
|
||||
display: block;
|
||||
position: relative;
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
i {
|
||||
animation: ticktac-blink 2s step-end infinite;
|
||||
}
|
||||
span {
|
||||
font-size: 1.8em;
|
||||
line-height: 1em;
|
||||
padding-left: 5px;
|
||||
padding-right: 0;
|
||||
}
|
||||
&:hover {
|
||||
background: rgba(0,0,0,.3);
|
||||
}
|
||||
}
|
||||
body.fixed {
|
||||
.navbar-nav {
|
||||
.user-menu {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: $screen-sm-min) {
|
||||
@media (max-width: $screen-sm-max) {
|
||||
.navbar-custom-menu>.navbar-nav>li>.dropdown-menu {
|
||||
right: 0;
|
||||
}
|
||||
.navbar-nav .user-menu {
|
||||
margin-right: 0;
|
||||
body.fixed {
|
||||
.navbar-nav {
|
||||
.user-menu {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
.navbar-nav .user-menu.open .dropdown-menu > li > a {
|
||||
padding: 5px 25px 5px 25px
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
.ticktac:hover i.running{
|
||||
color: #4ff131;
|
||||
@media (max-width: $screen-sm-max) {
|
||||
.ticktac-single {
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
.navbar-custom-menu .navbar-nav li>.ddt-small {
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
li.open .ticktac i.running{
|
||||
color: #4ff131;
|
||||
@media (max-width: $screen-md-max) {
|
||||
.ticktac-single {
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
.navbar-custom-menu .navbar-nav li>.ddt-small {
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.ticktac i.stopped{
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.ticktac:hover i.stopped{
|
||||
color: #4ff131;
|
||||
}
|
||||
.user-panel>.info {
|
||||
left: 40px;
|
||||
padding-top: 0px;
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -6,5 +6,21 @@
|
||||
*/
|
||||
|
||||
@media print{
|
||||
.sf-toolbar, .control-sidebar {display: none !important;}
|
||||
.sf-toolbar, .control-sidebar {
|
||||
display: none !important;
|
||||
}
|
||||
.col-print-12{
|
||||
width:100%;
|
||||
}
|
||||
.sidebar-mini.sidebar-collapse .content-wrapper, .sidebar-mini.sidebar-collapse .main-footer, .sidebar-mini.sidebar-collapse .right-side {
|
||||
margin: 0!important;
|
||||
}
|
||||
section.content {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
span i.dot {
|
||||
display: none;
|
||||
margin-right: 0!important;
|
||||
}
|
||||
}
|
||||
|
||||
20
assets/sass/progressbar.scss
Normal file
20
assets/sass/progressbar.scss
Normal file
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
make sure that the progressbar title does not wrap and does not need to be embedded in a container with white-space: nowrap
|
||||
used for example in the project-overview report
|
||||
*/
|
||||
.progress-group {
|
||||
.progress-title {
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
.progress-text {
|
||||
display: inline-block;
|
||||
width: 1%;
|
||||
}
|
||||
.progress-number {
|
||||
display: inline-block;
|
||||
width: 98%;
|
||||
text-align: right;
|
||||
float: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
12
assets/sass/reporting.scss
Normal file
12
assets/sass/reporting.scss
Normal file
@@ -0,0 +1,12 @@
|
||||
.dataTable {
|
||||
.weekend {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
}
|
||||
|
||||
.form-reporting .box-header .form-group {
|
||||
margin-right: 10px;
|
||||
label {
|
||||
font-size: $font-size-base;
|
||||
}
|
||||
}
|
||||
35
assets/sass/security.scss
Normal file
35
assets/sass/security.scss
Normal file
@@ -0,0 +1,35 @@
|
||||
body.login-page {
|
||||
.alert {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.login-box-body {
|
||||
text-align: center;
|
||||
padding: 30px 35px;
|
||||
box-shadow: 0 10px 30px 0 rgba(172, 168, 168, 0.43);
|
||||
.btn {
|
||||
padding: 7px;
|
||||
font-size: 115%;
|
||||
}
|
||||
}
|
||||
.login-head {
|
||||
margin-top: 0;
|
||||
}
|
||||
.login-actions a {
|
||||
display: block;
|
||||
}
|
||||
.form-group {
|
||||
input[type=password], input[type=text] {
|
||||
padding: 20px 15px;
|
||||
}
|
||||
}
|
||||
span.glyphicon {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@media (max-width: $screen-sm-max) {
|
||||
body.login-page {
|
||||
.login-box-body {
|
||||
padding: 20px 27px;
|
||||
}
|
||||
}
|
||||
}
|
||||
23
assets/sass/selectpicker.scss
Normal file
23
assets/sass/selectpicker.scss
Normal file
@@ -0,0 +1,23 @@
|
||||
@import '~select2/dist/css/select2.min.css';
|
||||
@import '~select2-bootstrap-theme/dist/select2-bootstrap.min.css';
|
||||
|
||||
/* some labels were placed incorrect due to a margin-top: -4px */
|
||||
.select2-container .select2-selection--single .select2-selection__rendered {
|
||||
margin-top: 0;
|
||||
}
|
||||
/* make sure the border looks like all other form elements */
|
||||
.select2-container--bootstrap .select2-selection {
|
||||
border-radius: 0;
|
||||
}
|
||||
/* placeholder in multi-selects were cut off */
|
||||
.select2-search.select2-search--inline .select2-search__field {
|
||||
width: 100% !important;
|
||||
}
|
||||
/* make sure that <optgroup> elements are padded, to allow visual difference to global activities */
|
||||
.select2-container--bootstrap .select2-results__options.select2-results__options--nested li.select2-results__option {
|
||||
padding-left: 30px;
|
||||
}
|
||||
/* empty <option> were too small to be noticed as clickable element */
|
||||
.select2-container--bootstrap .select2-results__option {
|
||||
min-height: 30px;
|
||||
}
|
||||
@@ -39,3 +39,13 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Simplify focus detection, when using keyboard tab navigation
|
||||
Deactivated, as it looks weird when the link is clicked
|
||||
*/
|
||||
/*
|
||||
.sidebar-menu > li > a:focus {
|
||||
text-decoration: underline;
|
||||
}
|
||||
*/
|
||||
@@ -1,26 +1,69 @@
|
||||
|
||||
$skins: blue, blue, black, green, purple, red, yellow;
|
||||
$skins:
|
||||
"blue" $yellow #fff $yellow $red,
|
||||
"black" $yellow #000 $green $red,
|
||||
"green" $yellow #fff $yellow $red,
|
||||
"purple" $yellow #fff $yellow $red,
|
||||
"red" $yellow #fff $yellow $yellow,
|
||||
"yellow" $blue #fff $green $red
|
||||
;
|
||||
|
||||
@each $skinColor in $skins {
|
||||
@media (max-width: $screen-sm-min) {
|
||||
.skin-#{$skinColor} .main-header .navbar .dropdown-menu li.divider {
|
||||
background-color: #ccc;
|
||||
}
|
||||
.skin-#{$skinColor} .main-header .navbar .dropdown-menu li a {
|
||||
color: #444;
|
||||
}
|
||||
.skin-#{$skinColor} .main-header .navbar .dropdown-menu li a:hover {
|
||||
background: #fff;
|
||||
}
|
||||
@each $skinColor, $navHighlight, $tickTacColor, $startButton, $stopButton in $skins {
|
||||
@each $prefix in (".skin-#{$skinColor}", ".skin-#{$skinColor}-light") {
|
||||
@media (max-width: $screen-sm-max) {
|
||||
#{$prefix} {
|
||||
.main-header .navbar .dropdown-menu li.divider {
|
||||
background-color: #ccc;
|
||||
}
|
||||
|
||||
.skin-#{$skinColor}-light .main-header .navbar .dropdown-menu li.divider {
|
||||
background-color: #ccc;
|
||||
.main-header .navbar .dropdown-menu li a {
|
||||
color: #444;
|
||||
}
|
||||
|
||||
.main-header .navbar .dropdown-menu li a:hover {
|
||||
background: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
.skin-#{$skinColor}-light .main-header .navbar .dropdown-menu li a {
|
||||
color: #444;
|
||||
}
|
||||
.skin-#{$skinColor}-light .main-header .navbar .dropdown-menu li a:hover {
|
||||
background: #fff;
|
||||
#{$prefix} {
|
||||
.navbar-nav {
|
||||
.user-menu {
|
||||
ul {
|
||||
li {
|
||||
a {
|
||||
&:hover {
|
||||
i {
|
||||
color: $navHighlight;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.notifications-menu > .dropdown-menu > li .menu > li > a:hover i {
|
||||
color: $navHighlight;
|
||||
}
|
||||
}
|
||||
.ticktac-single {
|
||||
i {
|
||||
color: $tickTacColor;
|
||||
}
|
||||
span {
|
||||
color: $tickTacColor;
|
||||
}
|
||||
&:hover {
|
||||
.ticktac-stop {
|
||||
i {
|
||||
color: $stopButton;
|
||||
}
|
||||
}
|
||||
.ticktac-start {
|
||||
i {
|
||||
color: $startButton;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,16 @@
|
||||
.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;
|
||||
}
|
||||
}
|
||||
|
||||
3
assets/sass/tabs.scss
Normal file
3
assets/sass/tabs.scss
Normal file
@@ -0,0 +1,3 @@
|
||||
.tab-content {
|
||||
margin-top: 20px;
|
||||
}
|
||||
@@ -5,20 +5,9 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/* Page based action buttons in the upper right corner of the content area */
|
||||
.content-header > .breadcrumb {
|
||||
position: absolute;
|
||||
float: right;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.toolbar-pad {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
/* The filter form is available on most pages above the datatable */
|
||||
@media (min-width: $screen-sm-min) {
|
||||
.toolbar {
|
||||
// @deprecated since 1.2 - using the navbar-form is deprecated and will be removed with 2.0
|
||||
form.navbar-form {
|
||||
font-size: $font-size-base;
|
||||
.form-control {
|
||||
@@ -34,8 +23,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
.toolbar-pad {
|
||||
padding: 10px 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,8 +5,13 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
@import "~bootstrap-sass/assets/stylesheets/bootstrap/variables";
|
||||
|
||||
/* For highlighting rows, fieldsets and so on */
|
||||
$highlight-bg: #f5f5f5;
|
||||
|
||||
/**
|
||||
* A simple one to one clone of /node_modules/admin-lte/build/less/variables.less
|
||||
* BELOW: A simple one to one clone of /node_modules/admin-lte/build/less/variables.less
|
||||
*/
|
||||
|
||||
// COLORS
|
||||
@@ -20,7 +25,7 @@ $green: #00a65a;
|
||||
// Info
|
||||
//$aqua: #00c0ef;
|
||||
// Warning
|
||||
//$yellow: #f39c12;
|
||||
$yellow: #f0ad4e;
|
||||
$blue: #0073b7;
|
||||
//$navy: #001F3F;
|
||||
//$teal: #39CCCC;
|
||||
@@ -32,6 +37,7 @@ $blue: #0073b7;
|
||||
//$maroon: #D81B60;
|
||||
//$black: #111;
|
||||
$gray-lte: #d2d6de;
|
||||
$kimai-default: $gray-lte;
|
||||
|
||||
// LAYOUT
|
||||
// --------------------------------------------------------
|
||||
|
||||
28
bin/console
28
bin/console
@@ -2,38 +2,28 @@
|
||||
<?php
|
||||
|
||||
use App\Kernel;
|
||||
use Symfony\Bundle\FrameworkBundle\Console\Application;
|
||||
use App\ConsoleApplication;
|
||||
use Symfony\Component\Console\Input\ArgvInput;
|
||||
use Symfony\Component\Debug\Debug;
|
||||
use Symfony\Component\ErrorHandler\Debug;
|
||||
use Symfony\Component\Dotenv\Dotenv;
|
||||
|
||||
set_time_limit(0);
|
||||
|
||||
require __DIR__.'/../vendor/autoload.php';
|
||||
require dirname(__DIR__) . '/vendor/autoload.php';
|
||||
|
||||
if (!class_exists(Application::class)) {
|
||||
throw new \RuntimeException('You need to add "symfony/framework-bundle" as a Composer dependency.');
|
||||
}
|
||||
|
||||
if (!isset($_SERVER['APP_ENV'])) {
|
||||
if (!class_exists(Dotenv::class)) {
|
||||
throw new \RuntimeException('APP_ENV environment variable is not defined. You need to define environment variables for configuration or add "symfony/dotenv" as a Composer dependency to load variables from a .env file.');
|
||||
}
|
||||
(new Dotenv())->load(__DIR__.'/../.env');
|
||||
}
|
||||
// do NOT rely on calls to getenv() as it is not thread safe
|
||||
(new Dotenv(false))->loadEnv(dirname(__DIR__).'/.env');
|
||||
|
||||
$input = new ArgvInput();
|
||||
$env = $input->getParameterOption(['--env', '-e'], $_SERVER['APP_ENV'] ?? 'dev');
|
||||
$debug = ($_SERVER['APP_DEBUG'] ?? ('prod' !== $env)) && !$input->hasParameterOption(['--no-debug', '']);
|
||||
$env = $input->getParameterOption(['--env', '-e'], $_SERVER['APP_ENV'] ?? 'prod');
|
||||
$debug = (bool) ($_SERVER['APP_DEBUG'] ?? (in_array($env, ['dev', 'test']))) && !$input->hasParameterOption(['--no-debug', '']);
|
||||
|
||||
if ($debug) {
|
||||
umask(0000);
|
||||
|
||||
if (class_exists(Debug::class)) {
|
||||
Debug::enable();
|
||||
}
|
||||
Debug::enable();
|
||||
}
|
||||
|
||||
$kernel = new Kernel($env, $debug);
|
||||
$application = new Application($kernel);
|
||||
$application = new ConsoleApplication($kernel);
|
||||
$application->run($input);
|
||||
|
||||
171
composer.json
171
composer.json
@@ -2,7 +2,7 @@
|
||||
"name": "kevinpapst/kimai2",
|
||||
"license": "MIT",
|
||||
"type": "project",
|
||||
"description": "Kimai Time-Tracking 2",
|
||||
"description": "Kimai - Time Tracking",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Kevin Papst",
|
||||
@@ -10,72 +10,99 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^7.2",
|
||||
"php": ">=7.3",
|
||||
"ext-gd": "*",
|
||||
"ext-intl": "*",
|
||||
"ext-json": "*",
|
||||
"ext-mbstring": "*",
|
||||
"ext-pdo": "*",
|
||||
"ext-xsl": "*",
|
||||
"ext-zip": "*",
|
||||
"beberlei/doctrineextensions": "^1.2",
|
||||
"doctrine/doctrine-fixtures-bundle": "^3.0",
|
||||
"composer/package-versions-deprecated": "^1.8",
|
||||
"doctrine/doctrine-bundle": "^2.0",
|
||||
"doctrine/doctrine-migrations-bundle": "^3.0",
|
||||
"doctrine/orm": "^2.8",
|
||||
"erusev/parsedown": "^1.6",
|
||||
"friendsofsymfony/rest-bundle": "^2.3",
|
||||
"friendsofsymfony/user-bundle": "~2.0",
|
||||
"fzaninotto/faker": "^1.8",
|
||||
"gedmo/doctrine-extensions": "^2.4",
|
||||
"friendsofsymfony/rest-bundle": "^3.0",
|
||||
"gedmo/doctrine-extensions": "^3.0",
|
||||
"handcraftedinthealps/rest-routing-bundle": "^1.0",
|
||||
"jms/metadata": "^2.0",
|
||||
"jms/serializer-bundle": "^3.2",
|
||||
"kevinpapst/adminlte-bundle": "~2.6",
|
||||
"kimai/kimai2-composer": "^0.1",
|
||||
"mpdf/mpdf": "^7.1",
|
||||
"jms/serializer-bundle": "^3.9",
|
||||
"kevinpapst/adminlte-bundle": "^3.5",
|
||||
"league/csv": "^9.4",
|
||||
"league/html-to-markdown": "^5.0",
|
||||
"mpdf/mpdf": "^8.0",
|
||||
"nelmio/api-doc-bundle": "^3.2",
|
||||
"nelmio/cors-bundle": "^1.5",
|
||||
"ocramius/proxy-manager": "2.1.1",
|
||||
"phpoffice/phpspreadsheet": "^1.4",
|
||||
"phpoffice/phpword": "^0.15.0",
|
||||
"nelmio/cors-bundle": "^2.0",
|
||||
"onelogin/php-saml": "^3.4",
|
||||
"pagerfanta/pagerfanta": "^2.1",
|
||||
"phpoffice/phpspreadsheet": "^1.16",
|
||||
"phpoffice/phpword": "^0.18",
|
||||
"psr/log": "^1.1",
|
||||
"sensio/framework-extra-bundle": "^5.2",
|
||||
"symfony/asset": "^4.0",
|
||||
"symfony/console": "^4.0",
|
||||
"symfony/debug-pack": "^1.0",
|
||||
"symfony/dotenv": "^4.0",
|
||||
"symfony/expression-language": "^4.0",
|
||||
"sensio/framework-extra-bundle": "^6.0",
|
||||
"symfony/amazon-mailer": "^4.4",
|
||||
"symfony/asset": "^4.4",
|
||||
"symfony/console": "^4.4",
|
||||
"symfony/dotenv": "^4.4",
|
||||
"symfony/expression-language": "^4.4",
|
||||
"symfony/flex": "^1.0",
|
||||
"symfony/form": "^4.0",
|
||||
"symfony/framework-bundle": "^4.0",
|
||||
"symfony/intl": "^4.0",
|
||||
"symfony/maker-bundle": "^1.0",
|
||||
"symfony/monolog-bundle": "^3.1",
|
||||
"symfony/orm-pack": "^1.0",
|
||||
"symfony/profiler-pack": "^1.0",
|
||||
"symfony/security-bundle": "~4.2.0",
|
||||
"symfony/security-csrf": "^4.0",
|
||||
"symfony/serializer": "^4.0",
|
||||
"symfony/swiftmailer-bundle": "^3.1",
|
||||
"symfony/translation": "^4.0",
|
||||
"symfony/twig-bundle": "^4.0",
|
||||
"symfony/validator": "^4.0",
|
||||
"symfony/web-server-bundle": "^4.0",
|
||||
"symfony/form": "^4.4",
|
||||
"symfony/framework-bundle": "^4.4",
|
||||
"symfony/google-mailer": "^4.4",
|
||||
"symfony/http-client": "^4.4",
|
||||
"symfony/intl": "^4.4",
|
||||
"symfony/mailchimp-mailer": "^4.4",
|
||||
"symfony/mailer": "^4.4",
|
||||
"symfony/mailgun-mailer": "^4.4",
|
||||
"symfony/monolog-bundle": "^3.4",
|
||||
"symfony/postmark-mailer": "^4.4",
|
||||
"symfony/security-bundle": "^4.4",
|
||||
"symfony/security-csrf": "^4.4",
|
||||
"symfony/sendgrid-mailer": "^4.4",
|
||||
"symfony/serializer": "^4.4",
|
||||
"symfony/translation": "^4.4",
|
||||
"symfony/twig-bundle": "^4.4",
|
||||
"symfony/uid": "^5.0",
|
||||
"symfony/validator": "^4.4",
|
||||
"symfony/webpack-encore-bundle": "^1.5",
|
||||
"symfony/yaml": "^4.0",
|
||||
"twig/extensions": "^1.5",
|
||||
"white-october/pagerfanta-bundle": "^1.1"
|
||||
"symfony/yaml": "^4.4",
|
||||
"twig/cssinliner-extra": "^3.0",
|
||||
"twig/extra-bundle": "^3.0",
|
||||
"twig/inky-extra": "^3.0",
|
||||
"twig/intl-extra": "^3.0",
|
||||
"twig/string-extra": "^3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"dama/doctrine-test-bundle": "^5.0",
|
||||
"friendsofphp/php-cs-fixer": "^2.10",
|
||||
"phpstan/phpstan": "^0.11.7",
|
||||
"phpstan/phpstan-doctrine": "^0.11.4",
|
||||
"phpstan/phpstan-phpunit": "^0.11.2",
|
||||
"phpstan/phpstan-symfony": "^0.11.6",
|
||||
"phpunit/phpunit": "^7.0",
|
||||
"symfony/browser-kit": "^4.0",
|
||||
"symfony/css-selector": "^4.0",
|
||||
"symfony/phpunit-bridge": "^4.0",
|
||||
"symfony/thanks": "^1.0"
|
||||
"dama/doctrine-test-bundle": "^6.0",
|
||||
"doctrine/doctrine-fixtures-bundle": "^3.2",
|
||||
"fakerphp/faker": "^1.15",
|
||||
"friendsofphp/php-cs-fixer": "^3.0",
|
||||
"phpstan/phpstan": "^0.12",
|
||||
"phpstan/phpstan-doctrine": "^0.12",
|
||||
"phpstan/phpstan-phpunit": "^0.12",
|
||||
"phpstan/phpstan-symfony": "^0.12",
|
||||
"phpunit/phpunit": "^8.0",
|
||||
"symfony/browser-kit": "^4.4",
|
||||
"symfony/css-selector": "^4.4",
|
||||
"symfony/debug-bundle": "^4.4",
|
||||
"symfony/dom-crawler": "4.4.4",
|
||||
"symfony/maker-bundle": "^1.12",
|
||||
"symfony/phpunit-bridge": "^4.4",
|
||||
"symfony/stopwatch": "^4.4",
|
||||
"symfony/var-dumper": "^4.4",
|
||||
"symfony/web-profiler-bundle": "^4.4"
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
"type": "artifact",
|
||||
"url": "var/packages/"
|
||||
}
|
||||
],
|
||||
"config": {
|
||||
"platform": {
|
||||
"php": "7.3"
|
||||
},
|
||||
"preferred-install": {
|
||||
"*": "dist"
|
||||
},
|
||||
@@ -93,14 +120,18 @@
|
||||
}
|
||||
},
|
||||
"replace": {
|
||||
"symfony/polyfill-mbstring": "*",
|
||||
"symfony/polyfill-intl": "*",
|
||||
"symfony/polyfill-iconv": "*",
|
||||
"symfony/polyfill-php72": "*",
|
||||
"symfony/polyfill-php71": "*",
|
||||
"symfony/polyfill-php70": "*",
|
||||
"symfony/polyfill-php56": "*"
|
||||
"symfony/polyfill-php56": "*",
|
||||
"symfony/polyfill-php55": "*",
|
||||
"symfony/polyfill-php54": "*"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-mbstring": "If ext-mbstring is not available you MUST install symfony/polyfill-mbstring",
|
||||
"zendframework/zend-ldap": "For LDAP authentication with Kimai"
|
||||
"laminas/laminas-ldap": "For LDAP authentication with Kimai"
|
||||
},
|
||||
"scripts": {
|
||||
"auto-scripts": {
|
||||
@@ -113,20 +144,38 @@
|
||||
"post-update-cmd": [
|
||||
"@auto-scripts"
|
||||
],
|
||||
"kimai:code-check": [
|
||||
"kimai:pre-commit": [
|
||||
"@kimai:codestyle",
|
||||
"@kimai:phpstan",
|
||||
"@kimai:tests"
|
||||
"@linting",
|
||||
"@kimai:tests-unit"
|
||||
],
|
||||
"kimai:code-check": "@code-check",
|
||||
"code-check": [
|
||||
"@kimai:pre-commit",
|
||||
"@kimai:tests-integration"
|
||||
],
|
||||
"kimai:code-lint": "@linting",
|
||||
"linting": [
|
||||
"composer validate --strict",
|
||||
"bin/console lint:container",
|
||||
"bin/console lint:yaml config --parse-tags",
|
||||
"bin/console lint:twig templates --show-deprecations",
|
||||
"bin/console lint:xliff translations",
|
||||
"bin/console doctrine:schema:validate --skip-sync -vvv --no-interaction"
|
||||
],
|
||||
"kimai:tests": "vendor/bin/phpunit tests/",
|
||||
"kimai:tests-unit": "vendor/bin/phpunit --exclude-group integration tests/",
|
||||
"kimai:tests-integration": "vendor/bin/phpunit --group integration tests/",
|
||||
"kimai:phpstan": [
|
||||
"vendor/bin/phpstan analyse src -c phpstan.neon --level=3",
|
||||
"vendor/bin/phpstan analyse tests -c tests/phpstan.neon --level=4"
|
||||
"kimai:phpstan": "@phpstan",
|
||||
"phpstan": [
|
||||
"vendor/bin/phpstan analyse src -c phpstan.neon --level=5",
|
||||
"vendor/bin/phpstan analyse tests -c tests/phpstan.neon --level=5"
|
||||
],
|
||||
"kimai:codestyle": "vendor/bin/php-cs-fixer fix --dry-run --verbose --show-progress=none",
|
||||
"kimai:codestyle-fix": "vendor/bin/php-cs-fixer fix"
|
||||
"kimai:codestyle": "@codestyle",
|
||||
"codestyle": "vendor/bin/php-cs-fixer fix --dry-run --verbose --show-progress=none",
|
||||
"kimai:codestyle-fix": "@codestyle-fix",
|
||||
"codestyle-fix": "vendor/bin/php-cs-fixer fix"
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/symfony": "*"
|
||||
@@ -135,7 +184,7 @@
|
||||
"symfony": {
|
||||
"id": "01C3FWRDJJEX9K6Y3A4XDFXPBR",
|
||||
"allow-contrib": true,
|
||||
"require": "~4.2.0"
|
||||
"require": "4.4.*"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
12527
composer.lock
generated
12527
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -2,8 +2,6 @@
|
||||
|
||||
return [
|
||||
Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
|
||||
Symfony\Bundle\WebServerBundle\WebServerBundle::class => ['dev' => true],
|
||||
Doctrine\Bundle\DoctrineCacheBundle\DoctrineCacheBundle::class => ['all' => true],
|
||||
Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],
|
||||
Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle::class => ['dev' => true, 'test' => true],
|
||||
Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true],
|
||||
@@ -12,16 +10,15 @@ return [
|
||||
Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true],
|
||||
Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true],
|
||||
Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true],
|
||||
Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle::class => ['all' => true],
|
||||
WhiteOctober\PagerfantaBundle\WhiteOctoberPagerfantaBundle::class => ['all' => true],
|
||||
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
|
||||
Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true, 'test' => true],
|
||||
DAMA\DoctrineTestBundle\DAMADoctrineTestBundle::class => ['test' => true],
|
||||
KevinPapst\AdminLTEBundle\AdminLTEBundle::class => ['all' => true],
|
||||
JMS\SerializerBundle\JMSSerializerBundle::class => ['all' => true],
|
||||
FOS\UserBundle\FOSUserBundle::class => ['all' => true],
|
||||
FOS\RestBundle\FOSRestBundle::class => ['all' => true],
|
||||
Nelmio\ApiDocBundle\NelmioApiDocBundle::class => ['all' => true],
|
||||
Nelmio\CorsBundle\NelmioCorsBundle::class => ['all' => true],
|
||||
Symfony\WebpackEncoreBundle\WebpackEncoreBundle::class => ['all' => true],
|
||||
Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true],
|
||||
HandcraftedInTheAlps\RestRoutingBundle\RestRoutingBundle::class => ['all' => true],
|
||||
];
|
||||
|
||||
@@ -7,7 +7,7 @@ admin_lte:
|
||||
# default image for missing user avatar images
|
||||
default_avatar: build/images/default_avatar.png
|
||||
# the color skin
|
||||
skin: skin-green
|
||||
skin: skin-blue
|
||||
# if activated, the boxed_layout will be deactivated
|
||||
fixed_layout: true
|
||||
# boxed layout (true) or full-screen (false)
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
framework:
|
||||
assets:
|
||||
json_manifest_path: '%kernel.project_dir%/public/build/manifest.json'
|
||||
packages:
|
||||
avatars:
|
||||
base_path: 'avatars'
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
services:
|
||||
EasyCorp\EasyLog\EasyLogHandler:
|
||||
public: false
|
||||
arguments: ['%kernel.logs_dir%/%kernel.environment%.log']
|
||||
|
||||
#// FIXME: How to add this configuration automatically without messing up with the monolog configuration?
|
||||
#monolog:
|
||||
# handlers:
|
||||
# buffered:
|
||||
# type: buffer
|
||||
# handler: easylog
|
||||
# channels: ['!event']
|
||||
# level: debug
|
||||
# easylog:
|
||||
# type: service
|
||||
# id: EasyCorp\EasyLog\EasyLogHandler
|
||||
@@ -1,7 +0,0 @@
|
||||
jms_serializer:
|
||||
visitors:
|
||||
json_serialization:
|
||||
options:
|
||||
- JSON_PRETTY_PRINT
|
||||
- JSON_UNESCAPED_SLASHES
|
||||
- JSON_PRESERVE_ZERO_FRACTION
|
||||
3
config/packages/dev/mailer.yaml
Normal file
3
config/packages/dev/mailer.yaml
Normal file
@@ -0,0 +1,3 @@
|
||||
framework:
|
||||
mailer:
|
||||
dsn: 'null://null'
|
||||
@@ -1,4 +0,0 @@
|
||||
# See https://symfony.com/doc/current/email/dev_environment.html
|
||||
swiftmailer:
|
||||
# send all emails to a specific address
|
||||
# delivery_addresses: ['me@example.com']
|
||||
2
config/packages/dev/webpack_encore.yaml
Normal file
2
config/packages/dev/webpack_encore.yaml
Normal file
@@ -0,0 +1,2 @@
|
||||
webpack_encore:
|
||||
cache: false
|
||||
@@ -4,17 +4,20 @@ parameters:
|
||||
# environment variables are not available yet.
|
||||
# You should not need to change this value.
|
||||
env(DATABASE_URL): ''
|
||||
env(DATABASE_VERSION): ~
|
||||
|
||||
doctrine:
|
||||
dbal:
|
||||
default_connection: default
|
||||
connections:
|
||||
default:
|
||||
url: '%env(resolve:DATABASE_URL)%'
|
||||
# existing migrations will fail if the schema filter is activated
|
||||
#schema_filter: ~^(?!(bundle_migration_|kimai2_sessions))~
|
||||
url: '%env(DATABASE_URL)%'
|
||||
driver: 'pdo_mysql'
|
||||
# this setting prevents automatic database detection and finds a lot of false-negatives on doctrine:migrations:diff
|
||||
# for null columns with MariaDB. Each migration tries to convert EVERY nullable column.
|
||||
# server_version: '5.7'
|
||||
server_version: '%env(string:DATABASE_VERSION)%'
|
||||
charset: utf8mb4
|
||||
default_table_options:
|
||||
charset: utf8mb4
|
||||
@@ -28,7 +31,7 @@ doctrine:
|
||||
entity_managers:
|
||||
default:
|
||||
connection: default
|
||||
naming_strategy: doctrine.orm.naming_strategy.underscore
|
||||
naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
|
||||
auto_mapping: true
|
||||
mappings:
|
||||
App:
|
||||
@@ -36,3 +39,125 @@ doctrine:
|
||||
dir: '%kernel.project_dir%/src/Entity'
|
||||
prefix: 'App\Entity'
|
||||
alias: Kimai
|
||||
dql:
|
||||
datetime_functions:
|
||||
addtime: DoctrineExtensions\Query\Mysql\AddTime
|
||||
convert_tz: DoctrineExtensions\Query\Mysql\ConvertTz
|
||||
date: DoctrineExtensions\Query\Mysql\Date
|
||||
date_format: DoctrineExtensions\Query\Mysql\DateFormat
|
||||
dateadd: DoctrineExtensions\Query\Mysql\DateAdd
|
||||
datesub: DoctrineExtensions\Query\Mysql\DateSub
|
||||
datediff: DoctrineExtensions\Query\Mysql\DateDiff
|
||||
day: DoctrineExtensions\Query\Mysql\Day
|
||||
dayname: DoctrineExtensions\Query\Mysql\DayName
|
||||
dayofweek: DoctrineExtensions\Query\Mysql\DayOfWeek
|
||||
dayofyear: DoctrineExtensions\Query\Mysql\DayOfYear
|
||||
div: DoctrineExtensions\Query\Mysql\Div
|
||||
from_unixtime: DoctrineExtensions\Query\Mysql\FromUnixtime
|
||||
hour: DoctrineExtensions\Query\Mysql\Hour
|
||||
last_day: DoctrineExtensions\Query\Mysql\LastDay
|
||||
makedate: DoctrineExtensions\Query\Mysql\MakeDate
|
||||
minute: DoctrineExtensions\Query\Mysql\Minute
|
||||
now: DoctrineExtensions\Query\Mysql\Now
|
||||
month: DoctrineExtensions\Query\Mysql\Month
|
||||
monthname: DoctrineExtensions\Query\Mysql\MonthName
|
||||
period_diff: DoctrineExtensions\Query\Mysql\PeriodDiff
|
||||
second: DoctrineExtensions\Query\Mysql\Second
|
||||
sectotime: DoctrineExtensions\Query\Mysql\SecToTime
|
||||
strtodate: DoctrineExtensions\Query\Mysql\StrToDate
|
||||
time: DoctrineExtensions\Query\Mysql\Time
|
||||
timediff: DoctrineExtensions\Query\Mysql\TimeDiff
|
||||
timestampadd: DoctrineExtensions\Query\Mysql\TimestampAdd
|
||||
timestampdiff: DoctrineExtensions\Query\Mysql\TimestampDiff
|
||||
timetosec: DoctrineExtensions\Query\Mysql\TimeToSec
|
||||
truncate: DoctrineExtensions\Query\Mysql\Truncate
|
||||
week: DoctrineExtensions\Query\Mysql\Week
|
||||
weekday: DoctrineExtensions\Query\Mysql\WeekDay
|
||||
year: DoctrineExtensions\Query\Mysql\Year
|
||||
yearmonth: DoctrineExtensions\Query\Mysql\YearMonth
|
||||
yearweek: DoctrineExtensions\Query\Mysql\YearWeek
|
||||
unix_timestamp: DoctrineExtensions\Query\Mysql\UnixTimestamp
|
||||
utc_timestamp: DoctrineExtensions\Query\Mysql\UtcTimestamp
|
||||
extract: DoctrineExtensions\Query\Mysql\Extract
|
||||
|
||||
numeric_functions:
|
||||
acos: DoctrineExtensions\Query\Mysql\Acos
|
||||
asin: DoctrineExtensions\Query\Mysql\Asin
|
||||
atan2: DoctrineExtensions\Query\Mysql\Atan2
|
||||
atan: DoctrineExtensions\Query\Mysql\Atan
|
||||
bit_count: DoctrineExtensions\Query\Mysql\BitCount
|
||||
bit_xor: DoctrineExtensions\Query\Mysql\BitXor
|
||||
ceil: DoctrineExtensions\Query\Mysql\Ceil
|
||||
cos: DoctrineExtensions\Query\Mysql\Cos
|
||||
cot: DoctrineExtensions\Query\Mysql\Cot
|
||||
degrees: DoctrineExtensions\Query\Mysql\Degrees
|
||||
exp: DoctrineExtensions\Query\Mysql\Exp
|
||||
floor: DoctrineExtensions\Query\Mysql\Floor
|
||||
json_contains: DoctrineExtensions\Query\Mysql\JsonContains
|
||||
json_depth: DoctrineExtensions\Query\Mysql\JsonDepth
|
||||
json_length: DoctrineExtensions\Query\Mysql\JsonLength
|
||||
log: DoctrineExtensions\Query\Mysql\Log
|
||||
log10: DoctrineExtensions\Query\Mysql\Log10
|
||||
log2: DoctrineExtensions\Query\Mysql\Log2
|
||||
pi: DoctrineExtensions\Query\Mysql\Pi
|
||||
power: DoctrineExtensions\Query\Mysql\Power
|
||||
quarter: DoctrineExtensions\Query\Mysql\Quarter
|
||||
radians: DoctrineExtensions\Query\Mysql\Radians
|
||||
rand: DoctrineExtensions\Query\Mysql\Rand
|
||||
round: DoctrineExtensions\Query\Mysql\Round
|
||||
stddev: DoctrineExtensions\Query\Mysql\StdDev
|
||||
sin: DoctrineExtensions\Query\Mysql\Sin
|
||||
std: DoctrineExtensions\Query\Mysql\Std
|
||||
tan: DoctrineExtensions\Query\Mysql\Tan
|
||||
variance: DoctrineExtensions\Query\Mysql\Variance
|
||||
|
||||
string_functions:
|
||||
aes_decrypt: DoctrineExtensions\Query\Mysql\AesDecrypt
|
||||
aes_encrypt: DoctrineExtensions\Query\Mysql\AesEncrypt
|
||||
any_value: DoctrineExtensions\Query\Mysql\AnyValue
|
||||
ascii: DoctrineExtensions\Query\Mysql\Ascii
|
||||
binary: DoctrineExtensions\Query\Mysql\Binary
|
||||
cast: DoctrineExtensions\Query\Mysql\Cast
|
||||
char_length: DoctrineExtensions\Query\Mysql\CharLength
|
||||
collate: DoctrineExtensions\Query\Mysql\Collate
|
||||
concat_ws: DoctrineExtensions\Query\Mysql\ConcatWs
|
||||
countif: DoctrineExtensions\Query\Mysql\CountIf
|
||||
crc32: DoctrineExtensions\Query\Mysql\Crc32
|
||||
degrees: DoctrineExtensions\Query\Mysql\Degrees
|
||||
field: DoctrineExtensions\Query\Mysql\Field
|
||||
find_in_set: DoctrineExtensions\Query\Mysql\FindInSet
|
||||
format: DoctrineExtensions\Query\Mysql\Format
|
||||
from_base64: DoctrineExtensions\Query\Mysql\FromBase64
|
||||
greatest: DoctrineExtensions\Query\Mysql\Greatest
|
||||
group_concat: DoctrineExtensions\Query\Mysql\GroupConcat
|
||||
hex: DoctrineExtensions\Query\Mysql\Hex
|
||||
ifelse: DoctrineExtensions\Query\Mysql\IfElse
|
||||
ifnull: DoctrineExtensions\Query\Mysql\IfNull
|
||||
inet_aton: DoctrineExtensions\Query\Mysql\InetAton
|
||||
inet_ntoa: DoctrineExtensions\Query\Mysql\InetNtoa
|
||||
inet6_aton: DoctrineExtensions\Query\Mysql\Inet6Aton
|
||||
inet6_ntoa: DoctrineExtensions\Query\Mysql\Inet6Ntoa
|
||||
instr: DoctrineExtensions\Query\Mysql\Instr
|
||||
is_ipv4: DoctrineExtensions\Query\Mysql\IsIpv4
|
||||
is_ipv4_compat: DoctrineExtensions\Query\Mysql\IsIpv4Compat
|
||||
is_ipv4_mapped: DoctrineExtensions\Query\Mysql\IsIpv4Mapped
|
||||
is_ipv6: DoctrineExtensions\Query\Mysql\IsIpv6
|
||||
lag: DoctrineExtensions\Query\Mysql\Lag
|
||||
lead: DoctrineExtensions\Query\Mysql\Lead
|
||||
least: DoctrineExtensions\Query\Mysql\Least
|
||||
lpad: DoctrineExtensions\Query\Mysql\Lpad
|
||||
match: DoctrineExtensions\Query\Mysql\MatchAgainst
|
||||
md5: DoctrineExtensions\Query\Mysql\Md5
|
||||
nullif: DoctrineExtensions\Query\Mysql\NullIf
|
||||
over: DoctrineExtensions\Query\Mysql\Over
|
||||
radians: DoctrineExtensions\Query\Mysql\Radians
|
||||
regexp: DoctrineExtensions\Query\Mysql\Regexp
|
||||
replace: DoctrineExtensions\Query\Mysql\Replace
|
||||
rpad: DoctrineExtensions\Query\Mysql\Rpad
|
||||
sha1: DoctrineExtensions\Query\Mysql\Sha1
|
||||
sha2: DoctrineExtensions\Query\Mysql\Sha2
|
||||
soundex: DoctrineExtensions\Query\Mysql\Soundex
|
||||
str_to_date: DoctrineExtensions\Query\Mysql\StrToDate
|
||||
substring_index: DoctrineExtensions\Query\Mysql\SubstringIndex
|
||||
unhex: DoctrineExtensions\Query\Mysql\Unhex
|
||||
uuid_short: DoctrineExtensions\Query\Mysql\UuidShort
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user